import { ESPLoader, Transport, type FlashOptions } from 'esptool-js'; import { serial } from "web-serial-polyfill"; export const useEspFlasher = (term) => { const isConnected = ref(false); const flashProgress = ref(0); const status = ref(''); const error = ref(''); const espLoaderTerminal = { clean() { term.clear(); }, writeLine(data) { term.writeln(data); }, write(data) { term.write(data); }, }; let port = null; let espLoader: ESPLoader = null; let transport: Transport; const SERIAL_FILTERS: SerialPortFilter[] = [ { usbVendorId: 0x1a86 }, // QinHeng Electronics CH340 { usbVendorId: 0x303a } // Espressif USB JTAG/serial debug unit ]; const connect = async () => { try { if (transport) { await disconnect(); } const serialLib = !navigator.serial && navigator.usb ? serial : navigator.serial; if (port === null) { port = await serialLib.requestPort({ filters: SERIAL_FILTERS }); // await port.open({ baudRate: 115200 }); transport = new Transport(port, true); } espLoader = new ESPLoader({ transport, baudrate: 115200, terminal: espLoaderTerminal, logger: (message: string) => { status.value = "LOG: " + message; } }); // await espLoader.connect(); // await espLoader.sync(); const chipInfo = await espLoader.main(); status.value = `Connected to ${chipInfo}`; // await espLoader.loadStub(); isConnected.value = true; error.value = ''; } catch (err: any) { error.value = err.message; isConnected.value = false; await disconnect(); } }; const flash = async (manifest: FirmwareManifest, eraseFlash: boolean) => { if (!espLoader || !isConnected.value) { error.value = 'Not connected to device'; return; } try { const build = manifest.builds[0]; if (eraseFlash) { status.value = 'Erasing flash...'; await espLoader.eraseFlash(); } const fileArray = []; status.value = `Flashing ${manifest.name}...`; for (const part of build.parts) { const response = await fetch(part.path); const uint8Array = new Uint8Array(await response.arrayBuffer()); const buffer = Array.from(uint8Array) .map(byte => String.fromCharCode(byte)) .join(''); fileArray.push({ data: buffer, address: part.offset }); } const flashOptions: FlashOptions = { fileArray: fileArray, flashSize: "keep", eraseAll: false, compress: true, reportProgress: (fileIndex, written, total) => { flashProgress.value = Math.round((written / total) * 100); }, } as FlashOptions; await espLoader.writeFlash( flashOptions ); status.value = 'Flash complete!'; flashProgress.value = 100; espLoader.hardReset(); } catch (err: any) { error.value = err.message; } }; const disconnect = async () => { if (transport) { await transport.disconnect(); transport = null; } if (espLoader) { espLoader = null; } isConnected.value = false; status.value = ''; port = null; flashProgress.value = 0; }; const reset = async() => { // console.log(transport) // // if (transport) { // await transport.setDTR(false); // await new Promise((resolve) => setTimeout(resolve, 100)); // await transport.setDTR(true); // //} await espLoader.hardReset(); await disconnect(); } return { isConnected, flashProgress, status, error, connect, flash, disconnect, reset }; };