esp32blockly/src/serial/driveFlasher.js

56 lines
1.8 KiB
JavaScript

/**
* Write a firmware file directly to a removable mass-storage drive
* selected by the user (e.g. MICROBIT or RPI-RP2).
*/
export async function flashFileToDrive(onLog, onProgress, options = {}) {
const { firmwareUrl, outputName = 'firmware.bin', driveHint = 'device drive' } = options;
if (!firmwareUrl) throw new Error('No firmware URL provided');
onLog?.('Fetching firmware...\n');
const resp = await fetch(firmwareUrl);
if (!resp.ok) throw new Error(`Firmware fetch failed: ${resp.status}`);
const fileData = new Uint8Array(await resp.arrayBuffer());
onLog?.(`Firmware: ${(fileData.length / 1024).toFixed(0)} KB\n\n`);
onLog?.(`Select the ${driveHint} in the folder picker.\n\n`);
let dirHandle;
try {
dirHandle = await window.showDirectoryPicker({ mode: 'readwrite' });
} catch (err) {
if (err?.name === 'AbortError') throw new Error('Cancelled - no folder selected');
throw err;
}
onLog?.(`Writing ${outputName} to ${dirHandle.name}...\n`);
const fileHandle = await dirHandle.getFileHandle(outputName, { create: true });
const writable = await fileHandle.createWritable();
const CHUNK_SIZE = 64 * 1024;
let written = 0;
try {
while (written < fileData.length) {
const end = Math.min(written + CHUNK_SIZE, fileData.length);
await writable.write({
type: 'write',
position: written,
data: fileData.slice(written, end),
});
written = end;
onProgress?.(Math.round((written / fileData.length) * 95));
}
await writable.close();
} catch (err) {
// Some bootloaders unmount as soon as the file is fully written.
if (written >= fileData.length) {
onLog?.('(Drive disconnected - device is rebooting with new firmware)\n');
} else {
throw err;
}
}
onProgress?.(100);
onLog?.('Done!\n');
}