mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-23 00:05:22 +03:00
Added flasing of Unified Targets from configurations.
This commit is contained in:
parent
e2c6cfae0f
commit
1079a854aa
3 changed files with 190 additions and 48 deletions
110
src/js/ConfigInserter.js
Normal file
110
src/js/ConfigInserter.js
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ConfigInserter = function () {
|
||||||
|
}
|
||||||
|
|
||||||
|
const CUSTOM_DEFAULTS_POINTER_ADDRESS = 0x08002800;
|
||||||
|
const BLOCK_SIZE = 16384;
|
||||||
|
|
||||||
|
function seek(firmware, address) {
|
||||||
|
var index = 0;
|
||||||
|
for (; index < firmware.data.length && address >= firmware.data[index].address + firmware.data[index].bytes; index++);
|
||||||
|
|
||||||
|
var result = {
|
||||||
|
lineIndex: index
|
||||||
|
};
|
||||||
|
|
||||||
|
if (firmware.data[index] && address >= firmware.data[index].address) {
|
||||||
|
result.byteIndex = address - firmware.data[index].address;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readUint32(firmware, index) {
|
||||||
|
var result = 0;
|
||||||
|
for (var position = 0; position < 4; position++) {
|
||||||
|
result += firmware.data[index.lineIndex].data[index.byteIndex++] << (8 * position);
|
||||||
|
if (index.byteIndex >= firmware.data[index.lineIndex].bytes) {
|
||||||
|
index.lineIndex++;
|
||||||
|
index.byteIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCustomDefaultsArea(firmware) {
|
||||||
|
var result = {};
|
||||||
|
|
||||||
|
var index = seek(firmware, CUSTOM_DEFAULTS_POINTER_ADDRESS);
|
||||||
|
|
||||||
|
if (index.byteIndex === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = {};
|
||||||
|
|
||||||
|
result.startAddress = readUint32(firmware, index);
|
||||||
|
result.endAddress = readUint32(firmware, index);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateData(firmware, input, startAddress) {
|
||||||
|
var address = startAddress;
|
||||||
|
|
||||||
|
var index = seek(firmware, address);
|
||||||
|
|
||||||
|
if (index.byteIndex !== undefined) {
|
||||||
|
throw new Error('Configuration area in firmware not free.');
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputIndex = 0;
|
||||||
|
while (inputIndex < input.length) {
|
||||||
|
var remaining = input.length - inputIndex;
|
||||||
|
var line = {
|
||||||
|
address: address,
|
||||||
|
bytes: BLOCK_SIZE > remaining ? remaining : BLOCK_SIZE,
|
||||||
|
data: []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (firmware.data[index.lineIndex] && (line.address + line.bytes) > firmware.data[index.lineIndex].address) {
|
||||||
|
throw new Error("Aborting data generation, free area too small.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < line.bytes; i++) {
|
||||||
|
line.data.push(input.charCodeAt(inputIndex++));
|
||||||
|
}
|
||||||
|
|
||||||
|
address = address + line.bytes;
|
||||||
|
|
||||||
|
firmware.data.splice(index.lineIndex++, 0, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
firmware.bytes_total += input.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function microtime() {
|
||||||
|
var now = new Date().getTime() / 1000;
|
||||||
|
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigInserter.prototype.insertConfig = function (firmware, input) {
|
||||||
|
var time_parsing_start = microtime(); // track time
|
||||||
|
|
||||||
|
var customDefaultsArea = getCustomDefaultsArea(firmware);
|
||||||
|
|
||||||
|
if (!customDefaultsArea || customDefaultsArea.endAddress - customDefaultsArea.startAddress === 0) {
|
||||||
|
return false;
|
||||||
|
} else if (input.length >= customDefaultsArea.endAddress - customDefaultsArea.startAddress) {
|
||||||
|
throw new Error(`Custom defaults area too small (${customDefaultsArea.endAddress - customDefaultsArea.startAddress} bytes), ${input.length + 1} bytes needed.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
generateData(firmware, input, customDefaultsArea.startAddress);
|
||||||
|
|
||||||
|
console.log('Custom defaults inserted in: ' + (microtime() - time_parsing_start).toFixed(4) + ' seconds.');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -15,8 +15,9 @@ TABS.firmware_flasher.initialize = function (callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var intel_hex = false, // standard intel hex in string format
|
var intel_hex = false; // standard intel hex in string format
|
||||||
parsed_hex = false; // parsed raw hex in array format
|
var parsed_hex = false; // parsed raw hex in array format
|
||||||
|
var targetConfig; // the Unified Target configuration to be spliced into the configuration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change boldness of firmware option depending on cache status
|
* Change boldness of firmware option depending on cache status
|
||||||
|
@ -365,7 +366,15 @@ TABS.firmware_flasher.initialize = function (callback) {
|
||||||
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHANNEL, undefined);
|
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHANNEL, undefined);
|
||||||
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SOURCE, 'file');
|
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SOURCE, 'file');
|
||||||
|
|
||||||
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: [{description: 'HEX files', extensions: ['hex']}]}, function (fileEntry) {
|
chrome.fileSystem.chooseEntry({
|
||||||
|
type: 'openFile',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
description: 'target files',
|
||||||
|
extensions: ['hex', 'config']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, function (fileEntry) {
|
||||||
if (chrome.runtime.lastError) {
|
if (chrome.runtime.lastError) {
|
||||||
console.error(chrome.runtime.lastError.message);
|
console.error(chrome.runtime.lastError.message);
|
||||||
|
|
||||||
|
@ -383,28 +392,30 @@ TABS.firmware_flasher.initialize = function (callback) {
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
|
|
||||||
reader.onloadend = function(e) {
|
reader.onloadend = function(e) {
|
||||||
|
|
||||||
if (e.total != 0 && e.total == e.loaded) {
|
if (e.total != 0 && e.total == e.loaded) {
|
||||||
|
|
||||||
console.log('File loaded (' + e.loaded + ')');
|
console.log('File loaded (' + e.loaded + ')');
|
||||||
|
|
||||||
intel_hex = e.target.result;
|
if (file.name.split('.').pop() === "hex") {
|
||||||
|
intel_hex = e.target.result;
|
||||||
|
|
||||||
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHECKSUM, objectHash.sha1(intel_hex));
|
analytics.setFirmwareData(analytics.DATA.FIRMWARE_CHECKSUM, objectHash.sha1(intel_hex));
|
||||||
|
|
||||||
parse_hex(intel_hex, function (data) {
|
parse_hex(intel_hex, function (data) {
|
||||||
parsed_hex = data;
|
parsed_hex = data;
|
||||||
|
|
||||||
if (parsed_hex) {
|
if (parsed_hex) {
|
||||||
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SIZE, parsed_hex.bytes_total);
|
analytics.setFirmwareData(analytics.DATA.FIRMWARE_SIZE, parsed_hex.bytes_total);
|
||||||
|
|
||||||
self.enableFlashing(true);
|
self.enableFlashing(true);
|
||||||
|
|
||||||
self.flashingMessage(i18n.getMessage('firmwareFlasherFirmwareLocalLoaded', parsed_hex.bytes_total), self.FLASH_MESSAGE_TYPES.NEUTRAL);
|
self.flashingMessage(i18n.getMessage('firmwareFlasherFirmwareLocalLoaded', parsed_hex.bytes_total), self.FLASH_MESSAGE_TYPES.NEUTRAL);
|
||||||
} else {
|
} else {
|
||||||
self.flashingMessage('firmwareFlasherHexCorrupted', self.FLASH_MESSAGE_TYPES.INVALID);
|
self.flashingMessage('firmwareFlasherHexCorrupted', self.FLASH_MESSAGE_TYPES.INVALID);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
targetConfig = e.target.result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -473,46 +484,66 @@ TABS.firmware_flasher.initialize = function (callback) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function flashFirmware(firmware) {
|
||||||
|
var options = {};
|
||||||
|
|
||||||
|
var eraseAll = false;
|
||||||
|
if ($('input.erase_chip').is(':checked')) {
|
||||||
|
options.erase_chip = true;
|
||||||
|
|
||||||
|
eraseAll = true
|
||||||
|
}
|
||||||
|
analytics.setFirmwareData(analytics.DATA.FIRMWARE_ERASE_ALL, eraseAll.toString());
|
||||||
|
|
||||||
|
if (String($('div#port-picker #port').val()) != 'DFU') {
|
||||||
|
if (String($('div#port-picker #port').val()) != '0') {
|
||||||
|
var port = String($('div#port-picker #port').val()), baud;
|
||||||
|
baud = 115200;
|
||||||
|
|
||||||
|
if ($('input.updating').is(':checked')) {
|
||||||
|
options.no_reboot = true;
|
||||||
|
} else {
|
||||||
|
options.reboot_baud = parseInt($('div#port-picker #baud').val());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($('input.flash_manual_baud').is(':checked')) {
|
||||||
|
baud = parseInt($('#flash_manual_baud_rate').val());
|
||||||
|
}
|
||||||
|
|
||||||
|
analytics.sendEvent(analytics.EVENT_CATEGORIES.FIRMWARE, 'Flashing');
|
||||||
|
|
||||||
|
STM32.connect(port, baud, firmware, options);
|
||||||
|
} else {
|
||||||
|
console.log('Please select valid serial port');
|
||||||
|
GUI.log(i18n.getMessage('firmwareFlasherNoValidPort'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
analytics.sendEvent(analytics.EVENT_CATEGORIES.FIRMWARE, 'Flashing');
|
||||||
|
|
||||||
|
STM32DFU.connect(usbDevices, firmware, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$('a.flash_firmware').click(function () {
|
$('a.flash_firmware').click(function () {
|
||||||
if (!$(this).hasClass('disabled')) {
|
if (!$(this).hasClass('disabled')) {
|
||||||
if (!GUI.connect_lock) { // button disabled while flashing is in progress
|
if (!GUI.connect_lock) { // button disabled while flashing is in progress
|
||||||
if (parsed_hex != false) {
|
if (parsed_hex != false) {
|
||||||
var options = {};
|
try {
|
||||||
|
if (targetConfig && !parsed_hex.configInserted) {
|
||||||
|
var configInserter = new ConfigInserter();
|
||||||
|
|
||||||
var eraseAll = false;
|
if (configInserter.insertConfig(parsed_hex, targetConfig)) {
|
||||||
if ($('input.erase_chip').is(':checked')) {
|
parsed_hex.configInserted = true;
|
||||||
options.erase_chip = true;
|
|
||||||
|
|
||||||
eraseAll = true
|
|
||||||
}
|
|
||||||
analytics.setFirmwareData(analytics.DATA.FIRMWARE_ERASE_ALL, eraseAll.toString());
|
|
||||||
|
|
||||||
if (String($('div#port-picker #port').val()) != 'DFU') {
|
|
||||||
if (String($('div#port-picker #port').val()) != '0') {
|
|
||||||
var port = String($('div#port-picker #port').val()), baud;
|
|
||||||
baud = 115200;
|
|
||||||
|
|
||||||
if ($('input.updating').is(':checked')) {
|
|
||||||
options.no_reboot = true;
|
|
||||||
} else {
|
} else {
|
||||||
options.reboot_baud = parseInt($('div#port-picker #baud').val());
|
console.log('Firmware does not support custom defaults.');
|
||||||
|
|
||||||
|
targetConfig = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($('input.flash_manual_baud').is(':checked')) {
|
|
||||||
baud = parseInt($('#flash_manual_baud_rate').val());
|
|
||||||
}
|
|
||||||
|
|
||||||
analytics.sendEvent(analytics.EVENT_CATEGORIES.FIRMWARE, 'Flashing');
|
|
||||||
|
|
||||||
STM32.connect(port, baud, parsed_hex, options);
|
|
||||||
} else {
|
|
||||||
console.log('Please select valid serial port');
|
|
||||||
GUI.log(i18n.getMessage('firmwareFlasherNoValidPort'));
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
analytics.sendEvent(analytics.EVENT_CATEGORIES.FIRMWARE, 'Flashing');
|
|
||||||
|
|
||||||
STM32DFU.connect(usbDevices, parsed_hex, options);
|
flashFirmware(parsed_hex);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Flashing failed: ${e.message}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$('span.progressLabel').attr('i18n','firmwareFlasherFirmwareNotLoaded').removeClass('i18n-replaced');
|
$('span.progressLabel').attr('i18n','firmwareFlasherFirmwareNotLoaded').removeClass('i18n-replaced');
|
||||||
|
|
|
@ -168,6 +168,7 @@
|
||||||
<script type="text/javascript" src="./node_modules/jquery-textcomplete/dist/jquery.textcomplete.min.js"></script>
|
<script type="text/javascript" src="./node_modules/jquery-textcomplete/dist/jquery.textcomplete.min.js"></script>
|
||||||
<script type="text/javascript" src="./js/CliAutoComplete.js"></script>
|
<script type="text/javascript" src="./js/CliAutoComplete.js"></script>
|
||||||
<script type="text/javascript" src="./js/DarkTheme.js"></script>
|
<script type="text/javascript" src="./js/DarkTheme.js"></script>
|
||||||
|
<script type="text/javascript" src="./js/ConfigInserter.js"></script>
|
||||||
<title i18n="windowTitle"></title>
|
<title i18n="windowTitle"></title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue