diff --git a/background.js b/background.js index cddc9572..7a2ef08e 100644 --- a/background.js +++ b/background.js @@ -1,11 +1,11 @@ /* resizable: false - Keep in mind this only disables the side/corner resizing via mouse, nothing more maxWidth / maxHeight - is defined to prevent application reaching maximized state through window manager - + We are setting Bounds through setBounds method after window was created because on linux setting Bounds as window.create property seemed to fail, probably because "previous" bounds was used instead according to docs. - - bounds - Size and position of the content in the window (excluding the titlebar). + + bounds - Size and position of the content in the window (excluding the titlebar). If an id is also specified and a window with a matching id has been shown before, the remembered bounds of the window will be used instead. */ function start_app() { @@ -16,19 +16,19 @@ function start_app() { }, function(createdWindow) { // set window size createdWindow.setBounds({'width': 962, 'height': 650}); - + // bind events createdWindow.onMaximized.addListener(function() { createdWindow.restore(); }); - + createdWindow.onClosed.addListener(function() { // connectionId is passed from the script side through the chrome.runtime.getBackgroundPage refference // allowing us to automatically close the port when application shut down - + // save connectionId in separate variable before app_window is destroyed var connectionId = app_window.serial.connectionId; - + if (connectionId > 0) { chrome.serial.disconnect(connectionId, function(result) { console.log('SERIAL: Connection closed - ' + result); @@ -43,8 +43,8 @@ chrome.app.runtime.onLaunched.addListener(function() { }); chrome.runtime.onInstalled.addListener(function(details) { - if (details.reason == 'update') { - var manifest = chrome.runtime.getManifest(); + if (details.reason == 'update') { + var manifest = chrome.runtime.getManifest(); var options = { priority: 0, type: 'basic', @@ -53,7 +53,7 @@ chrome.runtime.onInstalled.addListener(function(details) { iconUrl: '/images/icon_128.png', buttons: [{'title': 'Click this button to start the application'}] }; - + chrome.notifications.create('baseflight_update', options, function(notificationId) { // empty }); diff --git a/css/style.css b/css/style.css index d4733b0c..45950255 100644 --- a/css/style.css +++ b/css/style.css @@ -21,15 +21,15 @@ a:hover { } input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; - + display: block; opacity: 1; /* required for chromium 33+ beta */ - + width: 15px; - + background-image: url('../images/arrows.png'); background-repeat: no-repeat; - + border-left: 1px solid silver; } .clear-both { @@ -47,44 +47,44 @@ input[type="number"]::-webkit-inner-spin-button { #frame { height: 25px; line-height: 25px; - + -webkit-app-region: drag; - + background-color: white; } #frame .title { float: left; - + margin-left: 15px; - + font-weight: bold; /* text-shadow: 1px 1px #e4e4e4; */ } #frame .minimize { float: right; display: block; - + width: 26px; height: 20px; - + background: url('../images/controls/minimize.png') no-repeat 0 0; } #frame .maximize { float: right; display: block; - + width: 27px; height: 20px; - + background: url('../images/controls/maximize.png') no-repeat 0 0; - } + } #frame .close { float: right; display: block; - + width: 45px; height: 20px; - + background: url('../images/controls/close.png') no-repeat 0 0; } #frame a { @@ -93,7 +93,7 @@ input[type="number"]::-webkit-inner-spin-button { } #frame a:hover { /* hover is disabled untill webkit-app-region: drag; starts to fire events or chromium implements native frame */ - /* background-position: 0px -20px; */ + /* background-position: 0px -20px; */ } #frame a:active { background-position: 0px -40px; @@ -101,12 +101,12 @@ input[type="number"]::-webkit-inner-spin-button { #main-wrapper { width: 940px; margin: 10px auto 0 auto; - + padding: 0 10px 0 10px; } #port-picker { float: left; - + height: 22px; margin-bottom: 10px; } @@ -116,10 +116,10 @@ input[type="number"]::-webkit-inner-spin-button { #port-picker select { height: 20px; line-height: 20px; - + float: left; margin-right: 10px; - + border: 1px solid silver; } #port-picker #port { @@ -133,19 +133,19 @@ input[type="number"]::-webkit-inner-spin-button { } #port-picker a { float: left; - + display: block; - + width: 80px; height: 18px; border: 1px solid silver; - + line-height: 18px; text-align: center; } #port-picker a.connect { margin-right: 10px; - + background-color: #be2222; color: white; } @@ -161,32 +161,32 @@ input[type="number"]::-webkit-inner-spin-button { } #port-picker input.auto_connect { float: left; - + margin-top: 4px; } #port-picker span.auto_connect { float: left; - + margin: 3px 0 0 5px; } #sensor-status { float: right; - + height: 22px; line-height: 22px; } #sensor-status li { float: left; - + margin-left: 10px; padding: 0 12px 0 12px; - + height: 18px; line-height: 18px; - + color: white; text-align: center; - + border: 1px solid #c0c0c0; border-radius: 8px; background-color: #be2222; @@ -194,45 +194,45 @@ input[type="number"]::-webkit-inner-spin-button { #sensor-status .on { background-color: #0d8b13; } -#log { +#log { margin-bottom: 10px; - + height: 60px; - + border: 1px solid silver; background-color: white; - + overflow-y: scroll; -} +} #log .wrapper { padding: 5px; - + -webkit-user-select: text; - } + } #tabs { position: absolute; margin-top: 1px; - + z-index: 10; - + font-weight: bold; } #tabs li { float: left; margin-right: 5px; - + border: 1px solid #848484; border-bottom: 0; } #tabs li a { display: block; - + height: 15px; - + padding: 5px; padding-left: 12px; padding-right: 12px; - + background-color: #d0d0d0; } #tabs li a:hover { @@ -243,38 +243,38 @@ input[type="number"]::-webkit-inner-spin-button { } #tabs li.active a { height: 16px; - - background-color: white; + + background-color: white; } - #tabs li.active a:hover { + #tabs li.active a:hover { cursor: default; - background-color: white; - } + background-color: white; + } #content { margin-top: 37px; - + padding: 10px; - + background-color: white; height: 429px; - + overflow-x: hidden; overflow-y: auto; - + border: 1px solid #848484; - + -webkit-transform: rotateX(0deg); /* DO NOT REMOVE! this fixes the UI freezing bug on MAC OS X */ -} +} #status-bar { margin-left: -10px; margin-top: 10px; - + width: 940px; height: 20px; line-height: 20px; - + padding: 0 10px 0 10px; - + border-top: 1px solid #7d7d79; background-color: #bfbeb5; } \ No newline at end of file diff --git a/js/backup_restore.js b/js/backup_restore.js index c91ec712..2eef02bc 100644 --- a/js/backup_restore.js +++ b/js/backup_restore.js @@ -17,27 +17,27 @@ function configuration_backup() { }); }); }); - + var backup = function() { var chosenFileEntry = null; - + var accepts = [{ extensions: ['txt'] }]; - + // generate timestamp for the backup file var d = new Date(); - var now = d.getUTCFullYear() + '.' + d.getDate() + '.' + (d.getMonth() + 1) + '.' + d.getHours() + '.' + d.getMinutes(); + var now = d.getUTCFullYear() + '.' + d.getDate() + '.' + (d.getMonth() + 1) + '.' + d.getHours() + '.' + d.getMinutes(); // create or load the file chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: 'bf_mw_backup_' + now, accepts: accepts}, function(fileEntry) { if (!fileEntry) { console.log('No file selected, backup aborted.'); - + return; } - - chosenFileEntry = fileEntry; + + chosenFileEntry = fileEntry; // echo/console log path specified chrome.fileSystem.getDisplayPath(chosenFileEntry, function(path) { @@ -50,7 +50,7 @@ function configuration_backup() { chrome.fileSystem.isWritableEntry(fileEntryWritable, function(isWritable) { if (isWritable) { chosenFileEntry = fileEntryWritable; - + // create config object that will be used to store all downloaded data var configuration = { 'firmware_version': CONFIG.version, @@ -61,29 +61,29 @@ function configuration_backup() { 'AccelTrim': CONFIG.accelerometerTrims, 'MISC': MISC }; - + // crunch the config object var serialized_config_object = JSON.stringify(configuration); var blob = new Blob([serialized_config_object], {type: 'text/plain'}); // first parameter for Blob needs to be an array - + chosenFileEntry.createWriter(function(writer) { writer.onerror = function (e) { console.error(e); }; - + var truncated = false; writer.onwriteend = function() { if (!truncated) { // onwriteend will be fired again when truncation is finished truncated = true; writer.truncate(blob.size); - + return; } - + console.log('Write SUCCESSFUL'); }; - + writer.write(blob); }, function (e) { console.error(e); @@ -100,25 +100,25 @@ function configuration_backup() { function configuration_restore() { var chosenFileEntry = null; - + var accepts = [{ extensions: ['txt'] }]; - + // load up the file chrome.fileSystem.chooseEntry({type: 'openFile', accepts: accepts}, function(fileEntry) { if (!fileEntry) { console.log('No file selected, restore aborted.'); - + return; } - - chosenFileEntry = fileEntry; - + + chosenFileEntry = fileEntry; + // echo/console log path specified chrome.fileSystem.getDisplayPath(chosenFileEntry, function(path) { console.log('Restore file path: ' + path); - }); + }); // read contents into variable chosenFileEntry.file(function(file) { @@ -131,31 +131,31 @@ function configuration_restore() { reader.abort(); } }; - + reader.onloadend = function(e) { if (e.total != 0 && e.total == e.loaded) { console.log('Read SUCCESSFUL'); - + try { // check if string provided is a valid JSON var deserialized_configuration_object = JSON.parse(e.target.result); } catch (e) { // data provided != valid json object console.log('Data provided != valid JSON string, restore aborted.'); - + return; } - + // replacing "old configuration" with configuration from backup file var configuration = deserialized_configuration_object; - + // some configuration.VERSION code goes here? will see - + PIDs = configuration.PID; AUX_CONFIG_values = configuration.AUX_val; RC_tuning = configuration.RC; CONFIG.accelerometerTrims = configuration.AccelTrim; MISC = configuration.MISC; - + // all of the arrays/objects are set, upload changes configuration_upload(); } @@ -177,11 +177,11 @@ function configuration_upload() { var PID_buffer_needle = 0; for (var i = 0; i < PIDs.length; i++) { switch (i) { - case 0: - case 1: - case 2: - case 3: - case 7: + case 0: + case 1: + case 2: + case 3: + case 7: case 8: case 9: PID_buffer_out[PID_buffer_needle] = parseInt(PIDs[i][0] * 10); @@ -193,20 +193,20 @@ function configuration_upload() { PID_buffer_out[PID_buffer_needle + 1] = parseInt(PIDs[i][1] * 100); PID_buffer_out[PID_buffer_needle + 2] = parseInt(PIDs[i][2]); break; - case 5: + case 5: case 6: PID_buffer_out[PID_buffer_needle] = parseInt(PIDs[i][0] * 10); PID_buffer_out[PID_buffer_needle + 1] = parseInt(PIDs[i][1] * 100); PID_buffer_out[PID_buffer_needle + 2] = parseInt(PIDs[i][2] * 1000); - break; + break; } PID_buffer_needle += 3; } - + // Send over the PID changes send_message(MSP_codes.MSP_SET_PID, PID_buffer_out, false, function() { rc_tuning(); - }); + }); var rc_tuning = function() { // RC Tuning section @@ -218,23 +218,23 @@ function configuration_upload() { RC_tuning_buffer_out[4] = parseInt(RC_tuning.dynamic_THR_PID * 100); RC_tuning_buffer_out[5] = parseInt(RC_tuning.throttle_MID * 100); RC_tuning_buffer_out[6] = parseInt(RC_tuning.throttle_EXPO * 100); - + // Send over the RC_tuning changes send_message(MSP_codes.MSP_SET_RC_TUNING, RC_tuning_buffer_out, false, function() { aux(); }); }; - + var aux = function() { // AUX section var AUX_val_buffer_out = new Array(); - + var needle = 0; for (var i = 0; i < AUX_CONFIG_values.length; i++) { AUX_val_buffer_out[needle++] = lowByte(AUX_CONFIG_values[i]); AUX_val_buffer_out[needle++] = highByte(AUX_CONFIG_values[i]); } - + // Send over the AUX changes send_message(MSP_codes.MSP_SET_BOX, AUX_val_buffer_out, false, function() { trim(); @@ -248,7 +248,7 @@ function configuration_upload() { buffer_out[0] = lowByte(CONFIG.accelerometerTrims[0]); buffer_out[1] = highByte(CONFIG.accelerometerTrims[0]); buffer_out[2] = lowByte(CONFIG.accelerometerTrims[1]); - buffer_out[3] = highByte(CONFIG.accelerometerTrims[1]); + buffer_out[3] = highByte(CONFIG.accelerometerTrims[1]); // Send over the new trims send_message(MSP_codes.MSP_SET_ACC_TRIM, buffer_out, false, function() { @@ -282,7 +282,7 @@ function configuration_upload() { buffer_out[19] = MISC.vbatmincellvoltage; buffer_out[20] = MISC.vbatmaxcellvoltage; buffer_out[21] = 0; // vbatlevel_crit (unused) - + // Send ove the new MISC send_message(MSP_codes.MSP_SET_MISC, buffer_out, false, function() { // Save changes to EEPROM @@ -290,5 +290,5 @@ function configuration_upload() { GUI.log('EEPROM saved'); }); }); - }; + }; } \ No newline at end of file diff --git a/js/data_storage.js b/js/data_storage.js index e1d0e1e5..d8cd27d1 100644 --- a/js/data_storage.js +++ b/js/data_storage.js @@ -10,7 +10,7 @@ var CONFIG = { activeSensors: 0, mode: 0, profile: 0, - + uid: [0, 0, 0], accelerometerTrims: [0, 0] }; @@ -71,7 +71,7 @@ var GPS_DATA = { distanceToHome: 0, ditectionToHome: 0, update: 0, - + // baseflight specific gps stuff chn: new Array(), svid: new Array(), diff --git a/js/gui.js b/js/gui.js index c5db096a..caa806db 100644 --- a/js/gui.js +++ b/js/gui.js @@ -6,7 +6,7 @@ var GUI_control = function() { this.operating_system; this.interval_array = []; this.timeout_array = []; - + // check which operating system is user running if (navigator.appVersion.indexOf("Win") != -1) this.operating_system = "Windows"; else if (navigator.appVersion.indexOf("Mac") != -1) this.operating_system = "MacOS"; @@ -23,19 +23,19 @@ var GUI_control = function() { // first = true/false if code should be ran initially before next timer interval hits GUI_control.prototype.interval_add = function(name, code, interval, first) { var data = {'name': name, 'timer': undefined, 'code': code, 'interval': interval, 'fired': 0, 'paused': false}; - + if (first == true) { code(); // execute code - + data.fired++; // increment counter } - + data.timer = setInterval(function() { code(); // execute code - + data.fired++; // increment counter }, interval); - + this.interval_array.push(data); // push to primary interval array }; @@ -44,13 +44,13 @@ GUI_control.prototype.interval_remove = function(name) { for (var i = 0; i < this.interval_array.length; i++) { if (this.interval_array[i].name == name) { clearInterval(this.interval_array[i].timer); // stop timer - + this.interval_array.splice(i, 1); // remove element/object from array - + return true; } } - + return false; }; @@ -60,11 +60,11 @@ GUI_control.prototype.interval_pause = function(name) { if (this.interval_array[i].name == name) { clearInterval(this.interval_array[i].timer); this.interval_array[i].paused = true; - + return true; } } - + return false; }; @@ -73,19 +73,19 @@ GUI_control.prototype.interval_resume = function(name) { for (var i = 0; i < this.interval_array.length; i++) { if (this.interval_array[i].name == name && this.interval_array[i].paused) { var obj = this.interval_array[i]; - + obj.timer = setInterval(function() { obj.code(); // execute code - + obj.fired++; // increment counter }, obj.interval); - + obj.paused = false; - + return true; } } - + return false; }; @@ -94,7 +94,7 @@ GUI_control.prototype.interval_resume = function(name) { GUI_control.prototype.interval_kill_all = function(keep_array) { var self = this; var timers_killed = 0; - + for (var i = (this.interval_array.length - 1); i >= 0; i--) { // reverse iteration var keep = false; if (keep_array) { // only run through the array if it exists @@ -104,17 +104,17 @@ GUI_control.prototype.interval_kill_all = function(keep_array) { } }); } - + if (!keep) { clearInterval(this.interval_array[i].timer); // stop timer this.interval_array[i].timer = undefined; // set timer property to undefined (mostly for debug purposes, but it doesn't hurt to have it here) - + this.interval_array.splice(i, 1); // remove element/object from array - + timers_killed++; } } - + return timers_killed; }; @@ -126,10 +126,10 @@ GUI_control.prototype.timeout_add = function(name, code, timeout) { // start timer with "cleaning" callback var timer = setTimeout(function() { code(); // execute code - + self.timeout_remove(name); // cleanup }, timeout); - + this.timeout_array.push({'name': name, 'timer': timer, 'timeout': timeout}); // push to primary timeout array }; @@ -138,13 +138,13 @@ GUI_control.prototype.timeout_remove = function(name) { for (var i = 0; i < this.timeout_array.length; i++) { if (this.timeout_array[i].name == name) { clearTimeout(this.timeout_array[i].timer); // stop timer - + this.timeout_array.splice(i, 1); // remove element/object from array - + return true; } } - + return false; }; @@ -152,15 +152,15 @@ GUI_control.prototype.timeout_remove = function(name) { // return = returns timers killed in last call GUI_control.prototype.timeout_kill_all = function() { var timers_killed = 0; - + for (var i = 0; i < this.timeout_array.length; i++) { clearTimeout(this.timeout_array[i].timer); // stop timer - + timers_killed++; } - + this.timeout_array = []; // drop objects - + return timers_killed; }; @@ -168,12 +168,12 @@ GUI_control.prototype.timeout_kill_all = function() { GUI_control.prototype.log = function(message) { var command_log = $('div#log'); var d = new Date(); - var time = ((d.getHours() < 10) ? '0' + d.getHours(): d.getHours()) - + ':' + ((d.getMinutes() < 10) ? '0' + d.getMinutes(): d.getMinutes()) + var time = ((d.getHours() < 10) ? '0' + d.getHours(): d.getHours()) + + ':' + ((d.getMinutes() < 10) ? '0' + d.getMinutes(): d.getMinutes()) + ':' + ((d.getSeconds() < 10) ? '0' + d.getSeconds(): d.getSeconds()); - + $('div.wrapper', command_log).append('
' + time + ' -- ' + message + '
'); - command_log.scrollTop($('div.wrapper', command_log).height()); + command_log.scrollTop($('div.wrapper', command_log).height()); }; // Method is called every time a valid tab change event is received @@ -181,56 +181,56 @@ GUI_control.prototype.log = function(message) { // default switch doesn't require callback to be set GUI_control.prototype.tab_switch_cleanup = function(callback) { MSP.callbacks_cleanup(); // we don't care about any old data that might or might not arrive - + switch (this.active_tab) { case 'initial_setup': GUI.interval_remove('initial_setup_data_pull'); - + if (callback) callback(); break; case 'pid_tuning': GUI.interval_remove('pid_data_poll'); - + if (callback) callback(); break; case 'receiver': GUI.interval_remove('receiver_poll'); - + if (callback) callback(); break; case 'auxiliary_configuration': GUI.interval_remove('aux_data_poll'); - + if (callback) callback(); break; case 'servos': GUI.interval_remove('servos_data_poll'); - + if (callback) callback(); break; case 'gps': GUI.interval_remove('gps_pull'); - + if (callback) callback(); break; case 'motor_outputs': GUI.interval_remove('motor_poll'); - + // only enforce mincommand if necessary if (MOTOR_DATA != undefined) { var update = false; - + for (var i = 0; i < MOTOR_DATA.length; i++) { if (MOTOR_DATA[i] > MISC.mincommand) { update = true; break; } } - + if (update) { // send data to mcu var buffer_out = []; - + for (var i = 0; i < 8; i++) { buffer_out.push(lowByte(MISC.mincommand)); buffer_out.push(highByte(MISC.mincommand)); @@ -249,17 +249,17 @@ GUI_control.prototype.tab_switch_cleanup = function(callback) { case 'sensors': GUI.interval_kill_all(['port_usage']); serial.empty_output_buffer(); - + // sensor data tab uses scrollbars, emptying the content before loading another tab // prevents scrollbar exposure to any of the tabs while new content is loaded in $('#content').empty(); - + if (callback) callback(); break; case 'cli': var bufferOut = new ArrayBuffer(5); var bufView = new Uint8Array(bufferOut); - + bufView[0] = 0x65; // e bufView[1] = 0x78; // x bufView[2] = 0x69; // i @@ -274,18 +274,18 @@ GUI_control.prototype.tab_switch_cleanup = function(callback) { GUI.timeout_add('waiting_for_bootup', function() { CLI_active = false; CLI_valid = false; - + if (callback) callback(); }, 5000); // if we dont allow enough time to reboot, CRC of "first" command sent will fail, keep an eye for this one }); break; - + case 'firmware_flasher': PortHandler.flush_callbacks(); - + if (callback) callback(); break; - + default: if (callback) callback(); } diff --git a/js/msp.js b/js/msp.js index c2df0293..de46c08c 100644 --- a/js/msp.js +++ b/js/msp.js @@ -21,7 +21,7 @@ var MSP_codes = { MSP_WP: 118, MSP_BOXIDS: 119, MSP_SERVO_CONF: 120, - + MSP_SET_RAW_RC: 200, MSP_SET_RAW_GPS: 201, MSP_SET_PID: 202, @@ -36,14 +36,14 @@ var MSP_codes = { MSP_SET_HEAD: 211, MSP_SET_SERVO_CONF: 212, MSP_SET_MOTOR: 214, - + // MSP_BIND: 240, - + MSP_EEPROM_WRITE: 250, - + MSP_DEBUGMSG: 253, MSP_DEBUG: 254, - + // Additional baseflight commands that are not compatible with MultiWii MSP_UID: 160, // Unique device ID MSP_ACC_TRIM: 240, // get acc angle trim values @@ -63,29 +63,29 @@ var MSP = { message_buffer: undefined, message_buffer_uint8_view: undefined, message_checksum: 0, - + callbacks: [], packet_error: 0, - + callbacks_cleanup: function() { for (var i = 0; i < this.callbacks.length; i++) { clearInterval(this.callbacks[i].timer); } - + this.callbacks = []; }, - + disconnect_cleanup: function() { this.state = 0; // reset packet state for "clean" initial entry (this is only required if user hot-disconnects) this.packet_error = 0; // reset CRC packet error counter for next session - + this.callbacks_cleanup(); } }; MSP.read = function(readInfo) { var data = new Uint8Array(readInfo.data); - + for (var i = 0; i < data.length; i++) { switch (this.state) { case 0: // sync char 1 @@ -106,24 +106,24 @@ MSP.read = function(readInfo) { } else { // unknown message_status = 0; } - + this.state++; break; case 3: this.message_length_expected = data[i]; - + this.message_checksum = data[i]; - + // setup arraybuffer this.message_buffer = new ArrayBuffer(this.message_length_expected); this.message_buffer_uint8_view = new Uint8Array(this.message_buffer); - + this.state++; break; case 4: this.code = data[i]; this.message_checksum ^= data[i]; - + if (this.message_length_expected != 0) { // standard message this.state++; } else { // MSP_ACC_CALIBRATION, etc... @@ -134,7 +134,7 @@ MSP.read = function(readInfo) { this.message_buffer_uint8_view[this.message_length_received] = data[i]; this.message_checksum ^= data[i]; this.message_length_received++; - + if (this.message_length_received >= this.message_length_expected) { this.state++; } @@ -145,24 +145,24 @@ MSP.read = function(readInfo) { this.process_data(this.code, this.message_buffer, this.message_length_expected); } else { console.log('code: ' + this.code + ' - crc failed'); - + this.packet_error++; $('span.packet-error').html(this.packet_error); } - + // Reset variables this.message_length_received = 0; - this.state = 0; + this.state = 0; break; } - + char_counter++; } }; 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) { case MSP_codes.MSP_IDENT: CONFIG.version = parseFloat((data.getUint8(0) / 100).toFixed(2)); @@ -176,7 +176,7 @@ MSP.process_data = function(code, message_buffer, message_length) { CONFIG.activeSensors = data.getUint16(4, 1); CONFIG.mode = data.getUint32(6, 1); CONFIG.profile = data.getUint8(10); - + sensor_status(CONFIG.activeSensors); $('span.cycle-time').html(CONFIG.cycleTime); break; @@ -186,7 +186,7 @@ MSP.process_data = function(code, message_buffer, message_length) { SENSOR_DATA.accelerometer[0] = data.getInt16(0, 1) / 512; SENSOR_DATA.accelerometer[1] = data.getInt16(2, 1) / 512; SENSOR_DATA.accelerometer[2] = data.getInt16(4, 1) / 512; - + // properly scaled SENSOR_DATA.gyroscope[0] = data.getInt16(6, 1) * (4 / 16.4); SENSOR_DATA.gyroscope[1] = data.getInt16(8, 1) * (4 / 16.4); @@ -201,29 +201,29 @@ MSP.process_data = function(code, message_buffer, message_length) { var needle = 0; for (var i = 0; i < 8; i++) { SERVO_DATA[i] = data.getUint16(needle, 1); - + needle += 2; - } - break; + } + break; case MSP_codes.MSP_MOTOR: var needle = 0; for (var i = 0; i < 8; i++) { MOTOR_DATA[i] = data.getUint16(needle, 1); - + needle += 2; - } - break; + } + break; case MSP_codes.MSP_RC: RC.roll = data.getUint16(0, 1); RC.pitch = data.getUint16(2, 1); RC.yaw = data.getUint16(4, 1); RC.throttle = data.getUint16(6, 1); - + RC.AUX1 = data.getUint16(8, 1); RC.AUX2 = data.getUint16(10, 1); RC.AUX3 = data.getUint16(12, 1); RC.AUX4 = data.getUint16(14, 1); - break; + break; case MSP_codes.MSP_RAW_GPS: GPS_DATA.fix = data.getUint8(0); GPS_DATA.numSat = data.getUint8(1); @@ -232,26 +232,26 @@ MSP.process_data = function(code, message_buffer, message_length) { GPS_DATA.alt = data.getUint16(10, 1); GPS_DATA.speed = data.getUint16(12, 1); GPS_DATA.ground_course = data.getUint16(14, 1); - break; + break; case MSP_codes.MSP_COMP_GPS: GPS_DATA.distanceToHome = data.getUint16(0, 1); GPS_DATA.directionToHome = data.getUint16(2, 1); GPS_DATA.update = data.getUint8(4); - break; + break; case MSP_codes.MSP_ATTITUDE: SENSOR_DATA.kinematicsX = data.getInt16(0, 1) / 10.0; SENSOR_DATA.kinematicsY = data.getInt16(2, 1) / 10.0; SENSOR_DATA.kinematicsZ = data.getInt16(4, 1); - break; + break; case MSP_codes.MSP_ALTITUDE: SENSOR_DATA.altitude = parseFloat((data.getInt32(0, 1) / 100.0).toFixed(2)); // correct scale factor - break; + break; case MSP_codes.MSP_ANALOG: ANALOG.voltage = data.getUint8(0) / 10.0; ANALOG.power = data.getUint16(1, 1); ANALOG.rssi = data.getUint16(3, 1); ANALOG.amperage = data.getUint16(5, 1); - break; + break; case MSP_codes.MSP_RC_TUNING: RC_tuning.RC_RATE = parseFloat((data.getUint8(0) / 100).toFixed(2)); RC_tuning.RC_EXPO = parseFloat((data.getUint8(1) / 100).toFixed(2)); @@ -260,17 +260,17 @@ MSP.process_data = function(code, message_buffer, message_length) { RC_tuning.dynamic_THR_PID = parseFloat((data.getUint8(4) / 100).toFixed(2)); RC_tuning.throttle_MID = parseFloat((data.getUint8(5) / 100).toFixed(2)); RC_tuning.throttle_EXPO = parseFloat((data.getUint8(6) / 100).toFixed(2)); - break; + break; case MSP_codes.MSP_PID: // PID data arrived, we need to scale it and save to appropriate bank / array for (var i = 0, needle = 0; i < (message_length / 3); i++, needle += 3) { - // main for loop selecting the pid section + // main for loop selecting the pid section switch (i) { - case 0: - case 1: - case 2: - case 3: - case 7: + case 0: + case 1: + case 2: + case 3: + case 7: case 8: case 9: PIDs[i][0] = data.getUint8(needle) / 10; @@ -282,24 +282,24 @@ MSP.process_data = function(code, message_buffer, message_length) { PIDs[i][1] = data.getUint8(needle + 1) / 100; PIDs[i][2] = data.getUint8(needle + 2) / 1000; break; - case 5: + case 5: case 6: PIDs[i][0] = data.getUint8(needle) / 10; PIDs[i][1] = data.getUint8(needle + 1) / 100; PIDs[i][2] = data.getUint8(needle + 2) / 1000; - break; + break; } } - break; + break; case MSP_codes.MSP_BOX: // dump previous data (if there was any) AUX_CONFIG_values = new Array(); - + // fill in current data for (var i = 0; i < data.byteLength; i += 2) { // + 2 because uint16_t = 2 bytes AUX_CONFIG_values.push(data.getUint16(i, 1)); } - break; + break; case MSP_codes.MSP_MISC: // 22 bytes MISC.PowerTrigger1 = data.getInt16(0, 1); MISC.minthrottle = data.getUint16(2, 1); // 0-2000 @@ -313,29 +313,29 @@ MSP.process_data = function(code, message_buffer, message_length) { MISC.vbatmincellvoltage = data.getUint8(19, 1) / 10; // 10-50 MISC.vbatmaxcellvoltage = data.getUint8(20, 1) / 10; // 10-50 MISC.empty = data.getUint8(21, 1); - break; + break; case MSP_codes.MSP_MOTOR_PINS: console.log(data); - break; + break; case MSP_codes.MSP_BOXNAMES: AUX_CONFIG = []; // empty the array as new data is coming in - + var buff = new Array(); for (var i = 0; i < data.byteLength; i++) { if (data.getUint8(i) == 0x3B) { // ; (delimeter char) AUX_CONFIG.push(String.fromCharCode.apply(null, buff)); // convert bytes into ASCII and save as strings - + // empty buffer buff = []; } else { buff.push(data.getUint8(i)); } - + } - break; + break; case MSP_codes.MSP_PIDNAMES: console.log(data); - break; + break; case MSP_codes.MSP_WP: console.log(data); break; @@ -345,43 +345,43 @@ MSP.process_data = function(code, message_buffer, message_length) { case MSP_codes.MSP_SERVO_CONF: // drop previous data SERVO_CONFIG = []; - + for (var i = 0; i < 56; i += 7) { var arr = { - 'min': data.getInt16(i, 1), - 'max': data.getInt16(i + 2, 1), - 'middle': data.getInt16(i + 4, 1), + '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); } break; case MSP_codes.MSP_SET_RAW_RC: - break; + break; case MSP_codes.MSP_SET_RAW_GPS: - break; + break; case MSP_codes.MSP_SET_PID: console.log('PID settings saved'); - break; + break; case MSP_codes.MSP_SET_BOX: console.log('AUX Configuration saved'); - break; + break; case MSP_codes.MSP_SET_RC_TUNING: console.log('RC Tuning saved'); - break; + break; case MSP_codes.MSP_ACC_CALIBRATION: console.log('Accel calibration executed'); - break; + break; case MSP_codes.MSP_MAG_CALIBRATION: console.log('Mag calibration executed'); break; case MSP_codes.MSP_SET_MISC: console.log('MISC Configuration saved'); - break; + break; case MSP_codes.MSP_RESET_CONF: console.log('Settings Reset'); - + // With new flight software settings in place, we have to re-pull // latest values send_message(MSP_codes.MSP_IDENT, MSP_codes.MSP_IDENT); @@ -390,22 +390,22 @@ MSP.process_data = function(code, message_buffer, message_length) { send_message(MSP_codes.MSP_RC_TUNING, MSP_codes.MSP_RC_TUNING); send_message(MSP_codes.MSP_BOXNAMES, MSP_codes.MSP_BOXNAMES); send_message(MSP_codes.MSP_BOX, MSP_codes.MSP_BOX); - + // baseflight specific send_message(MSP_codes.MSP_UID, MSP_codes.MSP_UID); send_message(MSP_codes.MSP_ACC_TRIM, MSP_codes.MSP_ACC_TRIM); - break; + break; case MSP_codes.MSP_SELECT_SETTING: console.log('Profile selected'); break; case MSP_codes.MSP_SET_SERVO_CONF: console.log('Servo Configuration saved'); - break; + break; case MSP_codes.MSP_EEPROM_WRITE: console.log('Settings Saved in EEPROM'); - break; + break; case MSP_codes.MSP_DEBUGMSG: - break; + break; case MSP_codes.MSP_DEBUG: for (var i = 0; i < 4; i++) SENSOR_DATA.debug[i] = data.getInt16((2 * i), 1); @@ -429,39 +429,39 @@ MSP.process_data = function(code, message_buffer, message_length) { case MSP_codes.MSP_GPSSVINFO: if (data.byteLength > 0) { var numCh = data.getUint8(0); - + var needle = 1; for (var i = 0; i < numCh; i++) { GPS_DATA.chn[i] = data.getUint8(needle); GPS_DATA.svid[i] = data.getUint8(needle + 1); GPS_DATA.quality[i] = data.getUint8(needle + 2); GPS_DATA.cno[i] = data.getUint8(needle + 3); - + needle += 4; } } break; - + default: console.log('Unknown code detected: ' + code); } - + // trigger callbacks, cleanup/remove callback after trigger for (var i = (this.callbacks.length - 1); i >= 0; i--) { // itterating in reverse because we use .splice which modifies array length if (this.callbacks[i].code == code) { // saving current obj for after-callback comparison var obj = this.callbacks[i]; - + // remove timeout clearInterval(obj.timer); - + // fire callback if (obj.callback) obj.callback({'command': code, 'data': data, 'length': message_length}); - + // remove object from array // we need to check if the callback object still exists as it could have been touched/moved/removed in callback routine var index = this.callbacks.indexOf(obj); - + if (index > -1) this.callbacks.splice(index, 1); } } @@ -470,26 +470,26 @@ MSP.process_data = function(code, message_buffer, message_length) { function send_message(code, data, callback_sent, callback_msp) { var bufferOut; var bufView; - + // always reserve 6 bytes for protocol overhead ! if (typeof data === 'object') { var size = data.length + 6; var checksum = 0; - + bufferOut = new ArrayBuffer(size); - bufView = new Uint8Array(bufferOut); - + bufView = new Uint8Array(bufferOut); + bufView[0] = 36; // $ bufView[1] = 77; // M bufView[2] = 60; // < bufView[3] = data.length; bufView[4] = code; - + checksum = bufView[3] ^ bufView[4]; - + for (var i = 0; i < data.length; i++) { bufView[i + 5] = data[i]; - + checksum ^= bufView[i + 5]; } @@ -497,7 +497,7 @@ function send_message(code, data, callback_sent, callback_msp) { } else { bufferOut = new ArrayBuffer(7); bufView = new Uint8Array(bufferOut); - + bufView[0] = 36; // $ bufView[1] = 77; // M bufView[2] = 60; // < @@ -514,14 +514,14 @@ function send_message(code, data, callback_sent, callback_msp) { return false; // skips the code below } } - + var obj = {'code': code, 'callback': (callback_msp) ? callback_msp : false}; obj.timer = setInterval(function() { console.log('MSP data request timed-out: ' + code); - + serial.send(bufferOut, function(writeInfo) {}); }, 1000); // we should be able to define timeout in the future - + MSP.callbacks.push(obj); serial.send(bufferOut, function(writeInfo) { @@ -529,6 +529,6 @@ function send_message(code, data, callback_sent, callback_msp) { if (callback_sent) callback_sent(); } }); - + return true; } \ No newline at end of file diff --git a/js/port_handler.js b/js/port_handler.js index eae1f161..e1efa5ce 100644 --- a/js/port_handler.js +++ b/js/port_handler.js @@ -1,7 +1,7 @@ function port_handler() { this.main_timeout_reference; this.initial_ports = false; - + this.port_detected_callbacks = []; this.port_removed_callbacks = []; } @@ -13,12 +13,12 @@ port_handler.prototype.initialize = function() { port_handler.prototype.check = function() { var self = this; - + serial.getDevices(function(current_ports) { // port got removed or initial_ports wasn't initialized yet if (self.array_difference(self.initial_ports, current_ports).length > 0 || !self.initial_ports) { var removed_ports = self.array_difference(self.initial_ports, current_ports); - + if (self.initial_ports != false) { if (removed_ports.length > 1) { console.log('PortHandler - Removed: ' + removed_ports); @@ -26,7 +26,7 @@ port_handler.prototype.check = function() { console.log('PortHandler - Removed: ' + removed_ports[0]); } } - + // disconnect "UI" if necessary // Keep in mind that this routine can not fire during atmega32u4 reboot procedure !!! if (GUI.connected_to) { @@ -36,35 +36,35 @@ port_handler.prototype.check = function() { } } } - + self.update_port_select(current_ports); - + // trigger callbacks (only after initialization) if (self.initial_ports) { for (var i = (self.port_removed_callbacks.length - 1); i >= 0; i--) { var obj = self.port_removed_callbacks[i]; - + // remove timeout clearTimeout(obj.timer); - + // trigger callback obj.code(removed_ports); - + // remove object from array var index = self.port_removed_callbacks.indexOf(obj); if (index > -1) self.port_removed_callbacks.splice(index, 1); } } - + // auto-select last used port (only during initialization) if (!self.initial_ports) { chrome.storage.local.get('last_used_port', function(result) { // if last_used_port was set, we try to select it - if (result.last_used_port) { + if (result.last_used_port) { current_ports.forEach(function(port) { if (port == result.last_used_port) { console.log('Selecting last used port: ' + result.last_used_port); - + $('div#port-picker .port select').val(result.last_used_port); } }); @@ -73,7 +73,7 @@ port_handler.prototype.check = function() { } }); } - + if (!self.initial_ports) { // initialize self.initial_ports = current_ports; @@ -83,26 +83,26 @@ port_handler.prototype.check = function() { } } } - + // new port detected var new_ports = self.array_difference(current_ports, self.initial_ports); - + if (new_ports.length) { if (new_ports.length > 1) { console.log('PortHandler - Found: ' + new_ports); } else { console.log('PortHandler - Found: ' + new_ports[0]); } - + self.update_port_select(current_ports); - + // select / highlight new port, if connected -> select connected port if (!GUI.connected_to) { $('div#port-picker .port select').val(new_ports[0]); - } else { + } else { $('div#port-picker .port select').val(GUI.connected_to); } - + // start connect procedure (if statement is valid) if (GUI.auto_connect && !GUI.connecting_to && !GUI.connected_to) { // we need firmware flasher protection over here @@ -112,25 +112,25 @@ port_handler.prototype.check = function() { }, 50); // small timeout so we won't get any nasty connect errors due to system initializing the bus } } - + // trigger callbacks for (var i = (self.port_detected_callbacks.length - 1); i >= 0; i--) { var obj = self.port_detected_callbacks[i]; - + // remove timeout clearTimeout(obj.timer); - + // trigger callback obj.code(new_ports); - + // remove object from array var index = self.port_detected_callbacks.indexOf(obj); if (index > -1) self.port_detected_callbacks.splice(index, 1); } - + self.initial_ports = current_ports; } - + self.main_timeout_reference = setTimeout(function() { self.check(); }, 250); @@ -139,27 +139,27 @@ port_handler.prototype.check = function() { port_handler.prototype.update_port_select = function(ports) { $('div#port-picker .port select').html(''); // drop previous one - + if (ports.length > 0) { for (var i = 0; i < ports.length; i++) { $('div#port-picker .port select').append($("", {value: ports[i], text: ports[i]})); } } else { $('div#port-picker .port select').append($("", {value: 0, text: 'No Ports'})); - } + } }; port_handler.prototype.port_detected = function(name, code, timeout, ignore_timeout) { var self = this; var obj = {'name': name, 'code': code, 'timeout': (timeout) ? timeout : 10000}; - + if (!ignore_timeout) { obj.timer = setTimeout(function() { console.log('PortHandler - timeout - ' + obj.name); - + // trigger callback code(false); - + // remove object from array var index = self.port_detected_callbacks.indexOf(obj); if (index > -1) self.port_detected_callbacks.splice(index, 1); @@ -168,7 +168,7 @@ port_handler.prototype.port_detected = function(name, code, timeout, ignore_time obj.timer = false; obj.timeout = false; } - + this.port_detected_callbacks.push(obj); return obj; @@ -177,14 +177,14 @@ port_handler.prototype.port_detected = function(name, code, timeout, ignore_time port_handler.prototype.port_removed = function(name, code, timeout, ignore_timeout) { var self = this; var obj = {'name': name, 'code': code, 'timeout': (timeout) ? timeout : 10000}; - + if (!ignore_timeout) { obj.timer = setTimeout(function() { console.log('PortHandler - timeout - ' + obj.name); - + // trigger callback code(false); - + // remove object from array var index = self.port_removed_callbacks.indexOf(obj); if (index > -1) self.port_removed_callbacks.splice(index, 1); @@ -193,44 +193,44 @@ port_handler.prototype.port_removed = function(name, code, timeout, ignore_timeo obj.timer = false; obj.timeout = false; } - + this.port_removed_callbacks.push(obj); - + return obj; }; // accepting single level array with "value" as key port_handler.prototype.array_difference = function(firstArray, secondArray) { var cloneArray = []; - + // create hardcopy for (var i = 0; i < firstArray.length; i++) { cloneArray.push(firstArray[i]); } - + for (var i = 0; i < secondArray.length; i++) { if (cloneArray.indexOf(secondArray[i]) != -1) { cloneArray.splice(cloneArray.indexOf(secondArray[i]), 1); } } - + return cloneArray; }; port_handler.prototype.flush_callbacks = function() { var killed = 0; - - for (var i = this.port_detected_callbacks.length - 1; i >= 0; i--) { + + for (var i = this.port_detected_callbacks.length - 1; i >= 0; i--) { if (this.port_detected_callbacks[i].timer) clearTimeout(this.port_detected_callbacks[i].timer); this.port_detected_callbacks.splice(i, 1); - + killed++; } - + for (var i = this.port_removed_callbacks.length - 1; i >= 0; i--) { if (this.port_removed_callbacks[i].timer) clearTimeout(this.port_removed_callbacks[i].timer); this.port_removed_callbacks.splice(i, 1); - + killed++; } diff --git a/js/serial.js b/js/serial.js index d3a0f4e6..e25db0d2 100644 --- a/js/serial.js +++ b/js/serial.js @@ -2,25 +2,25 @@ var serial = { connectionId: -1, bytes_received: 0, bytes_sent: 0, - + transmitting: false, output_buffer: [], - + connect: function(path, options, callback) { var self = this; - + chrome.serial.connect(path, options, function(connectionInfo) { if (connectionInfo !== undefined) { self.connectionId = connectionInfo.connectionId; self.bytes_received = 0; self.bytes_sent = 0; - + self.onReceive.addListener(function log_bytes_received(info) { self.bytes_received += info.data.byteLength; }); - + console.log('SERIAL: Connection opened with ID: ' + connectionInfo.connectionId + ', Baud: ' + connectionInfo.bitrate); - + callback(connectionInfo); } else { console.log('SERIAL: Failed to open serial port'); @@ -30,25 +30,25 @@ var serial = { }, disconnect: function(callback) { var self = this; - + self.empty_output_buffer(); // remove listeners for (var i = (self.onReceive.listeners.length - 1); i >= 0; i--) { self.onReceive.removeListener(self.onReceive.listeners[i]); } - + chrome.serial.disconnect(this.connectionId, function(result) { if (result) { console.log('SERIAL: Connection with ID: ' + self.connectionId + ' closed'); } else { console.log('SERIAL: Failed to close connection with ID: ' + self.connectionId + ' closed'); } - + console.log('SERIAL: Statistics - Sent: ' + self.bytes_sent + ' bytes, Received: ' + self.bytes_received + ' bytes'); - + self.connectionId = -1; - + callback(result); }); }, @@ -58,7 +58,7 @@ var serial = { devices_array.forEach(function(device) { devices.push(device.path); }); - + callback(devices); }); }, @@ -68,54 +68,54 @@ var serial = { send: function(data, callback) { var self = this; self.output_buffer.push({'data': data, 'callback': callback}); - + if (!self.transmitting) { self.transmitting = true; - + var sending = function() { // store inside separate variables in case array gets destroyed var data = self.output_buffer[0].data; var callback = self.output_buffer[0].callback; - + chrome.serial.send(self.connectionId, data, function(sendInfo) { callback(sendInfo); self.output_buffer.shift(); - + self.bytes_sent += sendInfo.bytesSent; - + if (self.output_buffer.length) { // keep the buffer withing reasonable limits while (self.output_buffer.length > 500) { self.output_buffer.pop(); } - + sending(); } else { self.transmitting = false; } }); }; - + sending(); } }, onReceive: { listeners: [], - + addListener: function(function_reference) { var listener = chrome.serial.onReceive.addListener(function_reference); - + this.listeners.push(function_reference); }, - removeListener: function(function_reference) { + removeListener: function(function_reference) { for (var i = (this.listeners.length - 1); i >= 0; i--) { if (this.listeners[i] == function_reference) { chrome.serial.onReceive.removeListener(function_reference); - + this.listeners.splice(i, 1); break; } - } + } } }, empty_output_buffer: function() { diff --git a/js/serial_backend.js b/js/serial_backend.js index f5c89cc9..3905e33b 100644 --- a/js/serial_backend.js +++ b/js/serial_backend.js @@ -2,55 +2,55 @@ var configuration_received = false; var CLI_active = false; var CLI_valid = false; -$(document).ready(function() { +$(document).ready(function() { $('div#port-picker a.connect').click(function() { if (GUI.connect_lock != true) { // GUI control overrides the user control var clicks = $(this).data('clicks'); - + var selected_port = String($('div#port-picker .port select').val()); var selected_baud = parseInt($('div#port-picker #baud').val()); - + if (selected_port != '0') { if (!clicks) { console.log('Connecting to: ' + selected_port); GUI.connecting_to = selected_port; - + // lock port select & baud while we are connecting / connected $('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', true); - $('div#port-picker a.connect').text('Connecting'); - + $('div#port-picker a.connect').text('Connecting'); + serial.connect(selected_port, {bitrate: selected_baud}, onOpen); } else { // Disable any active "data pulling" timer GUI.interval_kill_all(); - + GUI.tab_switch_cleanup(); GUI.timeout_remove('connecting'); - + serial.disconnect(onClosed); - + GUI.connected_to = false; - + // Reset various UI elements $('span.port-usage').html('0%'); $('.software-version').html('0.0'); $('span.cycle-time').html('0'); - + MSP.disconnect_cleanup(); configuration_received = false; // reset valid config received variable (used to block tabs while not connected properly) - + // unlock port select & baud $('div#port-picker #port').prop('disabled', false); if (!GUI.auto_connect) $('div#port-picker #baud').prop('disabled', false); - + $(this).text('Connect'); $(this).removeClass('active'); - + sensor_status(sensors_detected = 0); // reset active sensor indicators $('#tabs > ul li').removeClass('active'); // de-select any selected tabs tab_initialize_default(); } - + $(this).data("clicks", !clicks); } } @@ -61,62 +61,62 @@ $(document).ready(function() { if (typeof result.auto_connect === 'undefined') { // auto_connect wasn't saved yet, save and push true to the GUI GUI.auto_connect = true; - + $('input.auto_connect').prop('checked', true); $('input.auto_connect, span.auto_connect').prop('title', 'Auto-Connect: Enabled - Configurator automatically tries to connect when new port is detected'); $('select#baud').val(115200).prop('disabled', true); - + // save chrome.storage.local.set({'auto_connect': true}); } else { - if (result.auto_connect) { + if (result.auto_connect) { // enabled by user GUI.auto_connect = true; - + $('input.auto_connect').prop('checked', true); $('input.auto_connect, span.auto_connect').prop('title', 'Auto-Connect: Enabled - Configurator automatically tries to connect when new serial port is detected'); - + $('select#baud').val(115200).prop('disabled', true); - } else { + } else { // disabled by user GUI.auto_connect = false; - + $('input.auto_connect').prop('checked', false); $('input.auto_connect, span.auto_connect').prop('title', 'Auto-Connect: Disabled - User needs to select the correct serial port and click "Connect" button on its own'); } } - + // bind UI hook to auto-connect checkbos $('input.auto_connect').change(function() { GUI.auto_connect = $(this).is(':checked'); - + // update title/tooltip if (GUI.auto_connect) { $('input.auto_connect, span.auto_connect').prop('title', 'Auto-Connect: Enabled - Configurator automatically tries to connect when new port is detected'); - + $('select#baud').val(115200).prop('disabled', true); } else { $('input.auto_connect, span.auto_connect').prop('title', 'Auto-Connect: Disabled - User needs to select the correct serial port and click "Connect" button on its own'); - + if (!GUI.connected_to && !GUI.connecting_to) $('select#baud').prop('disabled', false); } - + chrome.storage.local.set({'auto_connect': GUI.auto_connect}); }); }); PortHandler.initialize(); -}); +}); -function onOpen(openInfo) { +function onOpen(openInfo) { if (openInfo) { // update connected_to GUI.connected_to = GUI.connecting_to; - + // reset connecting_to GUI.connecting_to = false; - + GUI.log('Serial port successfully opened with ID: ' + openInfo.connectionId); - + // save selected port with chrome.storage if the port differs chrome.storage.local.get('last_used_port', function(result) { if (typeof result.last_used_port != 'undefined') { @@ -138,12 +138,12 @@ function onOpen(openInfo) { serial.onReceive.addListener(read_serial); GUI.interval_add('port_usage', port_usage, 1000, true); - + // disconnect after 10 seconds with error if we don't get IDENT data GUI.timeout_add('connecting', function() { if (!configuration_received) { GUI.log('No configuration received within 10 seconds, communication failed'); - + $('div#port-picker a.connect').click(); // disconnect } }, 10000); @@ -153,11 +153,11 @@ function onOpen(openInfo) { GUI.log('Unique device ID received - 0x' + CONFIG.uid[0].toString(16) + CONFIG.uid[1].toString(16) + CONFIG.uid[2].toString(16) + ''); send_message(MSP_codes.MSP_IDENT, MSP_codes.MSP_IDENT, false, function() { GUI.timeout_remove('connecting'); // kill connecting timer - + if (CONFIG.version >= firmware_version_accepted) { // Update UI elements that doesn't need consistent refreshing $('.software-version').html(CONFIG.version); - + configuration_received = true; $('div#port-picker a.connect').text('Disconnect').addClass('active'); $('#tabs li a:first').click(); @@ -170,13 +170,13 @@ function onOpen(openInfo) { } else { console.log('Failed to open serial port'); GUI.log('Failed to open serial port', 'red'); - + $('div#port-picker a.connect').text('Connect'); - $('div#port-picker a.connect').removeClass('active'); - + $('div#port-picker a.connect').removeClass('active'); + // unlock port select & baud $('div#port-picker #port, div#port-picker #baud, div#port-picker #delay').prop('disabled', false); - + // reset data $('div#port-picker a.connect').data("clicks", false); } @@ -187,7 +187,7 @@ function onClosed(result) { GUI.log('Serial port successfully closed'); } else { // Something went wrong GUI.log('Failed to close serial port'); - } + } } function read_serial(info) { @@ -199,7 +199,7 @@ function read_serial(info) { } function port_usage() { - var port_usage = (char_counter * 10 / parseInt($('div#port-picker #baud').val())) * 100; + var port_usage = (char_counter * 10 / parseInt($('div#port-picker #baud').val())) * 100; $('span.port-usage').html(parseInt(port_usage) + '%'); // reset counter @@ -211,11 +211,11 @@ function sensor_status(sensors_detected) { if (typeof sensor_status.previous_sensors_detected == 'undefined') { sensor_status.previous_sensors_detected = 0; } - + // update UI (if necessary) if (sensor_status.previous_sensors_detected != sensors_detected) { var e_sensor_status = $('div#sensor-status'); - + if (bit_check(sensors_detected, 0)) { // Gyroscope & accel detected $('.gyro', e_sensor_status).addClass('on'); $('.accel', e_sensor_status).addClass('on'); @@ -228,26 +228,26 @@ function sensor_status(sensors_detected) { $('.baro', e_sensor_status).addClass('on'); } else { $('.baro', e_sensor_status).removeClass('on'); - } + } if (bit_check(sensors_detected, 2)) { // Magnetometer detected $('.mag', e_sensor_status).addClass('on'); } else { $('.mag', e_sensor_status).removeClass('on'); - } + } if (bit_check(sensors_detected, 3)) { // GPS detected $('.gps', e_sensor_status).addClass('on'); } else { $('.gps', e_sensor_status).removeClass('on'); - } + } if (bit_check(sensors_detected, 4)) { // Sonar detected $('.sonar', e_sensor_status).addClass('on'); } else { $('.sonar', e_sensor_status).removeClass('on'); } - + // set current value sensor_status.previous_sensors_detected = sensors_detected; } diff --git a/js/stm32.js b/js/stm32.js index 50126661..d015f78f 100644 --- a/js/stm32.js +++ b/js/stm32.js @@ -1,4 +1,4 @@ -/* +/* STM32 F103 serial bus seems to properly initialize with quite a huge auto-baud range From 921600 down to 1200, i don't recommend getting any lower then that Official "specs" are from 115200 to 1200 @@ -7,20 +7,20 @@ var STM32_protocol = function() { this.hex; // ref this.verify_hex; - + this.receive_buffer; - + this.bytes_to_read = 0; // ref this.read_callback; // ref - + this.upload_time_start; this.upload_process_alive; - + this.status = { ACK: 0x79, // y NACK: 0x1F }; - + this.command = { get: 0x00, // Gets the version and the allowed commands supported by the current version of the bootloader get_ver_r_protect_s: 0x01, // Gets the bootloader version and the Read Protection status of the Flash memory @@ -35,7 +35,7 @@ var STM32_protocol = function() { readout_protect: 0x82, // Enables the read protection readout_unprotect: 0x92 // Disables the read protection }; - + // Erase (x043) and Extended Erase (0x44) are exclusive. A device may support either the Erase command or the Extended Erase command but not both. }; @@ -48,14 +48,14 @@ STM32_protocol.prototype.GUI_status = function(string) { STM32_protocol.prototype.connect = function(hex) { var self = this; self.hex = hex; - + var selected_port = String($('div#port-picker .port select').val()); var baud = parseInt($('div#port-picker #baud').val()); - + if (selected_port != '0') { // popular choices - 921600, 460800, 256000, 230400, 153600, 128000, 115200, 57600, 38400, 28800, 19200 var flashing_bitrate; - + switch (GUI.operating_system) { case 'Windows': flashing_bitrate = 921600; @@ -68,11 +68,11 @@ STM32_protocol.prototype.connect = function(hex) { case 'UNIX': flashing_bitrate = 921600; break; - + default: flashing_bitrate = 115200; } - + if (!$('input.updating').is(':checked')) { serial.connect(selected_port, {bitrate: baud}, function(openInfo) { if (openInfo) { @@ -80,17 +80,17 @@ STM32_protocol.prototype.connect = function(hex) { // we are connected, disabling connect button in the UI GUI.connect_lock = true; - + var bufferOut = new ArrayBuffer(1); var bufferView = new Uint8Array(bufferOut); - + bufferView[0] = 0x52; - + serial.send(bufferOut, function() { serial.disconnect(function(result) { - if (result) { + if (result) { serial.connect(selected_port, {bitrate: flashing_bitrate, parityBit: 'even', stopBits: 'one'}, function(openInfo) { - if (openInfo) { + if (openInfo) { self.initialize(); } else { GUI.log('Failed to open serial port'); @@ -107,10 +107,10 @@ STM32_protocol.prototype.connect = function(hex) { }); } else { serial.connect(selected_port, {bitrate: flashing_bitrate, parityBit: 'even', stopBits: 'one'}, function(openInfo) { - if (openInfo) { + if (openInfo) { // we are connected, disabling connect button in the UI GUI.connect_lock = true; - + self.initialize(); } else { GUI.log('Failed to open serial port'); @@ -126,14 +126,14 @@ STM32_protocol.prototype.connect = function(hex) { // initialize certain variables and start timers that oversee the communication STM32_protocol.prototype.initialize = function() { var self = this; - - // reset and set some variables before we start + + // reset and set some variables before we start self.receive_buffer = []; self.verify_hex = []; - + self.upload_time_start = microtime(); self.upload_process_alive = false; - + // reset progress bar to initial state self.progress_bar_e = $('.progress'); self.progress_bar_e.val(0); @@ -142,42 +142,42 @@ STM32_protocol.prototype.initialize = function() { serial.onReceive.addListener(function(info) { self.read(info); }); - + GUI.interval_add('STM32_timeout', function() { if (self.upload_process_alive) { // process is running self.upload_process_alive = false; } else { console.log('STM32 - timed out, programming failed ...'); STM32.GUI_status('STM32 - timed out, programming: FAILED'); - + // protocol got stuck, clear timer and disconnect GUI.interval_remove('STM32_timeout'); - + // exit self.upload_procedure(99); } }, 1000); - + self.upload_procedure(1); }; // no input parameters // this method should be executed every 1 ms via interval timer -STM32_protocol.prototype.read = function(readInfo) { +STM32_protocol.prototype.read = function(readInfo) { // routine that fills the buffer var data = new Uint8Array(readInfo.data); - + for (var i = 0; i < data.length; i++) { - this.receive_buffer.push(data[i]); + this.receive_buffer.push(data[i]); } - + // routine that fetches data from buffer if statement is true if (this.receive_buffer.length >= this.bytes_to_read && this.bytes_to_read != 0) { var data = this.receive_buffer.slice(0, this.bytes_to_read); // bytes requested this.receive_buffer.splice(0, this.bytes_to_read); // remove read bytes - + this.bytes_to_read = 0; // reset trigger - + this.read_callback(data); } }; @@ -188,7 +188,7 @@ STM32_protocol.prototype.retrieve = function(n_bytes, callback) { // data that we need are there, process immediately var data = this.receive_buffer.slice(0, n_bytes); this.receive_buffer.splice(0, n_bytes); // remove read bytes - + callback(data); } else { // still waiting for data, add callback @@ -203,38 +203,38 @@ STM32_protocol.prototype.retrieve = function(n_bytes, callback) { STM32_protocol.prototype.send = function(Array, bytes_to_read, callback) { // flip flag this.upload_process_alive = true; - + var bufferOut = new ArrayBuffer(Array.length); var bufferView = new Uint8Array(bufferOut); - + // set Array values inside bufferView (alternative to for loop) bufferView.set(Array); - + // update references this.bytes_to_read = bytes_to_read; this.read_callback = callback; - + // empty receive buffer before next command is out this.receive_buffer = []; // send over the actual data - serial.send(bufferOut, function(writeInfo) {}); + serial.send(bufferOut, function(writeInfo) {}); }; -// val = single byte to be verified +// val = single byte to be verified // data = response of n bytes from mcu (array) // result = true/false STM32_protocol.prototype.verify_response = function(val, data) { if (val != data[0]) { console.log('STM32 Communication failed, wrong response, expected: ' + val + ' received: ' + data[0]); STM32.GUI_status('STM32 Communication failed, wrong response, expected: ' + val + ' received: ' + data[0]); - + // disconnect this.upload_procedure(99); - + return false; } - + return true; }; @@ -242,7 +242,7 @@ STM32_protocol.prototype.verify_response = function(val, data) { // result = true/false STM32_protocol.prototype.verify_chip_signature = function(signature) { var available_flash_size = 0; - + switch (signature) { case 0x412: // not tested console.log('Chip recognized as F1 Low-density'); @@ -297,19 +297,19 @@ STM32_protocol.prototype.verify_chip_signature = function(signature) { console.log('Chip recognized as F3 STM32F30xxx, STM32F31xxx'); break; } - + if (available_flash_size > 0) { if (this.hex.bytes_total < available_flash_size) { return true; } else { console.log('Supplied hex is bigger then flash available on the chip, HEX: ' + this.hex.bytes_total + ' bytes, limit = ' + available_flash_size + ' bytes'); - + return false; } - } - + } + console.log('Chip NOT recognized: ' + signature); - + return false; }; @@ -323,16 +323,16 @@ STM32_protocol.prototype.verify_flash = function(first_array, second_array) { return false; } } - + console.log('Verification successful, matching: ' + first_array.length + ' bytes'); - + return true; }; // step = value depending on current state of upload_procedure STM32_protocol.prototype.upload_procedure = function(step) { var self = this; - + switch (step) { case 1: // initialize serial interface on the MCU side, auto baud rate settings @@ -342,18 +342,18 @@ STM32_protocol.prototype.upload_procedure = function(step) { if (reply[0] == 0x7F || reply[0] == self.status.ACK || reply[0] == self.status.NACK) { GUI.interval_remove('stm32_initialize_mcu'); console.log('STM32 - Serial interface initialized on the MCU side'); - + // proceed to next step self.upload_procedure(2); } else { GUI.interval_remove('stm32_initialize_mcu'); STM32.GUI_status('STM32 Communication with bootloader failed'); - + // disconnect self.upload_procedure(99); } }); - + if (send_counter++ > 3) { // stop retrying, its too late to get any response from MCU GUI.interval_remove('stm32_initialize_mcu'); @@ -366,7 +366,7 @@ STM32_protocol.prototype.upload_procedure = function(step) { if (self.verify_response(self.status.ACK, data)) { self.retrieve(data[1] + 1 + 1, function(data) { // data[1] = number of bytes that will follow [– 1 except current and ACKs] console.log('STM32 - Bootloader version: ' + (parseInt(data[0].toString(16)) / 10).toFixed(1)); // convert dec to hex, hex to dec and add floating point - + // proceed to next step self.upload_procedure(3); }); @@ -380,7 +380,7 @@ STM32_protocol.prototype.upload_procedure = function(step) { self.retrieve(data[1] + 1 + 1, function(data) { // data[1] = number of bytes that will follow [– 1 (N = 1 for STM32), except for current byte and ACKs] var signature = (data[0] << 8) | data[1]; console.log('STM32 - Signature: 0x' + signature.toString(16)); // signature in hex representation - + if (self.verify_chip_signature(signature)) { // proceed to next step self.upload_procedure(4); @@ -396,7 +396,7 @@ STM32_protocol.prototype.upload_procedure = function(step) { // erase memory console.log('Executing global chip erase'); STM32.GUI_status('Erasing'); - + self.send([self.command.erase, 0xBC], 1, function(reply) { // 0x43 ^ 0xFF if (self.verify_response(self.status.ACK, reply)) { self.send([0xFF, 0x00], 1, function(reply) { @@ -404,9 +404,9 @@ STM32_protocol.prototype.upload_procedure = function(step) { console.log('Erasing: done'); console.log('Writing data ...'); STM32.GUI_status('Flashing ...'); - + // proceed to next step - self.upload_procedure(5); + self.upload_procedure(5); } }); } @@ -417,36 +417,36 @@ STM32_protocol.prototype.upload_procedure = function(step) { var blocks = self.hex.data.length - 1; var flashing_block = 0; var address = self.hex.data[flashing_block].address; - + var bytes_flashed = 0; var bytes_flashed_total = 0; // used for progress bar - + var write = function() { if (bytes_flashed < self.hex.data[flashing_block].bytes) { var bytes_to_write = ((bytes_flashed + 128) <= self.hex.data[flashing_block].bytes) ? 128 : (self.hex.data[flashing_block].bytes - bytes_flashed); - + // console.log('STM32 - Writing to: 0x' + address.toString(16) + ', ' + bytes_to_write + ' bytes'); - + self.send([self.command.write_memory, 0xCE], 1, function(reply) { // 0x31 ^ 0xFF if (self.verify_response(self.status.ACK, reply)) { // address needs to be transmitted as 32 bit integer, we need to bit shift each byte out and then calculate address checksum var address_arr = [(address >> 24), (address >> 16), (address >> 8), address]; var address_checksum = address_arr[0] ^ address_arr[1] ^ address_arr[2] ^ address_arr[3]; - + self.send([address_arr[0], address_arr[1], address_arr[2], address_arr[3], address_checksum], 1, function(reply) { // write start address + checksum if (self.verify_response(self.status.ACK, reply)) { var array_out = new Array(bytes_to_write + 2); // 2 byte overhead [N, ...., checksum] array_out[0] = bytes_to_write - 1; // number of bytes to be written (to write 128 bytes, N must be 127, to write 256 bytes, N must be 255) - + var checksum = array_out[0]; for (var i = 0; i < bytes_to_write; i++) { array_out[i + 1] = self.hex.data[flashing_block].data[bytes_flashed]; // + 1 because of the first byte offset checksum ^= self.hex.data[flashing_block].data[bytes_flashed]; - + bytes_flashed++; } array_out[array_out.length - 1] = checksum; // checksum (last byte in the array_out array) - + address += bytes_to_write; bytes_flashed_total += bytes_to_write @@ -454,7 +454,7 @@ STM32_protocol.prototype.upload_procedure = function(step) { if (self.verify_response(self.status.ACK, reply)) { // update progress bar self.progress_bar_e.val(bytes_flashed_total / (self.hex.bytes_total * 2) * 100); - + // flash another page write(); } @@ -467,23 +467,23 @@ STM32_protocol.prototype.upload_procedure = function(step) { // move to another block if (flashing_block < blocks) { flashing_block++; - + address = self.hex.data[flashing_block].address; bytes_flashed = 0; - + write(); } else { // all blocks flashed console.log('Writing: done'); console.log('Verifying data ...'); STM32.GUI_status('Verifying ...'); - + // proceed to next step self.upload_procedure(6); } } }; - + // start writing write(); break; @@ -492,44 +492,44 @@ STM32_protocol.prototype.upload_procedure = function(step) { var blocks = self.hex.data.length - 1; var reading_block = 0; var address = self.hex.data[reading_block].address; - + var bytes_verified = 0; var bytes_verified_total = 0; // used for progress bar - + // initialize arrays for (var i = 0; i <= blocks; i++) { self.verify_hex.push([]); } - + var reading = function() { if (bytes_verified < self.hex.data[reading_block].bytes) { var bytes_to_read = ((bytes_verified + 128) <= self.hex.data[reading_block].bytes) ? 128 : (self.hex.data[reading_block].bytes - bytes_verified); - + // console.log('STM32 - Reading from: 0x' + address.toString(16) + ', ' + bytes_to_read + ' bytes'); - + self.send([self.command.read_memory, 0xEE], 1, function(reply) { // 0x11 ^ 0xFF if (self.verify_response(self.status.ACK, reply)) { var address_arr = [(address >> 24), (address >> 16), (address >> 8), address]; var address_checksum = address_arr[0] ^ address_arr[1] ^ address_arr[2] ^ address_arr[3]; - + self.send([address_arr[0], address_arr[1], address_arr[2], address_arr[3], address_checksum], 1, function(reply) { // read start address + checksum if (self.verify_response(self.status.ACK, reply)) { var bytes_to_read_n = bytes_to_read - 1; - + self.send([bytes_to_read_n, (~bytes_to_read_n) & 0xFF], 1, function(reply) { // bytes to be read + checksum XOR(complement of bytes_to_read_n) if (self.verify_response(self.status.ACK, reply)) { self.retrieve(bytes_to_read, function(data) { for (var i = 0; i < data.length; i++) { self.verify_hex[reading_block].push(data[i]); } - + address += bytes_to_read; bytes_verified += bytes_to_read; bytes_verified_total += bytes_to_read; - + // update progress bar - self.progress_bar_e.val((self.hex.bytes_total + bytes_verified_total) / (self.hex.bytes_total * 2) * 100); - + self.progress_bar_e.val((self.hex.bytes_total + bytes_verified_total) / (self.hex.bytes_total * 2) * 100); + // verify another page reading(); }); @@ -543,44 +543,44 @@ STM32_protocol.prototype.upload_procedure = function(step) { // move to another block if (reading_block < blocks) { reading_block++; - + address = self.hex.data[reading_block].address; bytes_verified = 0; - + reading(); } else { // all blocks read, verify - + var verify = true; for (var i = 0; i <= blocks; i++) { verify = self.verify_flash(self.hex.data[i].data, self.verify_hex[i]); - + if (!verify) break; } - + if (verify) { console.log('Programming: SUCCESSFUL'); STM32.GUI_status('Programming: SUCCESSFUL'); - + // update progress bar self.progress_bar_e.addClass('valid'); - + // proceed to next step - self.upload_procedure(7); + self.upload_procedure(7); } else { console.log('Programming: FAILED'); STM32.GUI_status('Programming: FAILED'); - + // update progress bar self.progress_bar_e.addClass('invalid'); - + // disconnect - self.upload_procedure(99); + self.upload_procedure(99); } } } }; - + // start reading reading(); break; @@ -594,7 +594,7 @@ STM32_protocol.prototype.upload_procedure = function(step) { var gt_address = 0x8000000; var address = [(gt_address >> 24), (gt_address >> 16), (gt_address >> 8), gt_address]; var address_checksum = address[0] ^ address[1] ^ address[2] ^ address[3]; - + self.send([address[0], address[1], address[2], address[3], address_checksum], 1, function(reply) { if (self.verify_response(self.status.ACK, reply)) { // disconnect @@ -607,15 +607,15 @@ STM32_protocol.prototype.upload_procedure = function(step) { case 99: // disconnect GUI.interval_remove('STM32_timeout'); // stop STM32 timeout timer (everything is finished now) - + console.log('Script finished after: ' + (microtime() - self.upload_time_start).toFixed(4) + ' seconds'); - + // close connection serial.disconnect(function(result) { if (result) { // All went as expected } else { // Something went wrong } - + // unlocking connect button GUI.connect_lock = false; }); diff --git a/js/workers/hex_parser.js b/js/workers/hex_parser.js index dd4826f3..bf23ae12 100644 --- a/js/workers/hex_parser.js +++ b/js/workers/hex_parser.js @@ -3,24 +3,24 @@ // if hex file wasn't valid (crc check failed on any of the lines), result will be false function read_hex_file(data) { data = data.split("\n"); - + // check if there is an empty line in the end of hex file, if there is, remove it if (data[data.length - 1] == "") { data.pop(); } - + var hexfile_valid = true; // if any of the crc checks failed, this variable flips to false - + var result = { data: [], end_of_file: false, bytes_total: 0, start_linear_address: 0 }; - + var extended_linear_address = 0; var next_address = 0; - + for (var i = 0; i < data.length && hexfile_valid; i++) { // each byte is represnted by two chars var byte_count = parseInt(data[i].substr(1, 2), 16); @@ -28,33 +28,33 @@ function read_hex_file(data) { var record_type = parseInt(data[i].substr(7, 2), 16); var content = data[i].substr(9, byte_count * 2); // still in string format var checksum = parseInt(data[i].substr(9 + byte_count * 2, 2), 16); // (this is a 2's complement value) - + switch (record_type) { - case 0x00: // data record + case 0x00: // data record if (address != next_address || next_address == 0) { result.data.push({'address': extended_linear_address + address, 'bytes': 0, 'data': []}); } - + // store address for next comparison next_address = address + byte_count; - + // process data var crc = byte_count + parseInt(data[i].substr(3, 2), 16) + parseInt(data[i].substr(5, 2), 16) + record_type; for (var needle = 0; needle < byte_count * 2; needle += 2) { // * 2 because of 2 hex chars per 1 byte var num = parseInt(content.substr(needle, 2), 16); // get one byte in hex and convert it to decimal var data_block = result.data.length - 1; - + result.data[data_block].data.push(num); - result.data[data_block].bytes++; - + result.data[data_block].bytes++; + crc += num; result.bytes_total++; } - + // change crc to 2's complement crc = (~crc + 1) & 0xFF; - - // verify + + // verify if (crc != checksum) { hexfile_valid = false; } @@ -82,7 +82,7 @@ function read_hex_file(data) { break; } } - + if (result.end_of_file && hexfile_valid) { postMessage(result); } else { @@ -98,11 +98,11 @@ function microtime() { onmessage = function(event) { var time_parsing_start = microtime(); // track time - + read_hex_file(event.data); - + console.log('HEX_PARSER - File parsed in: ' + (microtime() - time_parsing_start).toFixed(4) + ' seconds'); - + // terminate worker close(); }; \ No newline at end of file diff --git a/main.html b/main.html index a24ec28d..1aaf616b 100644 --- a/main.html +++ b/main.html @@ -3,9 +3,9 @@ - - - + + + @@ -19,7 +19,7 @@ - + @@ -31,7 +31,7 @@ - + @@ -82,7 +82,7 @@ Auto-Connect - +