tasmota-webserial-esptool 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/.github/dependabot.yml +10 -0
  2. package/.github/workflows/build_upload.yml +31 -0
  3. package/.github/workflows/ci.yml +22 -0
  4. package/.prettierignore +1 -0
  5. package/README.md +18 -0
  6. package/css/dark.css +91 -0
  7. package/css/light.css +79 -0
  8. package/css/style.css +383 -0
  9. package/dist/const.d.ts +159 -0
  10. package/dist/const.js +252 -0
  11. package/dist/esp_loader.d.ts +166 -0
  12. package/dist/esp_loader.js +931 -0
  13. package/dist/index.d.ts +6 -0
  14. package/dist/index.js +12 -0
  15. package/dist/struct.d.ts +2 -0
  16. package/dist/struct.js +105 -0
  17. package/dist/stubs/esp32.json +1 -0
  18. package/dist/stubs/esp32c3.json +1 -0
  19. package/dist/stubs/esp32s2.json +1 -0
  20. package/dist/stubs/esp32s3.json +1 -0
  21. package/dist/stubs/esp8266.json +1 -0
  22. package/dist/stubs/index.d.ts +10 -0
  23. package/dist/stubs/index.js +26 -0
  24. package/dist/util.d.ts +14 -0
  25. package/dist/util.js +46 -0
  26. package/dist/web/esp32-a2dcbc2e.js +1 -0
  27. package/dist/web/esp32c3-18e9678b.js +1 -0
  28. package/dist/web/esp32s2-3109ccc6.js +1 -0
  29. package/dist/web/esp32s3-c1dbd867.js +1 -0
  30. package/dist/web/esp8266-144419c0.js +1 -0
  31. package/dist/web/index.js +1 -0
  32. package/index.html +196 -0
  33. package/js/esptool.js +1292 -0
  34. package/js/modules/esp32-a2dcbc2e.js +1 -0
  35. package/js/modules/esp32c3-18e9678b.js +1 -0
  36. package/js/modules/esp32s2-3109ccc6.js +1 -0
  37. package/js/modules/esp32s3-c1dbd867.js +1 -0
  38. package/js/modules/esp8266-144419c0.js +1 -0
  39. package/js/modules/esptool.js +1 -0
  40. package/js/script.js +447 -0
  41. package/js/utilities.js +148 -0
  42. package/license.md +12 -0
  43. package/package.json +36 -0
  44. package/rollup.config.js +27 -0
  45. package/script/build +8 -0
  46. package/script/develop +17 -0
  47. package/script/stubgen.py +47 -0
  48. package/src/const.ts +315 -0
  49. package/src/esp_loader.ts +1204 -0
  50. package/src/index.ts +27 -0
  51. package/src/struct.ts +115 -0
  52. package/src/stubs/esp32.json +1 -0
  53. package/src/stubs/esp32c2.json +1 -0
  54. package/src/stubs/esp32c3.json +1 -0
  55. package/src/stubs/esp32h2.json +1 -0
  56. package/src/stubs/esp32s2.json +1 -0
  57. package/src/stubs/esp32s3.json +1 -0
  58. package/src/stubs/esp8266.json +1 -0
  59. package/src/stubs/index.ts +48 -0
  60. package/src/util.ts +49 -0
  61. package/stubs/esp32c2.json +1 -0
  62. package/stubs/esp32c3.json +1 -0
  63. package/stubs/esp32h2.json +1 -0
  64. package/stubs/esp32s3.json +1 -0
  65. package/tsconfig.json +19 -0
