mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-15 12:25:15 +03:00
Verify board from firmware before flashing
This commit is contained in:
parent
2273b36fa3
commit
7af9fb28a9
6 changed files with 181 additions and 28 deletions
|
@ -3146,6 +3146,24 @@
|
||||||
"firmwareFlasherFlashTrigger": {
|
"firmwareFlasherFlashTrigger": {
|
||||||
"message": "Detected: <strong>$1</strong> - triggering flash on connect"
|
"message": "Detected: <strong>$1</strong> - triggering flash on connect"
|
||||||
},
|
},
|
||||||
|
"firmwareFlasherVerifyBoard": {
|
||||||
|
"message": "<h3>Firmware mismatch</h3><br />The connected board is <strong>{{verified_board}}</strong> while you selected <strong>{{selected_board}}</strong>.<br /><br />Do you want to continue flashing?",
|
||||||
|
"description": "Make a quick connection to read firmware target and never flash a wrong target again"
|
||||||
|
},
|
||||||
|
"firmwareFlasherBoardVerificationSuccess": {
|
||||||
|
"message": "Configurator has <span class=\"message-positive\">successfully</span> detected and verified the board: <strong>{{boardName}}</strong>",
|
||||||
|
"description": "Board verification has succeeded."
|
||||||
|
},
|
||||||
|
"firmwareFlasherBoardVerificationFail": {
|
||||||
|
"message": "Configurator <span class=\"message-negative\">failed</span> to verify the board, if this does not work please try switching tab slowly to retry, make a new usb connection or connect first if you might have forgotten to apply custom defaults",
|
||||||
|
"description": "Sometimes MSP values cannot be read from firmware correctly"
|
||||||
|
},
|
||||||
|
"firmwareFlasherButtonAbort": {
|
||||||
|
"message": "Abort"
|
||||||
|
},
|
||||||
|
"firmwareFlasherButtonContinue": {
|
||||||
|
"message": "Continue"
|
||||||
|
},
|
||||||
"unstableFirmwareAcknoledgementDialog": {
|
"unstableFirmwareAcknoledgementDialog": {
|
||||||
"message": "You are about to flash a <strong>development build of the firmware</strong>. These builds are a work in progress, and any of the following can be the case:<strong><ul><li>the firmware does not work at all;</li><li>the firmware is not flyable;</li><li>there are safety issues with the firmware, for example flyaways</li><li>the firmware can cause the flight controller to become unresponsive, or damaged</li></ul></strong>If you proceed with flashing this firmware, <strong>you are assuming full responsibility for the risk of any of the above happening</strong>. Furthermore you acknowledge that it is necessary to perform <strong>thorough bench tests with props off</strong> before any attempts to fly this firmware."
|
"message": "You are about to flash a <strong>development build of the firmware</strong>. These builds are a work in progress, and any of the following can be the case:<strong><ul><li>the firmware does not work at all;</li><li>the firmware is not flyable;</li><li>there are safety issues with the firmware, for example flyaways</li><li>the firmware can cause the flight controller to become unresponsive, or damaged</li></ul></strong>If you proceed with flashing this firmware, <strong>you are assuming full responsibility for the risk of any of the above happening</strong>. Furthermore you acknowledge that it is necessary to perform <strong>thorough bench tests with props off</strong> before any attempts to fly this firmware."
|
||||||
},
|
},
|
||||||
|
|
|
@ -170,6 +170,11 @@ PortHandler.detectPort = function(currentPorts) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Signal board verification
|
||||||
|
if (GUI.active_tab === 'firmware_flasher') {
|
||||||
|
TABS.firmware_flasher.boardNeedsVerification = true;
|
||||||
|
}
|
||||||
|
|
||||||
// auto-connect if enabled
|
// auto-connect if enabled
|
||||||
if (GUI.auto_connect && !GUI.connecting_to && !GUI.connected_to) {
|
if (GUI.auto_connect && !GUI.connecting_to && !GUI.connected_to) {
|
||||||
// start connect procedure. We need firmware flasher protection over here
|
// start connect procedure. We need firmware flasher protection over here
|
||||||
|
|
|
@ -92,7 +92,7 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
|
||||||
var startFlashing = function() {
|
var startFlashing = function() {
|
||||||
// refresh device list
|
// refresh device list
|
||||||
PortHandler.check_usb_devices(function(dfu_available) {
|
PortHandler.check_usb_devices(function(dfu_available) {
|
||||||
if(dfu_available) {
|
if (dfu_available) {
|
||||||
STM32DFU.connect(usbDevices, hex, options);
|
STM32DFU.connect(usbDevices, hex, options);
|
||||||
} else {
|
} else {
|
||||||
serial.connect(self.port, {bitrate: self.baud, parityBit: 'even', stopBits: 'one'}, function (openInfo) {
|
serial.connect(self.port, {bitrate: self.baud, parityBit: 'even', stopBits: 'one'}, function (openInfo) {
|
||||||
|
@ -107,6 +107,16 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onDisconnect = 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var legacyRebootAndFlash = function() {
|
var legacyRebootAndFlash = function() {
|
||||||
serial.connect(self.port, {bitrate: self.options.reboot_baud}, function (openInfo) {
|
serial.connect(self.port, {bitrate: self.options.reboot_baud}, function (openInfo) {
|
||||||
if (!openInfo) {
|
if (!openInfo) {
|
||||||
|
@ -124,15 +134,7 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
|
||||||
bufferView[0] = 0x52;
|
bufferView[0] = 0x52;
|
||||||
|
|
||||||
serial.send(bufferOut, function () {
|
serial.send(bufferOut, function () {
|
||||||
serial.disconnect(function (disconnectionResult) {
|
serial.disconnect(disconnectionResult => onDisconnect(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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -151,7 +153,7 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
|
||||||
} else {
|
} else {
|
||||||
console.log('Looking for capabilities via MSP');
|
console.log('Looking for capabilities via MSP');
|
||||||
|
|
||||||
MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, function () {
|
MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, () => {
|
||||||
var rebootMode = 0; // FIRMWARE
|
var rebootMode = 0; // FIRMWARE
|
||||||
if (bit_check(FC.CONFIG.targetCapabilities, FC.TARGET_CAPABILITIES_FLAGS.HAS_FLASH_BOOTLOADER)) {
|
if (bit_check(FC.CONFIG.targetCapabilities, FC.TARGET_CAPABILITIES_FLAGS.HAS_FLASH_BOOTLOADER)) {
|
||||||
// Board has flash bootloader
|
// Board has flash bootloader
|
||||||
|
@ -164,25 +166,36 @@ STM32_protocol.prototype.connect = function (port, baud, hex, options, callback)
|
||||||
rebootMode = 1; // MSP_REBOOT_BOOTLOADER_ROM;
|
rebootMode = 1; // MSP_REBOOT_BOOTLOADER_ROM;
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = [];
|
const selectedBoard = TABS.firmware_flasher.selectedBoard;
|
||||||
buffer.push8(rebootMode);
|
const connectedBoard = FC.CONFIG.boardName ? FC.CONFIG.boardName : 'UNKNOWN';
|
||||||
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.
|
function reboot() {
|
||||||
|
const buffer = [];
|
||||||
|
buffer.push8(rebootMode);
|
||||||
|
MSP.send_message(MSPCodes.MSP_SET_REBOOT, buffer, () => {
|
||||||
|
|
||||||
self.msp_connector.disconnect(function (disconnectionResult) {
|
// 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.
|
||||||
if (disconnectionResult) {
|
|
||||||
// delay to allow board to boot in bootloader mode
|
self.msp_connector.disconnect(disconnectionResult => onDisconnect(disconnectionResult));
|
||||||
// required to detect if a DFU device appears
|
|
||||||
setTimeout(startFlashing, 1000);
|
}, () => console.log('Reboot request received by device'));
|
||||||
} else {
|
|
||||||
GUI.connect_lock = false;
|
}
|
||||||
}
|
|
||||||
});
|
function onAbort() {
|
||||||
|
GUI.connect_lock = false;
|
||||||
|
rebootMode = 0;
|
||||||
|
console.log('User cancelled because selected target does not match verified board');
|
||||||
|
reboot();
|
||||||
|
TABS.firmware_flasher.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedBoard !== connectedBoard) {
|
||||||
|
TABS.firmware_flasher.showDialogVerifyBoard(selectedBoard, connectedBoard, onAbort, reboot);
|
||||||
|
} else {
|
||||||
|
reboot();
|
||||||
|
}
|
||||||
|
|
||||||
}, function () {
|
|
||||||
console.log('Reboot request recevied by device');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,10 @@ const serial = {
|
||||||
connect: function (path, options, callback) {
|
connect: function (path, options, callback) {
|
||||||
const self = this;
|
const self = this;
|
||||||
const testUrl = path.match(/^tcp:\/\/([A-Za-z0-9\.-]+)(?:\:(\d+))?$/);
|
const testUrl = path.match(/^tcp:\/\/([A-Za-z0-9\.-]+)(?:\:(\d+))?$/);
|
||||||
|
if (self.connectionId || self.connected) {
|
||||||
|
console.warn('We already connected. Aborting', self.connectionId, self.connected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (testUrl) {
|
if (testUrl) {
|
||||||
self.connectTcp(testUrl[1], testUrl[2], options, callback);
|
self.connectTcp(testUrl[1], testUrl[2], options, callback);
|
||||||
} else if (path === 'virtual') {
|
} else if (path === 'virtual') {
|
||||||
|
|
|
@ -7,6 +7,7 @@ const firmware_flasher = {
|
||||||
gitHubApi: new GitHubApi(),
|
gitHubApi: new GitHubApi(),
|
||||||
localFirmwareLoaded: false,
|
localFirmwareLoaded: false,
|
||||||
selectedBoard: undefined,
|
selectedBoard: undefined,
|
||||||
|
boardNeedsVerification: false,
|
||||||
intel_hex: undefined, // standard intel hex in string format
|
intel_hex: undefined, // standard intel hex in string format
|
||||||
parsed_hex: undefined, // parsed raw hex in array format
|
parsed_hex: undefined, // parsed raw hex in array format
|
||||||
unifiedTarget: {}, // the Unified Target configuration to be spliced into the configuration
|
unifiedTarget: {}, // the Unified Target configuration to be spliced into the configuration
|
||||||
|
@ -316,6 +317,8 @@ firmware_flasher.initialize = function (callback) {
|
||||||
$('select[name="board"]').val(boardReleases ? result.selected_board : 0).trigger('change');
|
$('select[name="board"]').val(boardReleases ? result.selected_board : 0).trigger('change');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
verifyBoard();
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildTypes = [
|
const buildTypes = [
|
||||||
|
@ -535,7 +538,7 @@ firmware_flasher.initialize = function (callback) {
|
||||||
$("a.load_remote_file").addClass('disabled');
|
$("a.load_remote_file").addClass('disabled');
|
||||||
const target = $(this).val();
|
const target = $(this).val();
|
||||||
|
|
||||||
if (!GUI.connect_lock) {
|
if (!GUI.connect_lock && target) {
|
||||||
if (TABS.firmware_flasher.selectedBoard !== target) {
|
if (TABS.firmware_flasher.selectedBoard !== target) {
|
||||||
// We're sure the board actually changed
|
// We're sure the board actually changed
|
||||||
if (self.isConfigLocal) {
|
if (self.isConfigLocal) {
|
||||||
|
@ -766,6 +769,68 @@ firmware_flasher.initialize = function (callback) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function verifyBoard() {
|
||||||
|
if (!$('option:selected', portPickerElement).data().isDFU) {
|
||||||
|
|
||||||
|
function onFinishClose() {
|
||||||
|
MSP.clearListeners();
|
||||||
|
GUI.connect_lock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
serial.disconnect(onFinishClose);
|
||||||
|
MSP.disconnect_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFinish() {
|
||||||
|
const board = FC.CONFIG.boardName;
|
||||||
|
if (board) {
|
||||||
|
$('select[name="board"]').val(board).trigger('change');
|
||||||
|
GUI.log(i18n.getMessage('firmwareFlasherBoardVerificationSuccess', {boardName: board}));
|
||||||
|
} else {
|
||||||
|
GUI.log(i18n.getMessage('firmwareFlasherBoardVerificationFail'));
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBoard() {
|
||||||
|
console.log(`Requesting board information`);
|
||||||
|
MSP.send_message(MSPCodes.MSP_API_VERSION, false, false, () => MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, onFinish));
|
||||||
|
}
|
||||||
|
|
||||||
|
function onConnect(openInfo) {
|
||||||
|
if (openInfo) {
|
||||||
|
GUI.connect_lock = true;
|
||||||
|
serial.onReceive.addListener(data => MSP.read(data));
|
||||||
|
const mspHelper = new MspHelper();
|
||||||
|
MSP.listen(mspHelper.process_data.bind(mspHelper));
|
||||||
|
getBoard();
|
||||||
|
} else {
|
||||||
|
console.dir('Failed to open connection:', openInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can only verify if not in DFU mode.
|
||||||
|
if (String(portPickerElement.val()) !== '0') {
|
||||||
|
const port = String(portPickerElement.val());
|
||||||
|
let baud = 115200;
|
||||||
|
if ($('input.flash_manual_baud').is(':checked')) {
|
||||||
|
baud = parseInt($('#flash_manual_baud_rate').val());
|
||||||
|
}
|
||||||
|
console.log('Query board information to preselect right firmware');
|
||||||
|
if (!(serial.connected || serial.connectionId)) {
|
||||||
|
serial.connect(port, {bitrate: baud}, onConnect);
|
||||||
|
} else {
|
||||||
|
console.warn('Attempting to connect while there still is a connection', serial.connected, serial.connectionId);
|
||||||
|
serial.disconnect();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('Please select valid serial port');
|
||||||
|
GUI.log(i18n.getMessage('firmwareFlasherNoValidPort'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ConfigStorage.get('erase_chip', function (result) {
|
ConfigStorage.get('erase_chip', function (result) {
|
||||||
if (result.erase_chip) {
|
if (result.erase_chip) {
|
||||||
$('input.erase_chip').prop('checked', true);
|
$('input.erase_chip').prop('checked', true);
|
||||||
|
@ -1015,6 +1080,13 @@ firmware_flasher.initialize = function (callback) {
|
||||||
if ($('option:selected', this).data().isDFU) {
|
if ($('option:selected', this).data().isDFU) {
|
||||||
exitDfuElement.removeClass('disabled');
|
exitDfuElement.removeClass('disabled');
|
||||||
} else {
|
} else {
|
||||||
|
// Porthandler resets board on port detect
|
||||||
|
if (self.boardNeedsVerification) {
|
||||||
|
// reset to prevent multiple calls
|
||||||
|
self.boardNeedsVerification = false;
|
||||||
|
verifyBoard();
|
||||||
|
}
|
||||||
|
|
||||||
$("a.load_remote_file").removeClass('disabled');
|
$("a.load_remote_file").removeClass('disabled');
|
||||||
$("a.load_file").removeClass('disabled');
|
$("a.load_file").removeClass('disabled');
|
||||||
exitDfuElement.addClass('disabled');
|
exitDfuElement.addClass('disabled');
|
||||||
|
@ -1241,6 +1313,37 @@ firmware_flasher.enableFlashing = function (enabled) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
firmware_flasher.refresh = function (callback) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
GUI.tab_switch_cleanup(function() {
|
||||||
|
self.initialize();
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
firmware_flasher.showDialogVerifyBoard = function (selected, verified, onAbort, onAccept) {
|
||||||
|
const dialogVerifyBoard = $('#dialog-verify-board')[0];
|
||||||
|
|
||||||
|
$('#dialog-verify-board-content').html(i18n.getMessage('firmwareFlasherVerifyBoard', {selected_board: selected, verified_board: verified}));
|
||||||
|
|
||||||
|
if (!dialogVerifyBoard.hasAttribute('open')) {
|
||||||
|
dialogVerifyBoard.showModal();
|
||||||
|
$('#dialog-verify-board-abort-confirmbtn').click(function() {
|
||||||
|
ConfigStorage.set({'selected_board': FC.CONFIG.boardName});
|
||||||
|
dialogVerifyBoard.close();
|
||||||
|
onAbort();
|
||||||
|
});
|
||||||
|
$('#dialog-verify-board-continue-confirmbtn').click(function() {
|
||||||
|
dialogVerifyBoard.close();
|
||||||
|
onAccept();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
firmware_flasher.FLASH_MESSAGE_TYPES = {
|
firmware_flasher.FLASH_MESSAGE_TYPES = {
|
||||||
NEUTRAL : 'NEUTRAL',
|
NEUTRAL : 'NEUTRAL',
|
||||||
VALID : 'VALID',
|
VALID : 'VALID',
|
||||||
|
|
|
@ -180,4 +180,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="dialog-verify-board">
|
||||||
|
<div id="dialog-verify-board-content-wrapper">
|
||||||
|
<div id="dialog-verify-board-content"></div>
|
||||||
|
<div class="btn dialog-buttons">
|
||||||
|
<a href="#" id="dialog-verify-board-abort-confirmbtn" class="regular-button" i18n="firmwareFlasherButtonAbort"></a>
|
||||||
|
<a href="#" id="dialog-verify-board-continue-confirmbtn" class="regular-button" i18n="firmwareFlasherButtonContinue"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue