UNPKG

6.32 kBPlain TextView Raw
1import { Transport, ESPLoader } from "esptool-js";
2import {
3 Build,
4 FlashError,
5 FlashState,
6 Manifest,
7 FlashStateType,
8} from "./const";
9import { sleep } from "./util/sleep";
10
11const resetTransport = async (transport: Transport) => {
12 await transport.device.setSignals({
13 dataTerminalReady: false,
14 requestToSend: true,
15 });
16 await sleep(250);
17 await transport.device.setSignals({
18 dataTerminalReady: false,
19 requestToSend: false,
20 });
21 await sleep(250);
22};
23
24export const flash = async (
25 onEvent: (state: FlashState) => void,
26 port: SerialPort,
27 manifestPath: string,
28 manifest: Manifest,
29 eraseFirst: boolean,
30) => {
31 let build: Build | undefined;
32 let chipFamily: Build["chipFamily"];
33
34 const fireStateEvent = (stateUpdate: FlashState) =>
35 onEvent({
36 ...stateUpdate,
37 manifest,
38 build,
39 chipFamily,
40 });
41
42 const transport = new Transport(port);
43 const esploader = new ESPLoader({
44 transport,
45 baudrate: 115200,
46 romBaudrate: 115200,
47 enableTracing: false,
48 });
49
50 // For debugging
51 (window as any).esploader = esploader;
52
53 fireStateEvent({
54 state: FlashStateType.INITIALIZING,
55 message: "Initializing...",
56 details: { done: false },
57 });
58
59 try {
60 await esploader.main();
61 await esploader.flashId();
62 } catch (err: any) {
63 console.error(err);
64 fireStateEvent({
65 state: FlashStateType.ERROR,
66 message:
67 "Failed to initialize. Try resetting your device or holding the BOOT button while clicking INSTALL.",
68 details: { error: FlashError.FAILED_INITIALIZING, details: err },
69 });
70 await resetTransport(transport);
71 await transport.disconnect();
72 return;
73 }
74
75 chipFamily = esploader.chip.CHIP_NAME as any;
76
77 if (!esploader.chip.ROM_TEXT) {
78 fireStateEvent({
79 state: FlashStateType.ERROR,
80 message: `Chip ${chipFamily} is not supported`,
81 details: {
82 error: FlashError.NOT_SUPPORTED,
83 details: `Chip ${chipFamily} is not supported`,
84 },
85 });
86 await resetTransport(transport);
87 await transport.disconnect();
88 return;
89 }
90
91 fireStateEvent({
92 state: FlashStateType.INITIALIZING,
93 message: `Initialized. Found ${chipFamily}`,
94 details: { done: true },
95 });
96
97 build = manifest.builds.find((b) => b.chipFamily === chipFamily);
98
99 if (!build) {
100 fireStateEvent({
101 state: FlashStateType.ERROR,
102 message: `Your ${chipFamily} board is not supported.`,
103 details: { error: FlashError.NOT_SUPPORTED, details: chipFamily },
104 });
105 await resetTransport(transport);
106 await transport.disconnect();
107 return;
108 }
109
110 fireStateEvent({
111 state: FlashStateType.PREPARING,
112 message: "Preparing installation...",
113 details: { done: false },
114 });
115
116 const manifestURL = new URL(manifestPath, location.toString()).toString();
117 const filePromises = build.parts.map(async (part) => {
118 const url = new URL(part.path, manifestURL).toString();
119 const resp = await fetch(url);
120 if (!resp.ok) {
121 throw new Error(
122 `Downlading firmware ${part.path} failed: ${resp.status}`,
123 );
124 }
125
126 const reader = new FileReader();
127 const blob = await resp.blob();
128
129 return new Promise<string>((resolve) => {
130 reader.addEventListener("load", () => resolve(reader.result as string));
131 reader.readAsBinaryString(blob);
132 });
133 });
134
135 const fileArray: Array<{ data: string; address: number }> = [];
136 let totalSize = 0;
137
138 for (let part = 0; part < filePromises.length; part++) {
139 try {
140 const data = await filePromises[part];
141 fileArray.push({ data, address: build.parts[part].offset });
142 totalSize += data.length;
143 } catch (err: any) {
144 fireStateEvent({
145 state: FlashStateType.ERROR,
146 message: err.message,
147 details: {
148 error: FlashError.FAILED_FIRMWARE_DOWNLOAD,
149 details: err.message,
150 },
151 });
152 await resetTransport(transport);
153 await transport.disconnect();
154 return;
155 }
156 }
157
158 fireStateEvent({
159 state: FlashStateType.PREPARING,
160 message: "Installation prepared",
161 details: { done: true },
162 });
163
164 if (eraseFirst) {
165 fireStateEvent({
166 state: FlashStateType.ERASING,
167 message: "Erasing device...",
168 details: { done: false },
169 });
170 await esploader.eraseFlash();
171 fireStateEvent({
172 state: FlashStateType.ERASING,
173 message: "Device erased",
174 details: { done: true },
175 });
176 }
177
178 fireStateEvent({
179 state: FlashStateType.WRITING,
180 message: `Writing progress: 0%`,
181 details: {
182 bytesTotal: totalSize,
183 bytesWritten: 0,
184 percentage: 0,
185 },
186 });
187
188 let totalWritten = 0;
189
190 try {
191 await esploader.writeFlash({
192 fileArray,
193 flashSize: "keep",
194 flashMode: "keep",
195 flashFreq: "keep",
196 eraseAll: false,
197 compress: true,
198 // report progress
199 reportProgress: (fileIndex: number, written: number, total: number) => {
200 const uncompressedWritten =
201 (written / total) * fileArray[fileIndex].data.length;
202
203 const newPct = Math.floor(
204 ((totalWritten + uncompressedWritten) / totalSize) * 100,
205 );
206
207 // we're done with this file
208 if (written === total) {
209 totalWritten += uncompressedWritten;
210 return;
211 }
212
213 fireStateEvent({
214 state: FlashStateType.WRITING,
215 message: `Writing progress: ${newPct}%`,
216 details: {
217 bytesTotal: totalSize,
218 bytesWritten: totalWritten + written,
219 percentage: newPct,
220 },
221 });
222 },
223 });
224 } catch (err: any) {
225 fireStateEvent({
226 state: FlashStateType.ERROR,
227 message: err.message,
228 details: { error: FlashError.WRITE_FAILED, details: err },
229 });
230 await resetTransport(transport);
231 await transport.disconnect();
232 return;
233 }
234
235 fireStateEvent({
236 state: FlashStateType.WRITING,
237 message: "Writing complete",
238 details: {
239 bytesTotal: totalSize,
240 bytesWritten: totalWritten,
241 percentage: 100,
242 },
243 });
244
245 await sleep(100);
246 console.log("HARD RESET");
247 await resetTransport(transport);
248 console.log("DISCONNECT");
249 await transport.disconnect();
250
251 fireStateEvent({
252 state: FlashStateType.FINISHED,
253 message: "All done!",
254 });
255};
256
\No newline at end of file