tasmota-webserial-esptool 9.1.7 → 9.1.9

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.
@@ -94,6 +94,7 @@ export declare class ESPLoader extends EventTarget {
94
94
  readLoop(): Promise<void>;
95
95
  sleep(ms?: number): Promise<unknown>;
96
96
  state_DTR: boolean;
97
+ state_RTS: boolean;
97
98
  setRTS(state: boolean): Promise<void>;
98
99
  setDTR(state: boolean): Promise<void>;
99
100
  /**
@@ -25,17 +25,18 @@ export class ESPLoader extends EventTarget {
25
25
  this.__commandLock = Promise.resolve([0, []]);
26
26
  this.__isReconfiguring = false;
27
27
  this.__abandonCurrentOperation = false;
28
- // Adaptive speed adjustment for flash read operations - DISABLED
29
- // Using fixed conservative values that work reliably
28
+ // Adaptive speed adjustment for flash read operations
30
29
  this.__adaptiveBlockMultiplier = 1;
31
30
  this.__adaptiveMaxInFlightMultiplier = 1;
32
31
  this.__consecutiveSuccessfulChunks = 0;
33
32
  this.__lastAdaptiveAdjustment = 0;
34
33
  this.__isCDCDevice = false;
35
34
  this.state_DTR = false;
35
+ this.state_RTS = false;
36
36
  this.__writeChain = Promise.resolve();
37
37
  }
38
38
  // Chip properties with parent delegation
39
+ // chipFamily accessed before initialization as designed
39
40
  get chipFamily() {
40
41
  return this._parent ? this._parent.chipFamily : this.__chipFamily;
41
42
  }
@@ -81,7 +82,13 @@ export class ESPLoader extends EventTarget {
81
82
  }
82
83
  }
83
84
  get _inputBuffer() {
84
- return this._parent ? this._parent._inputBuffer : this.__inputBuffer;
85
+ if (this._parent) {
86
+ return this._parent._inputBuffer;
87
+ }
88
+ if (this.__inputBuffer === undefined) {
89
+ throw new Error("_inputBuffer accessed before initialization");
90
+ }
91
+ return this.__inputBuffer;
85
92
  }
86
93
  get _inputBufferReadIndex() {
87
94
  return this._parent
@@ -553,6 +560,7 @@ export class ESPLoader extends EventTarget {
553
560
  // WebUSB (Android) - DTR/RTS Signal Handling & Reset Strategies
554
561
  // ============================================================================
555
562
  async setRTSWebUSB(state) {
563
+ this.state_RTS = state;
556
564
  // Always specify both signals to avoid flipping the other line
557
565
  // The WebUSB setSignals() now preserves unspecified signals, but being explicit is safer
558
566
  await this.port.setSignals({
@@ -565,11 +573,12 @@ export class ESPLoader extends EventTarget {
565
573
  // Always specify both signals to avoid flipping the other line
566
574
  await this.port.setSignals({
567
575
  dataTerminalReady: state,
568
- requestToSend: undefined, // Let setSignals preserve current RTS state
576
+ requestToSend: this.state_RTS, // Explicitly preserve current RTS state
569
577
  });
570
578
  }
571
579
  async setDTRandRTSWebUSB(dtr, rts) {
572
580
  this.state_DTR = dtr;
581
+ this.state_RTS = rts;
573
582
  await this.port.setSignals({
574
583
  dataTerminalReady: dtr,
575
584
  requestToSend: rts,
@@ -753,28 +762,28 @@ export class ESPLoader extends EventTarget {
753
762
  // Strategy 1: USB-JTAG/Serial (works in CDC mode on Desktop)
754
763
  resetStrategies.push({
755
764
  name: "USB-JTAG/Serial (WebUSB) - ESP32-S2",
756
- fn: async function () {
765
+ fn: async () => {
757
766
  return await self.hardResetUSBJTAGSerialWebUSB();
758
767
  },
759
768
  });
760
769
  // Strategy 2: USB-JTAG/Serial Inverted DTR (works in JTAG mode)
761
770
  resetStrategies.push({
762
771
  name: "USB-JTAG/Serial Inverted DTR (WebUSB) - ESP32-S2",
763
- fn: async function () {
772
+ fn: async () => {
764
773
  return await self.hardResetUSBJTAGSerialInvertedDTRWebUSB();
765
774
  },
766
775
  });
767
776
  // Strategy 3: UnixTight (CDC fallback)
768
777
  resetStrategies.push({
769
778
  name: "UnixTight (WebUSB) - ESP32-S2 CDC",
770
- fn: async function () {
779
+ fn: async () => {
771
780
  return await self.hardResetUnixTightWebUSB();
772
781
  },
773
782
  });
774
783
  // Strategy 4: Classic reset (CDC fallback)
775
784
  resetStrategies.push({
776
785
  name: "Classic (WebUSB) - ESP32-S2 CDC",
777
- fn: async function () {
786
+ fn: async () => {
778
787
  return await self.hardResetClassicWebUSB();
779
788
  },
780
789
  });
@@ -783,19 +792,19 @@ export class ESPLoader extends EventTarget {
783
792
  // Other USB-JTAG chips: Try Inverted DTR first - works best for ESP32-H2 and other JTAG chips
784
793
  resetStrategies.push({
785
794
  name: "USB-JTAG/Serial Inverted DTR (WebUSB)",
786
- fn: async function () {
795
+ fn: async () => {
787
796
  return await self.hardResetUSBJTAGSerialInvertedDTRWebUSB();
788
797
  },
789
798
  });
790
799
  resetStrategies.push({
791
800
  name: "USB-JTAG/Serial (WebUSB)",
792
- fn: async function () {
801
+ fn: async () => {
793
802
  return await self.hardResetUSBJTAGSerialWebUSB();
794
803
  },
795
804
  });
796
805
  resetStrategies.push({
797
806
  name: "Inverted DTR Classic (WebUSB)",
798
- fn: async function () {
807
+ fn: async () => {
799
808
  return await self.hardResetInvertedDTRWebUSB();
800
809
  },
801
810
  });
@@ -807,31 +816,31 @@ export class ESPLoader extends EventTarget {
807
816
  // CH340/CH343: UnixTight works best (like CP2102)
808
817
  resetStrategies.push({
809
818
  name: "UnixTight (WebUSB) - CH34x",
810
- fn: async function () {
819
+ fn: async () => {
811
820
  return await self.hardResetUnixTightWebUSB();
812
821
  },
813
822
  });
814
823
  resetStrategies.push({
815
824
  name: "Classic (WebUSB) - CH34x",
816
- fn: async function () {
825
+ fn: async () => {
817
826
  return await self.hardResetClassicWebUSB();
818
827
  },
819
828
  });
820
829
  resetStrategies.push({
821
830
  name: "Inverted Both (WebUSB) - CH34x",
822
- fn: async function () {
831
+ fn: async () => {
823
832
  return await self.hardResetInvertedWebUSB();
824
833
  },
825
834
  });
826
835
  resetStrategies.push({
827
836
  name: "Inverted RTS (WebUSB) - CH34x",
828
- fn: async function () {
837
+ fn: async () => {
829
838
  return await self.hardResetInvertedRTSWebUSB();
830
839
  },
831
840
  });
832
841
  resetStrategies.push({
833
842
  name: "Inverted DTR (WebUSB) - CH34x",
834
- fn: async function () {
843
+ fn: async () => {
835
844
  return await self.hardResetInvertedDTRWebUSB();
836
845
  },
837
846
  });
@@ -841,31 +850,31 @@ export class ESPLoader extends EventTarget {
841
850
  // Try it first, then fallback to other strategies
842
851
  resetStrategies.push({
843
852
  name: "UnixTight (WebUSB) - CP2102",
844
- fn: async function () {
853
+ fn: async () => {
845
854
  return await self.hardResetUnixTightWebUSB();
846
855
  },
847
856
  });
848
857
  resetStrategies.push({
849
858
  name: "Classic (WebUSB) - CP2102",
850
- fn: async function () {
859
+ fn: async () => {
851
860
  return await self.hardResetClassicWebUSB();
852
861
  },
853
862
  });
854
863
  resetStrategies.push({
855
864
  name: "Inverted Both (WebUSB) - CP2102",
856
- fn: async function () {
865
+ fn: async () => {
857
866
  return await self.hardResetInvertedWebUSB();
858
867
  },
859
868
  });
860
869
  resetStrategies.push({
861
870
  name: "Inverted RTS (WebUSB) - CP2102",
862
- fn: async function () {
871
+ fn: async () => {
863
872
  return await self.hardResetInvertedRTSWebUSB();
864
873
  },
865
874
  });
866
875
  resetStrategies.push({
867
876
  name: "Inverted DTR (WebUSB) - CP2102",
868
- fn: async function () {
877
+ fn: async () => {
869
878
  return await self.hardResetInvertedDTRWebUSB();
870
879
  },
871
880
  });
@@ -874,7 +883,7 @@ export class ESPLoader extends EventTarget {
874
883
  // For other USB-Serial chips, try UnixTight first, then multiple strategies
875
884
  resetStrategies.push({
876
885
  name: "UnixTight (WebUSB)",
877
- fn: async function () {
886
+ fn: async () => {
878
887
  return await self.hardResetUnixTightWebUSB();
879
888
  },
880
889
  });
@@ -948,7 +957,6 @@ export class ESPLoader extends EventTarget {
948
957
  }
949
958
  }
950
959
  else {
951
- // Web Serial (Desktop) strategies
952
960
  // Strategy: USB-JTAG/Serial reset
953
961
  if (isUSBJTAGSerial || isEspressifUSB) {
954
962
  resetStrategies.push({
@@ -1043,9 +1051,9 @@ export class ESPLoader extends EventTarget {
1043
1051
  // just reset (no bootloader mode)
1044
1052
  if (this.isWebUSB()) {
1045
1053
  // WebUSB: Use longer delays for better compatibility
1046
- await this.setRTS(true); // EN->LOW
1054
+ await this.setRTSWebUSB(true); // EN->LOW
1047
1055
  await this.sleep(200);
1048
- await this.setRTS(false);
1056
+ await this.setRTSWebUSB(false);
1049
1057
  await this.sleep(200);
1050
1058
  this.logger.log("Hard reset (WebUSB).");
1051
1059
  }
@@ -1265,6 +1273,11 @@ export class ESPLoader extends EventTarget {
1265
1273
  // Process all available bytes without going back to outer loop
1266
1274
  // This is critical for handling high-speed burst transfers
1267
1275
  while (this._inputBufferAvailable > 0) {
1276
+ // Periodic timeout check to prevent hang on slow data
1277
+ if (Date.now() - startTime > timeout) {
1278
+ const waitingFor = partialPacket === null ? "header" : "content";
1279
+ throw new SlipReadError("Timed out waiting for packet " + waitingFor);
1280
+ }
1268
1281
  const b = this._readByte();
1269
1282
  if (partialPacket === null) {
1270
1283
  // waiting for packet header
@@ -2163,7 +2176,15 @@ export class ESPLoader extends EventTarget {
2163
2176
  resolve(undefined);
2164
2177
  return;
2165
2178
  }
2166
- this.addEventListener("disconnect", resolve, { once: true });
2179
+ // Set a timeout to prevent hanging (important for node-usb)
2180
+ const timeout = setTimeout(() => {
2181
+ this.logger.debug("Disconnect timeout - forcing resolution");
2182
+ resolve(undefined);
2183
+ }, 1000);
2184
+ this.addEventListener("disconnect", () => {
2185
+ clearTimeout(timeout);
2186
+ resolve(undefined);
2187
+ }, { once: true });
2167
2188
  // Only cancel if reader is still active
2168
2189
  try {
2169
2190
  this._reader.cancel();
@@ -2171,10 +2192,19 @@ export class ESPLoader extends EventTarget {
2171
2192
  catch (err) {
2172
2193
  this.logger.debug(`Reader cancel error: ${err}`);
2173
2194
  // Reader already released, resolve immediately
2195
+ clearTimeout(timeout);
2174
2196
  resolve(undefined);
2175
2197
  }
2176
2198
  });
2177
2199
  this.connected = false;
2200
+ // Close the port (important for node-usb adapter)
2201
+ try {
2202
+ await this.port.close();
2203
+ this.logger.debug("Port closed successfully");
2204
+ }
2205
+ catch (err) {
2206
+ this.logger.debug(`Port close error: ${err}`);
2207
+ }
2178
2208
  }
2179
2209
  /**
2180
2210
  * @name reconnectAndResume
@@ -2693,6 +2723,10 @@ class EspStubLoader extends ESPLoader {
2693
2723
  if (size > maxValue) {
2694
2724
  throw new Error(`Size ${size} exceeds maximum value ${maxValue}`);
2695
2725
  }
2726
+ // Check for wrap-around
2727
+ if (offset + size > maxValue) {
2728
+ throw new Error(`Region end (offset + size = ${offset + size}) exceeds maximum addressable range ${maxValue}`);
2729
+ }
2696
2730
  const timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
2697
2731
  const buffer = pack("<II", offset, size);
2698
2732
  await this.checkCommand(ESP_ERASE_REGION, buffer, 0, timeout);
package/dist/index.d.ts CHANGED
@@ -2,6 +2,6 @@ import { Logger } from "./const";
2
2
  import { ESPLoader } from "./esp_loader";
3
3
  export type { Logger } from "./const";
4
4
  export { ESPLoader } from "./esp_loader";
5
- export { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP8266, 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, } from "./const";
5
+ export { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP8266, 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, ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_END, ESP_MEM_DATA, ESP_SYNC, ESP_WRITE_REG, ESP_READ_REG, ESP_ERASE_FLASH, ESP_ERASE_REGION, ESP_READ_FLASH, ESP_SPI_SET_PARAMS, ESP_SPI_ATTACH, ESP_CHANGE_BAUDRATE, ESP_SPI_FLASH_MD5, ESP_GET_SECURITY_INFO, ESP_CHECKSUM_MAGIC, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, ROM_INVALID_RECV_MSG, USB_RAM_BLOCK, ESP_RAM_BLOCK, DEFAULT_TIMEOUT, CHIP_ERASE_TIMEOUT, MAX_TIMEOUT, SYNC_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, MEM_END_ROM_TIMEOUT, FLASH_READ_TIMEOUT, } from "./const";
6
6
  export declare const connect: (logger: Logger) => Promise<ESPLoader>;
7
7
  export declare const connectWithPort: (port: SerialPort, logger: Logger) => Promise<ESPLoader>;
package/dist/index.js CHANGED
@@ -2,30 +2,34 @@
2
2
  import { ESP_ROM_BAUD } from "./const";
3
3
  import { ESPLoader } from "./esp_loader";
4
4
  export { ESPLoader } from "./esp_loader";
5
- export { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP8266, 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, } from "./const";
5
+ export { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP8266, 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,
6
+ // Command constants
7
+ ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_END, ESP_MEM_DATA, ESP_SYNC, ESP_WRITE_REG, ESP_READ_REG, ESP_ERASE_FLASH, ESP_ERASE_REGION, ESP_READ_FLASH, ESP_SPI_SET_PARAMS, ESP_SPI_ATTACH, ESP_CHANGE_BAUDRATE, ESP_SPI_FLASH_MD5, ESP_GET_SECURITY_INFO, ESP_CHECKSUM_MAGIC, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, ROM_INVALID_RECV_MSG,
8
+ // Block size constants
9
+ USB_RAM_BLOCK, ESP_RAM_BLOCK,
10
+ // Timeout constants
11
+ DEFAULT_TIMEOUT, CHIP_ERASE_TIMEOUT, MAX_TIMEOUT, SYNC_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, MEM_END_ROM_TIMEOUT, FLASH_READ_TIMEOUT, } from "./const";
6
12
  export const connect = async (logger) => {
13
+ // - Request a port and open a connection.
14
+ // Try to use requestSerialPort if available (supports WebUSB for Android)
7
15
  let port;
8
- // Check if a custom requestSerialPort function is available (e.g., from WebUSB wrapper)
9
16
  const customRequestPort = globalThis.requestSerialPort;
10
17
  if (typeof customRequestPort === "function") {
11
- // Use custom port request function (handles Android/WebUSB automatically)
12
- logger.log("Using custom port request function");
13
18
  port = await customRequestPort();
14
19
  }
15
20
  else {
16
- // Fallback to standard Web Serial API
21
+ // Check if Web Serial API is available
17
22
  if (!navigator.serial) {
18
23
  throw new Error("Web Serial API is not supported in this browser. " +
19
- "Please use Chrome 89+, Edge 89+, or Opera on desktop, or Chrome 61+ on Android with USB OTG. " +
24
+ "Please use Chrome, Edge, or Opera on desktop, or Chrome on Android. " +
20
25
  "Note: The page must be served over HTTPS or localhost.");
21
26
  }
22
27
  port = await navigator.serial.requestPort();
23
28
  }
24
- // Only open if not already open (WebUSB may return an opened port)
29
+ // Only open if not already open (requestSerialPort may return an opened port)
25
30
  if (!port.readable || !port.writable) {
26
31
  await port.open({ baudRate: ESP_ROM_BAUD });
27
32
  }
28
- logger.log("Connected successfully.");
29
33
  return new ESPLoader(port, logger);
30
34
  };
31
35
  export const connectWithPort = async (port, logger) => {
@@ -33,10 +37,9 @@ export const connectWithPort = async (port, logger) => {
33
37
  if (!port) {
34
38
  throw new Error("Port is required");
35
39
  }
36
- // Only open if not already open
40
+ // Check if port is already open, if not open it
37
41
  if (!port.readable || !port.writable) {
38
42
  await port.open({ baudRate: ESP_ROM_BAUD });
39
43
  }
40
- logger.log("Connected successfully.");
41
44
  return new ESPLoader(port, logger);
42
45
  };