|
@@ -1,4 +1,7 @@
|
|
-import { ESPLoader, Logger } from "esp-web-flasher";
|
|
|
|
|
|
+// @ts-ignore-next-line
|
|
|
|
+import { Transport } from "esptool-js/webserial.js";
|
|
|
|
+// @ts-ignore-next-line
|
|
|
|
+import { ESPLoader } from "esptool-js/esploader.js";
|
|
import {
|
|
import {
|
|
Build,
|
|
Build,
|
|
FlashError,
|
|
FlashError,
|
|
@@ -6,19 +9,28 @@ import {
|
|
Manifest,
|
|
Manifest,
|
|
FlashStateType,
|
|
FlashStateType,
|
|
} from "./const";
|
|
} from "./const";
|
|
-import { getChipFamilyName } from "./util/chip-family-name";
|
|
|
|
import { sleep } from "./util/sleep";
|
|
import { sleep } from "./util/sleep";
|
|
|
|
|
|
|
|
+const resetTransport = async (transport: Transport) => {
|
|
|
|
+ await transport.device.setSignals({
|
|
|
|
+ dataTerminalReady: false,
|
|
|
|
+ requestToSend: true,
|
|
|
|
+ });
|
|
|
|
+ await transport.device.setSignals({
|
|
|
|
+ dataTerminalReady: false,
|
|
|
|
+ requestToSend: false,
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+
|
|
export const flash = async (
|
|
export const flash = async (
|
|
onEvent: (state: FlashState) => void,
|
|
onEvent: (state: FlashState) => void,
|
|
port: SerialPort,
|
|
port: SerialPort,
|
|
- logger: Logger,
|
|
|
|
manifestPath: string,
|
|
manifestPath: string,
|
|
|
|
+ manifest: Manifest,
|
|
eraseFirst: boolean
|
|
eraseFirst: boolean
|
|
) => {
|
|
) => {
|
|
- let manifest: Manifest;
|
|
|
|
let build: Build | undefined;
|
|
let build: Build | undefined;
|
|
- let chipFamily: ReturnType<typeof getChipFamilyName>;
|
|
|
|
|
|
+ let chipFamily: Build["chipFamily"];
|
|
|
|
|
|
const fireStateEvent = (stateUpdate: FlashState) =>
|
|
const fireStateEvent = (stateUpdate: FlashState) =>
|
|
onEvent({
|
|
onEvent({
|
|
@@ -28,12 +40,8 @@ export const flash = async (
|
|
chipFamily,
|
|
chipFamily,
|
|
});
|
|
});
|
|
|
|
|
|
- const manifestURL = new URL(manifestPath, location.toString()).toString();
|
|
|
|
- const manifestProm = fetch(manifestURL).then(
|
|
|
|
- (resp): Promise<Manifest> => resp.json()
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- const esploader = new ESPLoader(port, logger);
|
|
|
|
|
|
+ const transport = new Transport(port);
|
|
|
|
+ const esploader = new ESPLoader(transport, 115200);
|
|
|
|
|
|
// For debugging
|
|
// For debugging
|
|
(window as any).esploader = esploader;
|
|
(window as any).esploader = esploader;
|
|
@@ -45,61 +53,53 @@ export const flash = async (
|
|
});
|
|
});
|
|
|
|
|
|
try {
|
|
try {
|
|
- await esploader.initialize();
|
|
|
|
|
|
+ await esploader.main_fn();
|
|
|
|
+ await esploader.flash_id();
|
|
} catch (err: any) {
|
|
} catch (err: any) {
|
|
- logger.error(err);
|
|
|
|
|
|
+ console.error(err);
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
state: FlashStateType.ERROR,
|
|
state: FlashStateType.ERROR,
|
|
message:
|
|
message:
|
|
"Failed to initialize. Try resetting your device or holding the BOOT button while clicking INSTALL.",
|
|
"Failed to initialize. Try resetting your device or holding the BOOT button while clicking INSTALL.",
|
|
details: { error: FlashError.FAILED_INITIALIZING, details: err },
|
|
details: { error: FlashError.FAILED_INITIALIZING, details: err },
|
|
});
|
|
});
|
|
- if (esploader.connected) {
|
|
|
|
- await esploader.disconnect();
|
|
|
|
- }
|
|
|
|
|
|
+ await resetTransport(transport);
|
|
|
|
+ await transport.disconnect();
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- chipFamily = getChipFamilyName(esploader);
|
|
|
|
|
|
+ chipFamily = await esploader.chip.CHIP_NAME;
|
|
|
|
|
|
- fireStateEvent({
|
|
|
|
- state: FlashStateType.INITIALIZING,
|
|
|
|
- message: `Initialized. Found ${chipFamily}`,
|
|
|
|
- details: { done: true },
|
|
|
|
- });
|
|
|
|
- fireStateEvent({
|
|
|
|
- state: FlashStateType.MANIFEST,
|
|
|
|
- message: "Fetching manifest...",
|
|
|
|
- details: { done: false },
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- try {
|
|
|
|
- manifest = await manifestProm;
|
|
|
|
- } catch (err: any) {
|
|
|
|
|
|
+ if (!esploader.chip.ROM_TEXT) {
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
state: FlashStateType.ERROR,
|
|
state: FlashStateType.ERROR,
|
|
- message: `Unable to fetch manifest: ${err}`,
|
|
|
|
- details: { error: FlashError.FAILED_MANIFEST_FETCH, details: err },
|
|
|
|
|
|
+ message: `Chip ${chipFamily} is not supported`,
|
|
|
|
+ details: {
|
|
|
|
+ error: FlashError.NOT_SUPPORTED,
|
|
|
|
+ details: `Chip ${chipFamily} is not supported`,
|
|
|
|
+ },
|
|
});
|
|
});
|
|
- await esploader.disconnect();
|
|
|
|
|
|
+ await resetTransport(transport);
|
|
|
|
+ await transport.disconnect();
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- build = manifest.builds.find((b) => b.chipFamily === chipFamily);
|
|
|
|
-
|
|
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
- state: FlashStateType.MANIFEST,
|
|
|
|
- message: `Found manifest for ${manifest.name}`,
|
|
|
|
|
|
+ state: FlashStateType.INITIALIZING,
|
|
|
|
+ message: `Initialized. Found ${chipFamily}`,
|
|
details: { done: true },
|
|
details: { done: true },
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ build = manifest.builds.find((b) => b.chipFamily === chipFamily);
|
|
|
|
+
|
|
if (!build) {
|
|
if (!build) {
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
state: FlashStateType.ERROR,
|
|
state: FlashStateType.ERROR,
|
|
message: `Your ${chipFamily} board is not supported.`,
|
|
message: `Your ${chipFamily} board is not supported.`,
|
|
details: { error: FlashError.NOT_SUPPORTED, details: chipFamily },
|
|
details: { error: FlashError.NOT_SUPPORTED, details: chipFamily },
|
|
});
|
|
});
|
|
- await esploader.disconnect();
|
|
|
|
|
|
+ await resetTransport(transport);
|
|
|
|
+ await transport.disconnect();
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -109,6 +109,7 @@ export const flash = async (
|
|
details: { done: false },
|
|
details: { done: false },
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ const manifestURL = new URL(manifestPath, location.toString()).toString();
|
|
const filePromises = build.parts.map(async (part) => {
|
|
const filePromises = build.parts.map(async (part) => {
|
|
const url = new URL(part.path, manifestURL).toString();
|
|
const url = new URL(part.path, manifestURL).toString();
|
|
const resp = await fetch(url);
|
|
const resp = await fetch(url);
|
|
@@ -117,20 +118,24 @@ export const flash = async (
|
|
`Downlading firmware ${part.path} failed: ${resp.status}`
|
|
`Downlading firmware ${part.path} failed: ${resp.status}`
|
|
);
|
|
);
|
|
}
|
|
}
|
|
- return resp.arrayBuffer();
|
|
|
|
- });
|
|
|
|
|
|
|
|
- // Run the stub while we wait for files to download
|
|
|
|
- const espStub = await esploader.runStub();
|
|
|
|
|
|
+ const reader = new FileReader();
|
|
|
|
+ const blob = await resp.blob();
|
|
|
|
+
|
|
|
|
+ return new Promise<string>((resolve) => {
|
|
|
|
+ reader.addEventListener("load", () => resolve(reader.result as string));
|
|
|
|
+ reader.readAsBinaryString(blob);
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
|
|
- const files: ArrayBuffer[] = [];
|
|
|
|
|
|
+ const fileArray: Array<{ data: string; address: number }> = [];
|
|
let totalSize = 0;
|
|
let totalSize = 0;
|
|
|
|
|
|
- for (const prom of filePromises) {
|
|
|
|
|
|
+ for (let part = 0; part < filePromises.length; part++) {
|
|
try {
|
|
try {
|
|
- const data = await prom;
|
|
|
|
- files.push(data);
|
|
|
|
- totalSize += data.byteLength;
|
|
|
|
|
|
+ const data = await filePromises[part];
|
|
|
|
+ fileArray.push({ data, address: build.parts[part].offset });
|
|
|
|
+ totalSize += data.length;
|
|
} catch (err: any) {
|
|
} catch (err: any) {
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
state: FlashStateType.ERROR,
|
|
state: FlashStateType.ERROR,
|
|
@@ -140,7 +145,8 @@ export const flash = async (
|
|
details: err.message,
|
|
details: err.message,
|
|
},
|
|
},
|
|
});
|
|
});
|
|
- await esploader.disconnect();
|
|
|
|
|
|
+ await resetTransport(transport);
|
|
|
|
+ await transport.disconnect();
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -157,7 +163,7 @@ export const flash = async (
|
|
message: "Erasing device...",
|
|
message: "Erasing device...",
|
|
details: { done: false },
|
|
details: { done: false },
|
|
});
|
|
});
|
|
- await espStub.eraseFlash();
|
|
|
|
|
|
+ await esploader.erase_flash();
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
state: FlashStateType.ERASING,
|
|
state: FlashStateType.ERASING,
|
|
message: "Device erased",
|
|
message: "Device erased",
|
|
@@ -165,56 +171,55 @@ export const flash = async (
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
- let lastPct = 0;
|
|
|
|
-
|
|
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
state: FlashStateType.WRITING,
|
|
state: FlashStateType.WRITING,
|
|
- message: `Writing progress: ${lastPct}%`,
|
|
|
|
|
|
+ message: `Writing progress: 0%`,
|
|
details: {
|
|
details: {
|
|
bytesTotal: totalSize,
|
|
bytesTotal: totalSize,
|
|
bytesWritten: 0,
|
|
bytesWritten: 0,
|
|
- percentage: lastPct,
|
|
|
|
|
|
+ percentage: 0,
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
|
|
let totalWritten = 0;
|
|
let totalWritten = 0;
|
|
|
|
|
|
- for (const part of build.parts) {
|
|
|
|
- const file = files.shift()!;
|
|
|
|
- try {
|
|
|
|
- await espStub.flashData(
|
|
|
|
- file,
|
|
|
|
- (bytesWritten: number) => {
|
|
|
|
- const newPct = Math.floor(
|
|
|
|
- ((totalWritten + bytesWritten) / totalSize) * 100
|
|
|
|
- );
|
|
|
|
- if (newPct === lastPct) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- lastPct = newPct;
|
|
|
|
- fireStateEvent({
|
|
|
|
- state: FlashStateType.WRITING,
|
|
|
|
- message: `Writing progress: ${newPct}%`,
|
|
|
|
- details: {
|
|
|
|
- bytesTotal: totalSize,
|
|
|
|
- bytesWritten: totalWritten + bytesWritten,
|
|
|
|
- percentage: newPct,
|
|
|
|
- },
|
|
|
|
- });
|
|
|
|
- },
|
|
|
|
- part.offset,
|
|
|
|
- true
|
|
|
|
- );
|
|
|
|
- } catch (err: any) {
|
|
|
|
- fireStateEvent({
|
|
|
|
- state: FlashStateType.ERROR,
|
|
|
|
- message: err.message,
|
|
|
|
- details: { error: FlashError.WRITE_FAILED, details: err },
|
|
|
|
- });
|
|
|
|
- await esploader.disconnect();
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- totalWritten += file.byteLength;
|
|
|
|
|
|
+ try {
|
|
|
|
+ await esploader.write_flash({
|
|
|
|
+ fileArray,
|
|
|
|
+ reportProgress(fileIndex: number, written: number, total: number) {
|
|
|
|
+ const uncompressedWritten =
|
|
|
|
+ (written / total) * fileArray[fileIndex].data.length;
|
|
|
|
+
|
|
|
|
+ const newPct = Math.floor(
|
|
|
|
+ ((totalWritten + uncompressedWritten) / totalSize) * 100
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // we're done with this file
|
|
|
|
+ if (written === total) {
|
|
|
|
+ totalWritten += uncompressedWritten;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fireStateEvent({
|
|
|
|
+ state: FlashStateType.WRITING,
|
|
|
|
+ message: `Writing progress: ${newPct}%`,
|
|
|
|
+ details: {
|
|
|
|
+ bytesTotal: totalSize,
|
|
|
|
+ bytesWritten: totalWritten + written,
|
|
|
|
+ percentage: newPct,
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+ } catch (err: any) {
|
|
|
|
+ fireStateEvent({
|
|
|
|
+ state: FlashStateType.ERROR,
|
|
|
|
+ message: err.message,
|
|
|
|
+ details: { error: FlashError.WRITE_FAILED, details: err },
|
|
|
|
+ });
|
|
|
|
+ await resetTransport(transport);
|
|
|
|
+ await transport.disconnect();
|
|
|
|
+ return;
|
|
}
|
|
}
|
|
|
|
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
@@ -228,10 +233,10 @@ export const flash = async (
|
|
});
|
|
});
|
|
|
|
|
|
await sleep(100);
|
|
await sleep(100);
|
|
- console.log("DISCONNECT");
|
|
|
|
- await esploader.disconnect();
|
|
|
|
console.log("HARD RESET");
|
|
console.log("HARD RESET");
|
|
- await esploader.hardReset();
|
|
|
|
|
|
+ await resetTransport(transport);
|
|
|
|
+ console.log("DISCONNECT");
|
|
|
|
+ await transport.disconnect();
|
|
|
|
|
|
fireStateEvent({
|
|
fireStateEvent({
|
|
state: FlashStateType.FINISHED,
|
|
state: FlashStateType.FINISHED,
|