feat(core): 在线版支持通过web serial烧录k210固件
This commit is contained in:
@@ -7,6 +7,7 @@ goog.require('DAPWrapper');
|
||||
goog.require('PartialFlashing');
|
||||
goog.require('esptooljs');
|
||||
goog.require('CryptoJS');
|
||||
goog.require('JSZip');
|
||||
goog.require('Mixly.Env');
|
||||
goog.require('Mixly.LayerExt');
|
||||
goog.require('Mixly.Config');
|
||||
@@ -21,6 +22,7 @@ goog.require('Mixly.LayerFirmware');
|
||||
goog.require('Mixly.LayerProgress');
|
||||
goog.require('Mixly.Web.Serial');
|
||||
goog.require('Mixly.Web.Ampy');
|
||||
goog.require('Mixly.Web.KFlash');
|
||||
goog.require('Mixly.Web.SerialTransport');
|
||||
goog.provide('Mixly.Web.BU');
|
||||
|
||||
@@ -44,6 +46,7 @@ const {
|
||||
Serial,
|
||||
BU,
|
||||
Ampy,
|
||||
KFlash,
|
||||
SerialTransport
|
||||
} = Web;
|
||||
|
||||
@@ -62,13 +65,8 @@ BU.firmwareLayer = new LayerFirmware({
|
||||
cancelDisplay: false
|
||||
});
|
||||
BU.firmwareLayer.bind('burn', (info) => {
|
||||
const boardKey = Boards.getSelectedBoardKey();
|
||||
const { web } = SELECTED_BOARD;
|
||||
if (boardKey.indexOf('micropython:esp32s2') !== -1) {
|
||||
BU.burnWithAdafruitEsptool(info, web.burn.erase);
|
||||
} else {
|
||||
BU.burnWithEsptool(info, web.burn.erase);
|
||||
}
|
||||
BU.burnWithEsptool(info, web.burn.erase);
|
||||
});
|
||||
BU.progressLayer = new LayerProgress({
|
||||
width: 200,
|
||||
@@ -136,9 +134,46 @@ const readBinFileAsArrayBuffer = (path, offset) => {
|
||||
});
|
||||
}
|
||||
|
||||
const decodeKfpkgFromArrayBuffer = async(arrayBuf) => {
|
||||
const zip = await JSZip.loadAsync(arrayBuf);
|
||||
const manifestEntry = zip.file('flash-list.json');
|
||||
if (!manifestEntry) {
|
||||
throw new Error('kfpkg is missing flash-list.json');
|
||||
}
|
||||
const manifestText = await manifestEntry.async('string');
|
||||
const manifest = JSON.parse(manifestText);
|
||||
|
||||
const items = [];
|
||||
for (const f of manifest.files || []) {
|
||||
const entry = zip.file(f.bin);
|
||||
if (!entry) {
|
||||
throw new Error(`Missing files in package: ${f.bin}`);
|
||||
}
|
||||
const data = new Uint8Array(await entry.async('uint8array'));
|
||||
items.push({
|
||||
address: f.address >>> 0,
|
||||
filename: f.bin,
|
||||
sha256Prefix: !!f.sha256Prefix,
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
return { manifest, items };
|
||||
}
|
||||
|
||||
BU.initBurn = async () => {
|
||||
if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
|
||||
await BU.burnByUSB();
|
||||
await BU.burnWithDAP();
|
||||
} else if (['MixGo AI'].includes(BOARD.boardType)) {
|
||||
const { web } = SELECTED_BOARD;
|
||||
const boardKey = Boards.getSelectedBoardKey();
|
||||
if (!web?.burn?.binFile) {
|
||||
return;
|
||||
}
|
||||
if (typeof web.burn.binFile !== 'object') {
|
||||
return;
|
||||
}
|
||||
await BU.burnWithKFlash(web.burn.binFile, web.burn.erase);
|
||||
} else {
|
||||
const { web } = SELECTED_BOARD;
|
||||
const boardKey = Boards.getSelectedBoardKey();
|
||||
@@ -156,7 +191,7 @@ BU.initBurn = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
BU.burnByUSB = async () => {
|
||||
BU.burnWithDAP = async () => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
let portName = Serial.getSelectedPortName();
|
||||
if (!portName) {
|
||||
@@ -353,6 +388,88 @@ BU.burnWithEsptool = async (binFile, erase) => {
|
||||
}
|
||||
}
|
||||
|
||||
BU.burnWithKFlash = async (binFile, erase) => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
let portName = Serial.getSelectedPortName();
|
||||
if (!portName) {
|
||||
try {
|
||||
await BU.requestPort();
|
||||
portName = Serial.getSelectedPortName();
|
||||
if (!portName) {
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const port = Serial.getPort(portName);
|
||||
if (['HIDDevice', 'USBDevice'].includes(port.constructor.name)) {
|
||||
layer.msg(Msg.Lang['burn.notSupport'], { time: 1000 });
|
||||
return;
|
||||
}
|
||||
const statusBarSerial = mainStatusBarTabs.getStatusBarById(portName);
|
||||
if (statusBarSerial) {
|
||||
await statusBarSerial.close();
|
||||
}
|
||||
const statusBarTerminal = mainStatusBarTabs.getStatusBarById('output');
|
||||
statusBarTerminal.setValue(Msg.Lang['shell.burning'] + '...\n');
|
||||
mainStatusBarTabs.show();
|
||||
mainStatusBarTabs.changeTo('output');
|
||||
BU.progressLayer.title(`${Msg.Lang['shell.burning']}...`);
|
||||
BU.progressLayer.show();
|
||||
statusBarTerminal.addValue(Msg.Lang['shell.bin.reading'] + "...");
|
||||
statusBarTerminal.addValue("\n");
|
||||
|
||||
let data = [];
|
||||
try {
|
||||
for (let i of binFile) {
|
||||
if (i.path && i.offset) {
|
||||
const extname = path.extname(i.path);
|
||||
const absolutePath = path.join(Env.boardDirPath, i.path);
|
||||
const info = await readBinFileAsArrayBuffer(absolutePath, i.offset);
|
||||
if (extname === '.kfpkg') {
|
||||
const result = await decodeKfpkgFromArrayBuffer(info.data);
|
||||
data.push(...result.items);
|
||||
} else {
|
||||
data.push(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
statusBarTerminal.addValue("Failed!\n" + Msg.Lang['shell.bin.readFailed'] + "!\n");
|
||||
statusBarTerminal.addValue("\n" + error + "\n", true);
|
||||
BU.progressLayer.hide();
|
||||
statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
|
||||
return;
|
||||
}
|
||||
statusBarTerminal.addValue("Done!\n");
|
||||
let serial = null;
|
||||
try {
|
||||
serial = new Serial(portName);
|
||||
const kflash = new KFlash(serial, (message) => {
|
||||
statusBarTerminal.addValue(message);
|
||||
});
|
||||
await kflash.enter();
|
||||
for (let item of data) {
|
||||
await kflash.write(item.data, item.address, item.sha256Prefix ?? true, item?.filename ?? 'main.bin');
|
||||
}
|
||||
BU.progressLayer.hide();
|
||||
layer.msg(Msg.Lang['shell.burnSucc'], { time: 1000 });
|
||||
statusBarTerminal.addValue(`==${Msg.Lang['shell.burnSucc']}==\n`);
|
||||
} catch (error) {
|
||||
statusBarTerminal.addValue(`[ERROR] ${error.message}\n`);
|
||||
BU.progressLayer.hide();
|
||||
statusBarTerminal.addValue(`==${Msg.Lang['shell.burnFailed']}==\n`);
|
||||
} finally {
|
||||
try {
|
||||
serial && await serial.close();
|
||||
} catch (error) {
|
||||
Debug.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BU.getImportModulesName = (code) => {
|
||||
// 正则表达式: 匹配 import 或 from 导入语句
|
||||
const importRegex = /(?:import\s+([a-zA-Z0-9_]+)|from\s+([a-zA-Z0-9_]+)\s+import)/g;
|
||||
@@ -426,13 +543,13 @@ BU.initUpload = async () => {
|
||||
}
|
||||
}
|
||||
if (['BBC micro:bit', 'Mithon CC'].includes(BOARD.boardType)) {
|
||||
await BU.uploadByUSB(portName);
|
||||
await BU.uploadWithDAP(portName);
|
||||
} else {
|
||||
await BU.uploadWithAmpy(portName);
|
||||
}
|
||||
}
|
||||
|
||||
BU.uploadByUSB = async (portName) => {
|
||||
BU.uploadWithDAP = async (portName) => {
|
||||
const { mainStatusBarTabs } = Mixly;
|
||||
if (!portName) {
|
||||
try {
|
||||
|
||||
509
common/modules/mixly-modules/web/kflash.js
Normal file
509
common/modules/mixly-modules/web/kflash.js
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user