1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-24 00:35:26 +03:00

Merge branch 'development'

This commit is contained in:
Dominic Clifton 2015-10-31 11:18:51 +00:00
commit 716457b0ed
51 changed files with 2251 additions and 1492 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
.DS_store
nbproject/

View file

@ -211,7 +211,7 @@
"message": "Request Optional Permissions"
},
"defaultWelcomeText": {
"message": "Welcome to <strong>Cleanflight - Configurator</strong>, a utility designed to simplify updating, configuring and tuning of your flight controller.<br /><br />The application supports all hardware that can run cleanflight (<a href=\"http://seriouslypro.com/spracingf3\" target=\"_blank\">SPRacingF3</a>, <a href=\"http://www.immersionrc.com/fpv-products/vortex-racing-quad/\" target=\"_blank\">Vortex</a>, <a href=\"https://github.com/TauLabs/TauLabs/wiki/Sparky\" target=\"_blank\">Sparky</a>, <a href=\"https://www.openpilot.org/product/coptercontrol/\" target=\"_blank\">CC3D/EVO</a>, <a href=\"http://www.multiwiicopter.com/products/paris-air-hero-32-naze\" target=\"_blank\">Air Hero 32</a>, <a href=\"http://www.readytoflyquads.com/flight-controllers/flip-series\" target=\"_blank\">Flip32/+/Deluxe</a>, <a href=\"http://www.goodluckbuy.com/micro-quadcopter-flight-driver-controller-9dof-9-axis-altitude-sensor-stm32f103.html\" target=\"_blank\">CJMCU Microquad</a>, Chebuzz F3, <a href=\"http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF254044\" target=\"_blank\">STM32F3Discovery</a>, <a href=\"http://www.overskyrc.com/hermit-micro-fpv-brushless-quadcopter-145mm-98g-fully-assembled-p-621.html\" target=\"_blank\">Hermit</a>, <a href=\"http://rcexplorer.se/blog/2015/05/introducing-the-naze32-tricopter-frame/\" target=\"_blank\">Naze32 Tricopter Frame</a>, <a href=\"http://www.2dogrc.com/skyline-32-naze-32-bit-flight-controller-acro-version.html\" target=\"_blank\">Skyline32</a>, <a href=\"http://abusemark.com/store/index.php?main_page=index&cPath=1\" target=\"_blank\">Naze/32/Mini/Pro</a>/<a href=\"http://www.massiverc.com/PrestaShop/en/574-massive-acro-blackbox-flight-control-board.html\" target=\"_blank\">Blackbox</a> etc)<br /><br />The firmware source code can be downloaded from <a href=\"https://github.com/cleanflight/cleanflight\" title=\"www.github.com\" target=\"_blank\">here</a><br />The newest binary firmware image is available <a href=\"https://github.com/cleanflight/cleanflight/releases\" title=\"www.github.com\" target=\"_blank\">here</a><br /><br />Latest <strong>CP210x Drivers</strong> can be downloaded from <a href=\"http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx\" title=\"http://www.silabs.com/\" target=\"_blank\">here</a><br />"
"message": "Welcome to <strong>Cleanflight - Configurator</strong>, a utility designed to simplify updating, configuring and tuning of your flight controller.<br /><br />The application supports all hardware that can run cleanflight (<a href=\"http://seriouslypro.com/spracingf3\" target=\"_blank\">SPRacingF3</a>, <a href=\"http://www.immersionrc.com/fpv-products/vortex-racing-quad/\" target=\"_blank\">Vortex</a>, <a href=\"https://github.com/TauLabs/TauLabs/wiki/Sparky\" target=\"_blank\">Sparky</a>, <a href=\"http://www.readymaderc.com/store/index.php?main_page=product_info&cPath=76_156&products_id=4221\" target=\"_blank\">DoDo</a>, <a href=\"https://www.openpilot.org/product/coptercontrol/\" target=\"_blank\">CC3D/EVO</a>, <a href=\"http://www.multiwiicopter.com/products/paris-air-hero-32-naze\" target=\"_blank\">Air Hero 32</a>, <a href=\"http://www.readytoflyquads.com/flight-controllers/flip-series\" target=\"_blank\">Flip32/+/Deluxe</a>, <a href=\"http://multirotormania.com/129-dragonfly32\" target=\"_blank\">DragonFly32</a>, <a href=\"http://www.goodluckbuy.com/micro-quadcopter-flight-driver-controller-9dof-9-axis-altitude-sensor-stm32f103.html\" target=\"_blank\">CJMCU Microquad</a>, Chebuzz F3, <a href=\"http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF254044\" target=\"_blank\">STM32F3Discovery</a>, <a href=\"http://www.overskyrc.com/hermit-micro-fpv-brushless-quadcopter-145mm-98g-fully-assembled-p-621.html\" target=\"_blank\">Hermit</a>, <a href=\"http://rcexplorer.se/blog/2015/05/introducing-the-naze32-tricopter-frame/\" target=\"_blank\">Naze32 Tricopter Frame</a>, <a href=\"http://www.2dogrc.com/skyline-32-naze-32-bit-flight-controller-acro-version.html\" target=\"_blank\">Skyline32</a>, <a href=\"http://abusemark.com/store/index.php?main_page=index&cPath=1\" target=\"_blank\">Naze/32/Mini/Pro</a>/<a href=\"http://www.massiverc.com/PrestaShop/en/574-massive-acro-blackbox-flight-control-board.html\" target=\"_blank\">Blackbox</a> etc)<br /><br />The firmware source code can be downloaded from <a href=\"https://github.com/cleanflight/cleanflight\" title=\"www.github.com\" target=\"_blank\">here</a><br />The newest binary firmware image is available <a href=\"https://github.com/cleanflight/cleanflight/releases\" title=\"www.github.com\" target=\"_blank\">here</a>, development builds available <a href=\"http://cleanflight.memoryleaks.org/builds/\" target=\"_blank\">here</a><br /><br />Latest <strong>CP210x Drivers</strong> can be downloaded from <a href=\"http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx\" title=\"http://www.silabs.com/\" target=\"_blank\">here</a><br />"
},
"defaultContributingHead": {
"message": "Contributing"
@ -372,8 +372,8 @@
"initialSetupModel": {
"message": "Model: $1"
},
"initialSetupHeading": {
"message": "Heading: $1 deg"
"initialSetupAttitude": {
"message": "$1 deg"
},
"initialSetupAccelCalibStarted": {
"message": "Accelerometer calibration started"
@ -426,6 +426,10 @@
"configurationFeaturesHelp": {
"message": "<strong>Note:</strong> Not all combinations of features are valid. When the flight controller firmware detects invalid feature combinations conflicting features will be disabled.<br /><strong>Note:</strong> Configure serial ports <span style=\"color: red\">before</span> enabling the features that will use the ports."
},
"configurationSerialRXHelp": {
"message": "<strong>Note:</strong> Rememer to configure a Serial Port (via Ports tab) and choose a Serial Receiver Provider when using RX_SERIAL feature."
},
"configurationBoardAlignment": {
"message": "Board Alignment"
@ -499,6 +503,18 @@
"configurationBatteryMultiwiiCurrent": {
"message": "Enable support for legacy Multiwii MSP current output"
},
"configurationSystem": {
"message": "System configuration"
},
"configurationLoopTime": {
"message": "Flight Controller Loop Time"
},
"configurationCalculatedCyclesSec": {
"message": "Cycles/Sec (Hz)"
},
"configurationLoopTimeHelp": {
"message": "<strong>Note:</strong> Changing this may require PID re-tuning."
},
"configurationGPS": {
"message": "GPS"
},
@ -511,6 +527,10 @@
"configurationGPSubxSbas": {
"message": "Ground Assistance Type"
},
"configurationGPSHelp": {
"message": "<strong>Note:</strong> Remember to configure a Serial Port (via Ports tab) when using GPS feature."
},
"configurationSerialRX": {
"message": "Serial Receiver Provider"
},
@ -522,7 +542,10 @@
},
"portsHelp": {
"message": "Configure serial ports. <strong>Note:</strong> not all combinations are valid. When the flight controller firmware detects this the serial port configuration will be reset."
"message": "<strong>Note:</strong> not all combinations are valid. When the flight controller firmware detects this the serial port configuration will be reset."
},
"portsMSPHelp": {
"message": "<strong>Note:</strong> Do <span style=\"color: red\">NOT</span> disable MSP on the first serial port unless you know what you are doing. You may have to reflash and erase your configuration if you do."
},
"portsFirmwareUpgradeRequired": {
"message": "Firmware upgrade <span style=\"color: red\">required</span>. Serial port configurations of firmware &lt; 1.8.0 is not supported."
@ -613,6 +636,9 @@
"message": "EEPROM <span style=\"color: green\">saved</span>"
},
"receiverHelp": {
"message": "Please read receiver chapter of the documentation. Configure serial port (if required), receiver mode (serial/ppm/pwm), provider (for serial receivers), bind receiver, set channel map, configure channel endpoints/range on TX so that all channels go from ~1000 to ~2000. Set midpoint (default 1500), trim channels to 1500, configure stick deadband, verify behaviour when TX is off or out of range.<br /><span style=\"color: red\">IMPORTANT:</span> Before flying read failsafe chapter of documentation and configure failsafe."
},
"receiverThrottleMid": {
"message": "Throttle MID"
},
@ -646,6 +672,9 @@
"receiverButtonRefresh": {
"message": "Refresh"
},
"receiverButtonSticks": {
"message": "Control sticks"
},
"receiverDataRefreshed": {
"message": "RC Tuning data <strong>refreshed</strong>"
},
@ -791,15 +820,13 @@
},
"servosModel": {
"message": "Model:"
"servosFirmwareUpgradeRequired": {
"message": "Servos requires firmware &gt;= 1.10.0."
},
"servosChangeDirection": {
"message": "Change Direction in TX To Match"
},
"servosGyroAccelDirection": {
"message": "Gyroscope / Accelerometer Direction"
},
"servosName": {
"message": "Name"
},
@ -812,8 +839,14 @@
"servosMax": {
"message": "MAX"
},
"servosDirection": {
"message": "Direction"
"servosAngleAtMin": {
"message": "Angle at min"
},
"servosAngleAtMax": {
"message": "Angle at max"
},
"servosDirectionAndRate": {
"message": "Direction and rate"
},
"servosLiveMode": {
"message": "Enable Live mode:"
@ -821,9 +854,6 @@
"servosButtonSave": {
"message": "Save"
},
"servosModelNoSupport": {
"message": "This model doesn't support servos"
},
"servosNormal": {
"message": "Normal"
},
@ -1042,7 +1072,7 @@
"message": "Attempt to flash the board automatically (triggered by newly detected serial port)"
},
"firmwareFlasherFullChipErase": {
"message": "Full Chip Erase"
"message": "Full chip erase"
},
"firmwareFlasherFullChipEraseDescription": {
"message": "Wipes all configuration data currently stored on the board"
@ -1054,7 +1084,7 @@
"message": "Flash most recent (untested) development firmware"
},
"firmwareFlasherManualBaud": {
"message": "Manual Baud Rate"
"message": "Manual baud rate"
},
"firmwareFlasherManualBaudDescription": {
"message": "Manual selection of baud rate for boards that don't support the default speed or for flashing via bluetooth."
@ -1099,7 +1129,7 @@
"message": "Warning"
},
"firmwareFlasherWarningText": {
"message": "Please do not try to flash <strong>non-cleanflight</strong> hardware with this firmware flasher (it wont work).<br />Do not <strong>disconnect</strong> the board or <strong>turn off</strong> your computer while flashing.<br /><br />Note: <strong>STM32</strong> bootloader is stored in <strong>ROM</strong>, it cannot be bricked.<br />Note: <strong>Auto-Connect</strong> is always disabled while you are inside firmware flasher.<br />Note: This can wipe your configuration including but not limited to PIDs, Auxiliary, and FEATURES. Make sure you have a backup.<br />"
"message": "Please do <span style=\"color: red\">not</span> try to flash <strong>non-cleanflight</strong> hardware with this firmware flasher.<br />Do <span style=\"color: red\">not</span> <strong>disconnect</strong> the board or <strong>turn off</strong> your computer while flashing.<br /><br /><span style=\"color: green\">Note: </span>STM32 bootloader is stored in ROM, it cannot be bricked.<br />Note: <strong>Auto-Connect</strong> is always disabled while you are inside firmware flasher.<br /><span style=\"color: red\">Note: </span>Make sure you have a backup; some upgrades/downgrades will wipe your configuration.<br /><span style=\"color: green\">Note:</span> If you have problems flashing try disconnecting all cables from your FC.<br /><br /><span style=\"color: green\">Note: </span>If you have lost comminication with your board then power off the board, jumper the bootloader pins, power on, enable 'No reboot sequence', enable 'Full chip erase', re-flash, then power off, remove bootloader jumper, power on and connect (For all firmware except OPBL firmware)."
},
"firmwareFlasherButtonLeave": {
"message": "Leave Firmware Flasher"
@ -1125,5 +1155,41 @@
},
"ledStripEepromSaved": {
"message": "EEPROM <span style=\"color: green\">saved</span>"
},
"controlAxisRoll": {
"message": "Roll"
},
"controlAxisPitch": {
"message": "Pitch"
},
"controlAxisYaw": {
"message": "Yaw"
},
"controlAxisThrottle": {
"message": "Throttle"
},
"controlAxisAux1": {
"message": "AUX 1"
},
"controlAxisAux2": {
"message": "AUX 2"
},
"controlAxisAux3": {
"message": "AUX 3"
},
"controlAxisAux4": {
"message": "AUX 4"
},
"controlAxisAux5": {
"message": "AUX 5"
},
"controlAxisAux6": {
"message": "AUX 6"
},
"controlAxisAux7": {
"message": "AUX 7"
},
"controlAxisAux8": {
"message": "AUX 8"
}
}
}

View file

@ -1,3 +1,12 @@
<span>2015.10.31 - 0.66.0 - cleanflight</span>
<p>
- Update servo configuration (requires firmware &gt;= 1.10.0).<br />
- Add some notes and help messages for common issues.<br />
- Update sponsors panel and add links to new boards.<br />
- Upgrade some JS libraries to latest versions (Three, JQuery/UI, D3)<br />
- Allow stick input from UI (when using RX_MSP)<br />
- Align servo numbering with firmware and documentation<br />
</p>
<span>2015.05.23 - 0.65.0 - cleanflight</span>
<p>
- Support flashing of the SPRacingF3.<br />
@ -23,10 +32,10 @@
</p>
<span>2015.03.29 - 0.63.0 - cleanflight</span>
<p>
- Configuration tab supports auto_disarm_delay and disarm_kill_switch - Requires 1.8.0 firmware.<br />
- PID Tuning tab allows TPA Breakpoint changes - Requires 1.8.0 firmware.<br />
- Corrected Artificial Horizon Pitch/Roll views.<br />
- Changed logging time stamp to include date stamp.<br />
- Configuration tab supports auto_disarm_delay and disarm_kill_switch (requires firmware &gt;= 1.8.0).<br />
- PID Tuning tab allows TPA Breakpoint changes (requires firmware &gt;= 1.8.0).<br />
- Correct Artificial Horizon Pitch/Roll views.<br />
- Change logging time stamp to include date stamp.<br />
- Support new firmware 1.8 serial port configuration.<br />
- Move documentation and help to new tab.<br />
- Add contributing section to welcome tab.<br />
@ -38,7 +47,7 @@
<span>2015.02.26 - 0.62.0 - cleanflight</span>
<p>
- Add flight indicators to setup screen tab.<br />
- Add dataflash tab. Requires 1.8.0 firmware.<br />
- Add dataflash tab (requires firmware &gt;= 1.8.0)..<br />
- Add Cleanflight logos.<br />
- Fix loading online flash files - github moved them to amazon aws.<br />
- Fix to fallback 3D model.<br />
@ -46,7 +55,7 @@
</p>
<span>2015.02.03 - 0.61.0 - cleanflight</span>
<p>
- Support changing PID controller - there new PID controllers in 1.7.0 firmware.<br />
- Support changing PID controller - three new PID controllers in 1.7.0 firmware.<br />
- Support for LED thrust ring.<br />
- Support for LED colors.<br />
- Support for displaying sonar sensor reading on the sensors tab.<br />
@ -54,21 +63,21 @@
- New Logo. (Tom McCullough)<br />
- New 3D models (AkFreak).<br />
- Update presentation of LEDs that have multiple functions.<br />
- Added Documentation and Support panels to welcome tab.<br />
- Add Documentation and Support panels to welcome tab.<br />
- Add support for backup and restore of LED strip configuration.<br />
- Fix for disappearing tabs in chrome 41 beta.<br />
- Various other minor improvements.<br />
</p>
<span>2015.01.08 - 0.60.0 - cleanflight</span>
<p>
- Add LED strip tab for LED configuration - requires v1.6.0 firmware to save.<br />
- Add LED strip tab for LED configuration (requires firmware &gt;= 1.6.0)..<br />
- Replace motor order images (stronnag)<br />
</p>
<span>2015.01.08 - 0.59.1 - cleanflight</span>
<p>
- Add support for Blackbox flight recorder feature (requires v1.5.0 firmware).<br />
- Add support for Blackbox flight recorder feature (requires firmware &gt;= 1.5.0).<br />
- Update RSSI channel section to allow any channel.<br />
- Implemented configuration migration to aid with backwards compatibility.<br />
- Implement configuration migration to aid with backwards compatibility.<br />
- Allow CLI access when connecting firmware with an out-of-date API.<br />
- Support 'release candidate' and 'stable' releases.
</p>
@ -88,7 +97,7 @@
<span>2014.12.06 - 0.57.1 - cleanflight</span>
<p>
- Latest firmware <strong>required</strong>.<br />
- Merged latest baseflight configurator changes.<br />
- Merge latest baseflight configurator changes.<br />
- Added support for choosing and downloading firmware via the github relases API.<br />
- Added Armattan Quads as a sponsor.<br />
- Various UI tweaks.<br />
@ -96,17 +105,17 @@
<span>2014.12.06 - 0.57</span>
<p>
- Firmware flasher now contains firmware builds picker<br />
- Implemented new and experimental review mechanism<br />
- Implement new and experimental review mechanism<br />
- Support for new A-tail Quad craft type<br />
- Firmware flasher flashing speed optimizations<br />
- Added specific 3D models for atail/vtail quad (norem)<br />
- Add specific 3D models for atail/vtail quad (norem)<br />
- Major bugfix for stuck UI after delayed port open procedure<br />
- Bugfix for GPS distance to home not displaying<br />
- Bugfix for backup/restore misbehaving (mostly on OSX)<br />
</p>
<span>2014.11.04 - 0.56</span>
<p>
- Added hex plus, hex X, tri, y4, y6 3D models (jef79m)<br />
- Add hex plus, hex X, tri, y4, y6 3D models (jef79m)<br />
- Bugfix for battery voltage saving sequence<br />
- Bugfix for fetching development firmware<br />
</p>
@ -116,9 +125,9 @@
- Minimum accepted firmware version set to 2.31<br />
- Support for flashing development firmware<br />
- Setup tab performance improvements, faster 3D model<br />
- Strongly improved amount of data stored in backups<br />
- Improved flash on connect in firmware flasher<br />
- Rearranged texts and options in firmware flasher<br />
- Strongly improve amount of data stored in backups<br />
- Improve flash on connect in firmware flasher<br />
- Rearrange texts and options in firmware flasher<br />
- Bugfix for corrupted minimum window size on Windows 7<br />
- Bugfix for incorrect date &amp; time in firmware flasher<br />
- Bugfix for port handler firing before port being initialized<br />

View file

@ -30,12 +30,21 @@ function configuration_backup(callback) {
MSP_codes.MSP_PID,
MSP_codes.MSP_RC_TUNING,
MSP_codes.MSP_ACC_TRIM,
MSP_codes.MSP_SERVO_CONF,
MSP_codes.MSP_CHANNEL_FORWARDING,
MSP_codes.MSP_SERVO_CONFIGURATIONS,
MSP_codes.MSP_MODE_RANGES,
MSP_codes.MSP_ADJUSTMENT_RANGES
];
function update_profile_specific_data_list() {
if (semver.lt(CONFIG.apiVersion, "1.12.0")) {
profileSpecificData.push(MSP_codes.MSP_CHANNEL_FORWARDING);
} else {
profileSpecificData.push(MSP_codes.MSP_SERVO_MIX_RULES);
}
}
update_profile_specific_data_list();
function fetch_specific_data() {
var fetchingProfile = 0,
codeKey = 0;
@ -54,6 +63,7 @@ function configuration_backup(callback) {
'RC': jQuery.extend(true, {}, RC_tuning),
'AccTrim': jQuery.extend(true, [], CONFIG.accelerometerTrims),
'ServoConfig': jQuery.extend(true, [], SERVO_CONFIG),
'ServoRules': jQuery.extend(true, [], SERVO_RULES),
'ModeRanges': jQuery.extend(true, [], MODE_RANGES),
'AdjustmentRanges': jQuery.extend(true, [], ADJUSTMENT_RANGES)
});
@ -428,6 +438,38 @@ function configuration_restore(callback) {
}
}
if (semver.lt(migratedVersion, '0.66.0')) {
// api 1.12 updated servo configuration protocol and added servo mixer rules
for (var profileIndex = 0; i < configuration.profiles.length; i++) {
if (semver.eq(configuration.apiVersion, '1.10.0')) {
// drop two unused servo configurations
while (configuration.profiles[profileIndex].ServoConfig.length > 8) {
configuration.profiles[profileIndex].ServoConfig.pop();
}
}
for (var i = 0; i < configuration.profiles[profileIndex].ServoConfig.length; i++) {
var servoConfig = profiles[profileIndex].ServoConfig;
servoConfig[i].angleAtMin = 90;
servoConfig[i].angleAtMax = 90;
servoConfig[i].reversedInputSources = 0;
// set the rate to 0 if an invalid value is detected.
if (servoConfig[i].rate < -100 || servoConfig[i].rate > 100) {
servoConfig[i].rate = 0;
}
}
configuration.profiles[profileIndex].ServoRules = [];
}
migratedVersion = '0.66.0';
appliedMigrationsCount++;
}
if (appliedMigrationsCount > 0) {
GUI.log(chrome.i18n.getMessage('configMigrationSuccessful', [appliedMigrationsCount]));
}
@ -444,9 +486,7 @@ function configuration_restore(callback) {
MSP_codes.MSP_SET_PID_CONTROLLER,
MSP_codes.MSP_SET_PID,
MSP_codes.MSP_SET_RC_TUNING,
MSP_codes.MSP_SET_ACC_TRIM,
MSP_codes.MSP_SET_SERVO_CONF,
MSP_codes.MSP_SET_CHANNEL_FORWARDING
MSP_codes.MSP_SET_ACC_TRIM
];
MSP.send_message(MSP_codes.MSP_STATUS, false, false, function () {
@ -472,6 +512,7 @@ function configuration_restore(callback) {
RC_tuning = configuration.profiles[profile].RC;
CONFIG.accelerometerTrims = configuration.profiles[profile].AccTrim;
SERVO_CONFIG = configuration.profiles[profile].ServoConfig;
SERVO_RULES = configuration.profiles[profile].ServoRules;
MODE_RANGES = configuration.profiles[profile].ModeRanges;
ADJUSTMENT_RANGES = configuration.profiles[profile].AdjustmentRanges;
}
@ -501,6 +542,14 @@ function configuration_restore(callback) {
});
}
function upload_servo_mix_rules() {
MSP.sendServoMixRules(upload_servo_configuration);
}
function upload_servo_configuration() {
MSP.sendServoConfigurations(upload_mode_ranges);
}
function upload_mode_ranges() {
MSP.sendModeRanges(upload_adjustment_ranges);
}
@ -510,7 +559,7 @@ function configuration_restore(callback) {
}
// start uploading
load_objects(0);
upload_mode_ranges();
upload_servo_configuration();
}
function upload_unique_data() {

View file

@ -1,7 +1,7 @@
'use strict';
var CONFIGURATOR = {
'releaseDate': 1432389468227, // new Date().getTime() - Sat May 23 2015 14:57:54 GMT+0100 (BST)
'releaseDate': 1446278768375, // new Date().getTime() - Fri Oct 02 2015 20:50:49 GMT+0100 (GMT Daylight Time)
// all versions are specified and compared using semantic versioning http://semver.org/
'apiVersionAccepted': '1.2.0',
@ -86,6 +86,7 @@ var MODE_RANGES = [];
var ADJUSTMENT_RANGES = [];
var SERVO_CONFIG = [];
var SERVO_RULES = [];
var SERIAL_CONFIG = {
ports: [],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
js/libraries/jquery-2.1.4.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

13
js/libraries/jquery-ui-1.11.4.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -16,13 +16,13 @@ THREE.SpriteCanvasMaterial = function ( parameters ) {
};
THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype );
THREE.SpriteCanvasMaterial.prototype.constructor = THREE.SpriteCanvasMaterial;
THREE.SpriteCanvasMaterial.prototype.clone = function () {
var material = new THREE.SpriteCanvasMaterial();
THREE.Material.prototype.clone.call( this, material );
material.copy( this );
material.color.copy( this.color );
material.program = this.program;
@ -58,12 +58,14 @@ THREE.CanvasRenderer = function ( parameters ) {
_viewportWidth = _canvasWidth,
_viewportHeight = _canvasHeight,
pixelRatio = 1,
_context = _canvas.getContext( '2d', {
alpha: parameters.alpha === true
} ),
_clearColor = new THREE.Color( 0x000000 ),
_clearAlpha = 0,
_clearAlpha = parameters.alpha === true ? 0 : 1,
_contextGlobalAlpha = 1,
_contextGlobalCompositeOperation = 0,
@ -122,12 +124,6 @@ THREE.CanvasRenderer = function ( parameters ) {
this.domElement = _canvas;
this.devicePixelRatio = parameters.devicePixelRatio !== undefined
? parameters.devicePixelRatio
: self.devicePixelRatio !== undefined
? self.devicePixelRatio
: 1;
this.autoClear = true;
this.sortObjects = true;
this.sortElements = true;
@ -141,17 +137,43 @@ THREE.CanvasRenderer = function ( parameters ) {
}
}
};
// WebGLRenderer compatibility
this.supportsVertexTextures = function () {};
this.setFaceCulling = function () {};
// API
this.getContext = function () {
return _context;
};
this.getContextAttributes = function () {
return _context.getContextAttributes();
};
this.getPixelRatio = function () {
return pixelRatio;
};
this.setPixelRatio = function ( value ) {
if ( value !== undefined ) pixelRatio = value;
};
this.setSize = function ( width, height, updateStyle ) {
_canvasWidth = width * this.devicePixelRatio;
_canvasHeight = height * this.devicePixelRatio;
_canvasWidth = width * pixelRatio;
_canvasHeight = height * pixelRatio;
_canvas.width = _canvasWidth;
_canvas.height = _canvasHeight;
@ -166,7 +188,7 @@ THREE.CanvasRenderer = function ( parameters ) {
}
_clipBox.min.set( -_canvasWidthHalf, -_canvasHeightHalf ),
_clipBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf );
_clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf );
_clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf );
@ -186,11 +208,11 @@ THREE.CanvasRenderer = function ( parameters ) {
this.setViewport = function ( x, y, width, height ) {
_viewportX = x * this.devicePixelRatio;
_viewportY = y * this.devicePixelRatio;
_viewportX = x * pixelRatio;
_viewportY = y * pixelRatio;
_viewportWidth = width * this.devicePixelRatio;
_viewportHeight = height * this.devicePixelRatio;
_viewportWidth = width * pixelRatio;
_viewportHeight = height * pixelRatio;
};
@ -240,17 +262,17 @@ THREE.CanvasRenderer = function ( parameters ) {
_clearBox.expandByScalar( 2 );
_clearBox.min.x = _clearBox.min.x + _canvasWidthHalf;
_clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf;
_clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf; // higher y value !
_clearBox.max.x = _clearBox.max.x + _canvasWidthHalf;
_clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf;
_clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf; // lower y value !
if ( _clearAlpha < 1 ) {
_context.clearRect(
_clearBox.min.x | 0,
_clearBox.min.y | 0,
_clearBox.max.y | 0,
( _clearBox.max.x - _clearBox.min.x ) | 0,
( _clearBox.max.y - _clearBox.min.y ) | 0
( _clearBox.min.y - _clearBox.max.y ) | 0
);
}
@ -264,9 +286,9 @@ THREE.CanvasRenderer = function ( parameters ) {
_context.fillRect(
_clearBox.min.x | 0,
_clearBox.min.y | 0,
_clearBox.max.y | 0,
( _clearBox.max.x - _clearBox.min.x ) | 0,
( _clearBox.max.y - _clearBox.min.y ) | 0
( _clearBox.min.y - _clearBox.max.y ) | 0
);
}
@ -496,53 +518,42 @@ THREE.CanvasRenderer = function ( parameters ) {
var texture = material.map;
if ( texture !== null && texture.image !== undefined ) {
if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) {
if ( texture.image.width > 0 ) {
textureToPattern( texture );
}
texture.addEventListener( 'update', onTextureUpdate );
}
if ( texture !== null ) {
var pattern = _patterns[ texture.id ];
if ( pattern !== undefined ) {
if ( pattern === undefined || pattern.version !== texture.version ) {
setFillStyle( pattern );
} else {
setFillStyle( 'rgba( 0, 0, 0, 1 )' );
pattern = textureToPattern( texture );
_patterns[ texture.id ] = pattern;
}
//
if ( pattern.canvas !== undefined ) {
var bitmap = texture.image;
setFillStyle( pattern.canvas );
var ox = bitmap.width * texture.offset.x;
var oy = bitmap.height * texture.offset.y;
var bitmap = texture.image;
var sx = bitmap.width * texture.repeat.x;
var sy = bitmap.height * texture.repeat.y;
var ox = bitmap.width * texture.offset.x;
var oy = bitmap.height * texture.offset.y;
var cx = scaleX / sx;
var cy = scaleY / sy;
var sx = bitmap.width * texture.repeat.x;
var sy = bitmap.height * texture.repeat.y;
_context.save();
_context.translate( v1.x, v1.y );
if ( material.rotation !== 0 ) _context.rotate( material.rotation );
_context.translate( - scaleX / 2, - scaleY / 2 );
_context.scale( cx, cy );
_context.translate( - ox, - oy );
_context.fillRect( ox, oy, sx, sy );
_context.restore();
var cx = scaleX / sx;
var cy = scaleY / sy;
_context.save();
_context.translate( v1.x, v1.y );
if ( material.rotation !== 0 ) _context.rotate( material.rotation );
_context.translate( - scaleX / 2, - scaleY / 2 );
_context.scale( cx, cy );
_context.translate( - ox, - oy );
_context.fillRect( ox, oy, sx, sy );
_context.restore();
}
} else {
@ -704,7 +715,9 @@ THREE.CanvasRenderer = function ( parameters ) {
if ( material.map !== null ) {
if ( material.map.mapping instanceof THREE.UVMapping ) {
var mapping = material.map.mapping;
if ( mapping === THREE.UVMapping ) {
_uvs = element.uvs;
patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map );
@ -713,7 +726,7 @@ THREE.CanvasRenderer = function ( parameters ) {
} else if ( material.envMap !== null ) {
if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) {
if ( material.envMap.mapping === THREE.SphericalReflectionMapping ) {
_normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix );
_uv1x = 0.5 * _normal.x + 0.5;
@ -729,25 +742,8 @@ THREE.CanvasRenderer = function ( parameters ) {
patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap );
} else if ( material.envMap.mapping instanceof THREE.SphericalRefractionMapping ) {
_normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix );
_uv1x = - 0.5 * _normal.x + 0.5;
_uv1y = - 0.5 * _normal.y + 0.5;
_normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix );
_uv2x = - 0.5 * _normal.x + 0.5;
_uv2y = - 0.5 * _normal.y + 0.5;
_normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix );
_uv3x = - 0.5 * _normal.x + 0.5;
_uv3y = - 0.5 * _normal.y + 0.5;
patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap );
}
} else {
_color.copy( material.color );
@ -826,18 +822,18 @@ THREE.CanvasRenderer = function ( parameters ) {
}
function onTextureUpdate ( event ) {
textureToPattern( event.target );
}
function textureToPattern( texture ) {
if ( texture instanceof THREE.CompressedTexture ) return;
if ( texture.version === 0 ||
texture instanceof THREE.CompressedTexture ||
texture instanceof THREE.DataTexture ) {
var repeatX = texture.wrapS === THREE.RepeatWrapping;
var repeatY = texture.wrapT === THREE.RepeatWrapping;
return {
canvas: undefined,
version: texture.version
}
}
var image = texture.image;
@ -849,45 +845,51 @@ THREE.CanvasRenderer = function ( parameters ) {
context.setTransform( 1, 0, 0, - 1, 0, image.height );
context.drawImage( image, 0, 0 );
_patterns[ texture.id ] = _context.createPattern(
canvas, repeatX === true && repeatY === true
? 'repeat'
: repeatX === true && repeatY === false
? 'repeat-x'
: repeatX === false && repeatY === true
? 'repeat-y'
: 'no-repeat'
);
var repeatX = texture.wrapS === THREE.RepeatWrapping;
var repeatY = texture.wrapT === THREE.RepeatWrapping;
var repeat = 'no-repeat';
if ( repeatX === true && repeatY === true ) {
repeat = 'repeat';
} else if ( repeatX === true ) {
repeat = 'repeat-x';
} else if ( repeatY === true ) {
repeat = 'repeat-y';
}
return {
canvas: _context.createPattern( canvas, repeat ),
version: texture.version
}
}
function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) {
if ( texture instanceof THREE.DataTexture ) return;
var pattern = _patterns[ texture.id ];
if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) {
if ( pattern === undefined || pattern.version !== texture.version ) {
if ( texture.image !== undefined && texture.image.width > 0 ) {
textureToPattern( texture );
}
texture.addEventListener( 'update', onTextureUpdate );
pattern = textureToPattern( texture );
_patterns[ texture.id ] = pattern;
}
var pattern = _patterns[ texture.id ];
if ( pattern.canvas !== undefined ) {
if ( pattern !== undefined ) {
setFillStyle( pattern );
setFillStyle( pattern.canvas );
} else {
setFillStyle( 'rgba(0,0,0,1)' );
setFillStyle( 'rgba( 0, 0, 0, 1)' );
_context.fill();
return;
}

View file

@ -10,6 +10,7 @@ THREE.RenderableObject = function () {
this.object = null;
this.z = 0;
this.renderOrder = 0;
};
@ -33,6 +34,7 @@ THREE.RenderableFace = function () {
this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ];
this.z = 0;
this.renderOrder = 0;
};
@ -68,6 +70,7 @@ THREE.RenderableLine = function () {
this.material = null;
this.z = 0;
this.renderOrder = 0;
};
@ -87,6 +90,7 @@ THREE.RenderableSprite = function () {
this.scale = new THREE.Vector2();
this.material = null;
this.renderOrder = 0;
};
@ -102,10 +106,6 @@ THREE.Projector = function () {
_renderData = { objects: [], lights: [], elements: [] },
_vA = new THREE.Vector3(),
_vB = new THREE.Vector3(),
_vC = new THREE.Vector3(),
_vector3 = new THREE.Vector3(),
_vector4 = new THREE.Vector4(),
@ -126,7 +126,7 @@ THREE.Projector = function () {
_clippedVertex1PositionScreen = new THREE.Vector4(),
_clippedVertex2PositionScreen = new THREE.Vector4();
//
this.projectVector = function ( vector, camera ) {
@ -145,10 +145,10 @@ THREE.Projector = function () {
this.pickingRay = function ( vector, camera ) {
console.error( 'THREE.Projector: .pickingRay() has been removed.' );
console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );
};
//
var RenderList = function () {
@ -247,6 +247,7 @@ THREE.Projector = function () {
_line.v1.copy( v1 );
_line.v2.copy( v2 );
_line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2;
_line.renderOrder = object.renderOrder;
_line.material = object.material;
@ -271,19 +272,21 @@ THREE.Projector = function () {
_face.v2.copy( v2 );
_face.v3.copy( v3 );
_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
_face.renderOrder = object.renderOrder;
// use first vertex normal as face normal
_face.normalModel.fromArray( normals, a * 3 );
_face.normalModel.applyMatrix3( normalMatrix ).normalize();
for ( var i = 0; i < 3; i ++ ) {
var offset = arguments[ i ] * 3;
var normal = _face.vertexNormalsModel[ i ];
normal.set( normals[ offset ], normals[ offset + 1 ], normals[ offset + 2 ] );
normal.fromArray( normals, arguments[ i ] * 3 );
normal.applyMatrix3( normalMatrix ).normalize();
var offset2 = arguments[ i ] * 2;
var uv = _face.uvs[ i ];
uv.set( uvs[ offset2 ], uvs[ offset2 + 1 ] );
uv.fromArray( uvs, arguments[ i ] * 2 );
}
@ -322,7 +325,7 @@ THREE.Projector = function () {
_renderData.elements.length = 0;
if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
if ( camera.parent === undefined ) camera.updateMatrixWorld();
if ( camera.parent === null ) camera.updateMatrixWorld();
_viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) );
_viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix );
@ -344,7 +347,9 @@ THREE.Projector = function () {
} else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) {
if ( object.material.visible === false ) return;
var material = object.material;
if ( material.visible === false ) return;
if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) {
@ -352,17 +357,10 @@ THREE.Projector = function () {
_object.id = object.id;
_object.object = object;
if ( object.renderDepth !== null ) {
_object.z = object.renderDepth;
} else {
_vector3.setFromMatrixPosition( object.matrixWorld );
_vector3.applyProjection( _viewProjectionMatrix );
_object.z = _vector3.z;
}
_vector3.setFromMatrixPosition( object.matrixWorld );
_vector3.applyProjection( _viewProjectionMatrix );
_object.z = _vector3.z;
_object.renderOrder = object.renderOrder;
_renderData.objects.push( _object );
@ -396,7 +394,7 @@ THREE.Projector = function () {
if ( geometry instanceof THREE.BufferGeometry ) {
var attributes = geometry.attributes;
var offsets = geometry.offsets;
var groups = geometry.groups;
if ( attributes.position === undefined ) continue;
@ -432,20 +430,19 @@ THREE.Projector = function () {
}
if ( attributes.index !== undefined ) {
if ( geometry.index !== null ) {
var indices = attributes.index.array;
var indices = geometry.index.array;
if ( offsets.length > 0 ) {
if ( groups.length > 0 ) {
for ( var o = 0; o < offsets.length; o ++ ) {
for ( var o = 0; o < groups.length; o ++ ) {
var offset = offsets[ o ];
var index = offset.index;
var group = groups[ o ];
for ( var i = offset.start, l = offset.start + offset.count; i < l; i += 3 ) {
for ( var i = group.start, l = group.start + group.count; i < l; i += 3 ) {
renderList.pushTriangle( indices[ i ] + index, indices[ i + 1 ] + index, indices[ i + 2 ] + index );
renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );
}
@ -479,13 +476,40 @@ THREE.Projector = function () {
_normalMatrix.getNormalMatrix( _modelMatrix );
var isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial;
var material = object.material;
var isFaceMaterial = material instanceof THREE.MeshFaceMaterial;
var objectMaterials = isFaceMaterial === true ? object.material : null;
for ( var v = 0, vl = vertices.length; v < vl; v ++ ) {
var vertex = vertices[ v ];
renderList.pushVertex( vertex.x, vertex.y, vertex.z );
_vector3.copy( vertex );
if ( material.morphTargets === true ) {
var morphTargets = geometry.morphTargets;
var morphInfluences = object.morphTargetInfluences;
for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {
var influence = morphInfluences[ t ];
if ( influence === 0 ) continue;
var target = morphTargets[ t ];
var targetVertex = target.vertices[ v ];
_vector3.x += ( targetVertex.x - vertex.x ) * influence;
_vector3.y += ( targetVertex.y - vertex.y ) * influence;
_vector3.z += ( targetVertex.z - vertex.z ) * influence;
}
}
renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z );
}
@ -493,7 +517,7 @@ THREE.Projector = function () {
var face = faces[ f ];
var material = isFaceMaterial === true
material = isFaceMaterial === true
? objectMaterials.materials[ face.materialIndex ]
: object.material;
@ -505,58 +529,15 @@ THREE.Projector = function () {
var v2 = _vertexPool[ face.b ];
var v3 = _vertexPool[ face.c ];
if ( material.morphTargets === true ) {
var morphTargets = geometry.morphTargets;
var morphInfluences = object.morphTargetInfluences;
var v1p = v1.position;
var v2p = v2.position;
var v3p = v3.position;
_vA.set( 0, 0, 0 );
_vB.set( 0, 0, 0 );
_vC.set( 0, 0, 0 );
for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {
var influence = morphInfluences[ t ];
if ( influence === 0 ) continue;
var targets = morphTargets[ t ].vertices;
_vA.x += ( targets[ face.a ].x - v1p.x ) * influence;
_vA.y += ( targets[ face.a ].y - v1p.y ) * influence;
_vA.z += ( targets[ face.a ].z - v1p.z ) * influence;
_vB.x += ( targets[ face.b ].x - v2p.x ) * influence;
_vB.y += ( targets[ face.b ].y - v2p.y ) * influence;
_vB.z += ( targets[ face.b ].z - v2p.z ) * influence;
_vC.x += ( targets[ face.c ].x - v3p.x ) * influence;
_vC.y += ( targets[ face.c ].y - v3p.y ) * influence;
_vC.z += ( targets[ face.c ].z - v3p.z ) * influence;
}
v1.position.add( _vA );
v2.position.add( _vB );
v3.position.add( _vC );
renderList.projectVertex( v1 );
renderList.projectVertex( v2 );
renderList.projectVertex( v3 );
}
if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue;
var visible = renderList.checkBackfaceCulling( v1, v2, v3 );
if ( side !== THREE.DoubleSide ) {
if ( side === THREE.FrontSide && visible === false ) continue;
if ( side === THREE.BackSide && visible === true ) continue;
}
_face = getNextFaceInPool();
@ -611,6 +592,7 @@ THREE.Projector = function () {
_face.material = material;
_face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3;
_face.renderOrder = object.renderOrder;
_renderData.elements.push( _face );
@ -634,9 +616,9 @@ THREE.Projector = function () {
}
if ( attributes.index !== undefined ) {
if ( geometry.index !== null ) {
var indices = attributes.index.array;
var indices = geometry.index.array;
for ( var i = 0, l = indices.length; i < l; i += 2 ) {
@ -646,7 +628,7 @@ THREE.Projector = function () {
} else {
var step = object.mode === THREE.LinePieces ? 2 : 1;
var step = object instanceof THREE.LineSegments ? 2 : 1;
for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) {
@ -669,8 +651,7 @@ THREE.Projector = function () {
v1 = getNextVertexInPool();
v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix );
// Handle LineStrip and LinePieces
var step = object.mode === THREE.LinePieces ? 2 : 1;
var step = object instanceof THREE.LineSegments ? 2 : 1;
for ( var v = 1, vl = vertices.length; v < vl; v ++ ) {
@ -697,6 +678,7 @@ THREE.Projector = function () {
_line.v2.positionScreen.copy( _clippedVertex2PositionScreen );
_line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z );
_line.renderOrder = object.renderOrder;
_line.material = object.material;
@ -731,6 +713,7 @@ THREE.Projector = function () {
_sprite.x = _vector4.x * invW;
_sprite.y = _vector4.y * invW;
_sprite.z = _vector4.z;
_sprite.renderOrder = object.renderOrder;
_sprite.object = object;
_sprite.rotation = object.rotation;
@ -816,7 +799,7 @@ THREE.Projector = function () {
var line = new THREE.RenderableLine();
_linePool.push( line );
_linePoolLength ++;
_lineCount ++
_lineCount ++;
return line;
}
@ -832,7 +815,7 @@ THREE.Projector = function () {
var sprite = new THREE.RenderableSprite();
_spritePool.push( sprite );
_spritePoolLength ++;
_spriteCount ++
_spriteCount ++;
return sprite;
}
@ -845,7 +828,11 @@ THREE.Projector = function () {
function painterSort( a, b ) {
if ( a.z !== b.z ) {
if ( a.renderOrder !== b.renderOrder ) {
return a.renderOrder - b.renderOrder;
} else if ( a.z !== b.z ) {
return b.z - a.z;

File diff suppressed because one or more lines are too long

View file

@ -2,27 +2,30 @@
// generate mixer
var mixerList = [
{name: 'Tricopter', model: 'tricopter', image: 'tri'},
{name: 'Quad +', model: 'quad_x', image: 'quad_p'},
{name: 'Quad X', model: 'quad_x', image: 'quad_x'},
{name: 'Bicopter', model: 'custom', image: 'bicopter'},
{name: 'Gimbal', model: 'custom', image: 'custom'},
{name: 'Y6', model: 'y6', image: 'y6'},
{name: 'Hex +', model: 'hex_plus', image: 'hex_p'},
{name: 'Flying Wing', model: 'custom', image: 'flying_wing'},
{name: 'Y4', model: 'y4', image: 'y4'},
{name: 'Hex X', model: 'hex_x', image: 'hex_x'},
{name: 'Octo X8', model: 'custom', image: 'octo_x8'},
{name: 'Octo Flat +', model: 'custom', image: 'octo_flat_p'},
{name: 'Octo Flat X', model: 'custom', image: 'octo_flat_x'},
{name: 'Airplane', model: 'custom', image: 'airplane'},
{name: 'Heli 120', model: 'custom', image: 'custom'},
{name: 'Heli 90', model: 'custom', image: 'custom'},
{name: 'V-tail Quad', model: 'quad_vtail', image: 'vtail_quad'},
{name: 'Hex H', model: 'custom', image: 'custom'},
{name: 'PPM to SERVO', model: 'custom', image: 'custom'},
{name: 'Dualcopter', model: 'custom', image: 'custom'},
{name: 'Singlecopter', model: 'custom', image: 'custom'},
{name: 'A-tail Quad', model: 'quad_atail', image: 'atail_quad'},
{name: 'Custom', model: 'custom', image: 'custom'}
{name: 'Tricopter', model: 'tricopter', image: 'tri'},
{name: 'Quad +', model: 'quad_x', image: 'quad_p'},
{name: 'Quad X', model: 'quad_x', image: 'quad_x'},
{name: 'Bicopter', model: 'custom', image: 'bicopter'},
{name: 'Gimbal', model: 'custom', image: 'custom'},
{name: 'Y6', model: 'y6', image: 'y6'},
{name: 'Hex +', model: 'hex_plus', image: 'hex_p'},
{name: 'Flying Wing', model: 'custom', image: 'flying_wing'},
{name: 'Y4', model: 'y4', image: 'y4'},
{name: 'Hex X', model: 'hex_x', image: 'hex_x'},
{name: 'Octo X8', model: 'custom', image: 'octo_x8'},
{name: 'Octo Flat +', model: 'custom', image: 'octo_flat_p'},
{name: 'Octo Flat X', model: 'custom', image: 'octo_flat_x'},
{name: 'Airplane', model: 'custom', image: 'airplane'},
{name: 'Heli 120', model: 'custom', image: 'custom'},
{name: 'Heli 90', model: 'custom', image: 'custom'},
{name: 'V-tail Quad', model: 'quad_vtail', image: 'vtail_quad'},
{name: 'Hex H', model: 'custom', image: 'custom'},
{name: 'PPM to SERVO', model: 'custom', image: 'custom'},
{name: 'Dualcopter', model: 'custom', image: 'custom'},
{name: 'Singlecopter', model: 'custom', image: 'custom'},
{name: 'A-tail Quad', model: 'quad_atail', image: 'atail_quad'},
{name: 'Custom', model: 'custom', image: 'custom'},
{name: 'Custom Airplane', model: 'custom', image: 'custom'},
{name: 'Custom Tricopter', model: 'custom', image: 'custom'}
];

280
js/msp.js
View file

@ -51,7 +51,7 @@ var MSP_codes = {
MSP_PIDNAMES: 117,
MSP_WP: 118,
MSP_BOXIDS: 119,
MSP_SERVO_CONF: 120,
MSP_SERVO_CONFIGURATIONS: 120,
MSP_SET_RAW_RC: 200,
MSP_SET_RAW_GPS: 201,
@ -65,10 +65,13 @@ var MSP_codes = {
MSP_SET_WP: 209,
MSP_SELECT_SETTING: 210,
MSP_SET_HEAD: 211,
MSP_SET_SERVO_CONF: 212,
MSP_SET_SERVO_CONFIGURATION: 212,
MSP_SET_MOTOR: 214,
// MSP_BIND: 240,
MSP_SERVO_MIX_RULES: 241,
MSP_SET_SERVO_MIX_RULE: 242,
MSP_EEPROM_WRITE: 250,
@ -80,7 +83,7 @@ var MSP_codes = {
MSP_ACC_TRIM: 240, // get acc angle trim values
MSP_SET_ACC_TRIM: 239, // set acc angle trim values
MSP_GPS_SV_INFO: 164, // get Signal Strength
// Additional private MSP for baseflight configurator (yes thats us \o/)
MSP_RX_MAP: 64, // get channel map (also returns number of channels total)
MSP_SET_RX_MAP: 65, // set rc map, numchannels to set comes from MSP_RX_MAP
@ -102,6 +105,7 @@ var MSP = {
callbacks: [],
packet_error: 0,
unsupported: 0,
ledDirectionLetters: ['n', 'e', 's', 'w', 'u', 'd'], // in LSB bit order
ledFunctionLetters: ['i', 'w', 'f', 'a', 't', 'r', 'c'], // in LSB bit order
@ -138,10 +142,14 @@ var MSP = {
}
break;
case 2: // direction (should be >)
this.unsupported = 0;
if (data[i] == 62) { // >
this.message_direction = 1;
} else { // <
} else if (data[i] == 60) { // <
this.message_direction = 0;
} else if (data[i] == 33) { // !
// FC reports unsupported message error
this.unsupported = 1;
}
this.state++;
@ -202,7 +210,7 @@ var MSP = {
process_data: function (code, message_buffer, message_length) {
var data = new DataView(message_buffer, 0); // DataView (allowing us to view arrayBuffer as struct/union)
switch (code) {
if (!this.unsupported) switch (code) {
case MSP_codes.MSP_IDENT:
console.log('Using deprecated msp command: MSP_IDENT');
// Deprecated
@ -444,19 +452,52 @@ var MSP = {
AUX_CONFIG_IDS.push(data.getUint8(i));
}
break;
case MSP_codes.MSP_SERVO_CONF:
case MSP_codes.MSP_SERVO_MIX_RULES:
break;
case MSP_codes.MSP_SERVO_CONFIGURATIONS:
SERVO_CONFIG = []; // empty the array as new data is coming in
if (data.byteLength % 7 == 0) {
for (var i = 0; i < data.byteLength; i += 7) {
var arr = {
'min': data.getInt16(i, 1),
'max': data.getInt16(i + 2, 1),
'middle': data.getInt16(i + 4, 1),
'rate': data.getInt8(i + 6)
};
SERVO_CONFIG.push(arr);
if (semver.gte(CONFIG.apiVersion, "1.12.0")) {
if (data.byteLength % 14 == 0) {
for (var i = 0; i < data.byteLength; i += 14) {
var arr = {
'min': data.getInt16(i + 0, 1),
'max': data.getInt16(i + 2, 1),
'middle': data.getInt16(i + 4, 1),
'rate': data.getInt8(i + 6),
'angleAtMin': data.getInt8(i + 7),
'angleAtMax': data.getInt8(i + 8),
'indexOfChannelToForward': data.getInt8(i + 9),
'reversedInputSources': data.getUint32(i + 10)
};
SERVO_CONFIG.push(arr);
}
}
} else {
if (data.byteLength % 7 == 0) {
for (var i = 0; i < data.byteLength; i += 7) {
var arr = {
'min': data.getInt16(i + 0, 1),
'max': data.getInt16(i + 2, 1),
'middle': data.getInt16(i + 4, 1),
'rate': data.getInt8(i + 6),
'angleAtMin': 90,
'angleAtMax': 90,
'indexOfChannelToForward': undefined,
'reversedInputSources': 0
};
SERVO_CONFIG.push(arr);
}
}
if (semver.eq(CONFIG.apiVersion, '1.10.0')) {
// drop two unused servo configurations due to MSP rx buffer to small)
while (SERVO_CONFIG.length > 8) {
SERVO_CONFIG.pop();
}
}
}
break;
@ -490,7 +531,7 @@ var MSP = {
case MSP_codes.MSP_SELECT_SETTING:
console.log('Profile selected');
break;
case MSP_codes.MSP_SET_SERVO_CONF:
case MSP_codes.MSP_SET_SERVO_CONFIGURATION:
console.log('Servo Configuration saved');
break;
case MSP_codes.MSP_EEPROM_WRITE:
@ -798,6 +839,8 @@ var MSP = {
default:
console.log('Unknown code detected: ' + code);
} else {
console.log('FC reports unsupported message error: ' + code);
}
// trigger callbacks, cleanup/remove callback after trigger
@ -944,43 +987,43 @@ MSP.crunch = function (code) {
case 7:
case 8:
case 9:
buffer.push(parseInt(PIDs[i][0] * 10));
buffer.push(parseInt(PIDs[i][1] * 1000));
buffer.push(Math.round(PIDs[i][0] * 10));
buffer.push(Math.round(PIDs[i][1] * 1000));
buffer.push(parseInt(PIDs[i][2]));
break;
case 4:
buffer.push(parseInt(PIDs[i][0] * 100));
buffer.push(parseInt(PIDs[i][1] * 100));
buffer.push(Math.round(PIDs[i][0] * 100));
buffer.push(Math.round(PIDs[i][1] * 100));
buffer.push(parseInt(PIDs[i][2]));
break;
case 5:
case 6:
buffer.push(parseInt(PIDs[i][0] * 10));
buffer.push(parseInt(PIDs[i][1] * 100));
buffer.push(parseInt(PIDs[i][2] * 1000));
buffer.push(Math.round(PIDs[i][0] * 10));
buffer.push(Math.round(PIDs[i][1] * 100));
buffer.push(Math.round(PIDs[i][2] * 1000));
break;
}
}
break;
case MSP_codes.MSP_SET_RC_TUNING:
buffer.push(parseInt(RC_tuning.RC_RATE * 100));
buffer.push(parseInt(RC_tuning.RC_EXPO * 100));
buffer.push(Math.round(RC_tuning.RC_RATE * 100));
buffer.push(Math.round(RC_tuning.RC_EXPO * 100));
if (semver.lt(CONFIG.apiVersion, "1.7.0")) {
buffer.push(parseInt(RC_tuning.roll_pitch_rate * 100));
buffer.push(Math.round(RC_tuning.roll_pitch_rate * 100));
} else {
buffer.push(parseInt(RC_tuning.roll_rate * 100));
buffer.push(parseInt(RC_tuning.pitch_rate * 100));
buffer.push(Math.round(RC_tuning.roll_rate * 100));
buffer.push(Math.round(RC_tuning.pitch_rate * 100));
}
buffer.push(parseInt(RC_tuning.yaw_rate * 100));
buffer.push(parseInt(RC_tuning.dynamic_THR_PID * 100));
buffer.push(parseInt(RC_tuning.throttle_MID * 100));
buffer.push(parseInt(RC_tuning.throttle_EXPO * 100));
buffer.push(Math.round(RC_tuning.yaw_rate * 100));
buffer.push(Math.round(RC_tuning.dynamic_THR_PID * 100));
buffer.push(Math.round(RC_tuning.throttle_MID * 100));
buffer.push(Math.round(RC_tuning.throttle_EXPO * 100));
if (semver.gte(CONFIG.apiVersion, "1.7.0")) {
buffer.push(lowByte(RC_tuning.dynamic_THR_breakpoint));
buffer.push(highByte(RC_tuning.dynamic_THR_breakpoint));
}
if (semver.gte(CONFIG.apiVersion, "1.10.0")) {
buffer.push(parseInt(RC_tuning.RC_YAW_EXPO * 100));
buffer.push(Math.round(RC_tuning.RC_YAW_EXPO * 100));
}
break;
// Disabled, cleanflight does not use MSP_SET_BOX.
@ -1028,26 +1071,12 @@ MSP.crunch = function (code) {
buffer.push(MISC.multiwiicurrentoutput);
buffer.push(MISC.rssi_channel);
buffer.push(MISC.placeholder2);
buffer.push(lowByte(MISC.mag_declination * 10));
buffer.push(highByte(MISC.mag_declination * 10));
buffer.push(lowByte(Math.round(MISC.mag_declination * 10)));
buffer.push(highByte(Math.round(MISC.mag_declination * 10)));
buffer.push(MISC.vbatscale);
buffer.push(MISC.vbatmincellvoltage * 10);
buffer.push(MISC.vbatmaxcellvoltage * 10);
buffer.push(MISC.vbatwarningcellvoltage * 10);
break;
case MSP_codes.MSP_SET_SERVO_CONF:
for (var i = 0; i < SERVO_CONFIG.length; i++) {
buffer.push(lowByte(SERVO_CONFIG[i].min));
buffer.push(highByte(SERVO_CONFIG[i].min));
buffer.push(lowByte(SERVO_CONFIG[i].max));
buffer.push(highByte(SERVO_CONFIG[i].max));
buffer.push(lowByte(SERVO_CONFIG[i].middle));
buffer.push(highByte(SERVO_CONFIG[i].middle));
buffer.push(lowByte(SERVO_CONFIG[i].rate));
}
buffer.push(Math.round(MISC.vbatmincellvoltage * 10));
buffer.push(Math.round(MISC.vbatmaxcellvoltage * 10));
buffer.push(Math.round(MISC.vbatwarningcellvoltage * 10));
break;
case MSP_codes.MSP_SET_CHANNEL_FORWARDING:
for (var i = 0; i < SERVO_CONFIG.length; i++) {
@ -1108,6 +1137,22 @@ MSP.crunch = function (code) {
return buffer;
};
/**
* Set raw Rx values over MSP protocol.
*
* Channels is an array of 16-bit unsigned integer channel values to be sent. 8 channels is probably the maximum.
*/
MSP.setRawRx = function(channels) {
var buffer = [];
for (var i = 0; i < channels.length; i++) {
buffer.push(specificByte(channels[i], 0));
buffer.push(specificByte(channels[i], 1));
}
MSP.send_message(MSP_codes.MSP_SET_RAW_RC, buffer, false);
}
/**
* Send a request to read a block of data from the dataflash at the given address and pass that address and a dataview
* of the returned data to the given callback (or null for the data if an error occured).
@ -1128,7 +1173,102 @@ MSP.dataflashRead = function(address, onDataCallback) {
onDataCallback(address, null);
}
});
} ;
};
MSP.sendServoMixRules = function(onCompleteCallback) {
// TODO implement
onCompleteCallback();
};
MSP.sendServoConfigurations = function(onCompleteCallback) {
var nextFunction = send_next_servo_configuration;
var servoIndex = 0;
if (SERVO_CONFIG.length == 0) {
onCompleteCallback();
}
nextFunction();
function send_next_servo_configuration() {
var buffer = [];
if (semver.lt(CONFIG.apiVersion, "1.12.0")) {
// send all in one go
// 1.9.0 had a bug where the MSP input buffer was too small, limit to 8.
for (var i = 0; i < SERVO_CONFIG.length && i < 8; i++) {
buffer.push(lowByte(SERVO_CONFIG[i].min));
buffer.push(highByte(SERVO_CONFIG[i].min));
buffer.push(lowByte(SERVO_CONFIG[i].max));
buffer.push(highByte(SERVO_CONFIG[i].max));
buffer.push(lowByte(SERVO_CONFIG[i].middle));
buffer.push(highByte(SERVO_CONFIG[i].middle));
buffer.push(lowByte(SERVO_CONFIG[i].rate));
}
nextFunction = send_channel_forwarding;
} else {
// send one at a time, with index
var servoConfiguration = SERVO_CONFIG[servoIndex];
buffer.push(servoIndex);
buffer.push(lowByte(servoConfiguration.min));
buffer.push(highByte(servoConfiguration.min));
buffer.push(lowByte(servoConfiguration.max));
buffer.push(highByte(servoConfiguration.max));
buffer.push(lowByte(servoConfiguration.middle));
buffer.push(highByte(servoConfiguration.middle));
buffer.push(lowByte(servoConfiguration.rate));
buffer.push(servoConfiguration.angleAtMin);
buffer.push(servoConfiguration.angleAtMax);
var out = servoConfiguration.indexOfChannelToForward;
if (out == undefined) {
out = 255; // Cleanflight defines "CHANNEL_FORWARDING_DISABLED" as "(uint8_t)0xFF"
}
buffer.push(out);
buffer.push(specificByte(servoConfiguration.reversedInputSources, 0));
buffer.push(specificByte(servoConfiguration.reversedInputSources, 1));
buffer.push(specificByte(servoConfiguration.reversedInputSources, 2));
buffer.push(specificByte(servoConfiguration.reversedInputSources, 3));
// prepare for next iteration
servoIndex++;
if (servoIndex == SERVO_CONFIG.length) {
nextFunction = onCompleteCallback;
}
}
MSP.send_message(MSP_codes.MSP_SET_SERVO_CONFIGURATION, buffer, false, nextFunction);
}
function send_channel_forwarding() {
var buffer = [];
for (var i = 0; i < SERVO_CONFIG.length; i++) {
var out = SERVO_CONFIG[i].indexOfChannelToForward;
if (out == undefined) {
out = 255; // Cleanflight defines "CHANNEL_FORWARDING_DISABLED" as "(uint8_t)0xFF"
}
buffer.push(out);
}
nextFunction = onCompleteCallback;
MSP.send_message(MSP_codes.MSP_SET_CHANNEL_FORWARDING, buffer, false, nextFunction);
}
};
MSP.sendModeRanges = function(onCompleteCallback) {
var nextFunction = send_next_mode_range;
@ -1146,12 +1286,12 @@ MSP.sendModeRanges = function(onCompleteCallback) {
var modeRange = MODE_RANGES[modeRangeIndex];
var AUX_val_buffer_out = [];
AUX_val_buffer_out.push(modeRangeIndex);
AUX_val_buffer_out.push(modeRange.id);
AUX_val_buffer_out.push(modeRange.auxChannelIndex);
AUX_val_buffer_out.push((modeRange.range.start - 900) / 25);
AUX_val_buffer_out.push((modeRange.range.end - 900) / 25);
var buffer = [];
buffer.push(modeRangeIndex);
buffer.push(modeRange.id);
buffer.push(modeRange.auxChannelIndex);
buffer.push((modeRange.range.start - 900) / 25);
buffer.push((modeRange.range.end - 900) / 25);
// prepare for next iteration
modeRangeIndex++;
@ -1159,7 +1299,7 @@ MSP.sendModeRanges = function(onCompleteCallback) {
nextFunction = onCompleteCallback;
}
MSP.send_message(MSP_codes.MSP_SET_MODE_RANGE, AUX_val_buffer_out, false, nextFunction);
MSP.send_message(MSP_codes.MSP_SET_MODE_RANGE, buffer, false, nextFunction);
}
};
@ -1179,14 +1319,14 @@ MSP.sendAdjustmentRanges = function(onCompleteCallback) {
var adjustmentRange = ADJUSTMENT_RANGES[adjustmentRangeIndex];
var ADJUSTMENT_val_buffer_out = [];
ADJUSTMENT_val_buffer_out.push(adjustmentRangeIndex);
ADJUSTMENT_val_buffer_out.push(adjustmentRange.slotIndex);
ADJUSTMENT_val_buffer_out.push(adjustmentRange.auxChannelIndex);
ADJUSTMENT_val_buffer_out.push((adjustmentRange.range.start - 900) / 25);
ADJUSTMENT_val_buffer_out.push((adjustmentRange.range.end - 900) / 25);
ADJUSTMENT_val_buffer_out.push(adjustmentRange.adjustmentFunction);
ADJUSTMENT_val_buffer_out.push(adjustmentRange.auxSwitchChannelIndex);
var buffer = [];
buffer.push(adjustmentRangeIndex);
buffer.push(adjustmentRange.slotIndex);
buffer.push(adjustmentRange.auxChannelIndex);
buffer.push((adjustmentRange.range.start - 900) / 25);
buffer.push((adjustmentRange.range.end - 900) / 25);
buffer.push(adjustmentRange.adjustmentFunction);
buffer.push(adjustmentRange.auxSwitchChannelIndex);
// prepare for next iteration
adjustmentRangeIndex++;
@ -1194,7 +1334,7 @@ MSP.sendAdjustmentRanges = function(onCompleteCallback) {
nextFunction = onCompleteCallback;
}
MSP.send_message(MSP_codes.MSP_SET_ADJUSTMENT_RANGE, ADJUSTMENT_val_buffer_out, false, nextFunction);
MSP.send_message(MSP_codes.MSP_SET_ADJUSTMENT_RANGE, buffer, false, nextFunction);
}
};

View file

@ -32,8 +32,8 @@
<script type="text/javascript" src="./js/libraries/q.js"></script>
<script type="text/javascript" src="./js/libraries/google-analytics-bundle.js"></script>
<script type="text/javascript" src="./js/libraries/jquery-2.1.3.min.js"></script>
<script type="text/javascript" src="./js/libraries/jquery-ui-1.11.2.min.js"></script>
<script type="text/javascript" src="./js/libraries/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="./js/libraries/jquery-ui-1.11.4.min.js"></script>
<script type="text/javascript" src="./js/libraries/d3.min.js"></script>
<script type="text/javascript" src="./js/libraries/jquery.nouislider.all.min.js"></script>
<script type="text/javascript" src="./js/libraries/three/three.min.js"></script>

View file

@ -1,7 +1,7 @@
{
"manifest_version": 2,
"minimum_chrome_version": "38",
"version": "0.65.0",
"version": "0.66.0",
"author": "Hydra",
"name": "Cleanflight - Configurator",
"short_name": "cleanflight",
@ -31,7 +31,8 @@
"fileSystem",
"fileSystem.write",
"fileSystem.retainEntries",
"notifications"
"notifications",
"alwaysOnTopWindows"
],
"optional_permissions": [

View file

@ -28,7 +28,7 @@
"DbgName": "Material.003",
"colorEmissive": [0.0,0.0,0.0],
"mapDiffuse": "fallback.png",
"opacity": 0.0,
"opacity": 1.0,
"specularCoef": 50,
"shading": "phong",
"mapDiffuseWrap": ["RepeatWrapping","RepeatWrapping"],

View file

@ -28,7 +28,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 0,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15597568,
"specularCoef": 50,
@ -45,7 +45,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 1,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 60928,
"specularCoef": 50,
@ -62,7 +62,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 2,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 238,
"specularCoef": 50,
@ -79,7 +79,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 3,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15658496,
"specularCoef": 50,
@ -96,6 +96,6 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 4,
"opacity": 0.0
"opacity": 1.0
}]
}

View file

@ -28,7 +28,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 0,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15597568,
"specularCoef": 50,
@ -45,7 +45,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 1,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 60928,
"specularCoef": 50,
@ -62,7 +62,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 2,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 238,
"specularCoef": 50,
@ -79,7 +79,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 3,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15658496,
"specularCoef": 50,
@ -96,6 +96,6 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 4,
"opacity": 0.0
"opacity": 1.0
}]
}

View file

@ -28,7 +28,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 0,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15597568,
"specularCoef": 50,
@ -45,7 +45,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 1,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 60928,
"specularCoef": 50,
@ -62,7 +62,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 2,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 238,
"specularCoef": 50,
@ -79,7 +79,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 3,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15658496,
"specularCoef": 50,
@ -96,6 +96,6 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 4,
"opacity": 0.0
"opacity": 1.0
}]
}

View file

@ -28,7 +28,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 0,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15597568,
"specularCoef": 50,
@ -45,7 +45,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 1,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 60928,
"specularCoef": 50,
@ -62,7 +62,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 2,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 238,
"specularCoef": 50,
@ -79,7 +79,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 3,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15658496,
"specularCoef": 50,
@ -96,6 +96,6 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 4,
"opacity": 0.0
"opacity": 1.0
}]
}

View file

@ -28,7 +28,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 0,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15597568,
"specularCoef": 50,
@ -45,7 +45,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 1,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 60928,
"specularCoef": 50,
@ -62,7 +62,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 2,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 238,
"specularCoef": 50,
@ -79,7 +79,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 3,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15658496,
"specularCoef": 50,
@ -96,6 +96,6 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 4,
"opacity": 0.0
"opacity": 1.0
}]
}

View file

@ -28,7 +28,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 0,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15597568,
"specularCoef": 50,
@ -45,7 +45,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 1,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 60928,
"specularCoef": 50,
@ -62,7 +62,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 2,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 238,
"specularCoef": 50,
@ -79,7 +79,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 3,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15658496,
"specularCoef": 50,
@ -96,6 +96,6 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 4,
"opacity": 0.0
"opacity": 1.0
}]
}

View file

@ -28,7 +28,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 0,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15597568,
"specularCoef": 50,
@ -45,7 +45,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 1,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 60928,
"specularCoef": 50,
@ -62,7 +62,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 2,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 238,
"specularCoef": 50,
@ -79,7 +79,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 3,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15658496,
"specularCoef": 50,
@ -96,6 +96,6 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 4,
"opacity": 0.0
"opacity": 1.0
}]
}

View file

@ -28,7 +28,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 0,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15597568,
"specularCoef": 50,
@ -45,7 +45,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 1,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 60928,
"specularCoef": 50,
@ -62,7 +62,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 2,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 238,
"specularCoef": 50,
@ -79,7 +79,7 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 3,
"opacity": 0.0
"opacity": 1.0
},{
"DbgColor": 15658496,
"specularCoef": 50,
@ -96,6 +96,6 @@
"visible": true,
"blending": "NormalBlending",
"DbgIndex": 4,
"opacity": 0.0
"opacity": 1.0
}]
}

View file

@ -315,10 +315,10 @@ def render_vtail
m.draw_body 100,180,140,160
m.draw_body 100,180,60,160
m.end_body
m.draw_circle 140,160,"1",Model::CW,Model::SE
m.draw_circle 160,40,"2",Model::CCW,Model::NE
m.draw_circle 60,160,"3",Model::CCW,Model::SW
m.draw_circle 40,40,"4",Model::CW,Model::NW
m.draw_circle 140,160,"1",Model::CCW,Model::SE
m.draw_circle 160,40,"2",Model::CW,Model::NE
m.draw_circle 60,160,"3",Model::CW,Model::SW
m.draw_circle 40,40,"4",Model::CCW,Model::NW
m.draw_dirn
m.close
end

View file

@ -141,6 +141,13 @@ TABS.cli.read = function (readInfo) {
text += "<br />";
}
break;
case 60:
text += '&lt';
break;
case 62:
text += '&gt';
break;
default:
text += String.fromCharCode(data[i]);
}

View file

@ -99,6 +99,13 @@
border: 1px solid silver;
}
.tab-configuration .number input.disabled {
width: 50px;
padding: 0px 5px;
background-color: #ececec;
}
.tab-configuration .number span {
margin-left: 10px;
line-height: 20px;
@ -154,7 +161,6 @@
.tab-configuration .disarm .checkbox span {
margin-left: 15px;
}
.tab-configuration .save {
display: block;
float: right;

View file

@ -127,19 +127,10 @@
<!-- list generated here -->
</select>
<div class="groupTitle" i18n="configurationRSSI"></div>
<table>
<thead>
<tr>
<th i18n="configurationFeatureEnabled"></th>
<th i18n="configurationFeatureName"></th>
<th i18n="configurationFeatureDescription"></th>
</tr>
</thead>
<tbody class="features rssi">
<!-- table generated here -->
</tbody>
</table>
<div class="help">
<p i18n="configurationSerialRXHelp"></p>
</div>
<div class="groupTitle" i18n="configurationFailsafe"></div>
<table>
<thead>
@ -159,8 +150,6 @@
<span i18n="configurationThrottleFailsafe"></span>
</label>
</div>
</div>
<div class="rightWrapper current voltage">
<div class="groupTitle" i18n="configurationBatteryVoltage"></div>
@ -201,7 +190,7 @@
<span i18n="configurationBatteryScale"></span>
</label>
</div>
<div class="groupTitle" i18n="configurationCurrent"></div>
<div class="groupTitle" i18n="configurationCurrent"></div>
<table>
<thead>
<tr>
@ -233,8 +222,42 @@
</div>
<span i18n="configurationBatteryMultiwiiCurrent"></span>
</label>
</div>
</div>
</div>
<div class="clear-both"></div>
<div class="leftWrapper rssi">
<div class="groupTitle" i18n="configurationRSSI"></div>
<table>
<thead>
<tr>
<th i18n="configurationFeatureEnabled"></th>
<th i18n="configurationFeatureName"></th>
<th i18n="configurationFeatureDescription"></th>
</tr>
</thead>
<tbody class="features rssi">
<!-- table generated here -->
</tbody>
</table>
</div>
<div class="rightWrapper system">
<div class="groupTitle" i18n="configurationSystem"></div>
<div class="number">
<label>
<input type="number" name="looptime" step="100" min="0" max="9000"/>
<span i18n="configurationLoopTime"></span>
</label>
</div>
<div class="number">
<label>
<input type="text" name="looptimehz" readonly="readonly" class="disabled"/>
<span i18n="configurationCalculatedCyclesSec"></span>
</label>
</div>
<p class="help" i18n="configurationLoopTimeHelp"></p>
</div>
<div class="clear-both"></div>
<div class="leftWrapper gps">
<div class="groupTitle" i18n="configurationGPS"></div>
@ -275,6 +298,11 @@
<span i18n="configurationMagDeclination"></span>
</label>
</div>
<div class="help">
<p i18n="configurationGPSHelp"></p>
</div>
</div>
<div class="rightWrapper">
<div class="groupTitle" i18n="configurationFeatures"></div>
@ -291,6 +319,7 @@
</tbody>
</table>
</div>
<div class="clear-both"></div>
<div class="buttons">
<a class="save" href="#" i18n="configurationButtonSave"></a>

View file

@ -36,7 +36,11 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
}
function load_arming_config() {
MSP.send_message(MSP_codes.MSP_ARMING_CONFIG, false, false, load_html);
MSP.send_message(MSP_codes.MSP_ARMING_CONFIG, false, false, load_loop_time);
}
function load_loop_time() {
MSP.send_message(MSP_codes.MSP_LOOP_TIME, false, false, load_html);
}
function load_html() {
@ -45,6 +49,17 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
MSP.send_message(MSP_codes.MSP_IDENT, false, false, load_config);
function recalculate_cycles_sec() {
var looptime = $('input[name="looptime"]').val();
var message = 'Max';
if (looptime > 0) {
message = parseFloat((1 / looptime) * 1000 * 1000).toFixed(0);
}
$('input[name="looptimehz"]').val(message);
}
function process_html() {
// translate to user-selected language
localize();
@ -73,7 +88,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
{bit: 3, group: 'rxMode', mode: 'group', name: 'RX_SERIAL', description: 'Serial-based receiver (SPEKSAT, SBUS, SUMD)'},
{bit: 4, group: 'esc', name: 'MOTOR_STOP', description: 'Don\'t spin the motors when armed'},
{bit: 5, group: 'other', name: 'SERVO_TILT', description: 'Servo gimbal'},
{bit: 6, group: 'other', name: 'SOFTSERIAL', description: 'Enable CPU based serial ports (configure port scenario first)'},
{bit: 6, group: 'other', name: 'SOFTSERIAL', description: 'Enable CPU based serial ports'},
{bit: 7, group: 'gps', name: 'GPS', description: 'GPS (configure port scenario first)'},
{bit: 8, group: 'rxFailsafe', name: 'FAILSAFE', description: 'Failsafe settings on RX signal loss'},
{bit: 9, group: 'other', name: 'SONAR', description: 'Sonar'},
@ -88,6 +103,12 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
{bit: 18, group: 'esc', name: 'ONESHOT125', description: 'ONESHOT ESC support (disconnect ESCs, remove props)'},
{bit: 19, group: 'other', name: 'BLACKBOX', description: 'Blackbox flight data recorder'}
];
if (semver.gte(CONFIG.apiVersion, "1.12.0")) {
features.push(
{bit: 20, group: 'other', name: 'CHANNEL_FORWARDING', description: 'Forward aux channels to remaining servo outputs'}
);
}
var radioGroups = [];
@ -256,7 +277,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
// fill magnetometer
$('input[name="mag_declination"]').val(MISC.mag_declination);
//fill motor disarm params
//fill motor disarm params and FC loop time
if(semver.gte(CONFIG.apiVersion, "1.8.0")) {
$('input[name="autodisarmdelay"]').val(ARMING_CONFIG.auto_disarm_delay);
$('input[name="disarmkillswitch"]').prop('checked', ARMING_CONFIG.disarm_kill_switch);
@ -265,6 +286,13 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
$('div.disarmdelay').show();
else
$('div.disarmdelay').hide();
// fill FC loop time
$('input[name="looptime"]').val(FC_CONFIG.loopTime);
recalculate_cycles_sec();
$('div.cycles').show();
}
// fill throttle
@ -287,6 +315,10 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
// UI hooks
$('input[name="looptime"]').change(function() {
recalculate_cycles_sec();
});
$('input[type="checkbox"].feature', features_e).change(function () {
var element = $(this),
index = element.data('bit'),
@ -338,6 +370,7 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
if(semver.gte(CONFIG.apiVersion, "1.8.0")) {
ARMING_CONFIG.auto_disarm_delay = parseInt($('input[name="autodisarmdelay"]').val());
ARMING_CONFIG.disarm_kill_switch = ~~$('input[name="disarmkillswitch"]').is(':checked'); // ~~ boolean to decimal conversion
FC_CONFIG.loopTime = parseInt($('input[name="looptime"]').val());
}
MISC.minthrottle = parseInt($('input[name="minthrottle"]').val());
@ -373,7 +406,11 @@ TABS.configuration.initialize = function (callback, scrollPosition) {
}
function save_arming_config() {
MSP.send_message(MSP_codes.MSP_SET_ARMING_CONFIG, MSP.crunch(MSP_codes.MSP_SET_ARMING_CONFIG), false, save_to_eeprom);
MSP.send_message(MSP_codes.MSP_SET_ARMING_CONFIG, MSP.crunch(MSP_codes.MSP_SET_ARMING_CONFIG), false, save_looptime_config);
}
function save_looptime_config() {
MSP.send_message(MSP_codes.MSP_SET_LOOP_TIME, MSP.crunch(MSP_codes.MSP_SET_LOOP_TIME), false, save_to_eeprom);
}
function save_to_eeprom() {

View file

@ -146,53 +146,66 @@ TABS.dataflash.initialize = function (callback) {
$(".dataflash-saving").addClass("done");
}
function flash_save_begin() {
var
maxBytes = DATAFLASH.usedSize;
if (GUI.connected_to) {
prepare_file(function(fileWriter) {
var
nextAddress = 0;
show_saving_dialog();
function onChunkRead(chunkAddress, chunkDataView) {
if (chunkDataView != null) {
// Did we receive any data?
if (chunkDataView.byteLength > 0) {
nextAddress += chunkDataView.byteLength;
$(".dataflash-saving progress").attr("value", nextAddress / maxBytes * 100);
function flash_update_summary(onDone) {
MSP.send_message(MSP_codes.MSP_DATAFLASH_SUMMARY, false, false, function() {
update_html();
if (onDone) {
onDone();
}
});
}
var
blob = new Blob([chunkDataView]);
fileWriter.onwriteend = function(e) {
if (saveCancelled || nextAddress >= maxBytes) {
if (saveCancelled) {
dismiss_saving_dialog();
} else {
mark_saving_dialog_done();
}
} else {
MSP.dataflashRead(nextAddress, onChunkRead);
}
};
fileWriter.write(blob);
} else {
// A zero-byte block indicates end-of-file, so we're done
mark_saving_dialog_done();
}
} else {
// There was an error with the received block (address didn't match the one we asked for), retry
MSP.dataflashRead(nextAddress, onChunkRead);
}
}
function flash_save_begin() {
if (GUI.connected_to) {
// Begin by refreshing the occupied size in case it changed while the tab was open
flash_update_summary(function() {
var
maxBytes = DATAFLASH.usedSize;
// Fetch the initial block
MSP.dataflashRead(nextAddress, onChunkRead);
prepare_file(function(fileWriter) {
var
nextAddress = 0;
show_saving_dialog();
function onChunkRead(chunkAddress, chunkDataView) {
if (chunkDataView != null) {
// Did we receive any data?
if (chunkDataView.byteLength > 0) {
nextAddress += chunkDataView.byteLength;
$(".dataflash-saving progress").attr("value", nextAddress / maxBytes * 100);
var
blob = new Blob([chunkDataView]);
fileWriter.onwriteend = function(e) {
if (saveCancelled || nextAddress >= maxBytes) {
if (saveCancelled) {
dismiss_saving_dialog();
} else {
mark_saving_dialog_done();
}
} else {
MSP.dataflashRead(nextAddress, onChunkRead);
}
};
fileWriter.write(blob);
} else {
// A zero-byte block indicates end-of-file, so we're done
mark_saving_dialog_done();
}
} else {
// There was an error with the received block (address didn't match the one we asked for), retry
MSP.dataflashRead(nextAddress, onChunkRead);
}
}
// Fetch the initial block
MSP.dataflashRead(nextAddress, onChunkRead);
});
});
}
}
@ -246,8 +259,7 @@ TABS.dataflash.initialize = function (callback) {
}
function poll_for_erase_completion() {
MSP.send_message(MSP_codes.MSP_DATAFLASH_SUMMARY, false, false, function() {
update_html();
flash_update_summary(function() {
if (!eraseCancelled) {
if (DATAFLASH.ready) {
$(".dataflash-confirm-erase")[0].close();

View file

@ -39,7 +39,7 @@ TABS.firmware_flasher.initialize = function (callback) {
var showDevReleases = ($('input.show_development_releases').is(':checked'));
releases_e.append($("<option value='0'>{0}</option>".format(chrome.i18n.getMessage('firmwareFlasherOptionLabelSelectFirmware'))));
var releaseDescritpors = [];
var releaseDescriptors = [];
TABS.firmware_flasher.releases.forEach(function(release){
release.assets.forEach(function(asset){
var targetFromFilenameExpression = /.*_(.*)\.(.*)/;
@ -81,11 +81,11 @@ TABS.firmware_flasher.initialize = function (callback) {
"status" : release.prerelease ? "release-candidate" : "stable"
};
releaseDescritpors.push(descriptor);
releaseDescriptors.push(descriptor);
});
});
releaseDescritpors.sort(function(o1,o2){
releaseDescriptors.sort(function(o1,o2){
// compare versions descending
var cmpVal = semver(o2.version).compare(semver(o1.version));
if (cmpVal == 0){
@ -96,7 +96,7 @@ TABS.firmware_flasher.initialize = function (callback) {
});
var optionIndex = 1;
releaseDescritpors.forEach(function(descriptor){
releaseDescriptors.forEach(function(descriptor){
var select_e =
$("<option value='{0}'>{1} {2} {3} ({4})</option>".format(
optionIndex++,

View file

@ -60,6 +60,9 @@
<p>
&bull; <a href="http://www.overskyrc.com" title="www.overskyrc.com" target="_blank">OverSkyRC</a><br />
</p>
<p>
&bull; <a href="http://multirotormania.com" title="multirotormania.com" target="_blank">Multi Rotor Mania</a><br />
</p>
</div>
</div>
</div>

View file

@ -272,7 +272,7 @@ TABS.motors.initialize = function (callback) {
');
servos_wrapper.append('\
<div class="m-block servo-' + i + '">\
<div class="m-block servo-' + (7 - i) + '">\
<div class="meter-bar">\
<div class="label"></div>\
<div class="indicator">\

View file

@ -71,13 +71,13 @@
border: 1px dashed silver;
margin-bottom: 8px;
}
.require-support {
.tab-ports .require-support {
display:none;
}
.tab-ports.supported .require-support {
display:block;
}
.require-upgrade {
.tab-ports .require-upgrade {
display:block;
}
.tab-ports.supported .require-upgrade {

View file

@ -18,6 +18,11 @@
<tbody>
</tbody>
</table>
<div class="help">
<p i18n="portsMSPHelp"></p>
</div>
<div class="clear-both"></div>
<div class="buttons">
<a class="save" href="#" i18n="portsButtonSave"></a>

View file

@ -3,6 +3,12 @@
.tab-receiver input[type="number"]::-webkit-inner-spin-button {
border: 0;
}
.tab-receiver .help {
padding: 10px;
background-color: #ffcb18;
margin-bottom: 10px;
}
.tab-receiver .bars {
float: left;
width: 45%;
@ -218,7 +224,7 @@
margin: 0 10px 10px 0;
width: 220px;
height: 58px;
height: 120px;
border: 1px solid silver;
}
@ -226,7 +232,7 @@
margin: 0 10px 0 0;
width: 220px;
height: 58px;
height: 120px;
border: 1px solid silver;
}
@ -293,6 +299,7 @@
position: absolute;
bottom: 10px;
}
.tab-receiver .sticks,
.tab-receiver .update,
.tab-receiver .refresh {
display: block;
@ -311,6 +318,7 @@
border: 1px solid silver;
background-color: #ececec;
}
.tab-receiver .sticks,
.tab-receiver .refresh {
margin-right: 10px;
}
@ -334,4 +342,4 @@
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
}

View file

@ -1,5 +1,9 @@
<div id="content-watermark"></div>
<div class="tab-receiver">
<div class="help">
<p i18n="receiverHelp"></p>
</div>
<div class="bars">
</div>
<div class="tunings">
@ -53,10 +57,10 @@
</div>
<div class="curves">
<div class="throttle_curve">
<canvas width="220" height="58"></canvas>
<canvas width="220" height="120"></canvas>
</div>
<div class="pitch_roll_curve">
<canvas width="220" height="58"></canvas>
<canvas width="220" height="120"></canvas>
</div>
</div>
<div class="clear-both"></div>
@ -82,5 +86,6 @@
<div class="buttons">
<a class="update" href="#" i18n="receiverButtonSave"></a>
<a class="refresh" href="#" i18n="receiverButtonRefresh"></a>
<a class="sticks" href="#" i18n="receiverButtonSticks"></a>
</div>
</div>

View file

@ -1,6 +1,9 @@
'use strict';
TABS.receiver = {};
TABS.receiver = {
rateChartHeight: 120
};
TABS.receiver.initialize = function (callback) {
var self = this;
@ -18,7 +21,12 @@ TABS.receiver.initialize = function (callback) {
}
function get_rc_map() {
MSP.send_message(MSP_codes.MSP_RX_MAP, false, false, load_html);
MSP.send_message(MSP_codes.MSP_RX_MAP, false, false, load_config);
}
// Fetch features so we can check if RX_MSP is enabled:
function load_config() {
MSP.send_message(MSP_codes.MSP_BF_CONFIG, false, false, load_html);
}
function load_html() {
@ -38,11 +46,11 @@ TABS.receiver.initialize = function (callback) {
$('.tunings .rate input[name="rate"]').val(RC_tuning.RC_RATE.toFixed(2));
$('.tunings .rate input[name="expo"]').val(RC_tuning.RC_EXPO.toFixed(2));
$('.tunings .yaw_rate input[name="yaw_expo"]').val(RC_tuning.RC_YAW_EXPO.toFixed(2));
if (semver.lt(CONFIG.apiVersion, "1.10.0")) {
$('.tunings .yaw_rate input[name="yaw_expo"]').hide();
}
chrome.storage.local.get('rx_refresh_rate', function (result) {
if (result.rx_refresh_rate) {
$('select[name="rx_refresh_rate"]').val(result.rx_refresh_rate).change();
@ -52,7 +60,12 @@ TABS.receiver.initialize = function (callback) {
});
// generate bars
var bar_names = ['Roll', 'Pitch', 'Yaw', 'Throttle'],
var bar_names = [
chrome.i18n.getMessage('controlAxisRoll'),
chrome.i18n.getMessage('controlAxisPitch'),
chrome.i18n.getMessage('controlAxisYaw'),
chrome.i18n.getMessage('controlAxisThrottle')
],
bar_container = $('.tab-receiver .bars'),
aux_index = 1;
@ -61,7 +74,7 @@ TABS.receiver.initialize = function (callback) {
if (i < bar_names.length) {
name = bar_names[i];
} else {
name = 'AUX ' + aux_index++;
name = chrome.i18n.getMessage("controlAxisAux" + (aux_index++));
}
bar_container.append('\
@ -176,6 +189,8 @@ TABS.receiver.initialize = function (callback) {
$('select[name="rssi_channel"]').val(MISC.rssi_channel);
var rateHeight = TABS.receiver.rateChartHeight;
// UI Hooks
// curves
$('.tunings .throttle input').on('input change', function () {
@ -201,14 +216,14 @@ TABS.receiver.initialize = function (callback) {
var midx = 220 * mid,
midxl = midx * 0.5,
midxr = (((220 - midx) * 0.5) + midx),
midy = 58 - (midx * (58 / 220)),
midyl = 58 - ((58 - midy) * 0.5 *(expo + 1)),
midy = rateHeight - (midx * (rateHeight / 220)),
midyl = rateHeight - ((rateHeight - midy) * 0.5 *(expo + 1)),
midyr = (midy / 2) * (expo + 1);
// draw
context.clearRect(0, 0, 220, 58);
context.clearRect(0, 0, 220, rateHeight);
context.beginPath();
context.moveTo(0, 58);
context.moveTo(0, rateHeight);
context.quadraticCurveTo(midxl, midyl, midx, midy);
context.moveTo(midx, midy);
context.quadraticCurveTo(midxr, midyr, 220, 0);
@ -237,13 +252,13 @@ TABS.receiver.initialize = function (callback) {
}
// math magic by englishman
var ratey = 58 * rate;
var ratey = rateHeight * rate;
// draw
context.clearRect(0, 0, 220, 58);
context.clearRect(0, 0, 220, rateHeight);
context.beginPath();
context.moveTo(0, 58);
context.quadraticCurveTo(110, 58 - ((ratey / 2) * (1 - expo)), 220, 58 - ratey);
context.moveTo(0, rateHeight);
context.quadraticCurveTo(110, rateHeight - ((ratey / 2) * (1 - expo)), 220, rateHeight - ratey);
context.lineWidth = 2;
context.stroke();
}, 0);
@ -302,6 +317,35 @@ TABS.receiver.initialize = function (callback) {
MSP.send_message(MSP_codes.MSP_SET_RC_TUNING, MSP.crunch(MSP_codes.MSP_SET_RC_TUNING), false, save_rc_map);
});
$("a.sticks").click(function() {
var
windowWidth = 370,
windowHeight = 510;
chrome.app.window.create("/tabs/receiver_msp.html", {
id: "receiver_msp",
innerBounds: {
minWidth: windowWidth, minHeight: windowHeight,
width: windowWidth, height: windowHeight,
maxWidth: windowWidth, maxHeight: windowHeight
},
alwaysOnTop: true
}, function(createdWindow) {
// Give the window a callback it can use to send the channels (otherwise it can't see those objects)
createdWindow.contentWindow.setRawRx = function(channels) {
if (CONFIGURATOR.connectionValid && GUI.active_tab != 'cli') {
MSP.setRawRx(channels);
return true;
} else {
return false;
}
}
});
});
// Only show the MSP control sticks if the MSP Rx feature is enabled
$("a.sticks").toggle(bit_check(BF_CONFIG.features, 14 /* RX_MSP */));
$('select[name="rx_refresh_rate"]').change(function () {
var plot_update_rate = parseInt($(this).val(), 10);

109
tabs/receiver_msp.css Normal file
View file

@ -0,0 +1,109 @@
body {
font-family: 'Segoe UI', Tahoma, sans-serif;
font-size: 12px;
color: #303030;
margin: 10px;
}
.control-gimbals {
/* A generous padding around the window edges ensures that we continue to receive mousemove events (since
* cursor stays in the window for longer)
*/
padding:25px;
padding-bottom:0;
text-align:center;
}
.control-gimbal {
position:relative;
width:120px;
height:120px;
background-color:#eee;
margin-left:1em;
margin-right:1em;
margin-bottom:2em;
display:inline-block;
border-radius:5px;
cursor:pointer;
}
.crosshair {
display:block;
position:absolute;
background-color:#ddd;
}
.crosshair-vert {
width:1px;
height:100%;
left:50%;
}
.crosshair-horz {
height:1px;
width:100%;
top:50%;
}
.gimbal-label {
display:block;
position:absolute;
text-align:center;
}
.gimbal-label-horz {
top:calc(100% + 0.5em);
width:100%;
}
.gimbal-label-vert {
transform:rotate(-90deg);
/*transform-origin:0% 100%;*/
top:calc(50% - 0.5em);
width:100%;
left:calc(-50% - 1em);
}
.control-stick {
background-color:rgba(255,50,50,1.0);
width:20px;
height:20px;
margin-left:-10px;
margin-top:-10px;
display:block;
border-radius:100%;
position:absolute;
cursor:pointer;
}
.control-slider {
margin:20px;
}
.tooltip {
position: absolute;
left: calc(100% + 24px);
top: 0;
}
.control-slider .slider {
margin-left:50px;
margin-right:50px;
}
.slider-label {
position:absolute;
text-align:right;
width:40px;
left:-65px;
}
.button-enable {
padding:0.5em;
font-size:110%;
margin-left:auto;
margin-right:auto;
display:block;
}

71
tabs/receiver_msp.html Normal file
View file

@ -0,0 +1,71 @@
<html>
<head>
<script type="text/javascript" src="/js/libraries/jquery-2.1.4.min.js"></script>
<script type="text/javascript" src="/js/libraries/jquery-ui-1.11.4.min.js"></script>
<script type="text/javascript" src="/js/libraries/jquery.nouislider.all.min.js"></script>
<script type="text/javascript" src="receiver_msp.js"></script>
<link type="text/css" rel="stylesheet" href="/js/libraries/jquery.nouislider.min.css">
<link type="text/css" rel="stylesheet" href="/js/libraries/jquery.nouislider.pips.min.css">
<link type="text/css" rel="stylesheet" href="receiver_msp.css" media="all" />
</head>
<body>
<div class="control-gimbals">
<div class="control-gimbal left">
<span class="gimbal-label gimbal-label-vert"></span>
<span class="gimbal-label gimbal-label-horz"></span>
<span class="crosshair crosshair-vert"></span>
<span class="crosshair crosshair-horz"></span>
<div class="control-stick">
</div>
</div>
<div class="control-gimbal right">
<span class="gimbal-label gimbal-label-vert"></span>
<span class="gimbal-label gimbal-label-horz"></span>
<span class="crosshair crosshair-vert"></span>
<span class="crosshair crosshair-horz"></span>
<div class="control-stick">
</div>
</div>
</div>
<div class="control-sliders">
<div class="control-slider">
<div class="slider">
<span class="slider-label"></span>
</div>
</div>
<div class="control-slider">
<div class="slider">
<span class="slider-label"></span>
</div>
</div>
<div class="control-slider">
<div class="slider">
<span class="slider-label"></span>
</div>
</div>
<div class="control-slider">
<div class="slider">
<span class="slider-label"></span>
</div>
</div>
</div>
<div class="warning">
<p>
These sticks allow Cleanflight to be armed and tested without a transmitter or receiver being
present. However, <strong>this feature is not intended for flight and propellers must not be attached.</strong>
</p>
<p>
This feature does not guarantee reliable control of your craft. <strong>Serious injury is likely to
result if propellers are left on.</strong>
</p>
<button class="button-enable" type="button">Enable controls</button>
</div>
</body>
</html>

185
tabs/receiver_msp.js Normal file
View file

@ -0,0 +1,185 @@
"use strict";
var
CHANNEL_MIN_VALUE = 1000,
CHANNEL_MID_VALUE = 1500,
CHANNEL_MAX_VALUE = 2000,
// What's the index of each channel in the MSP channel list?
channelMSPIndexes = {
roll: 0,
pitch: 1,
yaw: 2,
throttle: 3,
aux1: 4,
aux2: 5,
aux3: 6,
aux4: 7,
},
// Set reasonable initial stick positions (Mode 2)
stickValues = {
throttle: CHANNEL_MIN_VALUE,
pitch: CHANNEL_MID_VALUE,
roll: CHANNEL_MID_VALUE,
yaw: CHANNEL_MID_VALUE,
aux1: CHANNEL_MIN_VALUE,
aux2: CHANNEL_MIN_VALUE,
aux3: CHANNEL_MIN_VALUE,
aux4: CHANNEL_MIN_VALUE
},
// First the vertical axis, then the horizontal:
gimbals = [
["throttle", "yaw"],
["pitch", "roll"],
],
gimbalElems,
sliderElems,
enableTX = false;
function transmitChannels() {
var
channelValues = [0, 0, 0, 0, 0, 0, 0, 0];
if (!enableTX) {
return;
}
for (var stickName in stickValues) {
channelValues[channelMSPIndexes[stickName]] = stickValues[stickName];
}
// Callback given to us by the window creator so we can have it send data over MSP for us:
if (!window.setRawRx(channelValues)) {
// MSP connection has gone away
chrome.app.window.current().close();
}
}
function stickPortionToChannelValue(portion) {
portion = Math.min(Math.max(portion, 0.0), 1.0);
return Math.round(portion * (CHANNEL_MAX_VALUE - CHANNEL_MIN_VALUE) + CHANNEL_MIN_VALUE);
}
function channelValueToStickPortion(channel) {
return (channel - CHANNEL_MIN_VALUE) / (CHANNEL_MAX_VALUE - CHANNEL_MIN_VALUE);
}
function updateControlPositions() {
for (var stickName in stickValues) {
var
stickValue = stickValues[stickName];
// Look for the gimbal which corresponds to this stick name
for (var gimbalIndex in gimbals) {
var
gimbal = gimbals[gimbalIndex],
gimbalElem = gimbalElems.get(gimbalIndex),
gimbalSize = $(gimbalElem).width(),
stickElem = $(".control-stick", gimbalElem);
if (gimbal[0] == stickName) {
stickElem.css('top', (1.0 - channelValueToStickPortion(stickValue)) * gimbalSize + "px");
break;
} else if (gimbal[1] == stickName) {
stickElem.css('left', channelValueToStickPortion(stickValue) * gimbalSize + "px");
break;
}
}
}
}
function handleGimbalMouseDrag(e) {
var
gimbal = $(gimbalElems.get(e.data.gimbalIndex)),
gimbalOffset = gimbal.offset(),
gimbalSize = gimbal.width();
stickValues[gimbals[e.data.gimbalIndex][0]] = stickPortionToChannelValue(1.0 - (e.pageY - gimbalOffset.top) / gimbalSize);
stickValues[gimbals[e.data.gimbalIndex][1]] = stickPortionToChannelValue((e.pageX - gimbalOffset.left) / gimbalSize);
updateControlPositions();
}
function localizeAxisNames() {
for (var gimbalIndex in gimbals) {
var
gimbal = gimbalElems.get(gimbalIndex);
$(".gimbal-label-vert", gimbal).text(chrome.i18n.getMessage("controlAxis" + gimbals[gimbalIndex][0]));
$(".gimbal-label-horz", gimbal).text(chrome.i18n.getMessage("controlAxis" + gimbals[gimbalIndex][1]));
}
for (var sliderIndex = 0; sliderIndex < 4; sliderIndex++) {
$(".slider-label", sliderElems.get(sliderIndex)).text(chrome.i18n.getMessage("controlAxisAux" + (sliderIndex + 1)));
}
}
$(document).ready(function() {
$(".button-enable").click(function() {
var
shrinkHeight = $(".warning").height();
$(".warning").slideUp("short", function() {
chrome.app.window.current().innerBounds.minHeight -= shrinkHeight;
chrome.app.window.current().innerBounds.height -= shrinkHeight;
chrome.app.window.current().innerBounds.maxHeight -= shrinkHeight;
});
enableTX = true;
});
gimbalElems = $(".control-gimbal");
sliderElems = $(".control-slider");
gimbalElems.each(function(gimbalIndex) {
$(this).on('mousedown', {gimbalIndex: gimbalIndex}, function(e) {
if (e.which == 1) { // Only move sticks on left mouse button
handleGimbalMouseDrag(e);
$(window).on('mousemove', {gimbalIndex: gimbalIndex}, handleGimbalMouseDrag);
}
});
});
$(".slider", sliderElems).each(function(sliderIndex) {
var
initialValue = stickValues["aux" + (sliderIndex + 1)];
$(this)
.noUiSlider({
start: initialValue,
range: {
min: CHANNEL_MIN_VALUE,
max: CHANNEL_MAX_VALUE
}
}).on('slide change set', function(e, value) {
value = Math.round(parseFloat(value));
stickValues["aux" + (sliderIndex + 1)] = value;
$(".tooltip", this).text(value);
});
$(this).append('<div class="tooltip"></div>');
$(".tooltip", this).text(initialValue);
});
/*
* Mouseup handler needs to be bound to the window in order to receive mouseup if mouse leaves window.
*/
$(window).mouseup(function(e) {
$(this).off('mousemove', handleGimbalMouseDrag);
});
localizeAxisNames();
updateControlPositions();
setInterval(transmitChannels, 50);
});

View file

@ -14,7 +14,8 @@ TABS.sensors.initialize = function (callback) {
SENSOR_DATA.accelerometer[i] = 0;
SENSOR_DATA.gyroscope[i] = 0;
SENSOR_DATA.magnetometer[i] = 0;
SENSOR_DATA.sonar[i] = 0;
SENSOR_DATA.sonar = 0;
SENSOR_DATA.altitude = 0;
SENSOR_DATA.debug[i] = 0;
}
}

View file

@ -3,10 +3,6 @@
.tab-servos input[type="number"]::-webkit-inner-spin-button {
border: 0;
}
.tab-servos .supported_wrapper,
.tab-servos .direction_wrapper {
display: none;
}
.tab-servos .title {
margin-top: 10px;
@ -129,4 +125,17 @@
}
.tab-servos .update:hover {
background-color: #dedcdc;
}
}
.tab-servos .require-support {
display:none;
}
.tab-servos.supported .require-support {
display:block;
}
.tab-servos .require-upgrade {
display:block;
}
.tab-servos.supported .require-upgrade {
display:none;
}

View file

@ -1,6 +1,5 @@
<div class="tab-servos">
<span i18n="servosModel"></span> <strong class="model"></strong>
<div class="supported_wrapper">
<div class="require-support">
<div class="title" i18n="servosChangeDirection"></div>
<table class="fields">
<tr class="main">
@ -8,21 +7,14 @@
<th style="width: 120px" i18n="servosMid"></th>
<th style="width: 120px" i18n="servosMin"></th>
<th style="width: 120px" i18n="servosMax"></th>
<th style="width: 120px" i18n="servosAngleAtMin"></th>
<th style="width: 120px" i18n="servosAngleAtMax"></th>
<th style="width: 40px">CH1</th>
<th style="width: 40px">CH2</th>
<th style="width: 40px">CH3</th>
<th style="width: 40px">CH4</th>
</tr>
</table>
<div class="direction_wrapper">
<div class="title" style="width: 258px" i18n="servosGyroAccelDirection"></div>
<table class="directions" style="width: 260px">
<tr class="main">
<th style="width: 200px" i18n="servosName"></th>
<th i18n="servosDirection"></th>
</tr>
</table>
</div>
<div class="live">
<span i18n="servosLiveMode"></span> <input type="checkbox" />
</div>
@ -30,4 +22,7 @@
<a class="update" href="#" i18n="servosButtonSave"></a>
</div>
</div>
<div class="note require-upgrade" i18n="servosFirmwareUpgradeRequired">
</div>
</div>

View file

@ -1,9 +1,3 @@
/* Please don't take code in this file very seriously !!!
I was "kinda" forced to write this implementation "this way" because the Servo code implementation
from multiwii is so horrible, obstructive and non dynamic, not to mention it doesn't make any sense
that there was just no other way around this then hardcoding/implementing each model separately.
*/
'use strict';
TABS.servos = {};
@ -15,15 +9,25 @@ TABS.servos.initialize = function (callback) {
googleAnalytics.sendAppView('Servos');
}
function get_servo_conf_data() {
MSP.send_message(MSP_codes.MSP_SERVO_CONF, false, false, get_channel_forwarding_data);
function get_servo_configurations() {
MSP.send_message(MSP_codes.MSP_SERVO_CONFIGURATIONS, false, false, get_servo_mix_rules);
}
function get_channel_forwarding_data() {
MSP.send_message(MSP_codes.MSP_CHANNEL_FORWARDING, false, false, get_rc_data);
function get_servo_mix_rules() {
MSP.send_message(MSP_codes.MSP_SERVO_MIX_RULES, false, false, get_channel_forwarding);
}
function get_rc_data() {
function get_channel_forwarding() {
var nextFunction = get_rc_data;
if (semver.lt(CONFIG.apiVersion, "1.12.0")) {
MSP.send_message(MSP_codes.MSP_CHANNEL_FORWARDING, false, false, nextFunction);
} else {
nextFunction();
}
}
function get_rc_data() {
MSP.send_message(MSP_codes.MSP_RC, false, false, get_boxnames_data);
}
@ -35,10 +39,18 @@ TABS.servos.initialize = function (callback) {
$('#content').load("./tabs/servos.html", process_html);
}
MSP.send_message(MSP_codes.MSP_IDENT, false, false, get_servo_conf_data);
function process_html() {
MSP.send_message(MSP_codes.MSP_IDENT, false, false, get_servo_configurations);
function update_ui() {
if (semver.lt(CONFIG.apiVersion, "1.12.0")) {
$(".tab-servos").removeClass("supported");
return;
}
$(".tab-servos").addClass("supported");
var servoCheckbox = '';
var servoHeader = '';
for (var i = 0; i < RC.active_channels-4; i++) {
@ -46,94 +58,52 @@ TABS.servos.initialize = function (callback) {
<th >A' + (i+1) + '</th>\
';
}
servoHeader = servoHeader + '<th style="width: 200px" i18n="servosDirection"></th>';
servoHeader = servoHeader + '<th style="width: 200px" i18n="servosDirectionAndRate"></th>';
for (var i = 0; i < RC.active_channels; i++) {
servoCheckbox = servoCheckbox + '\
<td class="channel"><input type="checkbox"/></td>\
';
}
$('div.tab-servos table.fields tr.main').append(servoHeader);
function process_directions(name, obj, bitpos) {
$('div.direction_wrapper').show();
var val;
$('div.tab-servos table.directions').append('\
<tr>\
<td class="name" style="text-align: center">' + name + '</td>\
<td class="direction" style="text-align: right">\
<select name="direction">\
<option value="0">' + chrome.i18n.getMessage('servosNormal') + '</option>\
<option value="1">' + chrome.i18n.getMessage('servosReverse') + '</option>\
</select>\
</td>\
</tr>\
');
if (bit_check(SERVO_CONFIG[obj].rate, bitpos)) val = 1;
else val = 0;
$('div.tab-servos table.directions tr:last select').val(val);
$('div.tab-servos table.directions tr:last select').data('info', {'obj': obj, 'bitpos': bitpos});
}
function process_servos(name, alternate, obj, directions) {
function process_servos(name, alternate, obj) {
$('div.supported_wrapper').show();
$('div.tab-servos table.fields').append('\
<tr> \
<td style="text-align: center">' + name + '</td>\
<td class="middle"><input type="number" min="1000" max="2000" value="' + SERVO_CONFIG[obj].middle + '" /></td>\
<td class="min"><input type="number" min="1000" max="2000" value="' + SERVO_CONFIG[obj].min +'" /></td>\
<td class="max"><input type="number" min="1000" max="2000" value="' + SERVO_CONFIG[obj].max +'" /></td>\
<td class="middle"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].middle + '" /></td>\
<td class="min"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].min +'" /></td>\
<td class="max"><input type="number" min="500" max="2500" value="' + SERVO_CONFIG[obj].max +'" /></td>\
<td class="angleAtMin"><input type="number" min="-90" max="0" value="' + SERVO_CONFIG[obj].angleAtMin +'" /></td>\
<td class="angleAtMax"><input type="number" min="0" max="90" value="' + SERVO_CONFIG[obj].angleAtMax +'" /></td>\
' + servoCheckbox + '\
<td class="direction">\
<input class="first" type="checkbox"/><span class="name">' + name + '</span>\
<input class="second" type="checkbox"/><span class="alternate">' + alternate + '</span>\
</td>\
</tr> \
');
// translate to user-selected language
localize();
$('div.tab-servos table.fields tr:last td.channel input').eq(SERVO_CONFIG[obj].indexOfChannelToForward).prop('checked', true);
if (directions == true) {
$('div.tab-servos table.fields tr:last td.direction input:first').prop('checked', bit_check(SERVO_CONFIG[obj].rate, 0));
$('div.tab-servos table.fields tr:last td.direction input:last').prop('checked', bit_check(SERVO_CONFIG[obj].rate, 1));
} else if (directions == 2) {
// removing checkboxes
$('div.tab-servos table.fields tr:last td.direction').html('');
// adding select box and generating options
$('div.tab-servos table.fields tr:last td.direction').append('\
<select class="rate" name="rate"></select>\
');
var select = $('div.tab-servos table.fields tr:last td.direction select');
for (var i = 100; i > -101; i--) {
select.append('<option value="' + i + '">Rate: ' + i + '%</option>');
}
// select current rate
select.val(SERVO_CONFIG[obj].rate);
} else {
// removing checkboxes
$('div.tab-servos table.fields tr:last td.direction').html('');
if (SERVO_CONFIG[obj].indexOfChannelToForward >= 0) {
$('div.tab-servos table.fields tr:last td.channel input').eq(SERVO_CONFIG[obj].indexOfChannelToForward).prop('checked', true);
}
// adding select box and generating options
$('div.tab-servos table.fields tr:last td.direction').append('\
<select class="rate" name="rate"></select>\
');
var select = $('div.tab-servos table.fields tr:last td.direction select');
for (var i = 100; i > -101; i--) {
select.append('<option value="' + i + '">Rate: ' + i + '%</option>');
}
// select current rate
select.val(SERVO_CONFIG[obj].rate);
$('div.tab-servos table.fields tr:last').data('info', {'obj': obj});
// UI hooks
@ -146,18 +116,7 @@ TABS.servos.initialize = function (callback) {
});
}
function servos_update(save_to_eeprom) {
// update bitfields
$('div.tab-servos table.directions tr:not(".main")').each(function () {
var info = $('select', this).data('info');
var val = parseInt($('select', this).val());
// in this stage we need to know which bitfield and which bitposition needs to be flipped
if (val) SERVO_CONFIG[info.obj].rate = bit_set(SERVO_CONFIG[info.obj].rate, info.bitpos);
else SERVO_CONFIG[info.obj].rate = bit_clear(SERVO_CONFIG[info.obj].rate, info.bitpos);
});
// update the rest
function servos_update(save_configuration_to_eeprom) {
$('div.tab-servos table.fields tr:not(".main")').each(function () {
var info = $(this).data('info');
@ -174,122 +133,37 @@ TABS.servos.initialize = function (callback) {
SERVO_CONFIG[info.obj].middle = parseInt($('.middle input', this).val());
SERVO_CONFIG[info.obj].min = parseInt($('.min input', this).val());
SERVO_CONFIG[info.obj].max = parseInt($('.max input', this).val());
SERVO_CONFIG[info.obj].angleAtMin = parseInt($('.angleAtMin input', this).val());
SERVO_CONFIG[info.obj].angleAtMax = parseInt($('.angleAtMax input', this).val());
// update rate if direction fields exist
if ($('.direction input', this).length) {
if ($('.direction input:first', this).is(':checked')) SERVO_CONFIG[info.obj].rate = bit_set(SERVO_CONFIG[info.obj].rate, 0);
else SERVO_CONFIG[info.obj].rate = bit_clear(SERVO_CONFIG[info.obj].rate, 0);
if ($('.direction input:last', this).is(':checked')) SERVO_CONFIG[info.obj].rate = bit_set(SERVO_CONFIG[info.obj].rate, 1);
else SERVO_CONFIG[info.obj].rate = bit_clear(SERVO_CONFIG[info.obj].rate, 1);
} else if ($('.direction select', this).length) {
var val = parseInt($('.direction select', this).val());
SERVO_CONFIG[info.obj].rate = val;
}
var val = parseInt($('.direction select', this).val());
SERVO_CONFIG[info.obj].rate = val;
});
MSP.send_message(MSP_codes.MSP_SET_CHANNEL_FORWARDING, MSP.crunch(MSP_codes.MSP_SET_CHANNEL_FORWARDING), false, function () {
MSP.send_message(MSP_codes.MSP_SET_SERVO_CONF, MSP.crunch(MSP_codes.MSP_SET_SERVO_CONF), false, function () {
if (save_to_eeprom) {
// Save changes to EEPROM
MSP.send_message(MSP_codes.MSP_EEPROM_WRITE, false, false, function () {
GUI.log(chrome.i18n.getMessage('servosEepromSave'));
});
}
});
});
//
// send data to FC
//
MSP.sendServoConfigurations(send_servo_mixer_rules);
function send_servo_mixer_rules() {
MSP.sendServoConfigurations(save_to_eeprom);
}
function save_to_eeprom() {
if (save_configuration_to_eeprom) {
MSP.send_message(MSP_codes.MSP_EEPROM_WRITE, false, false, function () {
GUI.log(chrome.i18n.getMessage('servosEepromSave'));
});
}
}
}
// drop previous table
$('div.tab-servos table.fields tr:not(:first)').remove();
var model = $('div.tab-servos strong.model');
var supported_models = [1, 4, 5, 8, 14, 20, 21];
switch (CONFIG.multiType) {
case 1: // TRI
// looking ok so far
model.text('TRI');
process_directions('YAW', 5, 0);
process_servos('Yaw Servo', '', 5, false);
break;
case 4: // BI
// looking ok so far
model.text('BI');
process_directions('L YAW', 4, 1);
process_directions('R YAW', 5, 1);
process_directions('L NICK', 4, 0);
process_directions('R NICK', 5, 0);
process_servos('Left Servo', '', 4, false);
process_servos('Right Servo', '', 5, false);
break;
case 5: // Gimbal
// needs to be verified
model.text('Gimbal');
// rate
process_servos('Pitch Servo', '', 0, 2);
process_servos('Roll Servo', '', 1, 2);
break;
case 8: // Flying Wing
// looking ok so far
model.text('Flying Wing');
process_directions('L ROLL', 3, 1);
process_directions('R ROLL', 4, 1);
process_directions('L NICK', 3, 0);
process_directions('R NICK', 4, 0);
process_servos('Left Wing', '', 3, false);
process_servos('Right Wing', '', 4, false);
break;
case 14: // Airplane
model.text('Airplane');
// rate
process_servos('Wing 1', '', 3, 2);
process_servos('Wing 2', '', 4, 2);
process_servos('Rudd', '', 5, 2);
process_servos('Elev', '', 6, 2);
break;
case 20: // Dualcopter
// looking ok so far
model.text('Dualcopter');
process_directions('PITCH', 4, 0);
process_directions('ROLL', 5, 0);
process_servos('Roll', '', 5, false);
process_servos('Nick', '', 4, false);
break;
case 21: // Singlecopter
// looking ok so far
model.text('Singlecopter');
process_servos('Right', 'R YAW', 3, true);
process_servos('Left', 'L YAW', 4, true);
process_servos('Front', 'F YAW', 5, true);
process_servos('Rear', 'YAW', 6, true);
break;
default:
model.text(chrome.i18n.getMessage('servosModelNoSupport'));
// implementation of feature servo_tilt
if (AUX_CONFIG.indexOf('CAMSTAB') > -1 || AUX_CONFIG.indexOf('CAMTRIG') > -1) {
// Gimbal on
// needs to be verified
model.text('Gimbal / Tilt Servos');
// rate
process_servos('Pitch Servo', '', 0, 2);
process_servos('Roll Servo', '', 1, 2);
}
for (var servoIndex = 0; servoIndex < 8; servoIndex++) {
process_servos('Servo ' + servoIndex, '', servoIndex, false);
}
// UI hooks for dynamically generated elements
@ -299,14 +173,20 @@ TABS.servos.initialize = function (callback) {
GUI.timeout_add('servos_update', servos_update, 10);
}
});
$('a.update').click(function () {
// standard check for supported_models + custom implementation for feature servo_tilt
if (supported_models.indexOf(CONFIG.multiType) != -1 || AUX_CONFIG.indexOf('CAMSTAB') > -1 || AUX_CONFIG.indexOf('CAMTRIG') > -1) {
servos_update(true);
}
servos_update(true);
});
}
function process_html() {
update_ui();
// translate to user-selected language
localize();
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function () {
MSP.send_message(MSP_codes.MSP_STATUS);
@ -318,4 +198,4 @@ TABS.servos.initialize = function (callback) {
TABS.servos.cleanup = function (callback) {
if (callback) callback();
};
};

View file

@ -68,12 +68,23 @@
border: 1px solid silver;
background-color: white;
}
.tab-setup #interactive_block .heading {
.tab-setup #interactive_block .attitude {
float: right;
height: 15px;
margin: 10px 10px 0px 0px
}
margin: 10px 10px 0 0;
font-weight: bold;
.tab-setup #interactive_block .attitude dt {
float: left;
width: 64px;
font-weight: 700;
text-align: right
}
.tab-setup #interactive_block .attitude dd {
display: block;
margin-left: 64px;
width: 64px;
text-align: right
}
.tab-setup #interactive_block a.reset {
position: absolute;

View file

@ -25,7 +25,13 @@
<div id="canvas_wrapper">
<canvas id="canvas"></canvas>
</div>
<span class="heading"></span>
<div class="attitude">
<dl>
<dt>Heading:</dt><dd class="heading">&nbsp;</dd>
<dt>Pitch:</dt><dd class="pitch">&nbsp;</dd>
<dt>Roll:</dt><dd class="roll">&nbsp;</dd>
</dl>
</div>
<a class="reset" href="#" i18n="initialSetupButtonResetZaxis"></a>
</div>
<div class="block info">

View file

@ -47,8 +47,13 @@ TABS.setup.initialize = function (callback) {
// initialize 3D
self.initialize3D();
// set roll in interactive block
$('span.roll').text(chrome.i18n.getMessage('initialSetupAttitude', [0]));
// set pitch in interactive block
$('span.pitch').text(chrome.i18n.getMessage('initialSetupAttitude', [0]));
// set heading in interactive block
$('span.heading').text(chrome.i18n.getMessage('initialSetupheading', [0]));
$('span.heading').text(chrome.i18n.getMessage('initialSetupAttitude', [0]));
// check if we have magnetometer
if (!bit_check(CONFIG.activeSensors, 2)) {
@ -151,7 +156,9 @@ TABS.setup.initialize = function (callback) {
gpsSats_e = $('.gpsSats'),
gpsLat_e = $('.gpsLat'),
gpsLon_e = $('.gpsLon'),
heading_e = $('span.heading');
roll_e = $('dd.roll'),
pitch_e = $('dd.pitch'),
heading_e = $('dd.heading');
function get_slow_data() {
MSP.send_message(MSP_codes.MSP_STATUS);
@ -175,7 +182,9 @@ TABS.setup.initialize = function (callback) {
function get_fast_data() {
MSP.send_message(MSP_codes.MSP_ATTITUDE, false, false, function () {
heading_e.text(chrome.i18n.getMessage('initialSetupheading', [SENSOR_DATA.kinematics[2]]));
roll_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[0]]));
pitch_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[1]]));
heading_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[2]]));
self.render3D();
self.updateInstruments();
});
@ -216,13 +225,15 @@ TABS.setup.initialize3D = function (compatibility) {
renderer = new THREE.WebGLRenderer({canvas: canvas.get(0), alpha: true, antialias: true});
useWebGlRenderer = true;
} else {
renderer = new THREE.CanvasRenderer({canvas: canvas.get(0), alpha: true});
}
// initialize render size for current canvas size
renderer.setSize(wrapper.width(), wrapper.height());
// modelWrapper just adds an extra axis of rotation to avoid gimbal lock withe euler angles
// // modelWrapper adds an extra axis of rotation to avoid gimbal lock with the euler angles
modelWrapper = new THREE.Object3D()
//
// load the model including materials
if (useWebGlRenderer) {
model_file = mixerList[CONFIG.multiType - 1].model;
@ -237,30 +248,28 @@ TABS.setup.initialize3D = function (compatibility) {
useLegacyCustomModel = true;
}
// setup scene
scene = new THREE.Scene();
loader = new THREE.JSONLoader();
loader.load('./resources/models/' + model_file + '.json', function (geometry, materials) {
model = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial(materials));
var modelMaterial = new THREE.MeshFaceMaterial(materials);
model = new THREE.Mesh(geometry, modelMaterial);
model.scale.set(15, 15, 15);
modelWrapper.add(model);
scene.add(modelWrapper);
});
// stacionary camera
// stationary camera
camera = new THREE.PerspectiveCamera(50, wrapper.width() / wrapper.height(), 1, 10000);
// setup scene
scene = new THREE.Scene();
// some light
light = new THREE.AmbientLight(0x404040);
light2 = new THREE.DirectionalLight(new THREE.Color(1, 1, 1), 1.5);
light2.position.set(0, 1, 0);
// initialize render size for current canvas size
renderer.setSize(wrapper.width(), wrapper.height());
// move camera away from the model
camera.position.z = 125;