tasmota-webserial-esptool 7.0.1 → 7.2.0

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.
@@ -2,7 +2,7 @@
2
2
  import { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP32C2, CHIP_FAMILY_ESP32C3, CHIP_FAMILY_ESP32C5, CHIP_FAMILY_ESP32C6, CHIP_FAMILY_ESP32C61, CHIP_FAMILY_ESP32H2, CHIP_FAMILY_ESP32H4, CHIP_FAMILY_ESP32H21, CHIP_FAMILY_ESP32P4, CHIP_FAMILY_ESP32S31, CHIP_FAMILY_ESP8266, MAX_TIMEOUT, DEFAULT_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, ESP_CHANGE_BAUDRATE, ESP_CHECKSUM_MAGIC, ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_DATA, ESP_MEM_END, ESP_READ_REG, ESP_WRITE_REG, ESP_SPI_ATTACH, ESP_SYNC, ESP_GET_SECURITY_INFO, FLASH_SECTOR_SIZE, FLASH_WRITE_SIZE, STUB_FLASH_WRITE_SIZE, MEM_END_ROM_TIMEOUT, ROM_INVALID_RECV_MSG, SYNC_PACKET, SYNC_TIMEOUT, USB_RAM_BLOCK, ESP_ERASE_FLASH, ESP_READ_FLASH, CHIP_ERASE_TIMEOUT, FLASH_READ_TIMEOUT, timeoutPerMb, ESP_ROM_BAUD, USB_JTAG_SERIAL_PID, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, getSpiFlashAddresses, DETECTED_FLASH_SIZES, CHIP_DETECT_MAGIC_REG_ADDR, CHIP_DETECT_MAGIC_VALUES, CHIP_ID_TO_INFO, ESP32P4_EFUSE_BLOCK1_ADDR, SlipReadError, } from "./const";
3
3
  import { getStubCode } from "./stubs";
4
4
  import { hexFormatter, sleep, slipEncode, toHex } from "./util";
5
- // @ts-ignore
5
+ // @ts-expect-error pako ESM module doesn't have proper type definitions
6
6
  import { deflate } from "pako/dist/pako.esm.mjs";
7
7
  import { pack, unpack } from "./struct";
