From 4c0e9f06e51de607b4f9504237c01663be0a689a Mon Sep 17 00:00:00 2001 From: Hydra Date: Fri, 16 Aug 2019 21:21:41 +0200 Subject: [PATCH] Replace legacy firmware reboot sequence with MSP based sequence. This allows two things: 1. good user experience when flashing devices that use FLASH bootloaders. previously users would have to use `bl flash` or enter flash bootloader manually. 2. arbitrary setting of cli reboot character without breaking ability to reboot flash via configurator. Squashed commits: * Extract MSPConnector from code in stm32.js to allow reuse. * Use reboot_baud when connecting via MSP. * Ensure GUI connect is unlocked after connect failures. * Support legacy reboot method for old firmware. * Don't treat MSPConnector as a singleton. * Fix `port` usage in handlers. * Add short delay after closing serial port before re-opening it again. --- locales/en/messages.json | 12 ++++ src/js/msp/MSPConnector.js | 78 ++++++++++++++++++++++ src/js/protocols/stm32.js | 128 +++++++++++++++++++++++++++++-------- src/main.html | 1 + 4 files changed, 192 insertions(+), 27 deletions(-) create mode 100644 src/js/msp/MSPConnector.js diff --git a/locales/en/messages.json b/locales/en/messages.json index 78a0768b..d63c18a6 100644 --- a/locales/en/messages.json +++ b/locales/en/messages.json @@ -217,6 +217,12 @@ "deviceRebooting": { "message": "Device - Rebooting" }, + "deviceRebooting_flashBootloader": { + "message": "Device - Rebooting to FLASH BOOTLOADER" + }, + "deviceRebooting_romBootloader": { + "message": "Device - Rebooting to ROM BOOTLOADER" + }, "deviceReady": { "message": "Device - Ready" }, @@ -370,6 +376,12 @@ "stm32UsbDfuNotFound": { "message": "USB DFU not found" }, + "stm32RebootingToBootloader": { + "message": "Initiating reboot to bootloader ..." + }, + "stm32RebootingToBootloaderFailed": { + "message": "Rebooting device to bootloader: FAILED" + }, "stm32TimedOut": { "message": "STM32 - timed out, programming: FAILED" }, diff --git a/src/js/msp/MSPConnector.js b/src/js/msp/MSPConnector.js new file mode 100644 index 00000000..bb30e2c0 --- /dev/null +++ b/src/js/msp/MSPConnector.js @@ -0,0 +1,78 @@ +'use strict'; + +var MSPConnectorImpl = function () { + this.baud = undefined; + this.port = undefined; + this.onConnectCallback = undefined; + this.onTimeoutCallback = undefined; + this.onDisconnectCallback = undefined; +}; + +MSPConnectorImpl.prototype.connect = function (port, baud, onConnectCallback, onTimeoutCallback, onFailureCallback) { + + var self = this; + self.port = port; + self.baud = baud; + self.onConnectCallback = onConnectCallback; + self.onTimeoutCallback = onTimeoutCallback; + self.onFailureCallback = onFailureCallback; + + serial.connect(self.port, {bitrate: self.baud}, function (openInfo) { + if (openInfo) { + var disconnectAndCleanup = function() { + serial.disconnect(function(result) { + console.log('Disconnected'); + + MSP.clearListeners(); + + self.onTimeoutCallback(); + }); + + MSP.disconnect_cleanup(); + }; + + FC.resetState(); + + // disconnect after 10 seconds with error if we don't get IDENT data + GUI.timeout_add('msp_connector', function () { + if (!CONFIGURATOR.connectionValid) { + GUI.log(i18n.getMessage('noConfigurationReceived')); + + disconnectAndCleanup(); + } + }, 10000); + + serial.onReceive.addListener(read_serial); + + MSP.listen(update_packet_error); + mspHelper = new MspHelper(); + MSP.listen(mspHelper.process_data.bind(mspHelper)); + + MSP.send_message(MSPCodes.MSP_API_VERSION, false, false, function () { + CONFIGURATOR.connectionValid = true; + + GUI.timeout_remove('msp_connector'); + console.log('Connected'); + + self.onConnectCallback(); + }); + } else { + GUI.log(i18n.getMessage('serialPortOpenFail')); + self.onFailureCallback(); + } + }); +}; + +MSPConnectorImpl.prototype.disconnect = function(onDisconnectCallback) { + self.onDisconnectCallback = onDisconnectCallback; + + serial.disconnect(function (result) { + MSP.clearListeners(); + console.log('Disconnected'); + + self.onDisconnectCallback(result); + }); + + MSP.disconnect_cleanup(); +}; + diff --git a/src/js/protocols/stm32.js b/src/js/protocols/stm32.js index 4d4aa371..e63b17d0 100644 --- a/src/js/protocols/stm32.js +++ b/src/js/protocols/stm32.js @@ -22,6 +22,8 @@ var STM32_protocol = function () { this.upload_time_start; this.upload_process_alive; + this.msp_connector = new MSPConnectorImpl(); + this.status = { ACK: 0x79, // y NACK: 0x1F @@ -53,6 +55,7 @@ var STM32_protocol = function () { STM32_protocol.prototype.connect = function (port, baud, hex, options, callback) { var self = this; self.hex = hex; + self.port = port; self.baud = baud; self.callback = callback; @@ -85,49 +88,120 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback) } }); } else { - serial.connect(port, {bitrate: self.options.reboot_baud}, function (openInfo) { - if (openInfo) { + + var startFlashing = function() { + // refresh device list + PortHandler.check_usb_devices(function(dfu_available) { + if(dfu_available) { + STM32DFU.connect(usbDevices, hex, options); + } else { + serial.connect(self.port, {bitrate: self.baud, parityBit: 'even', stopBits: 'one'}, function (openInfo) { + if (openInfo) { + self.initialize(); + } else { + GUI.connect_lock = false; + GUI.log(i18n.getMessage('serialPortOpenFail')); + } + }); + } + }); + }; + + var legacyRebootAndFlash = function() { + serial.connect(self.port, {bitrate: self.options.reboot_baud}, function (openInfo) { + if (!openInfo) { + GUI.connect_lock = false; + GUI.log(i18n.getMessage('serialPortOpenFail')); + return; + } + + console.log('Using legacy reboot method'); + console.log('Sending ascii "R" to reboot'); - - // 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) { + serial.disconnect(function (disconnectionResult) { + if (disconnectionResult) { // delay to allow board to boot in bootloader mode // required to detect if a DFU device appears - setTimeout(function() { - // refresh device list - PortHandler.check_usb_devices(function(dfu_available) { - if(dfu_available) { - STM32DFU.connect(usbDevices, hex, options); - } else { - serial.connect(port, {bitrate: self.baud, parityBit: 'even', stopBits: 'one'}, function (openInfo) { - if (openInfo) { - self.initialize(); - } else { - GUI.connect_lock = false; - GUI.log(i18n.getMessage('serialPortOpenFail')); - } - }); - } - }); - }, 1000); + setTimeout(startFlashing, 1000); } else { GUI.connect_lock = false; } }); }); + }); + }; + + var onConnectHandler = function () { + + GUI.log(i18n.getMessage('apiVersionReceived', [CONFIG.apiVersion])); + + if (semver.lt(CONFIG.apiVersion, "1.42.0")) { + + self.msp_connector.disconnect(function (disconnectionResult) { + + // need some time for the port to be closed, serial port does not open if tried immediately + setTimeout(legacyRebootAndFlash, 500); + }); } else { - GUI.log(i18n.getMessage('serialPortOpenFail')); + console.log('Looking for capabilities via MSP'); + + MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, function () { + var rebootMode = 0; // FIRMWARE + if (bit_check(CONFIG.commCapabilities, 3)) { + // Board has flash bootloader + GUI.log(i18n.getMessage('deviceRebooting_flashBootloader')); + console.log('flash bootloader detected'); + rebootMode = 4; // MSP_REBOOT_BOOTLOADER_FLASH + } else { + GUI.log(i18n.getMessage('deviceRebooting_romBootloader')); + console.log('no flash bootloader detected'); + rebootMode = 1; // MSP_REBOOT_BOOTLOADER_ROM; + } + + var buffer = []; + buffer.push8(rebootMode); + MSP.send_message(MSPCodes.MSP_SET_REBOOT, buffer, function() { + + // if firmware doesn't flush MSP/serial send buffers and gracefully shutdown VCP connections we won't get a reply, so don't wait for it. + + self.msp_connector.disconnect(function (disconnectionResult) { + if (disconnectionResult) { + // delay to allow board to boot in bootloader mode + // required to detect if a DFU device appears + setTimeout(startFlashing, 1000); + } else { + GUI.connect_lock = false; + } + }); + + }, function () { + console.log('Reboot request recevied by device'); + }); + }); } - }); + } + + var onTimeoutHandler = function() { + GUI.connect_lock = false; + console.log('Looking for capabilities via MSP failed'); + + TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32RebootingToBootloaderFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID); + } + + var onFailureHandler = function() { + GUI.connect_lock = false; + }; + + GUI.connect_lock = true; + TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32RebootingToBootloader'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL); + + self.msp_connector.connect(self.port, self.options.reboot_baud, onConnectHandler, onTimeoutHandler, onFailureHandler); } }; diff --git a/src/main.html b/src/main.html index 01b5c140..3c82e89a 100644 --- a/src/main.html +++ b/src/main.html @@ -125,6 +125,7 @@ +