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.
- package/dist/esp_loader.js +51 -25
- package/dist/index.d.ts +1 -1
- package/dist/index.js +13 -10
- package/dist/web/index.js +1 -1
- package/js/modules/esptool.js +1 -1
- package/js/script.js +8 -1
- package/js/webusb-serial.js +9 -19
- package/package.json +1 -1
- package/src/esp_loader.ts +59 -26
- package/src/index.ts +40 -13
package/src/esp_loader.ts
CHANGED
|
@@ -101,8 +101,7 @@ export class ESPLoader extends EventTarget {
|
|
|
101
101
|
private __isReconfiguring: boolean = false;
|
|
102
102
|
private __abandonCurrentOperation: boolean = false;
|
|
103
103
|
|
|
104
|
-
// Adaptive speed adjustment for flash read operations
|
|
105
|
-
// Using fixed conservative values that work reliably
|
|
104
|
+
// Adaptive speed adjustment for flash read operations
|
|
106
105
|
private __adaptiveBlockMultiplier: number = 1;
|
|
107
106
|
private __adaptiveMaxInFlightMultiplier: number = 1;
|
|
108
107
|
private __consecutiveSuccessfulChunks: number = 0;
|
|
@@ -118,6 +117,7 @@ export class ESPLoader extends EventTarget {
|
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
// Chip properties with parent delegation
|
|
120
|
+
// chipFamily accessed before initialization as designed
|
|
121
121
|
get chipFamily(): ChipFamily {
|
|
122
122
|
return this._parent ? this._parent.chipFamily : this.__chipFamily!;
|
|
123
123
|
}
|
|
@@ -167,7 +167,13 @@ export class ESPLoader extends EventTarget {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
private get _inputBuffer(): number[] {
|
|
170
|
-
|
|
170
|
+
if (this._parent) {
|
|
171
|
+
return this._parent._inputBuffer;
|
|
172
|
+
}
|
|
173
|
+
if (this.__inputBuffer === undefined) {
|
|
174
|
+
throw new Error("_inputBuffer accessed before initialization");
|
|
175
|
+
}
|
|
176
|
+
return this.__inputBuffer;
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
private get _inputBufferReadIndex(): number {
|
|
@@ -993,7 +999,7 @@ export class ESPLoader extends EventTarget {
|
|
|
993
999
|
// Strategy 1: USB-JTAG/Serial (works in CDC mode on Desktop)
|
|
994
1000
|
resetStrategies.push({
|
|
995
1001
|
name: "USB-JTAG/Serial (WebUSB) - ESP32-S2",
|
|
996
|
-
fn: async
|
|
1002
|
+
fn: async () => {
|
|
997
1003
|
return await self.hardResetUSBJTAGSerialWebUSB();
|
|
998
1004
|
},
|
|
999
1005
|
});
|
|
@@ -1001,7 +1007,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1001
1007
|
// Strategy 2: USB-JTAG/Serial Inverted DTR (works in JTAG mode)
|
|
1002
1008
|
resetStrategies.push({
|
|
1003
1009
|
name: "USB-JTAG/Serial Inverted DTR (WebUSB) - ESP32-S2",
|
|
1004
|
-
fn: async
|
|
1010
|
+
fn: async () => {
|
|
1005
1011
|
return await self.hardResetUSBJTAGSerialInvertedDTRWebUSB();
|
|
1006
1012
|
},
|
|
1007
1013
|
});
|
|
@@ -1009,7 +1015,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1009
1015
|
// Strategy 3: UnixTight (CDC fallback)
|
|
1010
1016
|
resetStrategies.push({
|
|
1011
1017
|
name: "UnixTight (WebUSB) - ESP32-S2 CDC",
|
|
1012
|
-
fn: async
|
|
1018
|
+
fn: async () => {
|
|
1013
1019
|
return await self.hardResetUnixTightWebUSB();
|
|
1014
1020
|
},
|
|
1015
1021
|
});
|
|
@@ -1017,7 +1023,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1017
1023
|
// Strategy 4: Classic reset (CDC fallback)
|
|
1018
1024
|
resetStrategies.push({
|
|
1019
1025
|
name: "Classic (WebUSB) - ESP32-S2 CDC",
|
|
1020
|
-
fn: async
|
|
1026
|
+
fn: async () => {
|
|
1021
1027
|
return await self.hardResetClassicWebUSB();
|
|
1022
1028
|
},
|
|
1023
1029
|
});
|
|
@@ -1025,19 +1031,19 @@ export class ESPLoader extends EventTarget {
|
|
|
1025
1031
|
// Other USB-JTAG chips: Try Inverted DTR first - works best for ESP32-H2 and other JTAG chips
|
|
1026
1032
|
resetStrategies.push({
|
|
1027
1033
|
name: "USB-JTAG/Serial Inverted DTR (WebUSB)",
|
|
1028
|
-
fn: async
|
|
1034
|
+
fn: async () => {
|
|
1029
1035
|
return await self.hardResetUSBJTAGSerialInvertedDTRWebUSB();
|
|
1030
1036
|
},
|
|
1031
1037
|
});
|
|
1032
1038
|
resetStrategies.push({
|
|
1033
1039
|
name: "USB-JTAG/Serial (WebUSB)",
|
|
1034
|
-
fn: async
|
|
1040
|
+
fn: async () => {
|
|
1035
1041
|
return await self.hardResetUSBJTAGSerialWebUSB();
|
|
1036
1042
|
},
|
|
1037
1043
|
});
|
|
1038
1044
|
resetStrategies.push({
|
|
1039
1045
|
name: "Inverted DTR Classic (WebUSB)",
|
|
1040
|
-
fn: async
|
|
1046
|
+
fn: async () => {
|
|
1041
1047
|
return await self.hardResetInvertedDTRWebUSB();
|
|
1042
1048
|
},
|
|
1043
1049
|
});
|
|
@@ -1050,31 +1056,31 @@ export class ESPLoader extends EventTarget {
|
|
|
1050
1056
|
// CH340/CH343: UnixTight works best (like CP2102)
|
|
1051
1057
|
resetStrategies.push({
|
|
1052
1058
|
name: "UnixTight (WebUSB) - CH34x",
|
|
1053
|
-
fn: async
|
|
1059
|
+
fn: async () => {
|
|
1054
1060
|
return await self.hardResetUnixTightWebUSB();
|
|
1055
1061
|
},
|
|
1056
1062
|
});
|
|
1057
1063
|
resetStrategies.push({
|
|
1058
1064
|
name: "Classic (WebUSB) - CH34x",
|
|
1059
|
-
fn: async
|
|
1065
|
+
fn: async () => {
|
|
1060
1066
|
return await self.hardResetClassicWebUSB();
|
|
1061
1067
|
},
|
|
1062
1068
|
});
|
|
1063
1069
|
resetStrategies.push({
|
|
1064
1070
|
name: "Inverted Both (WebUSB) - CH34x",
|
|
1065
|
-
fn: async
|
|
1071
|
+
fn: async () => {
|
|
1066
1072
|
return await self.hardResetInvertedWebUSB();
|
|
1067
1073
|
},
|
|
1068
1074
|
});
|
|
1069
1075
|
resetStrategies.push({
|
|
1070
1076
|
name: "Inverted RTS (WebUSB) - CH34x",
|
|
1071
|
-
fn: async
|
|
1077
|
+
fn: async () => {
|
|
1072
1078
|
return await self.hardResetInvertedRTSWebUSB();
|
|
1073
1079
|
},
|
|
1074
1080
|
});
|
|
1075
1081
|
resetStrategies.push({
|
|
1076
1082
|
name: "Inverted DTR (WebUSB) - CH34x",
|
|
1077
|
-
fn: async
|
|
1083
|
+
fn: async () => {
|
|
1078
1084
|
return await self.hardResetInvertedDTRWebUSB();
|
|
1079
1085
|
},
|
|
1080
1086
|
});
|
|
@@ -1084,35 +1090,35 @@ export class ESPLoader extends EventTarget {
|
|
|
1084
1090
|
|
|
1085
1091
|
resetStrategies.push({
|
|
1086
1092
|
name: "UnixTight (WebUSB) - CP2102",
|
|
1087
|
-
fn: async
|
|
1093
|
+
fn: async () => {
|
|
1088
1094
|
return await self.hardResetUnixTightWebUSB();
|
|
1089
1095
|
},
|
|
1090
1096
|
});
|
|
1091
1097
|
|
|
1092
1098
|
resetStrategies.push({
|
|
1093
1099
|
name: "Classic (WebUSB) - CP2102",
|
|
1094
|
-
fn: async
|
|
1100
|
+
fn: async () => {
|
|
1095
1101
|
return await self.hardResetClassicWebUSB();
|
|
1096
1102
|
},
|
|
1097
1103
|
});
|
|
1098
1104
|
|
|
1099
1105
|
resetStrategies.push({
|
|
1100
1106
|
name: "Inverted Both (WebUSB) - CP2102",
|
|
1101
|
-
fn: async
|
|
1107
|
+
fn: async () => {
|
|
1102
1108
|
return await self.hardResetInvertedWebUSB();
|
|
1103
1109
|
},
|
|
1104
1110
|
});
|
|
1105
1111
|
|
|
1106
1112
|
resetStrategies.push({
|
|
1107
1113
|
name: "Inverted RTS (WebUSB) - CP2102",
|
|
1108
|
-
fn: async
|
|
1114
|
+
fn: async () => {
|
|
1109
1115
|
return await self.hardResetInvertedRTSWebUSB();
|
|
1110
1116
|
},
|
|
1111
1117
|
});
|
|
1112
1118
|
|
|
1113
1119
|
resetStrategies.push({
|
|
1114
1120
|
name: "Inverted DTR (WebUSB) - CP2102",
|
|
1115
|
-
fn: async
|
|
1121
|
+
fn: async () => {
|
|
1116
1122
|
return await self.hardResetInvertedDTRWebUSB();
|
|
1117
1123
|
},
|
|
1118
1124
|
});
|
|
@@ -1120,7 +1126,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1120
1126
|
// For other USB-Serial chips, try UnixTight first, then multiple strategies
|
|
1121
1127
|
resetStrategies.push({
|
|
1122
1128
|
name: "UnixTight (WebUSB)",
|
|
1123
|
-
fn: async
|
|
1129
|
+
fn: async () => {
|
|
1124
1130
|
return await self.hardResetUnixTightWebUSB();
|
|
1125
1131
|
},
|
|
1126
1132
|
});
|
|
@@ -1198,7 +1204,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1198
1204
|
}
|
|
1199
1205
|
}
|
|
1200
1206
|
} else {
|
|
1201
|
-
// Web Serial (Desktop) strategies
|
|
1202
1207
|
// Strategy: USB-JTAG/Serial reset
|
|
1203
1208
|
if (isUSBJTAGSerial || isEspressifUSB) {
|
|
1204
1209
|
resetStrategies.push({
|
|
@@ -1309,9 +1314,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1309
1314
|
// just reset (no bootloader mode)
|
|
1310
1315
|
if (this.isWebUSB()) {
|
|
1311
1316
|
// WebUSB: Use longer delays for better compatibility
|
|
1312
|
-
await this.
|
|
1317
|
+
await this.setRTSWebUSB(true); // EN->LOW
|
|
1313
1318
|
await this.sleep(200);
|
|
1314
|
-
await this.
|
|
1319
|
+
await this.setRTSWebUSB(false);
|
|
1315
1320
|
await this.sleep(200);
|
|
1316
1321
|
this.logger.log("Hard reset (WebUSB).");
|
|
1317
1322
|
} else {
|
|
@@ -1560,7 +1565,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1560
1565
|
"Timed out waiting for packet " + waitingFor,
|
|
1561
1566
|
);
|
|
1562
1567
|
}
|
|
1563
|
-
|
|
1564
1568
|
const b = this._readByte()!;
|
|
1565
1569
|
|
|
1566
1570
|
if (partialPacket === null) {
|
|
@@ -2676,7 +2680,21 @@ export class ESPLoader extends EventTarget {
|
|
|
2676
2680
|
resolve(undefined);
|
|
2677
2681
|
return;
|
|
2678
2682
|
}
|
|
2679
|
-
|
|
2683
|
+
|
|
2684
|
+
// Set a timeout to prevent hanging (important for node-usb)
|
|
2685
|
+
const timeout = setTimeout(() => {
|
|
2686
|
+
this.logger.debug("Disconnect timeout - forcing resolution");
|
|
2687
|
+
resolve(undefined);
|
|
2688
|
+
}, 1000);
|
|
2689
|
+
|
|
2690
|
+
this.addEventListener(
|
|
2691
|
+
"disconnect",
|
|
2692
|
+
() => {
|
|
2693
|
+
clearTimeout(timeout);
|
|
2694
|
+
resolve(undefined);
|
|
2695
|
+
},
|
|
2696
|
+
{ once: true },
|
|
2697
|
+
);
|
|
2680
2698
|
|
|
2681
2699
|
// Only cancel if reader is still active
|
|
2682
2700
|
try {
|
|
@@ -2684,10 +2702,19 @@ export class ESPLoader extends EventTarget {
|
|
|
2684
2702
|
} catch (err) {
|
|
2685
2703
|
this.logger.debug(`Reader cancel error: ${err}`);
|
|
2686
2704
|
// Reader already released, resolve immediately
|
|
2705
|
+
clearTimeout(timeout);
|
|
2687
2706
|
resolve(undefined);
|
|
2688
2707
|
}
|
|
2689
2708
|
});
|
|
2690
2709
|
this.connected = false;
|
|
2710
|
+
|
|
2711
|
+
// Close the port (important for node-usb adapter)
|
|
2712
|
+
try {
|
|
2713
|
+
await this.port.close();
|
|
2714
|
+
this.logger.debug("Port closed successfully");
|
|
2715
|
+
} catch (err) {
|
|
2716
|
+
this.logger.debug(`Port close error: ${err}`);
|
|
2717
|
+
}
|
|
2691
2718
|
}
|
|
2692
2719
|
|
|
2693
2720
|
/**
|
|
@@ -3357,6 +3384,12 @@ class EspStubLoader extends ESPLoader {
|
|
|
3357
3384
|
if (size > maxValue) {
|
|
3358
3385
|
throw new Error(`Size ${size} exceeds maximum value ${maxValue}`);
|
|
3359
3386
|
}
|
|
3387
|
+
// Check for wrap-around
|
|
3388
|
+
if (offset + size > maxValue) {
|
|
3389
|
+
throw new Error(
|
|
3390
|
+
`Region end (offset + size = ${offset + size}) exceeds maximum addressable range ${maxValue}`,
|
|
3391
|
+
);
|
|
3392
|
+
}
|
|
3360
3393
|
|
|
3361
3394
|
const timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
|
|
3362
3395
|
const buffer = pack("<II", offset, size);
|
package/src/index.ts
CHANGED
|
@@ -21,39 +21,68 @@ export {
|
|
|
21
21
|
CHIP_FAMILY_ESP32H21,
|
|
22
22
|
CHIP_FAMILY_ESP32P4,
|
|
23
23
|
CHIP_FAMILY_ESP32S31,
|
|
24
|
+
// Command constants
|
|
25
|
+
ESP_FLASH_BEGIN,
|
|
26
|
+
ESP_FLASH_DATA,
|
|
27
|
+
ESP_FLASH_END,
|
|
28
|
+
ESP_MEM_BEGIN,
|
|
29
|
+
ESP_MEM_END,
|
|
30
|
+
ESP_MEM_DATA,
|
|
31
|
+
ESP_SYNC,
|
|
32
|
+
ESP_WRITE_REG,
|
|
33
|
+
ESP_READ_REG,
|
|
34
|
+
ESP_ERASE_FLASH,
|
|
35
|
+
ESP_ERASE_REGION,
|
|
36
|
+
ESP_READ_FLASH,
|
|
37
|
+
ESP_SPI_SET_PARAMS,
|
|
38
|
+
ESP_SPI_ATTACH,
|
|
39
|
+
ESP_CHANGE_BAUDRATE,
|
|
40
|
+
ESP_SPI_FLASH_MD5,
|
|
41
|
+
ESP_GET_SECURITY_INFO,
|
|
42
|
+
ESP_CHECKSUM_MAGIC,
|
|
43
|
+
ESP_FLASH_DEFL_BEGIN,
|
|
44
|
+
ESP_FLASH_DEFL_DATA,
|
|
45
|
+
ESP_FLASH_DEFL_END,
|
|
46
|
+
ROM_INVALID_RECV_MSG,
|
|
47
|
+
// Block size constants
|
|
48
|
+
USB_RAM_BLOCK,
|
|
49
|
+
ESP_RAM_BLOCK,
|
|
50
|
+
// Timeout constants
|
|
51
|
+
DEFAULT_TIMEOUT,
|
|
52
|
+
CHIP_ERASE_TIMEOUT,
|
|
53
|
+
MAX_TIMEOUT,
|
|
54
|
+
SYNC_TIMEOUT,
|
|
55
|
+
ERASE_REGION_TIMEOUT_PER_MB,
|
|
56
|
+
MEM_END_ROM_TIMEOUT,
|
|
57
|
+
FLASH_READ_TIMEOUT,
|
|
24
58
|
} from "./const";
|
|
25
59
|
|
|
26
60
|
export const connect = async (logger: Logger) => {
|
|
61
|
+
// - Request a port and open a connection.
|
|
62
|
+
// Try to use requestSerialPort if available (supports WebUSB for Android)
|
|
27
63
|
let port: SerialPort;
|
|
28
|
-
|
|
29
|
-
// Check if a custom requestSerialPort function is available (e.g., from WebUSB wrapper)
|
|
30
64
|
const customRequestPort = (
|
|
31
65
|
globalThis as { requestSerialPort?: () => Promise<SerialPort> }
|
|
32
66
|
).requestSerialPort;
|
|
33
|
-
|
|
34
67
|
if (typeof customRequestPort === "function") {
|
|
35
|
-
// Use custom port request function (handles Android/WebUSB automatically)
|
|
36
|
-
logger.log("Using custom port request function");
|
|
37
68
|
port = await customRequestPort();
|
|
38
69
|
} else {
|
|
39
|
-
//
|
|
70
|
+
// Check if Web Serial API is available
|
|
40
71
|
if (!navigator.serial) {
|
|
41
72
|
throw new Error(
|
|
42
73
|
"Web Serial API is not supported in this browser. " +
|
|
43
|
-
"Please use Chrome
|
|
74
|
+
"Please use Chrome, Edge, or Opera on desktop, or Chrome on Android. " +
|
|
44
75
|
"Note: The page must be served over HTTPS or localhost.",
|
|
45
76
|
);
|
|
46
77
|
}
|
|
47
78
|
port = await navigator.serial.requestPort();
|
|
48
79
|
}
|
|
49
80
|
|
|
50
|
-
// Only open if not already open (
|
|
81
|
+
// Only open if not already open (requestSerialPort may return an opened port)
|
|
51
82
|
if (!port.readable || !port.writable) {
|
|
52
83
|
await port.open({ baudRate: ESP_ROM_BAUD });
|
|
53
84
|
}
|
|
54
85
|
|
|
55
|
-
logger.log("Connected successfully.");
|
|
56
|
-
|
|
57
86
|
return new ESPLoader(port, logger);
|
|
58
87
|
};
|
|
59
88
|
|
|
@@ -63,12 +92,10 @@ export const connectWithPort = async (port: SerialPort, logger: Logger) => {
|
|
|
63
92
|
throw new Error("Port is required");
|
|
64
93
|
}
|
|
65
94
|
|
|
66
|
-
//
|
|
95
|
+
// Check if port is already open, if not open it
|
|
67
96
|
if (!port.readable || !port.writable) {
|
|
68
97
|
await port.open({ baudRate: ESP_ROM_BAUD });
|
|
69
98
|
}
|
|
70
99
|
|
|
71
|
-
logger.log("Connected successfully.");
|
|
72
|
-
|
|
73
100
|
return new ESPLoader(port, logger);
|
|
74
101
|
};
|