8
8
  export class ESPLoader extends EventTarget {
@@ -21,6 +21,8 @@ export class ESPLoader extends EventTarget {
21
21
  this.connected = true;
22
22
  this.flashSize = null;
23
23
  this._currentBaudRate = ESP_ROM_BAUD;
24
+ this._isESP32S2NativeUSB = false;
25
+ this._initializationSucceeded = false;
24
26
  this.state_DTR = false;
25
27
  }
26
28
  get _inputBuffer() {
@@ -44,8 +46,13 @@ export class ESPLoader extends EventTarget {
44
46
  const chips = {
45
47
  0x1a86: {
46
48
  // QinHeng Electronics
49
+ 0x7522: { name: "CH340", maxBaudrate: 460800 },
47
50
  0x7523: { name: "CH340", maxBaudrate: 460800 },
51
+ 0x7584: { name: "CH340", maxBaudrate: 460800 },
52
+ 0x5523: { name: "CH341", maxBaudrate: 2000000 },
53
+ 0x55d3: { name: "CH343", maxBaudrate: 6000000 },
48
54
  0x55d4: { name: "CH9102", maxBaudrate: 6000000 },
55
+ 0x55d8: { name: "CH9101", maxBaudrate: 3000000 },
49
56
  },
50
57
  0x10c4: {
51
58
  // Silicon Labs
@@ -92,6 +99,10 @@ export class ESPLoader extends EventTarget {
92
99
  this._maxUSBSerialBaudrate = chipInfo.maxBaudrate;
93
100
  this.logger.log(`Max baudrate: ${chipInfo.maxBaudrate}`);
94
101
  }
102
+ // Detect ESP32-S2 Native USB
103
+ if (portInfo.usbVendorId === 0x303a && portInfo.usbProductId === 0x2) {
104
+ this._isESP32S2NativeUSB = true;
105
+ }
95
106
  }
96
107
  // Don't await this promise so it doesn't block rest of method.
97
108
  this.readLoop();
@@ -102,13 +113,15 @@ export class ESPLoader extends EventTarget {
102
113
  // Detect chip type
103
114
  await this.detectChip();
104
115
  // Read the OTP data for this chip and store into this.efuses array
105
- let FlAddr = getSpiFlashAddresses(this.getChipFamily());
106
- let AddrMAC = FlAddr.macFuse;
116
+ const FlAddr = getSpiFlashAddresses(this.getChipFamily());
117
+ const AddrMAC = FlAddr.macFuse;
107
118
  for (let i = 0; i < 4; i++) {
108
119
  this._efuses[i] = await this.readRegister(AddrMAC + 4 * i);
109
120
  }
110
121
  this.logger.log(`Chip type ${this.chipName}`);
111
122
  this.logger.debug(`Bootloader flash offset: 0x${FlAddr.flashOffs.toString(16)}`);
123
+ // Mark initialization as successful
124
+ this._initializationSucceeded = true;
112
125
  }
113
126
  /**
114
127
  * Detect chip type using GET_SECURITY_INFO (for newer chips) or magic value (for older chips)
@@ -140,9 +153,9 @@ export class ESPLoader extends EventTarget {
140
153
  }
141
154
  this.logger.debug(`Unknown IMAGE_CHIP_ID: ${chipId}, falling back to magic value detection`);
142
155
  }
143
- catch (err) {
156
+ catch (error) {
144
157
  // GET_SECURITY_INFO not supported, fall back to magic value detection
145
- this.logger.debug(`GET_SECURITY_INFO failed, using magic value detection: ${err}`);
158
+ this.logger.debug(`GET_SECURITY_INFO failed, using magic value detection: ${error}`);
146
159
  // Clear input buffer and re-sync to recover from failed command
147
160
  this._inputBuffer.length = 0;
148
161
  await sleep(SYNC_TIMEOUT);
@@ -154,15 +167,14 @@ export class ESPLoader extends EventTarget {
154
167
  this.logger.debug(`Re-sync after GET_SECURITY_INFO failure: ${syncErr}`);
155
168
  }
156
169
  }
157
- // Fallback: Use magic value detection for ESP8266, ESP32, ESP32-S2, and ESP32-P4 RC versions
158
- let chipMagicValue = await this.readRegister(CHIP_DETECT_MAGIC_REG_ADDR);
159
- let chip = CHIP_DETECT_MAGIC_VALUES[chipMagicValue >>> 0];
170
+ // Fallback: Use magic value detection for ESP8266, ESP32, ESP32-S2
171
+ const chipMagicValue = await this.readRegister(CHIP_DETECT_MAGIC_REG_ADDR);
172
+ const chip = CHIP_DETECT_MAGIC_VALUES[chipMagicValue >>> 0];
160
173
  if (chip === undefined) {
161
174
  throw new Error(`Unknown Chip: Hex: ${toHex(chipMagicValue >>> 0, 8).toLowerCase()} Number: ${chipMagicValue}`);
162
175
  }
163
176
  this.chipName = chip.name;
164
177
  this.chipFamily = chip.family;
165
- // For ESP32-P4 detected via magic value (old revisions), set variant
166
178
  if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
167
179
  this.chipRevision = await this.getChipRevision();
168
180
  this.logger.debug(`ESP32-P4 revision: ${this.chipRevision}`);
@@ -197,7 +209,7 @@ export class ESPLoader extends EventTarget {
197
209
  * Get security info including chip ID (ESP32-C3 and later)
198
210
  */
199
211
  async getSecurityInfo() {
200
- const [_, responseData] = await this.checkCommand(ESP_GET_SECURITY_INFO, [], 0);
212
+ const [, responseData] = await this.checkCommand(ESP_GET_SECURITY_INFO, [], 0);
201
213
  // Some chips/ROM versions return empty response or don't support this command
202
214
  if (responseData.length === 0) {
203
215
  throw new Error(`GET_SECURITY_INFO not supported or returned empty response`);
@@ -232,10 +244,12 @@ export class ESPLoader extends EventTarget {
232
244
  }
233
245
  this._reader = this.port.readable.getReader();
234
246
  try {
235
- while (true) {
247
+ let keepReading = true;
248
+ while (keepReading) {
236
249
  const { value, done } = await this._reader.read();
237
250
  if (done) {
238
251
  this._reader.releaseLock();
252
+ keepReading = false;
239
253
  break;
240
254
  }
241
255
  if (!value || value.length === 0) {
@@ -249,11 +263,19 @@ export class ESPLoader extends EventTarget {
249
263
  this._totalBytesRead += value.length;
250
264
  }
251
265
  }
252
- catch (err) {
253
- console.error("Read loop got disconnected");
266
+ catch {
267
+ this.logger.error("Read loop got disconnected");
254
268
  }
255
269
  // Disconnected!
256
270
  this.connected = false;
271
+ // Check if this is ESP32-S2 Native USB that needs port reselection
272
+ // Only trigger reconnect if initialization did NOT succeed (wrong port)
273
+ if (this._isESP32S2NativeUSB && !this._initializationSucceeded) {
274
+ this.logger.log("ESP32-S2 Native USB detected - requesting port reselection");
275
+ this.dispatchEvent(new CustomEvent("esp32s2-usb-reconnect", {
276
+ detail: { message: "ESP32-S2 Native USB requires port reselection" },
277
+ }));
278
+ }
257
279
  this.dispatchEvent(new Event("disconnect"));
258
280
  this.logger.debug("Finished read loop");
259
281
  }
@@ -321,11 +343,11 @@ export class ESPLoader extends EventTarget {
321
343
  * The MAC address burned into the OTP memory of the ESP chip
322
344
  */
323
345
  macAddr() {
324
- let macAddr = new Array(6).fill(0);
325
- let mac0 = this._efuses[0];
326
- let mac1 = this._efuses[1];
327
- let mac2 = this._efuses[2];
328
- let mac3 = this._efuses[3];
346
+ const macAddr = new Array(6).fill(0);
347
+ const mac0 = this._efuses[0];
348
+ const mac1 = this._efuses[1];
349
+ const mac2 = this._efuses[2];
350
+ const mac3 = this._efuses[3];
329
351
  let oui;
330
352
  if (this.chipFamily == CHIP_FAMILY_ESP8266) {
331
353
  if (mac3 != 0) {
@@ -383,9 +405,9 @@ export class ESPLoader extends EventTarget {
383
405
  if (this.debug) {
384
406
  this.logger.debug("Reading from Register " + toHex(reg, 8));
385
407
  }
386
- let packet = pack("<I", reg);
408
+ const packet = pack("<I", reg);
387
409
  await this.sendCommand(ESP_READ_REG, packet);
388
- let [val, _data] = await this.getResponse(ESP_READ_REG);
410
+ const [val] = await this.getResponse(ESP_READ_REG);
389
411
  return val;
390
412
  }
391
413
  /**
@@ -397,10 +419,11 @@ export class ESPLoader extends EventTarget {
397
419
  async checkCommand(opcode, buffer, checksum = 0, timeout = DEFAULT_TIMEOUT) {
398
420
  timeout = Math.min(timeout, MAX_TIMEOUT);
399
421
  await this.sendCommand(opcode, buffer, checksum);
400
- let [value, data] = await this.getResponse(opcode, timeout);
401
- if (data === null) {
422
+ const [value, responseData] = await this.getResponse(opcode, timeout);
423
+ if (responseData === null) {
402
424
  throw new Error("Didn't get enough status bytes");
403
425
  }
426
+ let data = responseData;
404
427
  let statusLen = 0;
405
428
  if (this.IS_STUB || this.chipFamily == CHIP_FAMILY_ESP8266) {
406
429
  statusLen = 2;
@@ -435,7 +458,7 @@ export class ESPLoader extends EventTarget {
435
458
  if (data.length < statusLen) {
436
459
  throw new Error("Didn't get enough status bytes");
437
460
  }
438
- let status = data.slice(-statusLen, data.length);
461
+ const status = data.slice(-statusLen, data.length);
439
462
  data = data.slice(0, -statusLen);
440
463
  if (this.debug) {
441
464
  this.logger.debug("status", status);
@@ -458,7 +481,7 @@ export class ESPLoader extends EventTarget {
458
481
  * does not check response
459
482
  */
460
483
  async sendCommand(opcode, buffer, checksum = 0) {
461
- let packet = slipEncode([
484
+ const packet = slipEncode([
462
485
  ...pack("<BBHI", 0x00, opcode, buffer.length, checksum),
463
486
  ...buffer,
464
487
  ]);
@@ -477,7 +500,7 @@ export class ESPLoader extends EventTarget {
477
500
  let inEscape = false;
478
501
  let readBytes = [];
479
502
  while (true) {
480
- let stamp = Date.now();
503
+ const stamp = Date.now();
481
504
  readBytes = [];
482
505
  while (Date.now() - stamp < timeout) {
483
506
  if (this._inputBuffer.length > 0) {
@@ -490,12 +513,12 @@ export class ESPLoader extends EventTarget {
490
513
  }
491
514
  }
492
515
  if (readBytes.length == 0) {
493
- let waitingFor = partialPacket === null ? "header" : "content";
516
+ const waitingFor = partialPacket === null ? "header" : "content";
494
517
  throw new SlipReadError("Timed out waiting for packet " + waitingFor);
495
518
  }
496
519
  if (this.debug)
497
520
  this.logger.debug("Read " + readBytes.length + " bytes: " + hexFormatter(readBytes));
498
- for (let b of readBytes) {
521
+ for (const b of readBytes) {
499
522
  if (partialPacket === null) {
500
523
  // waiting for packet header
501
524
  if (b == 0xc0) {
@@ -558,7 +581,7 @@ export class ESPLoader extends EventTarget {
558
581
  if (packet.length < 8) {
559
582
  continue;
560
583
  }
561
- const [resp, opRet, _lenRet, val] = unpack("<BBHI", packet.slice(0, 8));
584
+ const [resp, opRet, , val] = unpack("<BBHI", packet.slice(0, 8));
562
585
  if (resp != 1) {
563
586
  continue;
564
587
  }
@@ -578,7 +601,7 @@ export class ESPLoader extends EventTarget {
578
601
  * Calculate checksum of a blob, as it is defined by the ROM
579
602
  */
580
603
  checksum(data, state = ESP_CHECKSUM_MAGIC) {
581
- for (let b of data) {
604
+ for (const b of data) {
582
605
  state ^= b;
583
606
  }
584
607
  return state;
@@ -589,11 +612,11 @@ export class ESPLoader extends EventTarget {
589
612
  }
590
613
  try {
591
614
  // Send ESP_ROM_BAUD(115200) as the old one if running STUB otherwise 0
592
- let buffer = pack("<II", baud, this.IS_STUB ? ESP_ROM_BAUD : 0);
615
+ const buffer = pack("<II", baud, this.IS_STUB ? ESP_ROM_BAUD : 0);
593
616
  await this.checkCommand(ESP_CHANGE_BAUDRATE, buffer);
594
617
  }
595
618
  catch (e) {
596
- console.error(e);
619
+ this.logger.error(`Baudrate change error: ${e}`);
597
620
  throw new Error(`Unable to change the baud rate to ${baud}: No response from set baud rate command.`);
598
621
  }
599
622
  if (this._parent) {
@@ -636,7 +659,7 @@ export class ESPLoader extends EventTarget {
636
659
  this.readLoop();
637
660
  }
638
661
  catch (e) {
639
- console.error(e);
662
+ this.logger.error(`Reconfigure port error: ${e}`);
640
663
  throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
641
664
  }
642
665
  }
@@ -648,7 +671,7 @@ export class ESPLoader extends EventTarget {
648
671
  async sync() {
649
672
  for (let i = 0; i < 5; i++) {
650
673
  this._inputBuffer.length = 0;
651
- let response = await this._sync();
674
+ const response = await this._sync();
652
675
  if (response) {
653
676
  await sleep(SYNC_TIMEOUT);
654
677
  return true;
@@ -666,12 +689,12 @@ export class ESPLoader extends EventTarget {
666
689
  await this.sendCommand(ESP_SYNC, SYNC_PACKET);
667
690
  for (let i = 0; i < 8; i++) {
668
691
  try {
669
- let [_reply, data] = await this.getResponse(ESP_SYNC, SYNC_TIMEOUT);
692
+ const [, data] = await this.getResponse(ESP_SYNC, SYNC_TIMEOUT);
670
693
  if (data.length > 1 && data[0] == 0 && data[1] == 0) {
671
694
  return true;
672
695
  }
673
696
  }
674
- catch (err) {
697
+ catch {
675
698
  // If read packet fails.
676
699
  }
677
700
  }
@@ -697,13 +720,13 @@ export class ESPLoader extends EventTarget {
697
720
  async flashData(binaryData, updateProgress, offset = 0, compress = false) {
698
721
  if (binaryData.byteLength >= 8) {
699
722
  // unpack the (potential) image header
700
- var header = Array.from(new Uint8Array(binaryData, 0, 4));
701
- let headerMagic = header[0];
702
- let headerFlashMode = header[2];
703
- let headerFlashSizeFreq = header[3];
723
+ const header = Array.from(new Uint8Array(binaryData, 0, 4));
724
+ const headerMagic = header[0];
725
+ const headerFlashMode = header[2];
726
+ const headerFlashSizeFreq = header[3];
704
727
  this.logger.log(`Image header, Magic=${toHex(headerMagic)}, FlashMode=${toHex(headerFlashMode)}, FlashSizeFreq=${toHex(headerFlashSizeFreq)}`);
705
728
  }
706
- let uncompressedFilesize = binaryData.byteLength;
729
+ const uncompressedFilesize = binaryData.byteLength;
707
730
  let compressedFilesize = 0;
708
731
  let dataToFlash;
709
732
  let timeout = DEFAULT_TIMEOUT;
@@ -724,9 +747,9 @@ export class ESPLoader extends EventTarget {
724
747
  let seq = 0;
725
748
  let written = 0;
726
749
  let position = 0;
727
- let stamp = Date.now();
728
- let flashWriteSize = this.getFlashWriteSize();
729
- let filesize = compress ? compressedFilesize : uncompressedFilesize;
750
+ const stamp = Date.now();
751
+ const flashWriteSize = this.getFlashWriteSize();
752
+ const filesize = compress ? compressedFilesize : uncompressedFilesize;
730
753
  while (filesize - position > 0) {
731
754
  if (this.debug) {
732
755
  this.logger.log(`Writing at ${toHex(offset + seq * flashWriteSize, 8)} `);
@@ -787,8 +810,7 @@ export class ESPLoader extends EventTarget {
787
810
  // Flush serial buffers before flash write operation
788
811
  await this.flushSerialBuffers();
789
812
  let eraseSize;
790
- let buffer;
791
- let flashWriteSize = this.getFlashWriteSize();
813
+ const flashWriteSize = this.getFlashWriteSize();
792
814
  if (!this.IS_STUB &&
793
815
  [
794
816
  CHIP_FAMILY_ESP32,
@@ -807,22 +829,18 @@ export class ESPLoader extends EventTarget {
807
829
  ].includes(this.chipFamily)) {
808
830
  await this.checkCommand(ESP_SPI_ATTACH, new Array(8).fill(0));
809
831
  }
810
- let numBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
832
+ const numBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
811
833
  if (this.chipFamily == CHIP_FAMILY_ESP8266) {
812
834
  eraseSize = this.getEraseSize(offset, size);
813
835
  }
814
836
  else {
815
837
  eraseSize = size;
816
838
  }
817
- let timeout;
818
- if (this.IS_STUB) {
819
- timeout = DEFAULT_TIMEOUT;
820
- }
821
- else {
822
- timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
823
- }
824
- let stamp = Date.now();
825
- buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
839
+ const timeout = this.IS_STUB
840
+ ? DEFAULT_TIMEOUT
841
+ : timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
842
+ const stamp = Date.now();
843
+ let buffer = pack("<IIII", eraseSize, numBlocks, flashWriteSize, offset);
826
844
  if (this.chipFamily == CHIP_FAMILY_ESP32 ||
827
845
  this.chipFamily == CHIP_FAMILY_ESP32S2 ||
828
846
  this.chipFamily == CHIP_FAMILY_ESP32S3 ||
@@ -858,15 +876,14 @@ export class ESPLoader extends EventTarget {
858
876
  * @name flashDeflBegin
859
877
  *
860
878
  */
861
- async flashDeflBegin(size = 0, compressedSize = 0, offset = 0, encrypted = false) {
879
+ async flashDeflBegin(size = 0, compressedSize = 0, offset = 0) {
862
880
  // Start downloading compressed data to Flash (performs an erase)
863
881
  // Returns number of blocks to write.
864
- let flashWriteSize = this.getFlashWriteSize();
865
- let numBlocks = Math.floor((compressedSize + flashWriteSize - 1) / flashWriteSize);
866
- let eraseBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
882
+ const flashWriteSize = this.getFlashWriteSize();
883
+ const numBlocks = Math.floor((compressedSize + flashWriteSize - 1) / flashWriteSize);
884
+ const eraseBlocks = Math.floor((size + flashWriteSize - 1) / flashWriteSize);
867
885
  let writeSize = 0;
868
886
  let timeout = 0;
869
- let buffer;
870
887
  if (this.IS_STUB) {
871
888
  writeSize = size; // stub expects number of bytes here, manages erasing internally
872
889
  timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, writeSize); // ROM performs the erase up front
@@ -875,26 +892,26 @@ export class ESPLoader extends EventTarget {
875
892
  writeSize = eraseBlocks * flashWriteSize; // ROM expects rounded up to erase block size
876
893
  timeout = DEFAULT_TIMEOUT;
877
894
  }
878
- buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
895
+ const buffer = pack("<IIII", writeSize, numBlocks, flashWriteSize, offset);
879
896
  await this.checkCommand(ESP_FLASH_DEFL_BEGIN, buffer, 0, timeout);
880
897
  return timeout;
881
898
  }
882
899
  async flashFinish() {
883
- let buffer = pack("<I", 1);
900
+ const buffer = pack("<I", 1);
884
901
  await this.checkCommand(ESP_FLASH_END, buffer);
885
902
  }
886
903
  async flashDeflFinish() {
887
- let buffer = pack("<I", 1);
904
+ const buffer = pack("<I", 1);
888
905
  await this.checkCommand(ESP_FLASH_DEFL_END, buffer);
889
906
  }
890
907
  getBootloaderOffset() {
891
- let bootFlashOffs = getSpiFlashAddresses(this.getChipFamily());
892
- let BootldrFlashOffs = bootFlashOffs.flashOffs;
908
+ const bootFlashOffs = getSpiFlashAddresses(this.getChipFamily());
909
+ const BootldrFlashOffs = bootFlashOffs.flashOffs;
893
910
  return BootldrFlashOffs;
894
911
  }
895
912
  async flashId() {
896
- let SPIFLASH_RDID = 0x9f;
897
- let result = await this.runSpiFlashCommand(SPIFLASH_RDID, [], 24);
913
+ const SPIFLASH_RDID = 0x9f;
914
+ const result = await this.runSpiFlashCommand(SPIFLASH_RDID, [], 24);
898
915
  return result;
899
916
  }
900
917
  getChipFamily() {
@@ -904,15 +921,15 @@ export class ESPLoader extends EventTarget {
904
921
  let buffer = pack("<IIII", address, value, mask, delayUs);
905
922
  if (delayAfterUs > 0) {
906
923
  // add a dummy write to a date register as an excuse to have a delay
907
- buffer.concat(pack("<IIII", getSpiFlashAddresses(this.getChipFamily()).uartDateReg, 0, 0, delayAfterUs));
924
+ buffer = buffer.concat(pack("<IIII", getSpiFlashAddresses(this.getChipFamily()).uartDateReg, 0, 0, delayAfterUs));
908
925
  }
909
926
  await this.checkCommand(ESP_WRITE_REG, buffer);
910
927
  }
911
928
  async setDataLengths(spiAddresses, mosiBits, misoBits) {
912
929
  if (spiAddresses.mosiDlenOffs != -1) {
913
- // ESP32/32S2/32S3/32C3 has a more sophisticated way to set up "user" commands
914
- let SPI_MOSI_DLEN_REG = spiAddresses.regBase + spiAddresses.mosiDlenOffs;
915
- let SPI_MISO_DLEN_REG = spiAddresses.regBase + spiAddresses.misoDlenOffs;
930
+ // Actual MCUs have a more sophisticated way to set up "user" commands
931
+ const SPI_MOSI_DLEN_REG = spiAddresses.regBase + spiAddresses.mosiDlenOffs;
932
+ const SPI_MISO_DLEN_REG = spiAddresses.regBase + spiAddresses.misoDlenOffs;
916
933
  if (mosiBits > 0) {
917
934
  await this.writeRegister(SPI_MOSI_DLEN_REG, mosiBits - 1);
918
935
  }
@@ -921,18 +938,18 @@ export class ESPLoader extends EventTarget {
921
938
  }
922
939
  }
923
940
  else {
924
- let SPI_DATA_LEN_REG = spiAddresses.regBase + spiAddresses.usr1Offs;
925
- let SPI_MOSI_BITLEN_S = 17;
926
- let SPI_MISO_BITLEN_S = 8;
927
- let mosiMask = mosiBits == 0 ? 0 : mosiBits - 1;
928
- let misoMask = misoBits == 0 ? 0 : misoBits - 1;
929
- let value = (misoMask << SPI_MISO_BITLEN_S) | (mosiMask << SPI_MOSI_BITLEN_S);
941
+ const SPI_DATA_LEN_REG = spiAddresses.regBase + spiAddresses.usr1Offs;
942
+ const SPI_MOSI_BITLEN_S = 17;
943
+ const SPI_MISO_BITLEN_S = 8;
944
+ const mosiMask = mosiBits == 0 ? 0 : mosiBits - 1;
945
+ const misoMask = misoBits == 0 ? 0 : misoBits - 1;
946
+ const value = (misoMask << SPI_MISO_BITLEN_S) | (mosiMask << SPI_MOSI_BITLEN_S);
930
947
  await this.writeRegister(SPI_DATA_LEN_REG, value);
931
948
  }
932
949
  }
933
950
  async waitDone(spiCmdReg, spiCmdUsr) {
934
951
  for (let i = 0; i < 10; i++) {
935
- let cmdValue = await this.readRegister(spiCmdReg);
952
+ const cmdValue = await this.readRegister(spiCmdReg);
936
953
  if ((cmdValue & spiCmdUsr) == 0) {
937
954
  return;
938
955
  }
@@ -948,29 +965,29 @@ export class ESPLoader extends EventTarget {
948
965
  // After writing command byte, writes 'data' to MOSI and then
949
966
  // reads back 'read_bits' of reply on MISO. Result is a number.
950
967
  // SPI_USR register flags
951
- let SPI_USR_COMMAND = 1 << 31;
952
- let SPI_USR_MISO = 1 << 28;
953
- let SPI_USR_MOSI = 1 << 27;
954
- // SPI registers, base address differs ESP32* vs 8266
955
- let spiAddresses = getSpiFlashAddresses(this.getChipFamily());
956
- let base = spiAddresses.regBase;
957
- let SPI_CMD_REG = base;
958
- let SPI_USR_REG = base + spiAddresses.usrOffs;
959
- let SPI_USR2_REG = base + spiAddresses.usr2Offs;
960
- let SPI_W0_REG = base + spiAddresses.w0Offs;
968
+ const SPI_USR_COMMAND = 1 << 31;
969
+ const SPI_USR_MISO = 1 << 28;
970
+ const SPI_USR_MOSI = 1 << 27;
971
+ // SPI registers, base address differs
972
+ const spiAddresses = getSpiFlashAddresses(this.getChipFamily());
973
+ const base = spiAddresses.regBase;
974
+ const SPI_CMD_REG = base;
975
+ const SPI_USR_REG = base + spiAddresses.usrOffs;
976
+ const SPI_USR2_REG = base + spiAddresses.usr2Offs;
977
+ const SPI_W0_REG = base + spiAddresses.w0Offs;
961
978
  // SPI peripheral "command" bitmasks for SPI_CMD_REG
962
- let SPI_CMD_USR = 1 << 18;
979
+ const SPI_CMD_USR = 1 << 18;
963
980
  // shift values
964
- let SPI_USR2_COMMAND_LEN_SHIFT = 28;
981
+ const SPI_USR2_COMMAND_LEN_SHIFT = 28;
965
982
  if (readBits > 32) {
966
983
  throw new Error("Reading more than 32 bits back from a SPI flash operation is unsupported");
967
984
  }
968
985
  if (data.length > 64) {
969
986
  throw new Error("Writing more than 64 bytes of data with one SPI command is unsupported");
970
987
  }
971
- let dataBits = data.length * 8;
972
- let oldSpiUsr = await this.readRegister(SPI_USR_REG);
973
- let oldSpiUsr2 = await this.readRegister(SPI_USR2_REG);
988
+ const dataBits = data.length * 8;
989
+ const oldSpiUsr = await this.readRegister(SPI_USR_REG);
990
+ const oldSpiUsr2 = await this.readRegister(SPI_USR2_REG);
974
991
  let flags = SPI_USR_COMMAND;
975
992
  if (readBits > 0) {
976
993
  flags |= SPI_USR_MISO;
@@ -985,8 +1002,9 @@ export class ESPLoader extends EventTarget {
985
1002
  await this.writeRegister(SPI_W0_REG, 0); // clear data register before we read it
986
1003
  }
987
1004
  else {
988
- data.concat(new Array(data.length % 4).fill(0x00)); // pad to 32-bit multiple
989
- let words = unpack("I".repeat(Math.floor(data.length / 4)), data);
1005
+ const padLen = (4 - (data.length % 4)) % 4;
1006
+ data = data.concat(new Array(padLen).fill(0x00)); // pad to 32-bit multiple
1007
+ const words = unpack("I".repeat(Math.floor(data.length / 4)), data);
990
1008
  let nextReg = SPI_W0_REG;
991
1009
  this.logger.debug(`Words Length: ${words.length}`);
992
1010
  for (const word of words) {
@@ -997,7 +1015,7 @@ export class ESPLoader extends EventTarget {
997
1015
  }
998
1016
  await this.writeRegister(SPI_CMD_REG, SPI_CMD_USR);
999
1017
  await this.waitDone(SPI_CMD_REG, SPI_CMD_USR);
1000
- let status = await this.readRegister(SPI_W0_REG);
1018
+ const status = await this.readRegister(SPI_W0_REG);
1001
1019
  // restore some SPI controller registers
1002
1020
  await this.writeRegister(SPI_USR_REG, oldSpiUsr);
1003
1021
  await this.writeRegister(SPI_USR2_REG, oldSpiUsr2);
@@ -1005,9 +1023,9 @@ export class ESPLoader extends EventTarget {
1005
1023
  }
1006
1024
  async detectFlashSize() {
1007
1025
  this.logger.log("Detecting Flash Size");
1008
- let flashId = await this.flashId();
1009
- let manufacturer = flashId & 0xff;
1010
- let flashIdLowbyte = (flashId >> 16) & 0xff;
1026
+ const flashId = await this.flashId();
1027
+ const manufacturer = flashId & 0xff;
1028
+ const flashIdLowbyte = (flashId >> 16) & 0xff;
1011
1029
  this.logger.log(`FlashId: ${toHex(flashId)}`);
1012
1030
  this.logger.log(`Flash Manufacturer: ${manufacturer.toString(16)}`);
1013
1031
  this.logger.log(`Flash Device: ${((flashId >> 8) & 0xff).toString(16)}${flashIdLowbyte.toString(16)}`);
@@ -1020,10 +1038,10 @@ export class ESPLoader extends EventTarget {
1020
1038
  * Provides a workaround for the bootloader erase bug on ESP8266.
1021
1039
  */
1022
1040
  getEraseSize(offset, size) {
1023
- let sectorsPerBlock = 16;
1024
- let sectorSize = FLASH_SECTOR_SIZE;
1025
- let numSectors = Math.floor((size + sectorSize - 1) / sectorSize);
1026
- let startSector = Math.floor(offset / sectorSize);
1041
+ const sectorsPerBlock = 16;
1042
+ const sectorSize = FLASH_SECTOR_SIZE;
1043
+ const numSectors = Math.floor((size + sectorSize - 1) / sectorSize);
1044
+ const startSector = Math.floor(offset / sectorSize);
1027
1045
  let headSectors = sectorsPerBlock - (startSector % sectorsPerBlock);
1028
1046
  if (numSectors < headSectors) {
1029
1047
  headSectors = numSectors;
@@ -1057,8 +1075,8 @@ export class ESPLoader extends EventTarget {
1057
1075
  * ignore errors.
1058
1076
  */
1059
1077
  async memFinish(entrypoint = 0) {
1060
- let timeout = this.IS_STUB ? DEFAULT_TIMEOUT : MEM_END_ROM_TIMEOUT;
1061
- let data = pack("<II", entrypoint == 0 ? 1 : 0, entrypoint);
1078
+ const timeout = this.IS_STUB ? DEFAULT_TIMEOUT : MEM_END_ROM_TIMEOUT;
1079
+ const data = pack("<II", entrypoint == 0 ? 1 : 0, entrypoint);
1062
1080
  return await this.checkCommand(ESP_MEM_END, data, 0, timeout);
1063
1081
  }
1064
1082
  async runStub(skipFlashDetection = false) {
@@ -1069,29 +1087,27 @@ export class ESPLoader extends EventTarget {
1069
1087
  return this;
1070
1088
  }
1071
1089
  // We're transferring over USB, right?
1072
- let ramBlock = USB_RAM_BLOCK;
1090
+ const ramBlock = USB_RAM_BLOCK;
1073
1091
  // Upload
1074
1092
  this.logger.log("Uploading stub...");
1075
- for (let field of ["text", "data"]) {
1076
- if (Object.keys(stub).includes(field)) {
1077
- let offset = stub[field + "_start"];
1078
- let length = stub[field].length;
1079
- let blocks = Math.floor((length + ramBlock - 1) / ramBlock);
1080
- await this.memBegin(length, blocks, ramBlock, offset);
1081
- for (let seq of Array(blocks).keys()) {
1082
- let fromOffs = seq * ramBlock;
1083
- let toOffs = fromOffs + ramBlock;
1084
- if (toOffs > length) {
1085
- toOffs = length;
1086
- }
1087
- await this.memBlock(stub[field].slice(fromOffs, toOffs), seq);
1093
+ for (const field of ["text", "data"]) {
1094
+ const fieldData = stub[field];
1095
+ const offset = stub[`${field}_start`];
1096
+ const length = fieldData.length;
1097
+ const blocks = Math.floor((length + ramBlock - 1) / ramBlock);
1098
+ await this.memBegin(length, blocks, ramBlock, offset);
1099
+ for (const seq of Array(blocks).keys()) {
1100
+ const fromOffs = seq * ramBlock;
1101
+ let toOffs = fromOffs + ramBlock;
1102
+ if (toOffs > length) {
1103
+ toOffs = length;
1088
1104
  }
1105
+ await this.memBlock(fieldData.slice(fromOffs, toOffs), seq);
1089
1106
  }
1090
1107
  }
1091
- await this.memFinish(stub["entry"]);
1092
- let pChar;
1108
+ await this.memFinish(stub.entry);
1093
1109
  const p = await this.readPacket(500);
1094
- pChar = String.fromCharCode(...p);
1110
+ const pChar = String.fromCharCode(...p);
1095
1111
  if (pChar != "OHAI") {
1096
1112
  throw new Error("Failed to start stub. Unexpected response: " + pChar);
1097
1113
  }
@@ -1104,13 +1120,17 @@ export class ESPLoader extends EventTarget {
1104
1120
  return espStubLoader;
1105
1121
  }
1106
1122
  async writeToStream(data) {
1123
+ if (!this.port.writable) {
1124
+ this.logger.debug("Port writable stream not available, skipping write");
1125
+ return;
1126
+ }
1107
1127
  const writer = this.port.writable.getWriter();
1108
1128
  await writer.write(new Uint8Array(data));
1109
1129
  try {
1110
1130
  writer.releaseLock();
1111
1131
  }
1112
1132
  catch (err) {
1113
- console.error("Ignoring release lock error", err);
1133
+ this.logger.error(`Ignoring release lock error: ${err}`);
1114
1134
  }
1115
1135
  }
1116
1136
  async disconnect() {
@@ -1118,6 +1138,10 @@ export class ESPLoader extends EventTarget {
1118
1138
  await this._parent.disconnect();
1119
1139
  return;
1120
1140
  }
1141
+ if (!this.port.writable) {
1142
+ this.logger.debug("Port already closed, skipping disconnect");
1143
+ return;
1144
+ }
1121
1145
  await this.port.writable.getWriter().close();
1122
1146
  await new Promise((resolve) => {
1123
1147
  if (!this._reader) {
@@ -1150,7 +1174,6 @@ export class ESPLoader extends EventTarget {
1150
1174
  }
1151
1175
  this._reader = undefined;
1152
1176
  }
1153
- await sleep(SYNC_TIMEOUT);
1154
1177
  // Close port
1155
1178
  try {
1156
1179
  await this.port.close();
@@ -1159,8 +1182,6 @@ export class ESPLoader extends EventTarget {
1159
1182
  catch (err) {
1160
1183
  this.logger.debug(`Port close error: ${err}`);
1161
1184
  }
1162
- // Wait for port to fully close
1163
- await sleep(SYNC_TIMEOUT);
1164
1185
  // Open the port
1165
1186
  this.logger.debug("Opening port...");
1166
1187
  try {
@@ -1170,8 +1191,6 @@ export class ESPLoader extends EventTarget {
1170
1191
  catch (err) {
1171
1192
  throw new Error(`Failed to open port: ${err}`);
1172
1193
  }
1173
- // Wait for port to be fully ready
1174
- await sleep(SYNC_TIMEOUT);
1175
1194
  // Verify port streams are available
1176
1195
  if (!this.port.readable || !this.port.writable) {
1177
1196
  throw new Error(`Port streams not available after open (readable: ${!!this.port.readable}, writable: ${!!this.port.writable})`);
@@ -1182,7 +1201,7 @@ export class ESPLoader extends EventTarget {
1182
1201
  const savedChipRevision = this.chipRevision;
1183
1202
  const savedChipVariant = this.chipVariant;
1184
1203
  const savedFlashSize = this.flashSize;
1185
- // Reinitialize without chip detection
1204
+ // Reinitialize
1186
1205
  await this.hardReset(true);
1187
1206
  if (!this._parent) {
1188
1207
  this.__inputBuffer = [];
@@ -1191,7 +1210,7 @@ export class ESPLoader extends EventTarget {
1191
1210
  }
1192
1211
  await this.flushSerialBuffers();
1193
1212
  await this.sync();
1194
- // Restore chip info (skip detection)
1213
+ // Restore chip info
1195
1214
  this.chipFamily = savedChipFamily;
1196
1215
  this.chipName = savedChipName;
1197
1216
  this.chipRevision = savedChipRevision;
@@ -1202,14 +1221,12 @@ export class ESPLoader extends EventTarget {
1202
1221
  if (!this.port.writable || !this.port.readable) {
1203
1222
  throw new Error("Port not ready after reconnect");
1204
1223
  }
1205
- // Load stub (skip flash detection)
1224
+ // Load stub
1206
1225
  const stubLoader = await this.runStub(true);
1207
1226
  this.logger.debug("Stub loaded");
1208
1227
  // Restore baudrate if it was changed
1209
1228
  if (this._currentBaudRate !== ESP_ROM_BAUD) {
1210
1229
  await stubLoader.setBaudrate(this._currentBaudRate);
1211
- // Wait for port to be ready after baudrate change
1212
- await sleep(SYNC_TIMEOUT);
1213
1230
  // Verify port is still ready after baudrate change
1214
1231
  if (!this.port.writable || !this.port.readable) {
1215
1232
  throw new Error(`Port not ready after baudrate change (readable: ${!!this.port.readable}, writable: ${!!this.port.writable})`);
@@ -1227,18 +1244,12 @@ export class ESPLoader extends EventTarget {
1227
1244
  * This clears both the application RX buffer and waits for hardware buffers to drain
1228
1245
  */
1229
1246
  async flushSerialBuffers() {
1230
- // Clear application RX buffer
1247
+ // Clear application buffer
1231
1248
  if (!this._parent) {
1232
1249
  this.__inputBuffer = [];
1233
1250
  }
1234
- // Wait for any pending TX operations and in-flight RX data
1251
+ // Wait for any pending data
1235
1252
  await sleep(SYNC_TIMEOUT);
1236
- // Clear RX buffer again
1237
- if (!this._parent) {
1238
- this.__inputBuffer = [];
1239
- }
1240
- // Wait longer to ensure all stale data has been received and discarded
1241
- await sleep(SYNC_TIMEOUT * 2);
1242
1253
  // Final clear
1243
1254
  if (!this._parent) {
1244
1255
  this.__inputBuffer = [];
@@ -1257,20 +1268,6 @@ export class ESPLoader extends EventTarget {
1257
1268
  if (!this.IS_STUB) {
1258
1269
  throw new Error("Reading flash is only supported in stub mode. Please run runStub() first.");
1259
1270
  }
1260
- // Check if we should reconnect BEFORE starting the read
1261
- // Reconnect if total bytes read >= 4MB to ensure clean state
1262
- if (this._totalBytesRead >= 4 * 1024 * 1024) {
1263
- this.logger.log(
1264
- // `Total bytes read: ${this._totalBytesRead}. Reconnecting before new read...`,
1265
- `Reconnecting before new read...`);
1266
- try {
1267
- await this.reconnect();
1268
- }
1269
- catch (err) {
1270
- // If reconnect fails, throw error - don't continue with potentially broken state
1271
- throw new Error(`Reconnect failed: ${err}`);
1272
- }
1273
- }
1274
1271
  // Flush serial buffers before flash read operation
1275
1272
  await this.flushSerialBuffers();
1276
1273
  this.logger.log(`Reading ${size} bytes from flash at address 0x${addr.toString(16)}...`);
@@ -1279,16 +1276,6 @@ export class ESPLoader extends EventTarget {
1279
1276
  let currentAddr = addr;
1280
1277
  let remainingSize = size;
1281
1278
  while (remainingSize > 0) {
1282
- // Reconnect every 4MB to prevent browser buffer issues
1283
- if (allData.length > 0 && allData.length % (4 * 1024 * 1024) === 0) {
1284
- this.logger.debug(`Read ${allData.length} bytes. Reconnecting to clear buffers...`);
1285
- try {
1286
- await this.reconnect();
1287
- }
1288
- catch (err) {
1289
- throw new Error(`Reconnect failed during read: ${err}`);
1290
- }
1291
- }
1292
1279
  const chunkSize = Math.min(CHUNK_SIZE, remainingSize);
1293
1280
  let chunkSuccess = false;
1294
1281
  let retryCount = 0;
@@ -1298,8 +1285,8 @@ export class ESPLoader extends EventTarget {
1298
1285
  try {
1299
1286
  this.logger.debug(`Reading chunk at 0x${currentAddr.toString(16)}, size: 0x${chunkSize.toString(16)}`);
1300
1287
  // Send read flash command for this chunk
1301
- let pkt = pack("<IIII", currentAddr, chunkSize, 0x1000, 1024);
1302
- const [res, _] = await this.checkCommand(ESP_READ_FLASH, pkt);
1288
+ const pkt = pack("<IIII", currentAddr, chunkSize, 0x1000, 1024);
1289
+ const [res] = await this.checkCommand(ESP_READ_FLASH, pkt);
1303
1290
  if (res != 0) {
1304
1291
  throw new Error("Failed to read memory: " + res);
1305
1292
  }
@@ -1371,7 +1358,7 @@ export class ESPLoader extends EventTarget {
1371
1358
  }
1372
1359
  currentAddr += chunkSize;
1373
1360
  remainingSize -= chunkSize;
1374
- this.logger.debug(`Total progress: 0x${allData.length.toString(16)}/0x${size.toString(16)} bytes`);
1361
+ this.logger.debug(`Total progress: 0x${allData.length.toString(16)} from 0x${size.toString(16)} bytes`);
1375
1362
  }
1376
1363
  this.logger.debug(`Successfully read ${allData.length} bytes from flash`);
1377
1364
  return allData;
@@ -1390,17 +1377,17 @@ class EspStubLoader extends ESPLoader {
1390
1377
  * @name memBegin (592)
1391
1378
  * Start downloading an application image to RAM
1392
1379
  */
1393
- async memBegin(size, blocks, blocksize, offset) {
1394
- let stub = await getStubCode(this.chipFamily, this.chipRevision);
1380
+ async memBegin(size, _blocks, _blocksize, offset) {
1381
+ const stub = await getStubCode(this.chipFamily, this.chipRevision);
1395
1382
  // Stub may be null for chips without stub support
1396
1383
  if (stub === null) {
1397
- return;
1384
+ return [0, []];
1398
1385
  }
1399
- let load_start = offset;
1400
- let load_end = offset + size;
1401
- console.log(load_start, load_end);
1402
- console.log(stub.data_start, stub.data.length, stub.text_start, stub.text.length);
1403
- for (let [start, end] of [
1386
+ const load_start = offset;
1387
+ const load_end = offset + size;
1388
+ this.logger.debug(`Load range: ${toHex(load_start, 8)}-${toHex(load_end, 8)}`);
1389
+ this.logger.debug(`Stub data: ${toHex(stub.data_start, 8)}, len: ${stub.data.length}, text: ${toHex(stub.text_start, 8)}, len: ${stub.text.length}`);
1390
+ for (const [start, end] of [
1404
1391
  [stub.data_start, stub.data_start + stub.data.length],
1405
1392
  [stub.text_start, stub.text_start + stub.text.length],
1406
1393
  ]) {
@@ -1418,6 +1405,7 @@ class EspStubLoader extends ESPLoader {
1418
1405
  "Try changing the binary loading address.");
1419
1406
  }
1420
1407
  }
1408
+ return [0, []];
1421
1409
  }
1422
1410
  /**
1423
1411
  * @name getEraseSize