tasmota-webserial-esptool 9.1.8 → 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.
@@ -25,8 +25,7 @@ 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;
@@ -37,6 +36,7 @@ export class ESPLoader extends EventTarget {
37
36
  this.__writeChain = Promise.resolve();
38
37
  }
39
38
  // Chip properties with parent delegation
39
+ // chipFamily accessed before initialization as designed
40
40
  get chipFamily() {
41
41
  return this._parent ? this._parent.chipFamily : this.__chipFamily;
42
42
  }
@@ -82,7 +82,13 @@ export class ESPLoader extends EventTarget {
82
82
  }
83
83
  }
84
84
  get _inputBuffer() {
85
- 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;
86
92
  }
87
93
  get _inputBufferReadIndex() {
88
94
  return this._parent
@@ -756,28 +762,28 @@ export class ESPLoader extends EventTarget {
756
762
  // Strategy 1: USB-JTAG/Serial (works in CDC mode on Desktop)
757
763
  resetStrategies.push({
758
764
  name: "USB-JTAG/Serial (WebUSB) - ESP32-S2",
759
- fn: async function () {
765
+ fn: async () => {
760
766
  return await self.hardResetUSBJTAGSerialWebUSB();
761
767
  },
762
768
  });
763
769
  // Strategy 2: USB-JTAG/Serial Inverted DTR (works in JTAG mode)
764
770
  resetStrategies.push({
765
771
  name: "USB-JTAG/Serial Inverted DTR (WebUSB) - ESP32-S2",
766
- fn: async function () {
772
+ fn: async () => {
767
773
  return await self.hardResetUSBJTAGSerialInvertedDTRWebUSB();
768
774
  },
769
775
  });
770
776
  // Strategy 3: UnixTight (CDC fallback)
771
777
  resetStrategies.push({
772
778
  name: "UnixTight (WebUSB) - ESP32-S2 CDC",
773
- fn: async function () {
779
+ fn: async () => {
774
780
  return await self.hardResetUnixTightWebUSB();
775
781
  },
776
782
  });
777
783
  // Strategy 4: Classic reset (CDC fallback)
778
784
  resetStrategies.push({
779
785
  name: "Classic (WebUSB) - ESP32-S2 CDC",
780
- fn: async function () {
786
+ fn: async () => {
781
787
  return await self.hardResetClassicWebUSB();
782
788
  },
783
789
  });
@@ -786,19 +792,19 @@ export class ESPLoader extends EventTarget {
786
792
  // Other USB-JTAG chips: Try Inverted DTR first - works best for ESP32-H2 and other JTAG chips
787
793
  resetStrategies.push({
788
794
  name: "USB-JTAG/Serial Inverted DTR (WebUSB)",
789
- fn: async function () {
795
+ fn: async () => {
790
796
  return await self.hardResetUSBJTAGSerialInvertedDTRWebUSB();
791
797
  },
792
798
  });
793
799
  resetStrategies.push({
794
800
  name: "USB-JTAG/Serial (WebUSB)",
795
- fn: async function () {
801
+ fn: async () => {
796
802
  return await self.hardResetUSBJTAGSerialWebUSB();
797
803
  },
798
804
  });
799
805
  resetStrategies.push({
800
806
  name: "Inverted DTR Classic (WebUSB)",
801
- fn: async function () {
807
+ fn: async () => {
802
808
  return await self.hardResetInvertedDTRWebUSB();
803
809
  },
804
810
  });
@@ -810,31 +816,31 @@ export class ESPLoader extends EventTarget {
810
816
  // CH340/CH343: UnixTight works best (like CP2102)
811
817
  resetStrategies.push({
812
818
  name: "UnixTight (WebUSB) - CH34x",
813
- fn: async function () {
819
+ fn: async () => {
814
820
  return await self.hardResetUnixTightWebUSB();
815
821
  },
816
822
  });
817
823
  resetStrategies.push({
818
824
  name: "Classic (WebUSB) - CH34x",
819
- fn: async function () {
825
+ fn: async () => {
820
826
  return await self.hardResetClassicWebUSB();
821
827
  },
822
828
  });
823
829
  resetStrategies.push({
824
830
  name: "Inverted Both (WebUSB) - CH34x",
825
- fn: async function () {
831
+ fn: async () => {
826
832
  return await self.hardResetInvertedWebUSB();
827
833
  },
828
834
  });
829
835
  resetStrategies.push({
830
836
  name: "Inverted RTS (WebUSB) - CH34x",
831
- fn: async function () {
837
+ fn: async () => {
832
838
  return await self.hardResetInvertedRTSWebUSB();
833
839
  },
834
840
  });
835
841
  resetStrategies.push({
836
842
  name: "Inverted DTR (WebUSB) - CH34x",
837
- fn: async function () {
843
+ fn: async () => {
838
844
  return await self.hardResetInvertedDTRWebUSB();
839
845
  },
840
846
  });
@@ -844,31 +850,31 @@ export class ESPLoader extends EventTarget {
844
850
  // Try it first, then fallback to other strategies
845
851
  resetStrategies.push({
846
852
  name: "UnixTight (WebUSB) - CP2102",
847
- fn: async function () {
853
+ fn: async () => {
848
854
  return await self.hardResetUnixTightWebUSB();
849
855
  },
850
856
  });
851
857
  resetStrategies.push({
852
858
  name: "Classic (WebUSB) - CP2102",
853
- fn: async function () {
859
+ fn: async () => {
854
860
  return await self.hardResetClassicWebUSB();
855
861
  },
856
862
  });
857
863
  resetStrategies.push({
858
864
  name: "Inverted Both (WebUSB) - CP2102",
859
- fn: async function () {
865
+ fn: async () => {
860
866
  return await self.hardResetInvertedWebUSB();
861
867
  },
862
868
  });
863
869
  resetStrategies.push({
864
870
  name: "Inverted RTS (WebUSB) - CP2102",
865
- fn: async function () {
871
+ fn: async () => {
866
872
  return await self.hardResetInvertedRTSWebUSB();
867
873
  },
868
874
  });
869
875
  resetStrategies.push({
870
876
  name: "Inverted DTR (WebUSB) - CP2102",
871
- fn: async function () {
877
+ fn: async () => {
872
878
  return await self.hardResetInvertedDTRWebUSB();
873
879
  },
874
880
  });
@@ -877,7 +883,7 @@ export class ESPLoader extends EventTarget {
877
883
  // For other USB-Serial chips, try UnixTight first, then multiple strategies
878
884
  resetStrategies.push({
879
885
  name: "UnixTight (WebUSB)",
880
- fn: async function () {
886
+ fn: async () => {
881
887
  return await self.hardResetUnixTightWebUSB();
882
888
  },
883
889
  });
@@ -951,7 +957,6 @@ export class ESPLoader extends EventTarget {
951
957
  }
952
958
  }
953
959
  else {
954
- // Web Serial (Desktop) strategies
955
960
  // Strategy: USB-JTAG/Serial reset
956
961
  if (isUSBJTAGSerial || isEspressifUSB) {
957
962
  resetStrategies.push({
@@ -1046,9 +1051,9 @@ export class ESPLoader extends EventTarget {
1046
1051
  // just reset (no bootloader mode)
1047
1052
  if (this.isWebUSB()) {
1048
1053
  // WebUSB: Use longer delays for better compatibility
1049
- await this.setRTS(true); // EN->LOW
1054
+ await this.setRTSWebUSB(true); // EN->LOW
1050
1055
  await this.sleep(200);
1051
- await this.setRTS(false);
1056
+ await this.setRTSWebUSB(false);
1052
1057
  await this.sleep(200);
1053
1058
  this.logger.log("Hard reset (WebUSB).");
1054
1059
  }
@@ -2171,7 +2176,15 @@ export class ESPLoader extends EventTarget {
2171
2176
  resolve(undefined);
2172
2177
  return;
2173
2178
  }
2174
- 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 });
2175
2188
  // Only cancel if reader is still active
2176
2189
  try {
2177
2190
  this._reader.cancel();
@@ -2179,10 +2192,19 @@ export class ESPLoader extends EventTarget {
2179
2192
  catch (err) {
2180
2193
  this.logger.debug(`Reader cancel error: ${err}`);
2181
2194
  // Reader already released, resolve immediately
2195
+ clearTimeout(timeout);
2182
2196
  resolve(undefined);
2183
2197
  }
2184
2198
  });
2185
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
+ }
2186
2208
  }
2187
2209
  /**
2188
2210
  * @name reconnectAndResume
@@ -2701,6 +2723,10 @@ class EspStubLoader extends ESPLoader {
2701
2723
  if (size > maxValue) {
2702
2724
  throw new Error(`Size ${size} exceeds maximum value ${maxValue}`);
2703
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
+ }
2704
2730
  const timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
2705
2731
  const buffer = pack("<II", offset, size);
2706
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
  };