tasmota-webserial-esptool 9.1.8 → 9.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.
- package/dist/esp_loader.d.ts +7 -1
- package/dist/esp_loader.js +110 -37
- 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 +31 -2
- package/js/webusb-serial.js +9 -19
- package/package.json +1 -1
- package/src/esp_loader.ts +128 -40
- 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 {
|
|
@@ -715,6 +721,15 @@ export class ESPLoader extends EventTarget {
|
|
|
715
721
|
await this.port.setSignals({ dataTerminalReady: state });
|
|
716
722
|
}
|
|
717
723
|
|
|
724
|
+
async setDTRandRTS(dtr: boolean, rts: boolean) {
|
|
725
|
+
this.state_DTR = dtr;
|
|
726
|
+
this.state_RTS = rts;
|
|
727
|
+
await this.port.setSignals({
|
|
728
|
+
dataTerminalReady: dtr,
|
|
729
|
+
requestToSend: rts,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
|
|
718
733
|
/**
|
|
719
734
|
* @name hardResetUSBJTAGSerial
|
|
720
735
|
* USB-JTAG/Serial reset for Web Serial (Desktop)
|
|
@@ -741,7 +756,7 @@ export class ESPLoader extends EventTarget {
|
|
|
741
756
|
|
|
742
757
|
/**
|
|
743
758
|
* @name hardResetClassic
|
|
744
|
-
* Classic reset for Web Serial (Desktop)
|
|
759
|
+
* Classic reset for Web Serial (Desktop) DTR = IO0, RTS = EN
|
|
745
760
|
*/
|
|
746
761
|
async hardResetClassic() {
|
|
747
762
|
await this.setDTR(false); // IO0=HIGH
|
|
@@ -755,6 +770,23 @@ export class ESPLoader extends EventTarget {
|
|
|
755
770
|
await this.sleep(200);
|
|
756
771
|
}
|
|
757
772
|
|
|
773
|
+
/**
|
|
774
|
+
* @name hardResetUnixTight
|
|
775
|
+
* Unix Tight reset for Web Serial (Desktop) - sets DTR and RTS simultaneously
|
|
776
|
+
*/
|
|
777
|
+
async hardResetUnixTight() {
|
|
778
|
+
await this.setDTRandRTS(true, true);
|
|
779
|
+
await this.setDTRandRTS(false, false);
|
|
780
|
+
await this.setDTRandRTS(false, true); // IO0=HIGH & EN=LOW, chip in reset
|
|
781
|
+
await this.sleep(100);
|
|
782
|
+
await this.setDTRandRTS(true, false); // IO0=LOW & EN=HIGH, chip out of reset
|
|
783
|
+
await this.sleep(50);
|
|
784
|
+
await this.setDTRandRTS(false, false); // IO0=HIGH, done
|
|
785
|
+
await this.setDTR(false); // Needed in some environments to ensure IO0=HIGH
|
|
786
|
+
|
|
787
|
+
await this.sleep(200);
|
|
788
|
+
}
|
|
789
|
+
|
|
758
790
|
// ============================================================================
|
|
759
791
|
// WebUSB (Android) - DTR/RTS Signal Handling & Reset Strategies
|
|
760
792
|
// ============================================================================
|
|
@@ -971,10 +1003,12 @@ export class ESPLoader extends EventTarget {
|
|
|
971
1003
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
972
1004
|
const self = this;
|
|
973
1005
|
|
|
1006
|
+
// Detect if this is a USB-Serial chip (needs different sync approach)
|
|
1007
|
+
const isUSBSerialChip = !isUSBJTAGSerial && !isEspressifUSB;
|
|
1008
|
+
|
|
974
1009
|
// WebUSB (Android) uses different reset methods than Web Serial (Desktop)
|
|
975
1010
|
if (this.isWebUSB()) {
|
|
976
1011
|
// For USB-Serial chips (CP2102, CH340, etc.), try inverted strategies first
|
|
977
|
-
const isUSBSerialChip = !isUSBJTAGSerial && !isEspressifUSB;
|
|
978
1012
|
|
|
979
1013
|
// Detect specific chip types once
|
|
980
1014
|
const isCP2102 = portInfo.usbVendorId === 0x10c4;
|
|
@@ -993,7 +1027,7 @@ export class ESPLoader extends EventTarget {
|
|
|
993
1027
|
// Strategy 1: USB-JTAG/Serial (works in CDC mode on Desktop)
|
|
994
1028
|
resetStrategies.push({
|
|
995
1029
|
name: "USB-JTAG/Serial (WebUSB) - ESP32-S2",
|
|
996
|
-
fn: async
|
|
1030
|
+
fn: async () => {
|
|
997
1031
|
return await self.hardResetUSBJTAGSerialWebUSB();
|
|
998
1032
|
},
|
|
999
1033
|
});
|
|
@@ -1001,7 +1035,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1001
1035
|
// Strategy 2: USB-JTAG/Serial Inverted DTR (works in JTAG mode)
|
|
1002
1036
|
resetStrategies.push({
|
|
1003
1037
|
name: "USB-JTAG/Serial Inverted DTR (WebUSB) - ESP32-S2",
|
|
1004
|
-
fn: async
|
|
1038
|
+
fn: async () => {
|
|
1005
1039
|
return await self.hardResetUSBJTAGSerialInvertedDTRWebUSB();
|
|
1006
1040
|
},
|
|
1007
1041
|
});
|
|
@@ -1009,7 +1043,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1009
1043
|
// Strategy 3: UnixTight (CDC fallback)
|
|
1010
1044
|
resetStrategies.push({
|
|
1011
1045
|
name: "UnixTight (WebUSB) - ESP32-S2 CDC",
|
|
1012
|
-
fn: async
|
|
1046
|
+
fn: async () => {
|
|
1013
1047
|
return await self.hardResetUnixTightWebUSB();
|
|
1014
1048
|
},
|
|
1015
1049
|
});
|
|
@@ -1017,7 +1051,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1017
1051
|
// Strategy 4: Classic reset (CDC fallback)
|
|
1018
1052
|
resetStrategies.push({
|
|
1019
1053
|
name: "Classic (WebUSB) - ESP32-S2 CDC",
|
|
1020
|
-
fn: async
|
|
1054
|
+
fn: async () => {
|
|
1021
1055
|
return await self.hardResetClassicWebUSB();
|
|
1022
1056
|
},
|
|
1023
1057
|
});
|
|
@@ -1025,19 +1059,19 @@ export class ESPLoader extends EventTarget {
|
|
|
1025
1059
|
// Other USB-JTAG chips: Try Inverted DTR first - works best for ESP32-H2 and other JTAG chips
|
|
1026
1060
|
resetStrategies.push({
|
|
1027
1061
|
name: "USB-JTAG/Serial Inverted DTR (WebUSB)",
|
|
1028
|
-
fn: async
|
|
1062
|
+
fn: async () => {
|
|
1029
1063
|
return await self.hardResetUSBJTAGSerialInvertedDTRWebUSB();
|
|
1030
1064
|
},
|
|
1031
1065
|
});
|
|
1032
1066
|
resetStrategies.push({
|
|
1033
1067
|
name: "USB-JTAG/Serial (WebUSB)",
|
|
1034
|
-
fn: async
|
|
1068
|
+
fn: async () => {
|
|
1035
1069
|
return await self.hardResetUSBJTAGSerialWebUSB();
|
|
1036
1070
|
},
|
|
1037
1071
|
});
|
|
1038
1072
|
resetStrategies.push({
|
|
1039
1073
|
name: "Inverted DTR Classic (WebUSB)",
|
|
1040
|
-
fn: async
|
|
1074
|
+
fn: async () => {
|
|
1041
1075
|
return await self.hardResetInvertedDTRWebUSB();
|
|
1042
1076
|
},
|
|
1043
1077
|
});
|
|
@@ -1050,31 +1084,31 @@ export class ESPLoader extends EventTarget {
|
|
|
1050
1084
|
// CH340/CH343: UnixTight works best (like CP2102)
|
|
1051
1085
|
resetStrategies.push({
|
|
1052
1086
|
name: "UnixTight (WebUSB) - CH34x",
|
|
1053
|
-
fn: async
|
|
1087
|
+
fn: async () => {
|
|
1054
1088
|
return await self.hardResetUnixTightWebUSB();
|
|
1055
1089
|
},
|
|
1056
1090
|
});
|
|
1057
1091
|
resetStrategies.push({
|
|
1058
1092
|
name: "Classic (WebUSB) - CH34x",
|
|
1059
|
-
fn: async
|
|
1093
|
+
fn: async () => {
|
|
1060
1094
|
return await self.hardResetClassicWebUSB();
|
|
1061
1095
|
},
|
|
1062
1096
|
});
|
|
1063
1097
|
resetStrategies.push({
|
|
1064
1098
|
name: "Inverted Both (WebUSB) - CH34x",
|
|
1065
|
-
fn: async
|
|
1099
|
+
fn: async () => {
|
|
1066
1100
|
return await self.hardResetInvertedWebUSB();
|
|
1067
1101
|
},
|
|
1068
1102
|
});
|
|
1069
1103
|
resetStrategies.push({
|
|
1070
1104
|
name: "Inverted RTS (WebUSB) - CH34x",
|
|
1071
|
-
fn: async
|
|
1105
|
+
fn: async () => {
|
|
1072
1106
|
return await self.hardResetInvertedRTSWebUSB();
|
|
1073
1107
|
},
|
|
1074
1108
|
});
|
|
1075
1109
|
resetStrategies.push({
|
|
1076
1110
|
name: "Inverted DTR (WebUSB) - CH34x",
|
|
1077
|
-
fn: async
|
|
1111
|
+
fn: async () => {
|
|
1078
1112
|
return await self.hardResetInvertedDTRWebUSB();
|
|
1079
1113
|
},
|
|
1080
1114
|
});
|
|
@@ -1084,35 +1118,35 @@ export class ESPLoader extends EventTarget {
|
|
|
1084
1118
|
|
|
1085
1119
|
resetStrategies.push({
|
|
1086
1120
|
name: "UnixTight (WebUSB) - CP2102",
|
|
1087
|
-
fn: async
|
|
1121
|
+
fn: async () => {
|
|
1088
1122
|
return await self.hardResetUnixTightWebUSB();
|
|
1089
1123
|
},
|
|
1090
1124
|
});
|
|
1091
1125
|
|
|
1092
1126
|
resetStrategies.push({
|
|
1093
1127
|
name: "Classic (WebUSB) - CP2102",
|
|
1094
|
-
fn: async
|
|
1128
|
+
fn: async () => {
|
|
1095
1129
|
return await self.hardResetClassicWebUSB();
|
|
1096
1130
|
},
|
|
1097
1131
|
});
|
|
1098
1132
|
|
|
1099
1133
|
resetStrategies.push({
|
|
1100
1134
|
name: "Inverted Both (WebUSB) - CP2102",
|
|
1101
|
-
fn: async
|
|
1135
|
+
fn: async () => {
|
|
1102
1136
|
return await self.hardResetInvertedWebUSB();
|
|
1103
1137
|
},
|
|
1104
1138
|
});
|
|
1105
1139
|
|
|
1106
1140
|
resetStrategies.push({
|
|
1107
1141
|
name: "Inverted RTS (WebUSB) - CP2102",
|
|
1108
|
-
fn: async
|
|
1142
|
+
fn: async () => {
|
|
1109
1143
|
return await self.hardResetInvertedRTSWebUSB();
|
|
1110
1144
|
},
|
|
1111
1145
|
});
|
|
1112
1146
|
|
|
1113
1147
|
resetStrategies.push({
|
|
1114
1148
|
name: "Inverted DTR (WebUSB) - CP2102",
|
|
1115
|
-
fn: async
|
|
1149
|
+
fn: async () => {
|
|
1116
1150
|
return await self.hardResetInvertedDTRWebUSB();
|
|
1117
1151
|
},
|
|
1118
1152
|
});
|
|
@@ -1120,7 +1154,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1120
1154
|
// For other USB-Serial chips, try UnixTight first, then multiple strategies
|
|
1121
1155
|
resetStrategies.push({
|
|
1122
1156
|
name: "UnixTight (WebUSB)",
|
|
1123
|
-
fn: async
|
|
1157
|
+
fn: async () => {
|
|
1124
1158
|
return await self.hardResetUnixTightWebUSB();
|
|
1125
1159
|
},
|
|
1126
1160
|
});
|
|
@@ -1198,7 +1232,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1198
1232
|
}
|
|
1199
1233
|
}
|
|
1200
1234
|
} else {
|
|
1201
|
-
// Web Serial (Desktop) strategies
|
|
1202
1235
|
// Strategy: USB-JTAG/Serial reset
|
|
1203
1236
|
if (isUSBJTAGSerial || isEspressifUSB) {
|
|
1204
1237
|
resetStrategies.push({
|
|
@@ -1209,11 +1242,11 @@ export class ESPLoader extends EventTarget {
|
|
|
1209
1242
|
});
|
|
1210
1243
|
}
|
|
1211
1244
|
|
|
1212
|
-
// Strategy:
|
|
1245
|
+
// Strategy: UnixTight reset
|
|
1213
1246
|
resetStrategies.push({
|
|
1214
|
-
name: "
|
|
1247
|
+
name: "UnixTight",
|
|
1215
1248
|
fn: async function () {
|
|
1216
|
-
return await self.
|
|
1249
|
+
return await self.hardResetUnixTight();
|
|
1217
1250
|
},
|
|
1218
1251
|
});
|
|
1219
1252
|
|
|
@@ -1244,17 +1277,44 @@ export class ESPLoader extends EventTarget {
|
|
|
1244
1277
|
|
|
1245
1278
|
await strategy.fn();
|
|
1246
1279
|
|
|
1247
|
-
// Try to sync after reset
|
|
1248
|
-
|
|
1280
|
+
// Try to sync after reset
|
|
1281
|
+
// USB-Serial / native USB chips needs different sync approaches
|
|
1249
1282
|
|
|
1250
|
-
if (
|
|
1251
|
-
//
|
|
1252
|
-
this.logger.log(
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1283
|
+
if (isUSBSerialChip) {
|
|
1284
|
+
// USB-Serial chips: Use timeout strategy (2 seconds)
|
|
1285
|
+
// this.logger.log(`USB-Serial chip detected, using sync with timeout.`);
|
|
1286
|
+
const syncSuccess = await this.syncWithTimeout(2000);
|
|
1287
|
+
|
|
1288
|
+
if (syncSuccess) {
|
|
1289
|
+
// Sync succeeded
|
|
1290
|
+
this.logger.log(
|
|
1291
|
+
`Connected USB Serial successfully with ${strategy.name} reset.`,
|
|
1292
|
+
);
|
|
1293
|
+
return;
|
|
1294
|
+
} else {
|
|
1295
|
+
throw new Error("Sync timeout or abandoned");
|
|
1296
|
+
}
|
|
1256
1297
|
} else {
|
|
1257
|
-
|
|
1298
|
+
// Native USB chips
|
|
1299
|
+
// Note: We use Promise.race with sync() directly instead of syncWithTimeout()
|
|
1300
|
+
// because syncWithTimeout causes CDC/JTAG devices to hang for unknown reasons.
|
|
1301
|
+
// The abandon flag in readPacket() prevents overlapping I/O.
|
|
1302
|
+
// this.logger.log(`Native USB chip detected, using CDC/JTAG sync.`);
|
|
1303
|
+
const syncPromise = this.sync();
|
|
1304
|
+
const timeoutPromise = new Promise<void>((_, reject) =>
|
|
1305
|
+
setTimeout(() => reject(new Error("Sync timeout")), 1000),
|
|
1306
|
+
);
|
|
1307
|
+
|
|
1308
|
+
try {
|
|
1309
|
+
await Promise.race([syncPromise, timeoutPromise]);
|
|
1310
|
+
// Sync succeeded
|
|
1311
|
+
this.logger.log(
|
|
1312
|
+
`Connected CDC/JTAG successfully with ${strategy.name} reset.`,
|
|
1313
|
+
);
|
|
1314
|
+
return;
|
|
1315
|
+
} catch (error) {
|
|
1316
|
+
throw new Error("Sync timeout or abandoned");
|
|
1317
|
+
}
|
|
1258
1318
|
}
|
|
1259
1319
|
} catch (error) {
|
|
1260
1320
|
lastError = error as Error;
|
|
@@ -1309,9 +1369,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1309
1369
|
// just reset (no bootloader mode)
|
|
1310
1370
|
if (this.isWebUSB()) {
|
|
1311
1371
|
// WebUSB: Use longer delays for better compatibility
|
|
1312
|
-
await this.
|
|
1372
|
+
await this.setRTSWebUSB(true); // EN->LOW
|
|
1313
1373
|
await this.sleep(200);
|
|
1314
|
-
await this.
|
|
1374
|
+
await this.setRTSWebUSB(false);
|
|
1315
1375
|
await this.sleep(200);
|
|
1316
1376
|
this.logger.log("Hard reset (WebUSB).");
|
|
1317
1377
|
} else {
|
|
@@ -1560,7 +1620,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1560
1620
|
"Timed out waiting for packet " + waitingFor,
|
|
1561
1621
|
);
|
|
1562
1622
|
}
|
|
1563
|
-
|
|
1564
1623
|
const b = this._readByte()!;
|
|
1565
1624
|
|
|
1566
1625
|
if (partialPacket === null) {
|
|
@@ -2676,7 +2735,21 @@ export class ESPLoader extends EventTarget {
|
|
|
2676
2735
|
resolve(undefined);
|
|
2677
2736
|
return;
|
|
2678
2737
|
}
|
|
2679
|
-
|
|
2738
|
+
|
|
2739
|
+
// Set a timeout to prevent hanging (important for node-usb)
|
|
2740
|
+
const timeout = setTimeout(() => {
|
|
2741
|
+
this.logger.debug("Disconnect timeout - forcing resolution");
|
|
2742
|
+
resolve(undefined);
|
|
2743
|
+
}, 1000);
|
|
2744
|
+
|
|
2745
|
+
this.addEventListener(
|
|
2746
|
+
"disconnect",
|
|
2747
|
+
() => {
|
|
2748
|
+
clearTimeout(timeout);
|
|
2749
|
+
resolve(undefined);
|
|
2750
|
+
},
|
|
2751
|
+
{ once: true },
|
|
2752
|
+
);
|
|
2680
2753
|
|
|
2681
2754
|
// Only cancel if reader is still active
|
|
2682
2755
|
try {
|
|
@@ -2684,10 +2757,19 @@ export class ESPLoader extends EventTarget {
|
|
|
2684
2757
|
} catch (err) {
|
|
2685
2758
|
this.logger.debug(`Reader cancel error: ${err}`);
|
|
2686
2759
|
// Reader already released, resolve immediately
|
|
2760
|
+
clearTimeout(timeout);
|
|
2687
2761
|
resolve(undefined);
|
|
2688
2762
|
}
|
|
2689
2763
|
});
|
|
2690
2764
|
this.connected = false;
|
|
2765
|
+
|
|
2766
|
+
// Close the port (important for node-usb adapter)
|
|
2767
|
+
try {
|
|
2768
|
+
await this.port.close();
|
|
2769
|
+
this.logger.debug("Port closed successfully");
|
|
2770
|
+
} catch (err) {
|
|
2771
|
+
this.logger.debug(`Port close error: ${err}`);
|
|
2772
|
+
}
|
|
2691
2773
|
}
|
|
2692
2774
|
|
|
2693
2775
|
/**
|
|
@@ -3357,6 +3439,12 @@ class EspStubLoader extends ESPLoader {
|
|
|
3357
3439
|
if (size > maxValue) {
|
|
3358
3440
|
throw new Error(`Size ${size} exceeds maximum value ${maxValue}`);
|
|
3359
3441
|
}
|
|
3442
|
+
// Check for wrap-around
|
|
3443
|
+
if (offset + size > maxValue) {
|
|
3444
|
+
throw new Error(
|
|
3445
|
+
`Region end (offset + size = ${offset + size}) exceeds maximum addressable range ${maxValue}`,
|
|
3446
|
+
);
|
|
3447
|
+
}
|
|
3360
3448
|
|
|
3361
3449
|
const timeout = timeoutPerMb(ERASE_REGION_TIMEOUT_PER_MB, size);
|
|
3362
3450
|
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
|
};
|