@@ -0,0 +1,1204 @@
1
+ import {
2
+ CHIP_FAMILY_ESP32,
3
+ CHIP_FAMILY_ESP32S2,
4
+ CHIP_FAMILY_ESP32S3,
5
+ CHIP_FAMILY_ESP32C3,
6
+ CHIP_FAMILY_ESP8266,
7
+ MAX_TIMEOUT,
8
+ Logger,
9
+ DEFAULT_TIMEOUT,
10
+ ERASE_REGION_TIMEOUT_PER_MB,
11
+ ESP_CHANGE_BAUDRATE,
12
+ ESP_CHECKSUM_MAGIC,
13
+ ESP_FLASH_BEGIN,
14
+ ESP_FLASH_DATA,
15
+ ESP_FLASH_END,
16
+ ESP_MEM_BEGIN,
17
+ ESP_MEM_DATA,
18
+ ESP_MEM_END,
19
+ ESP_READ_REG,
20
+ ESP_WRITE_REG,
21
+ ESP_SPI_ATTACH,
22
+ ESP_SYNC,
23
+ FLASH_SECTOR_SIZE,
24
+ FLASH_WRITE_SIZE,
25
+ STUB_FLASH_WRITE_SIZE,
26
+ MEM_END_ROM_TIMEOUT,
27
+ ROM_INVALID_RECV_MSG,
28
+ SYNC_PACKET,
29
+ SYNC_TIMEOUT,
30
+ USB_RAM_BLOCK,
31
+ ChipFamily,
32
+ ESP_ERASE_FLASH,
33
+ CHIP_ERASE_TIMEOUT,
34
+ timeoutPerMb,
35
+ ESP_ROM_BAUD,
36
+ USB_JTAG_SERIAL_PID,
37
+ ESP_FLASH_DEFL_BEGIN,
38
+ ESP_FLASH_DEFL_DATA,
39
+ ESP_FLASH_DEFL_END,
40
+ getSpiFlashAddresses,
41
+ SpiFlashAddresses,
42
+ DETECTED_FLASH_SIZES,
43
+ CHIP_DETECT_MAGIC_REG_ADDR,
44
+ CHIP_DETECT_MAGIC_VALUES,
45
+ SlipReadError,
46
+ } from "./const";
47
+ import { getStubCode } from "./stubs";
48
+ import { hexFormatter, sleep, slipEncode, toHex } from "./util";
49
+ // @ts-ignore
50
+ import { deflate } from "pako/dist/pako.esm.mjs";
51
+ import { pack, unpack } from "./struct";
52
+
53
+ export class ESPLoader extends EventTarget {
54
+ chipFamily!: ChipFamily;
55
+ chipName: string | null = null;
56
+ _efuses = new Array(4).fill(0);
57
+ _flashsize = 4 * 1024 * 1024;
58
+ debug = false;
59
+ IS_STUB = false;
60
+ connected = true;
61
+ flashSize: string | null = null;
62
+
63
+ __inputBuffer?: number[];
64
+ private _reader?: ReadableStreamDefaultReader<Uint8Array>;
65
+
66
+ constructor(
67
+ public port: SerialPort,
68
+ public logger: Logger,
69
+ private _parent?: ESPLoader
70
+ ) {
71
+ super();
72
+ }
73
+
74
+ private get _inputBuffer(): number[] {
75
+ return this._parent ? this._parent._inputBuffer : this.__inputBuffer!;
76
+ }
77
+
78
+ async initialize() {
79
+ await this.hardReset(true);
80
+
81
+ if (!this._parent) {
82
+ this.__inputBuffer = [];
83
+ // Don't await this promise so it doesn't block rest of method.
84
+ this.readLoop();
85
+ }
86
+ await this.sync();
87
+
88
+ // Determine chip family and name
89
+ let chipMagicValue = await this.readRegister(CHIP_DETECT_MAGIC_REG_ADDR);
90
+ let chip = CHIP_DETECT_MAGIC_VALUES[chipMagicValue >>> 0];
91
+ if (chip === undefined) {
92
+ throw new Error(
93
+ `Unknown Chip: Hex: ${toHex(
94
+ chipMagicValue >>> 0,
95
+ 8
96
+ ).toLowerCase()} Number: ${chipMagicValue}`
97
+ );
98
+ }
99
+ this.chipName = chip.name;
100
+ this.chipFamily = chip.family;
101
+
102
+ // Read the OTP data for this chip and store into this.efuses array
103
+ let FlAddr = getSpiFlashAddresses(this.getChipFamily());
104
+ let AddrMAC = FlAddr.macFuse;
105
+ for (let i = 0; i < 4; i++) {
106
+ this._efuses[i] = await this.readRegister(AddrMAC + 4 * i);
107
+ }
108
+ this.logger.log(`Chip type ${this.chipName}`);
109
+ //this.logger.log("FLASHID");
110
+ }
111
+
112
+ /**
113
+ * @name readLoop
114
+ * Reads data from the input stream and places it in the inputBuffer
115
+ */
116
+ async readLoop() {
117
+ if (this.debug) {
118
+ this.logger.debug("Starting read loop");
119
+ }
120
+
121
+ this._reader = this.port.readable!.getReader();
122
+
123
+ try {
124
+ while (true) {
125
+ const { value, done } = await this._reader.read();
126
+ if (done) {
127
+ this._reader.releaseLock();
128
+ break;
129
+ }
130
+ if (!value || value.length === 0) {
131
+ continue;
132
+ }
133
+ this._inputBuffer.push(...Array.from(value));
134
+ }
135
+ } catch (err) {
136
+ console.error("Read loop got disconnected");
137
+ }
138
+ // Disconnected!
139
+ this.connected = false;
140
+ this.dispatchEvent(new Event("disconnect"));
141
+ this.logger.debug("Finished read loop");
142
+ }
143
+
144
+ sleep(ms = 100) {
145
+ return new Promise((resolve) => setTimeout(resolve, ms));
146
+ }
147
+
148
+ state_DTR = false;
149
+ async setRTS(state: boolean) {
150
+ await this.port.setSignals({ requestToSend: state });
151
+ // # Work-around for adapters on Windows using the usbser.sys driver:
152
+ // # generate a dummy change to DTR so that the set-control-line-state
153
+ // # request is sent with the updated RTS state and the same DTR state
154
+ // Referenced to esptool.py
155
+ await this.setDTR(this.state_DTR);
156
+ }
157
+
158
+ async setDTR(state: boolean) {
159
+ this.state_DTR = state;
160
+ await this.port.setSignals({ dataTerminalReady: state });
161
+ }
162
+
163
+ async hardReset(bootloader = false) {
164
+ this.logger.log("Try hard reset.");
165
+ if (bootloader) {
166
+ // enter flash mode
167
+ if (this.port.getInfo().usbProductId === USB_JTAG_SERIAL_PID) {
168
+ // esp32c3 esp32s3 etc. build-in USB serial.
169
+ // when connect to computer direct via usb, using following signals
170
+ // to enter flash mode automatically.
171
+ await this.setRTS(false);
172
+ await this.setDTR(false);
173
+ await this.sleep(100);
174
+
175
+ await this.setDTR(true);
176
+ await this.setRTS(false);
177
+ await this.sleep(100);
178
+
179
+ await this.setRTS(true);
180
+ await this.setDTR(false);
181
+ await this.setRTS(true);
182
+
183
+ await this.sleep(100);
184
+ await this.setRTS(false);
185
+ await this.setDTR(false);
186
+ } else {
187
+ // otherwise, esp chip should be connected to computer via usb-serial
188
+ // bridge chip like ch340,CP2102 etc.
189
+ // use normal way to enter flash mode.
190
+ await this.setDTR(false);
191
+ await this.setRTS(true);
192
+ await this.sleep(100);
193
+ await this.setDTR(true);
194
+ await this.setRTS(false);
195
+ await this.sleep(50);
196
+ await this.setDTR(false);
197
+ }
198
+ } else {
199
+ // just reset
200
+ await this.setRTS(true); // EN->LOW
201
+ await this.sleep(100);
202
+ await this.setRTS(false);
203
+ }
204
+ await new Promise((resolve) => setTimeout(resolve, 1000));
205
+ }
206
+
207
+ /**
208
+ * @name macAddr
209
+ * The MAC address burned into the OTP memory of the ESP chip
210
+ */
211
+ macAddr() {
212
+ let macAddr = new Array(6).fill(0);
213
+ let mac0 = this._efuses[0];
214
+ let mac1 = this._efuses[1];
215
+ let mac2 = this._efuses[2];
216
+ let mac3 = this._efuses[3];
217
+ let oui;
218
+ if (this.chipFamily == CHIP_FAMILY_ESP8266) {
219
+ if (mac3 != 0) {
220
+ oui = [(mac3 >> 16) & 0xff, (mac3 >> 8) & 0xff, mac3 & 0xff];
221
+ } else if (((mac1 >> 16) & 0xff) == 0) {
222
+ oui = [0x18, 0xfe, 0x34];
223
+ } else if (((mac1 >> 16) & 0xff) == 1) {
224
+ oui = [0xac, 0xd0, 0x74];
225
+ } else {
226
+ throw new Error("Couldnt determine OUI");
227
+ }
228
+
229
+ macAddr[0] = oui[0];
230
+ macAddr[1] = oui[1];
231
+ macAddr[2] = oui[2];
232
+ macAddr[3] = (mac1 >> 8) & 0xff;
233
+ macAddr[4] = mac1 & 0xff;
234
+ macAddr[5] = (mac0 >> 24) & 0xff;
235
+ } else if (this.chipFamily == CHIP_FAMILY_ESP32) {
236
+ macAddr[0] = (mac2 >> 8) & 0xff;
237
+ macAddr[1] = mac2 & 0xff;
238
+ macAddr[2] = (mac1 >> 24) & 0xff;
239
+ macAddr[3] = (mac1 >> 16) & 0xff;
240
+ macAddr[4] = (mac1 >> 8) & 0xff;
241
+ macAddr[5] = mac1 & 0xff;
242
+ } else if (
243
+ this.chipFamily == CHIP_FAMILY_ESP32S2 ||
244
+ this.chipFamily == CHIP_FAMILY_ESP32S3 ||
245
+ this.chipFamily == CHIP_FAMILY_ESP32C3
246
+ ) {
247
+ macAddr[0] = (mac1 >> 8) & 0xff;
248
+ macAddr[1] = mac1 & 0xff;
249
+ macAddr[2] = (mac0 >> 24) & 0xff;
250
+ macAddr[3] = (mac0 >> 16) & 0xff;
251
+ macAddr[4] = (mac0 >> 8) & 0xff;
252
+ macAddr[5] = mac0 & 0xff;
253
+ } else {
254
+ throw new Error("Unknown chip family");
255
+ }
256
+ return macAddr;
257
+ }
258
+
259
+ async readRegister(reg: number) {
260
+ if (this.debug) {
261
+ this.logger.debug("Reading from Register " + toHex(reg, 8));
262
+ }
263
+ let packet = pack("<I", reg);
264
+ await this.sendCommand(ESP_READ_REG, packet);
265
+ let [val, _data] = await this.getResponse(ESP_READ_REG);
266
+ return val;
267
+ }
268
+
269
+ /**
270
+ * @name checkCommand
271
+ * Send a command packet, check that the command succeeded and
272
+ * return a tuple with the value and data.
273
+ * See the ESP Serial Protocol for more details on what value/data are
274
+ */
275
+ async checkCommand(
276
+ opcode: number,
277
+ buffer: number[],
278
+ checksum = 0,
279
+ timeout = DEFAULT_TIMEOUT
280
+ ): Promise<[number, number[]]> {
281
+ timeout = Math.min(timeout, MAX_TIMEOUT);
282
+ await this.sendCommand(opcode, buffer, checksum);
283
+ let [value, data] = await this.getResponse(opcode, timeout);
284
+
285
+ if (data === null) {
286
+ throw new Error("Didn't get enough status bytes");
287
+ }
288
+
289
+ let statusLen = 0;
290
+
291
+ if (this.IS_STUB || this.chipFamily == CHIP_FAMILY_ESP8266) {
292
+ statusLen = 2;
293
+ } else if (
294
+ [
295
+ CHIP_FAMILY_ESP32,
296
+ CHIP_FAMILY_ESP32S2,
297
+ CHIP_FAMILY_ESP32S3,
298
+ CHIP_FAMILY_ESP32C3,
299
+ ].includes(this.chipFamily)
300
+ ) {
301
+ statusLen = 4;
302
+ } else {
303
+ if ([2, 4].includes(data.length)) {
304
+ statusLen = data.length;
305
+ }
306
+ }
307
+
308
+ if (data.length < statusLen) {
309
+ throw new Error("Didn't get enough status bytes");
310
+ }
311
+ let status = data.slice(-statusLen, data.length);
312
+ data = data.slice(0, -statusLen);
313
+ if (this.debug) {
314
+ this.logger.debug("status", status);
315
+ this.logger.debug("value", value);
316
+ this.logger.debug("data", data);
317
+ }
318
+ if (status[0] == 1) {
319
+ if (status[1] == ROM_INVALID_RECV_MSG) {
320
+ throw new Error("Invalid (unsupported) command " + toHex(opcode));
321
+ } else {
322
+ throw new Error("Command failure error code " + toHex(status[1]));
323
+ }
324
+ }
325
+
326
+ return [value, data];
327
+ }
328
+
329
+ /**
330
+ * @name sendCommand
331
+ * Send a slip-encoded, checksummed command over the UART,
332
+ * does not check response
333
+ */
334
+ async sendCommand(opcode: number, buffer: number[], checksum = 0) {
335
+ let packet = slipEncode([
336
+ ...pack("<BBHI", 0x00, opcode, buffer.length, checksum),
337
+ ...buffer,
338
+ ]);
339
+ if (this.debug) {
340
+ this.logger.debug(
341
+ `Writing ${packet.length} byte${packet.length == 1 ? "" : "s"}:`,
342
+ packet
343
+ );
344
+ }
345
+ await this.writeToStream(packet);
346
+ }
347
+
348
+ /**
349
+ * @name readPacket
350
+ * Generator to read SLIP packets from a serial port.
351
+ * Yields one full SLIP packet at a time, raises exception on timeout or invalid data.
352
+ * Designed to avoid too many calls to serial.read(1), which can bog
353
+ * down on slow systems.
354
+ */
355
+
356
+ async readPacket(timeout: number): Promise<number[]> {
357
+ let partialPacket: number[] | null = null;
358
+ let inEscape = false;
359
+ let readBytes: number[] = [];
360
+ while (true) {
361
+ let stamp = Date.now();
362
+ readBytes = [];
363
+ while (Date.now() - stamp < timeout) {
364
+ if (this._inputBuffer.length > 0) {
365
+ readBytes.push(this._inputBuffer.shift()!);
366
+ break;
367
+ } else {
368
+ await sleep(10);
369
+ }
370
+ }
371
+ if (readBytes.length == 0) {
372
+ let waitingFor = partialPacket === null ? "header" : "content";
373
+ throw new SlipReadError("Timed out waiting for packet " + waitingFor);
374
+ }
375
+ if (this.debug)
376
+ this.logger.debug(
377
+ "Read " + readBytes.length + " bytes: " + hexFormatter(readBytes)
378
+ );
379
+ for (let b of readBytes) {
380
+ if (partialPacket === null) {
381
+ // waiting for packet header
382
+ if (b == 0xc0) {
383
+ partialPacket = [];
384
+ } else {
385
+ if (this.debug) {
386
+ this.logger.debug(
387
+ "Read invalid data: " + hexFormatter(readBytes)
388
+ );
389
+ this.logger.debug(
390
+ "Remaining data in serial buffer: " +
391
+ hexFormatter(this._inputBuffer)
392
+ );
393
+ }
394
+ throw new SlipReadError(
395
+ "Invalid head of packet (" + toHex(b) + ")"
396
+ );
397
+ }
398
+ } else if (inEscape) {
399
+ // part-way through escape sequence
400
+ inEscape = false;
401
+ if (b == 0xdc) {
402
+ partialPacket.push(0xc0);
403
+ } else if (b == 0xdd) {
404
+ partialPacket.push(0xdb);
405
+ } else {
406
+ if (this.debug) {
407
+ this.logger.debug(
408
+ "Read invalid data: " + hexFormatter(readBytes)
409
+ );
410
+ this.logger.debug(
411
+ "Remaining data in serial buffer: " +
412
+ hexFormatter(this._inputBuffer)
413
+ );
414
+ }
415
+ throw new SlipReadError(
416
+ "Invalid SLIP escape (0xdb, " + toHex(b) + ")"
417
+ );
418
+ }
419
+ } else if (b == 0xdb) {
420
+ // start of escape sequence
421
+ inEscape = true;
422
+ } else if (b == 0xc0) {
423
+ // end of packet
424
+ if (this.debug)
425
+ this.logger.debug(
426
+ "Received full packet: " + hexFormatter(partialPacket)
427
+ );
428
+ return partialPacket;
429
+ } else {
430
+ // normal byte in packet
431
+ partialPacket.push(b);
432
+ }
433
+ }
434
+ }
435
+ throw new SlipReadError("Invalid state");
436
+ }
437
+
438
+ /**
439
+ * @name getResponse
440
+ * Read response data and decodes the slip packet, then parses
441
+ * out the value/data and returns as a tuple of (value, data) where
442
+ * each is a list of bytes
443
+ */
444
+ async getResponse(
445
+ opcode: number,
446
+ timeout = DEFAULT_TIMEOUT
447
+ ): Promise<[number, number[]]> {
448
+ for (let i = 0; i < 100; i++) {
449
+ const packet = await this.readPacket(timeout);
450
+
451
+ if (packet.length < 8) {
452
+ continue;
453
+ }
454
+
455
+ const [resp, opRet, _lenRet, val] = unpack("<BBHI", packet.slice(0, 8));
456
+ if (resp != 1) {
457
+ continue;
458
+ }
459
+ const data = packet.slice(8);
460
+ if (opcode == null || opRet == opcode) {
461
+ return [val, data];
462
+ }
463
+ if (data[0] != 0 && data[1] == ROM_INVALID_RECV_MSG) {
464
+ this._inputBuffer.length = 0;
465
+ throw new Error(`Invalid (unsupported) command ${toHex(opcode)}`);
466
+ }
467
+ }
468
+ throw "Response doesn't match request";
469
+ }
470
+
471
+ /**
472
+ * @name checksum
473
+ * Calculate checksum of a blob, as it is defined by the ROM
474
+ */
475
+ checksum(data: number[], state = ESP_CHECKSUM_MAGIC) {
476
+ for (let b of data) {
477
+ state ^= b;
478
+ }
479
+ return state;
480
+ }
481
+
482
+ async setBaudrate(baud: number) {
483
+ if (this.chipFamily == CHIP_FAMILY_ESP8266) {
484
+ throw new Error("Changing baud rate is not supported on the ESP8266");
485
+ }
486
+
487
+ this.logger.log("Attempting to change baud rate to " + baud + "...");
488
+
489
+ try {
490
+ // Send ESP_ROM_BAUD(115200) as the old one if running STUB otherwise 0
491
+ let buffer = pack("<II", baud, this.IS_STUB ? ESP_ROM_BAUD : 0);
492
+ await this.checkCommand(ESP_CHANGE_BAUDRATE, buffer);
493
+ } catch (e) {
494
+ console.error(e);
495
+ throw new Error(
496
+ `Unable to change the baud rate to ${baud}: No response from set baud rate command.`
497
+ );
498
+ }
499
+
500
+ if (this._parent) {
501
+ await this._parent.reconfigurePort(baud);
502
+ } else {
503
+ await this.reconfigurePort(baud);
504
+ }
505
+ }
506
+
507
+ async reconfigurePort(baud: number) {
508
+ try {
509
+ // SerialPort does not allow to be reconfigured while open so we close and re-open
510
+ // reader.cancel() causes the Promise returned by the read() operation running on
511
+ // the readLoop to return immediately with { value: undefined, done: true } and thus
512
+ // breaking the loop and exiting readLoop();
513
+ await this._reader?.cancel();
514
+ await this.port.close();
515
+
516
+ // Reopen Port
517
+ await this.port.open({ baudRate: baud });
518
+
519
+ // Restart Readloop
520
+ this.readLoop();
521
+
522
+ this.logger.log(`Changed baud rate to ${baud}`);
523
+ } catch (e) {
524
+ console.error(e);
525
+ throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
526
+ }
527
+ }
528
+
529
+ /**
530
+ * @name sync
531
+ * Put into ROM bootload mode & attempt to synchronize with the
532
+ * ESP ROM bootloader, we will retry a few times
533
+ */
534
+ async sync() {
535
+ for (let i = 0; i < 5; i++) {
536
+ this._inputBuffer.length = 0;
537
+ let response = await this._sync();
538
+ if (response) {
539
+ await sleep(100);
540
+ return true;
541
+ }
542
+ await sleep(100);
543
+ }
544
+
545
+ throw new Error("Couldn't sync to ESP. Try resetting.");
546
+ }
547
+
548
+ /**
549
+ * @name _sync
550
+ * Perform a soft-sync using AT sync packets, does not perform
551
+ * any hardware resetting
552
+ */
553
+ async _sync() {
554
+ await this.sendCommand(ESP_SYNC, SYNC_PACKET);
555
+ for (let i = 0; i < 8; i++) {
556
+ try {
557
+ let [_reply, data] = await this.getResponse(ESP_SYNC, SYNC_TIMEOUT);
558
+ if (data.length > 1 && data[0] == 0 && data[1] == 0) {
559
+ return true;
560
+ }
561
+ } catch (err) {
562
+ // If read packet fails.
563
+ }
564
+ }
565
+ return false;
566
+ }
567
+
568
+ /**
569
+ * @name getFlashWriteSize
570
+ * Get the Flash write size based on the chip
571
+ */
572
+ getFlashWriteSize() {
573
+ if (this.IS_STUB) {
574
+ return STUB_FLASH_WRITE_SIZE;
575
+ }
576
+ return FLASH_WRITE_SIZE;
577
+ }
578
+
579
+ /**
580
+ * @name flashData
581
+ * Program a full, uncompressed binary file into SPI Flash at
582
+ * a given offset. If an ESP32 and md5 string is passed in, will also
583
+ * verify memory. ESP8266 does not have checksum memory verification in
584
+ * ROM
585
+ */
586
+ async flashData(
587
+ binaryData: ArrayBuffer,
588
+ updateProgress: (bytesWritten: number, totalBytes: number) => void,
589
+ offset = 0,
590
+ compress = false
591
+ ) {
592
+ if (binaryData.byteLength >= 8) {
593
+ // unpack the (potential) image header
594
+ var header = Array.from(new Uint8Array(binaryData, 0, 4));
595
+ let headerMagic = header[0];
596
+ let headerFlashMode = header[2];
597
+ let headerFlashSizeFreq = header[3];
598
+
599
+ this.logger.log(
600
+ `Image header, Magic=${toHex(headerMagic)}, FlashMode=${toHex(
601
+ headerFlashMode
602
+ )}, FlashSizeFreq=${toHex(headerFlashSizeFreq)}`
603
+ );
604
+ }
605
+
606
+ let uncompressedFilesize = binaryData.byteLength;
607
+ let compressedFilesize = 0;
608
+
609
+ let dataToFlash;
610
+ let timeout = DEFAULT_TIMEOUT;
611
+
612
+ if (compress) {
613
+ dataToFlash = deflate(new Uint8Array(binaryData), {
614
+ level: 9,
615
+ }).buffer;
616
+ compressedFilesize = dataToFlash.byteLength;
617
+ this.logger.log(
618
+ `Writing data with filesize: ${uncompressedFilesize}. Compressed Size: ${compressedFilesize}`
619
+ );
620
+ timeout = await this.flashDeflBegin(
621
+ uncompressedFilesize,
622
+ compressedFilesize,
623
+ offset
624
+ );
625
+ } else {
626
+ this.logger.log(`Writing data with filesize: ${uncompressedFilesize}`);
627
+ dataToFlash = binaryData;
628
+ await this.flashBegin(uncompressedFilesize, offset);
629
+ }
630
+
631
+ let block = [];
632
+ let seq = 0;
633
+ let written = 0;
634
+ let position = 0;
635
+ let stamp = Date.now();
636
+ let flashWriteSize = this.getFlashWriteSize();
637
+
638
+ let filesize = compress ? compressedFilesize : uncompressedFilesize;
639
+
640
+ while (filesize - position > 0) {
641
+ if (this.debug) {
642
+ this.logger.log(
643
+ `Writing at ${toHex(offset + seq * flashWriteSize, 8)} `
644
+ );
645
+ }
646
+ if (filesize - position >= flashWriteSize) {
647
+ block = Array.from(
648
+ new Uint8Array(dataToFlash, position, flashWriteSize)
649
+ );
650
+ } else {
651
+ // Pad the last block only if we are sending uncompressed data.
652
+ block = Array.from(
653
+ new Uint8Array(dataToFlash, position, filesize - position)
654
+ );
655
+ if (!compress) {
656
+ block = block.concat(
657
+ new Array(flashWriteSize - block.length).fill(0xff)
658
+ );
659
+ }
660
+ }
661
+ if (compress) {
662
+ await this.flashDeflBlock(block, seq, timeout);
663
+ } else {
664
+ await this.flashBlock(block, seq);
665
+ }
666
+ seq += 1;
667
+ // If using compression we update the progress with the proportional size of the block taking into account the compression ratio.
668
+ // This way we report progress on the uncompressed size
669
+ written += compress
670
+ ? Math.round((block.length * uncompressedFilesize) / compressedFilesize)
671
+ : block.length;
672
+ position += flashWriteSize;
673
+ updateProgress(
674
+ Math.min(written, uncompressedFilesize),
675
+ uncompressedFilesize
676
+ );
677
+ }
678
+ this.logger.log(
679
+ "Took " + (Date.now() - stamp) + "ms to write " + filesize + " bytes"
680
+ );
681
+
682
+ // Only send flashF finish if running the stub because ir causes the ROM to exit and run user code
683
+ if (this.IS_STUB) {
684
+ await this.flashBegin(0, 0);
685
+ if (compress) {
686
+ await this.flashDeflFinish();
687
+ } else {
688
+ await this.flashFinish();
689
+ }
690
+ }
691
+ }
692
+
693
+ /**
694
+ * @name flashBlock
695
+ * Send one block of data to program into SPI Flash memory
696
+ */
697
+ async flashBlock(data: number[], seq: number, timeout = DEFAULT_TIMEOUT) {
698
+ await this.checkCommand(
699
+ ESP_FLASH_DATA,
700
+ pack("<IIII", data.length, seq, 0, 0).concat(data),
701
+ this.checksum(data),
702
+ timeout
703
+ );
704
+ }
705
+ async flashDeflBlock(data: number[], seq: number, timeout = DEFAULT_TIMEOUT) {
706
+ await this.checkCommand(
707
+ ESP_FLASH_DEFL_DATA,
708
+ pack("<IIII", data.length, seq, 0, 0).concat(data),
709
+ this.checksum(data),
710
+ timeout
711
+ );
712
+ }
713
+
714
+ /**
715
+ * @name flashBegin
716
+ * Prepare for flashing by attaching SPI chip and erasing the
717
+ * number of blocks requred.
718
+ */
719
+ async flashBegin(size = 0, offset = 0, encrypted = false) {
720
+ let eraseSize;
721
+ let buffer;
722
+ let flashWriteSize = this.getFlashWriteSize();
723
+ if (
724
+ !this.IS_STUB &&
725
+ [
726
+ CHIP_FAMILY_ESP32,
727
+ CHIP_FAMILY_ESP32S2,
728
+ CHIP_FAMILY_ESP32S3,
729
+ CHIP_FAMILY_ESP32C3,
730
+ ].includes(this.chipFamily)
731
+ ) {
732
+ await this.checkCommand(ESP_SPI_ATTACH, new Array(8).fill(0));
733
+ }
734
+ let numBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
735
+ if (this.chipFamily == CHIP_FAMILY_ESP8266) {
736
+ eraseSize = this.getEraseSize(offset, size);
737
+ } else {
738
+ eraseSize = size;
739
+ }
740
+
741
+ let timeout;
742
+ if (this.IS_STUB) {
743
+ timeout = DEFAULT_TIMEOUT;
744
+ } else {
745
+ timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
746
+ }
747
+
748
+ let stamp = Date.now();
749
+ buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
750
+ if (
751
+ this.chipFamily == CHIP_FAMILY_ESP32 ||
752
+ this.chipFamily == CHIP_FAMILY_ESP32S2 ||
753
+ this.chipFamily == CHIP_FAMILY_ESP32S3 ||
754
+ this.chipFamily == CHIP_FAMILY_ESP32C3
755
+ ) {
756
+ buffer = buffer.concat(pack("<I", encrypted ? 1 : 0));
757
+ }
758
+ this.logger.log(
759
+ "Erase size " +
760
+ eraseSize +
761
+ ", blocks " +
762
+ numBlocks +
763
+ ", block size " +
764
+ toHex(flashWriteSize, 4) +
765
+ ", offset " +
766
+ toHex(offset, 4) +
767
+ ", encrypted " +
768
+ (encrypted ? "yes" : "no")
769
+ );
770
+ await this.checkCommand(ESP_FLASH_BEGIN, buffer, 0, timeout);
771
+ if (size != 0 && !this.IS_STUB) {
772
+ this.logger.log(
773
+ "Took " + (Date.now() - stamp) + "ms to erase " + numBlocks + " bytes"
774
+ );
775
+ }
776
+ return numBlocks;
777
+ }
778
+
779
+ /**
780
+ * @name flashDeflBegin
781
+ *
782
+ */
783
+
784
+ async flashDeflBegin(
785
+ size = 0,
786
+ compressedSize = 0,
787
+ offset = 0,
788
+ encrypted = false
789
+ ) {
790
+ // Start downloading compressed data to Flash (performs an erase)
791
+ // Returns number of blocks to write.
792
+ let flashWriteSize = this.getFlashWriteSize();
793
+ let numBlocks = Math.floor(
794
+ (compressedSize + flashWriteSize - 1) / flashWriteSize
795
+ );
796
+ let eraseBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
797
+ let writeSize = 0;
798
+ let timeout = 0;
799
+ let buffer;
800
+
801
+ if (this.IS_STUB) {
802
+ writeSize = size; // stub expects number of bytes here, manages erasing internally
803
+ timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, writeSize); // ROM performs the erase up front
804
+ } else {
805
+ writeSize = eraseBlocks * flashWriteSize; // ROM expects rounded up to erase block size
806
+ timeout = DEFAULT_TIMEOUT;
807
+ }
808
+ buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
809
+
810
+ await this.checkCommand(ESP_FLASH_DEFL_BEGIN, buffer, 0, timeout);
811
+
812
+ return timeout;
813
+ }
814
+
815
+ async flashFinish() {
816
+ let buffer = pack("<I", 1);
817
+ await this.checkCommand(ESP_FLASH_END, buffer);
818
+ }
819
+
820
+ async flashDeflFinish() {
821
+ let buffer = pack("<I", 1);
822
+ await this.checkCommand(ESP_FLASH_DEFL_END, buffer);
823
+ }
824
+
825
+ getBootloaderOffset() {
826
+ let bootFlashOffs = getSpiFlashAddresses(this.getChipFamily());
827
+ let BootldrFlashOffs = bootFlashOffs.flashOffs;
828
+ return BootldrFlashOffs;
829
+ }
830
+
831
+ async flashId() {
832
+ let SPIFLASH_RDID = 0x9f;
833
+ let result = await this.runSpiFlashCommand(SPIFLASH_RDID, [], 24);
834
+ return result;
835
+ }
836
+
837
+ getChipFamily() {
838
+ return this._parent ? this._parent.chipFamily : this.chipFamily;
839
+ }
840
+
841
+ async writeRegister(
842
+ address: number,
843
+ value: number,
844
+ mask = 0xffffffff,
845
+ delayUs = 0,
846
+ delayAfterUs = 0
847
+ ) {
848
+ let buffer = pack("<IIII", address, value, mask, delayUs);
849
+ if (delayAfterUs > 0) {
850
+ // add a dummy write to a date register as an excuse to have a delay
851
+ buffer.concat(
852
+ pack(
853
+ "<IIII",
854
+ getSpiFlashAddresses(this.getChipFamily()).uartDateReg,
855
+ 0,
856
+ 0,
857
+ delayAfterUs
858
+ )
859
+ );
860
+ }
861
+ await this.checkCommand(ESP_WRITE_REG, buffer);
862
+ }
863
+
864
+ async setDataLengths(
865
+ spiAddresses: SpiFlashAddresses,
866
+ mosiBits: number,
867
+ misoBits: number
868
+ ) {
869
+ if (spiAddresses.mosiDlenOffs != -1) {
870
+ // ESP32/32S2/32S3/32C3 has a more sophisticated way to set up "user" commands
871
+ let SPI_MOSI_DLEN_REG = spiAddresses.regBase + spiAddresses.mosiDlenOffs;
872
+ let SPI_MISO_DLEN_REG = spiAddresses.regBase + spiAddresses.misoDlenOffs;
873
+ if (mosiBits > 0) {
874
+ await this.writeRegister(SPI_MOSI_DLEN_REG, mosiBits - 1);
875
+ }
876
+ if (misoBits > 0) {
877
+ await this.writeRegister(SPI_MISO_DLEN_REG, misoBits - 1);
878
+ }
879
+ } else {
880
+ let SPI_DATA_LEN_REG = spiAddresses.regBase + spiAddresses.usr1Offs;
881
+ let SPI_MOSI_BITLEN_S = 17;
882
+ let SPI_MISO_BITLEN_S = 8;
883
+ let mosiMask = mosiBits == 0 ? 0 : mosiBits - 1;
884
+ let misoMask = misoBits == 0 ? 0 : misoBits - 1;
885
+ let value =
886
+ (misoMask << SPI_MISO_BITLEN_S) | (mosiMask << SPI_MOSI_BITLEN_S);
887
+ await this.writeRegister(SPI_DATA_LEN_REG, value);
888
+ }
889
+ }
890
+ async waitDone(spiCmdReg: number, spiCmdUsr: number) {
891
+ for (let i = 0; i < 10; i++) {
892
+ let cmdValue = await this.readRegister(spiCmdReg);
893
+ if ((cmdValue & spiCmdUsr) == 0) {
894
+ return;
895
+ }
896
+ }
897
+ throw Error("SPI command did not complete in time");
898
+ }
899
+
900
+ async runSpiFlashCommand(
901
+ spiflashCommand: number,
902
+ data: number[],
903
+ readBits = 0
904
+ ) {
905
+ // Run an arbitrary SPI flash command.
906
+
907
+ // This function uses the "USR_COMMAND" functionality in the ESP
908
+ // SPI hardware, rather than the precanned commands supported by
909
+ // hardware. So the value of spiflash_command is an actual command
910
+ // byte, sent over the wire.
911
+
912
+ // After writing command byte, writes 'data' to MOSI and then
913
+ // reads back 'read_bits' of reply on MISO. Result is a number.
914
+
915
+ // SPI_USR register flags
916
+ let SPI_USR_COMMAND = 1 << 31;
917
+ let SPI_USR_MISO = 1 << 28;
918
+ let SPI_USR_MOSI = 1 << 27;
919
+
920
+ // SPI registers, base address differs ESP32* vs 8266
921
+ let spiAddresses = getSpiFlashAddresses(this.getChipFamily());
922
+ let base = spiAddresses.regBase;
923
+ let SPI_CMD_REG = base;
924
+ let SPI_USR_REG = base + spiAddresses.usrOffs;
925
+ let SPI_USR2_REG = base + spiAddresses.usr2Offs;
926
+ let SPI_W0_REG = base + spiAddresses.w0Offs;
927
+
928
+ // SPI peripheral "command" bitmasks for SPI_CMD_REG
929
+ let SPI_CMD_USR = 1 << 18;
930
+
931
+ // shift values
932
+ let SPI_USR2_COMMAND_LEN_SHIFT = 28;
933
+
934
+ if (readBits > 32) {
935
+ throw new Error(
936
+ "Reading more than 32 bits back from a SPI flash operation is unsupported"
937
+ );
938
+ }
939
+ if (data.length > 64) {
940
+ throw new Error(
941
+ "Writing more than 64 bytes of data with one SPI command is unsupported"
942
+ );
943
+ }
944
+
945
+ let dataBits = data.length * 8;
946
+ let oldSpiUsr = await this.readRegister(SPI_USR_REG);
947
+ let oldSpiUsr2 = await this.readRegister(SPI_USR2_REG);
948
+
949
+ let flags = SPI_USR_COMMAND;
950
+
951
+ if (readBits > 0) {
952
+ flags |= SPI_USR_MISO;
953
+ }
954
+ if (dataBits > 0) {
955
+ flags |= SPI_USR_MOSI;
956
+ }
957
+
958
+ await this.setDataLengths(spiAddresses, dataBits, readBits);
959
+
960
+ await this.writeRegister(SPI_USR_REG, flags);
961
+ await this.writeRegister(
962
+ SPI_USR2_REG,
963
+ (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflashCommand
964
+ );
965
+ if (dataBits == 0) {
966
+ await this.writeRegister(SPI_W0_REG, 0); // clear data register before we read it
967
+ } else {
968
+ data.concat(new Array(data.length % 4).fill(0x00)); // pad to 32-bit multiple
969
+
970
+ let words = unpack("I".repeat(Math.floor(data.length / 4)), data);
971
+ let nextReg = SPI_W0_REG;
972
+
973
+ this.logger.debug(`Words Length: ${words.length}`);
974
+
975
+ for (const word of words) {
976
+ this.logger.debug(
977
+ `Writing word ${toHex(word)} to register offset ${toHex(nextReg)}`
978
+ );
979
+ await this.writeRegister(nextReg, word);
980
+ nextReg += 4;
981
+ }
982
+ }
983
+ await this.writeRegister(SPI_CMD_REG, SPI_CMD_USR);
984
+ await this.waitDone(SPI_CMD_REG, SPI_CMD_USR);
985
+
986
+ let status = await this.readRegister(SPI_W0_REG);
987
+ // restore some SPI controller registers
988
+ await this.writeRegister(SPI_USR_REG, oldSpiUsr);
989
+ await this.writeRegister(SPI_USR2_REG, oldSpiUsr2);
990
+ return status;
991
+ }
992
+ async detectFlashSize() {
993
+ this.logger.log("Detecting Flash Size");
994
+
995
+ let flashId = await this.flashId();
996
+ let manufacturer = flashId & 0xff;
997
+ let flashIdLowbyte = (flashId >> 16) & 0xff;
998
+
999
+ this.logger.log(`FlashId: ${toHex(flashId)}`);
1000
+ this.logger.log(`Flash Manufacturer: ${manufacturer.toString(16)}`);
1001
+ this.logger.log(
1002
+ `Flash Device: ${((flashId >> 8) & 0xff).toString(
1003
+ 16
1004
+ )}${flashIdLowbyte.toString(16)}`
1005
+ );
1006
+
1007
+ this.flashSize = DETECTED_FLASH_SIZES[flashIdLowbyte];
1008
+ this.logger.log(`Auto-detected Flash size: ${this.flashSize}`);
1009
+ }
1010
+
1011
+ /**
1012
+ * @name getEraseSize
1013
+ * Calculate an erase size given a specific size in bytes.
1014
+ * Provides a workaround for the bootloader erase bug on ESP8266.
1015
+ */
1016
+ getEraseSize(offset: number, size: number) {
1017
+ let sectorsPerBlock = 16;
1018
+ let sectorSize = FLASH_SECTOR_SIZE;
1019
+ let numSectors = Math.floor((size + sectorSize - 1) / sectorSize);
1020
+ let startSector = Math.floor(offset / sectorSize);
1021
+
1022
+ let headSectors = sectorsPerBlock - (startSector % sectorsPerBlock);
1023
+ if (numSectors < headSectors) {
1024
+ headSectors = numSectors;
1025
+ }
1026
+
1027
+ if (numSectors < 2 * headSectors) {
1028
+ return Math.floor(((numSectors + 1) / 2) * sectorSize);
1029
+ }
1030
+
1031
+ return (numSectors - headSectors) * sectorSize;
1032
+ }
1033
+
1034
+ /**
1035
+ * @name memBegin (592)
1036
+ * Start downloading an application image to RAM
1037
+ */
1038
+ async memBegin(
1039
+ size: number,
1040
+ blocks: number,
1041
+ blocksize: number,
1042
+ offset: number
1043
+ ) {
1044
+ return await this.checkCommand(
1045
+ ESP_MEM_BEGIN,
1046
+ pack("<IIII", size, blocks, blocksize, offset)
1047
+ );
1048
+ }
1049
+
1050
+ /**
1051
+ * @name memBlock (609)
1052
+ * Send a block of an image to RAM
1053
+ */
1054
+ async memBlock(data: number[], seq: number) {
1055
+ return await this.checkCommand(
1056
+ ESP_MEM_DATA,
1057
+ pack("<IIII", data.length, seq, 0, 0).concat(data),
1058
+ this.checksum(data)
1059
+ );
1060
+ }
1061
+
1062
+ /**
1063
+ * @name memFinish (615)
1064
+ * Leave download mode and run the application
1065
+ *
1066
+ * Sending ESP_MEM_END usually sends a correct response back, however sometimes
1067
+ * (with ROM loader) the executed code may reset the UART or change the baud rate
1068
+ * before the transmit FIFO is empty. So in these cases we set a short timeout and
1069
+ * ignore errors.
1070
+ */
1071
+ async memFinish(entrypoint = 0) {
1072
+ let timeout = this.IS_STUB ? DEFAULT_TIMEOUT : MEM_END_ROM_TIMEOUT;
1073
+ let data = pack("<II", entrypoint == 0 ? 1 : 0, entrypoint);
1074
+ return await this.checkCommand(ESP_MEM_END, data, 0, timeout);
1075
+ }
1076
+
1077
+ async runStub(): Promise<EspStubLoader> {
1078
+ const stub: Record<string, any> = await getStubCode(this.chipFamily);
1079
+
1080
+ // We're transferring over USB, right?
1081
+ let ramBlock = USB_RAM_BLOCK;
1082
+
1083
+ // Upload
1084
+ this.logger.log("Uploading stub...");
1085
+ for (let field of ["text", "data"]) {
1086
+ if (Object.keys(stub).includes(field)) {
1087
+ let offset = stub[field + "_start"];
1088
+ let length = stub[field].length;
1089
+ let blocks = Math.floor((length + ramBlock - 1) / ramBlock);
1090
+ await this.memBegin(length, blocks, ramBlock, offset);
1091
+ for (let seq of Array(blocks).keys()) {
1092
+ let fromOffs = seq * ramBlock;
1093
+ let toOffs = fromOffs + ramBlock;
1094
+ if (toOffs > length) {
1095
+ toOffs = length;
1096
+ }
1097
+ await this.memBlock(stub[field].slice(fromOffs, toOffs), seq);
1098
+ }
1099
+ }
1100
+ }
1101
+ this.logger.log("Running stub...");
1102
+ await this.memFinish(stub["entry"]);
1103
+
1104
+ let pChar: string;
1105
+
1106
+ const p = await this.readPacket(500);
1107
+ pChar = String.fromCharCode(...p);
1108
+
1109
+ if (pChar != "OHAI") {
1110
+ throw new Error("Failed to start stub. Unexpected response: " + pChar);
1111
+ }
1112
+ this.logger.log("Stub is now running...");
1113
+ const espStubLoader = new EspStubLoader(this.port, this.logger, this);
1114
+
1115
+ // Try to autodetect the flash size as soon as the stub is running.
1116
+ await espStubLoader.detectFlashSize();
1117
+
1118
+ return espStubLoader;
1119
+ }
1120
+
1121
+ async writeToStream(data: number[]) {
1122
+ const writer = this.port.writable!.getWriter();
1123
+ await writer.write(new Uint8Array(data));
1124
+ try {
1125
+ writer.releaseLock();
1126
+ } catch (err) {
1127
+ console.error("Ignoring release lock error", err);
1128
+ }
1129
+ }
1130
+
1131
+ async disconnect() {
1132
+ if (this._parent) {
1133
+ await this._parent.disconnect();
1134
+ return;
1135
+ }
1136
+ await this.port.writable!.getWriter().close();
1137
+ await new Promise((resolve) => {
1138
+ if (!this._reader) {
1139
+ resolve(undefined);
1140
+ }
1141
+ this.addEventListener("disconnect", resolve, { once: true });
1142
+ this._reader!.cancel();
1143
+ });
1144
+ this.connected = false;
1145
+ }
1146
+ }
1147
+
1148
+ class EspStubLoader extends ESPLoader {
1149
+ /*
1150
+ The Stubloader has commands that run on the uploaded Stub Code in RAM
1151
+ rather than built in commands.
1152
+ */
1153
+ IS_STUB = true;
1154
+
1155
+ /**
1156
+ * @name memBegin (592)
1157
+ * Start downloading an application image to RAM
1158
+ */
1159
+ async memBegin(
1160
+ size: number,
1161
+ blocks: number,
1162
+ blocksize: number,
1163
+ offset: number
1164
+ ): Promise<any> {
1165
+ let stub = await getStubCode(this.chipFamily);
1166
+ let load_start = offset;
1167
+ let load_end = offset + size;
1168
+ console.log(load_start, load_end);
1169
+ console.log(
1170
+ stub.data_start,
1171
+ stub.data.length,
1172
+ stub.text_start,
1173
+ stub.text.length
1174
+ );
1175
+ for (let [start, end] of [
1176
+ [stub.data_start, stub.data_start + stub.data.length],
1177
+ [stub.text_start, stub.text_start + stub.text.length],
1178
+ ]) {
1179
+ if (load_start < end && load_end > start) {
1180
+ throw new Error(
1181
+ "Software loader is resident at " +
1182
+ toHex(start, 8) +
1183
+ "-" +
1184
+ toHex(end, 8) +
1185
+ ". " +
1186
+ "Can't load binary at overlapping address range " +
1187
+ toHex(load_start, 8) +
1188
+ "-" +
1189
+ toHex(load_end, 8) +
1190
+ ". " +
1191
+ "Try changing the binary loading address."
1192
+ );
1193
+ }
1194
+ }
1195
+ }
1196
+
1197
+ /**
1198
+ * @name getEraseSize
1199
+ * depending on flash chip model the erase may take this long (maybe longer!)
1200
+ */
1201
+ async eraseFlash() {
1202
+ await this.checkCommand(ESP_ERASE_FLASH, [], 0, CHIP_ERASE_TIMEOUT);
1203
+ }
1204
+ }