esp32tool 1.3.7 → 1.4.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/apple-touch-icon.png +0 -0
- package/dist/console.js +18 -7
- package/dist/const.d.ts +6 -0
- package/dist/const.js +8 -0
- package/dist/esp_loader.d.ts +37 -30
- package/dist/esp_loader.js +502 -367
- package/dist/web/index.js +1 -1
- package/icons/icon-128.png +0 -0
- package/icons/icon-144.png +0 -0
- package/icons/icon-152.png +0 -0
- package/icons/icon-192.png +0 -0
- package/icons/icon-384.png +0 -0
- package/icons/icon-512.png +0 -0
- package/icons/icon-72.png +0 -0
- package/icons/icon-96.png +0 -0
- package/js/console.js +91 -0
- package/js/improv.js +1163 -0
- package/js/modules/esptool.js +1 -1
- package/js/script.js +156 -113
- package/package.json +1 -1
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/console.ts +28 -11
- package/src/const.ts +8 -0
- package/src/esp_loader.ts +610 -399
- package/sw.js +1 -1
package/src/esp_loader.ts
CHANGED
|
@@ -109,6 +109,29 @@ import {
|
|
|
109
109
|
ESP32P4_PMU_0P1A_TARGET0_0,
|
|
110
110
|
ESP32P4_PMU_0P1A_FORCE_TIEH_SEL_0,
|
|
111
111
|
ESP32P4_PMU_DATE_REG,
|
|
112
|
+
ESP32S2_UARTDEV_BUF_NO,
|
|
113
|
+
ESP32S2_UARTDEV_BUF_NO_USB_OTG,
|
|
114
|
+
ESP32S3_UARTDEV_BUF_NO,
|
|
115
|
+
ESP32S3_UARTDEV_BUF_NO_USB_OTG,
|
|
116
|
+
ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL,
|
|
117
|
+
ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL,
|
|
118
|
+
ESP32C3_BUF_UART_NO_OFFSET,
|
|
119
|
+
ESP32C5_UARTDEV_BUF_NO,
|
|
120
|
+
ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL,
|
|
121
|
+
ESP32C6_UARTDEV_BUF_NO,
|
|
122
|
+
ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL,
|
|
123
|
+
ESP32C61_UARTDEV_BUF_NO_REV_LE2,
|
|
124
|
+
ESP32C61_UARTDEV_BUF_NO_REV_GT2,
|
|
125
|
+
ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_LE2,
|
|
126
|
+
ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_GT2,
|
|
127
|
+
ESP32H2_UARTDEV_BUF_NO,
|
|
128
|
+
ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL,
|
|
129
|
+
ESP32H4_UARTDEV_BUF_NO,
|
|
130
|
+
ESP32H4_UARTDEV_BUF_NO_USB_JTAG_SERIAL,
|
|
131
|
+
ESP32P4_UARTDEV_BUF_NO_REV0,
|
|
132
|
+
ESP32P4_UARTDEV_BUF_NO_REV300,
|
|
133
|
+
ESP32P4_UARTDEV_BUF_NO_USB_OTG,
|
|
134
|
+
ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL,
|
|
112
135
|
} from "./const";
|
|
113
136
|
import { getStubCode } from "./stubs";
|
|
114
137
|
import { hexFormatter, sleep, slipEncode, toHex } from "./util";
|
|
@@ -536,7 +559,15 @@ export class ESPLoader extends EventTarget {
|
|
|
536
559
|
);
|
|
537
560
|
} catch (err) {
|
|
538
561
|
this.logger.debug(`Could not detect USB connection type: ${err}`);
|
|
539
|
-
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
try {
|
|
565
|
+
const usbMode = await this.getUsbMode();
|
|
566
|
+
this.logger.debug(
|
|
567
|
+
`USB mode (register): ${usbMode.mode} (uartNo=${usbMode.uartNo})`,
|
|
568
|
+
);
|
|
569
|
+
} catch (err) {
|
|
570
|
+
this.logger.debug(`Could not detect USB mode: ${err}`);
|
|
540
571
|
}
|
|
541
572
|
|
|
542
573
|
// Read the OTP data for this chip and store into this.efuses array
|
|
@@ -952,10 +983,6 @@ export class ESPLoader extends EventTarget {
|
|
|
952
983
|
this.logger.debug("Finished read loop");
|
|
953
984
|
}
|
|
954
985
|
|
|
955
|
-
sleep(ms = 100) {
|
|
956
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
957
|
-
}
|
|
958
|
-
|
|
959
986
|
state_DTR = false;
|
|
960
987
|
state_RTS = false;
|
|
961
988
|
|
|
@@ -986,28 +1013,46 @@ export class ESPLoader extends EventTarget {
|
|
|
986
1013
|
});
|
|
987
1014
|
}
|
|
988
1015
|
|
|
1016
|
+
private async runSignalSequence(
|
|
1017
|
+
steps: Array<{ dtr?: boolean; rts?: boolean; delayMs?: number }>,
|
|
1018
|
+
): Promise<void> {
|
|
1019
|
+
const webusb = (this.port as any).isWebUSB === true;
|
|
1020
|
+
for (const step of steps) {
|
|
1021
|
+
if (step.dtr !== undefined && step.rts !== undefined) {
|
|
1022
|
+
if (webusb) {
|
|
1023
|
+
await this.setDTRandRTSWebUSB(step.dtr, step.rts);
|
|
1024
|
+
} else {
|
|
1025
|
+
await this.setDTRandRTS(step.dtr, step.rts);
|
|
1026
|
+
}
|
|
1027
|
+
} else {
|
|
1028
|
+
if (step.dtr !== undefined) {
|
|
1029
|
+
webusb
|
|
1030
|
+
? await this.setDTRWebUSB(step.dtr)
|
|
1031
|
+
: await this.setDTR(step.dtr);
|
|
1032
|
+
}
|
|
1033
|
+
if (step.rts !== undefined) {
|
|
1034
|
+
webusb
|
|
1035
|
+
? await this.setRTSWebUSB(step.rts)
|
|
1036
|
+
: await this.setRTS(step.rts);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
if (step.delayMs) await sleep(step.delayMs);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
989
1043
|
/**
|
|
990
1044
|
* @name hardResetUSBJTAGSerial
|
|
991
1045
|
* USB-JTAG/Serial reset for Web Serial (Desktop)
|
|
992
1046
|
*/
|
|
993
1047
|
async hardResetUSBJTAGSerial() {
|
|
994
|
-
await this.
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
await this.setRTS(true); // Reset
|
|
1003
|
-
await this.setDTR(false);
|
|
1004
|
-
await this.setRTS(true);
|
|
1005
|
-
await this.sleep(100);
|
|
1006
|
-
|
|
1007
|
-
await this.setDTR(false);
|
|
1008
|
-
await this.setRTS(false); // Chip out of reset
|
|
1009
|
-
|
|
1010
|
-
await this.sleep(200);
|
|
1048
|
+
await this.runSignalSequence([
|
|
1049
|
+
{ rts: false },
|
|
1050
|
+
{ dtr: false, delayMs: 100 },
|
|
1051
|
+
{ dtr: true, rts: false, delayMs: 100 },
|
|
1052
|
+
{ rts: true },
|
|
1053
|
+
{ dtr: false, rts: true, delayMs: 100 },
|
|
1054
|
+
{ dtr: false, rts: false, delayMs: 200 },
|
|
1055
|
+
]);
|
|
1011
1056
|
}
|
|
1012
1057
|
|
|
1013
1058
|
/**
|
|
@@ -1015,15 +1060,11 @@ export class ESPLoader extends EventTarget {
|
|
|
1015
1060
|
* Classic reset for Web Serial (Desktop) DTR = IO0, RTS = EN
|
|
1016
1061
|
*/
|
|
1017
1062
|
async hardResetClassic() {
|
|
1018
|
-
await this.
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
await this.sleep(50);
|
|
1024
|
-
await this.setDTR(false); // IO0=HIGH, done
|
|
1025
|
-
|
|
1026
|
-
await this.sleep(200);
|
|
1063
|
+
await this.runSignalSequence([
|
|
1064
|
+
{ dtr: false, rts: true, delayMs: 100 },
|
|
1065
|
+
{ dtr: true, rts: false, delayMs: 50 },
|
|
1066
|
+
{ dtr: false, delayMs: 200 },
|
|
1067
|
+
]);
|
|
1027
1068
|
}
|
|
1028
1069
|
|
|
1029
1070
|
/**
|
|
@@ -1031,27 +1072,11 @@ export class ESPLoader extends EventTarget {
|
|
|
1031
1072
|
* Keeps IO0=HIGH during reset so chip boots into firmware
|
|
1032
1073
|
*/
|
|
1033
1074
|
async hardResetToFirmware() {
|
|
1034
|
-
await this.
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
await this.sleep(200);
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
/**
|
|
1044
|
-
* Reset to firmware mode (not bootloader) for WebUSB
|
|
1045
|
-
* Keeps IO0=HIGH during reset so chip boots into firmware
|
|
1046
|
-
*/
|
|
1047
|
-
async hardResetToFirmwareWebUSB() {
|
|
1048
|
-
await this.setDTRWebUSB(false); // IO0=HIGH
|
|
1049
|
-
await this.setRTSWebUSB(true); // EN=LOW, chip in reset
|
|
1050
|
-
await this.sleep(100);
|
|
1051
|
-
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset (IO0 stays HIGH)
|
|
1052
|
-
await this.sleep(50);
|
|
1053
|
-
|
|
1054
|
-
await this.sleep(200);
|
|
1075
|
+
await this.runSignalSequence([
|
|
1076
|
+
{ dtr: false, rts: true, delayMs: 100 },
|
|
1077
|
+
{ rts: false, delayMs: 50 },
|
|
1078
|
+
{ delayMs: 200 },
|
|
1079
|
+
]);
|
|
1055
1080
|
}
|
|
1056
1081
|
|
|
1057
1082
|
/**
|
|
@@ -1059,16 +1084,14 @@ export class ESPLoader extends EventTarget {
|
|
|
1059
1084
|
* Unix Tight reset for Web Serial (Desktop) - sets DTR and RTS simultaneously
|
|
1060
1085
|
*/
|
|
1061
1086
|
async hardResetUnixTight() {
|
|
1062
|
-
await this.
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
await this.sleep(200);
|
|
1087
|
+
await this.runSignalSequence([
|
|
1088
|
+
{ dtr: true, rts: true },
|
|
1089
|
+
{ dtr: false, rts: false },
|
|
1090
|
+
{ dtr: false, rts: true, delayMs: 100 },
|
|
1091
|
+
{ dtr: true, rts: false, delayMs: 50 },
|
|
1092
|
+
{ dtr: false, rts: false },
|
|
1093
|
+
{ dtr: false, delayMs: 200 },
|
|
1094
|
+
]);
|
|
1072
1095
|
}
|
|
1073
1096
|
|
|
1074
1097
|
// ============================================================================
|
|
@@ -1103,83 +1126,17 @@ export class ESPLoader extends EventTarget {
|
|
|
1103
1126
|
});
|
|
1104
1127
|
}
|
|
1105
1128
|
|
|
1106
|
-
/**
|
|
1107
|
-
* @name hardResetUSBJTAGSerialWebUSB
|
|
1108
|
-
* USB-JTAG/Serial reset for WebUSB (Android)
|
|
1109
|
-
*/
|
|
1110
|
-
async hardResetUSBJTAGSerialWebUSB() {
|
|
1111
|
-
await this.setRTSWebUSB(false);
|
|
1112
|
-
await this.setDTRWebUSB(false); // Idle
|
|
1113
|
-
await this.sleep(100);
|
|
1114
|
-
|
|
1115
|
-
await this.setDTRWebUSB(true); // Set IO0
|
|
1116
|
-
await this.setRTSWebUSB(false);
|
|
1117
|
-
await this.sleep(100);
|
|
1118
|
-
|
|
1119
|
-
await this.setRTSWebUSB(true); // Reset
|
|
1120
|
-
await this.setDTRWebUSB(false);
|
|
1121
|
-
await this.setRTSWebUSB(true);
|
|
1122
|
-
await this.sleep(100);
|
|
1123
|
-
|
|
1124
|
-
await this.setDTRWebUSB(false);
|
|
1125
|
-
await this.setRTSWebUSB(false); // Chip out of reset
|
|
1126
|
-
|
|
1127
|
-
await this.sleep(200);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
1129
|
/**
|
|
1131
1130
|
* @name hardResetUSBJTAGSerialInvertedDTRWebUSB
|
|
1132
1131
|
* USB-JTAG/Serial reset with inverted DTR for WebUSB (Android)
|
|
1133
1132
|
*/
|
|
1134
1133
|
async hardResetUSBJTAGSerialInvertedDTRWebUSB() {
|
|
1135
|
-
await this.
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
await this.sleep(100);
|
|
1142
|
-
|
|
1143
|
-
await this.setRTSWebUSB(true); // Reset
|
|
1144
|
-
await this.setDTRWebUSB(true); // (DTR inverted)
|
|
1145
|
-
await this.setRTSWebUSB(true);
|
|
1146
|
-
await this.sleep(100);
|
|
1147
|
-
|
|
1148
|
-
await this.setDTRWebUSB(true); // (DTR inverted)
|
|
1149
|
-
await this.setRTSWebUSB(false); // Chip out of reset
|
|
1150
|
-
|
|
1151
|
-
await this.sleep(200);
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
/**
|
|
1155
|
-
* @name hardResetClassicWebUSB
|
|
1156
|
-
* Classic reset for WebUSB (Android)
|
|
1157
|
-
*/
|
|
1158
|
-
async hardResetClassicWebUSB() {
|
|
1159
|
-
await this.setDTRWebUSB(false); // IO0=HIGH
|
|
1160
|
-
await this.setRTSWebUSB(true); // EN=LOW, chip in reset
|
|
1161
|
-
await this.sleep(100);
|
|
1162
|
-
await this.setDTRWebUSB(true); // IO0=LOW
|
|
1163
|
-
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset
|
|
1164
|
-
await this.sleep(50);
|
|
1165
|
-
await this.setDTRWebUSB(false); // IO0=HIGH, done
|
|
1166
|
-
await this.sleep(200);
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
/**
|
|
1170
|
-
* @name hardResetUnixTightWebUSB
|
|
1171
|
-
* Unix Tight reset for WebUSB (Android) - sets DTR and RTS simultaneously
|
|
1172
|
-
*/
|
|
1173
|
-
async hardResetUnixTightWebUSB() {
|
|
1174
|
-
await this.setDTRandRTSWebUSB(false, false);
|
|
1175
|
-
await this.setDTRandRTSWebUSB(true, true);
|
|
1176
|
-
await this.setDTRandRTSWebUSB(false, true); // IO0=HIGH & EN=LOW, chip in reset
|
|
1177
|
-
await this.sleep(100);
|
|
1178
|
-
await this.setDTRandRTSWebUSB(true, false); // IO0=LOW & EN=HIGH, chip out of reset
|
|
1179
|
-
await this.sleep(50);
|
|
1180
|
-
await this.setDTRandRTSWebUSB(false, false); // IO0=HIGH, done
|
|
1181
|
-
await this.setDTRWebUSB(false); // Ensure IO0=HIGH
|
|
1182
|
-
await this.sleep(200);
|
|
1134
|
+
await this.runSignalSequence([
|
|
1135
|
+
{ rts: false, dtr: true, delayMs: 100 },
|
|
1136
|
+
{ dtr: false, rts: false, delayMs: 100 },
|
|
1137
|
+
{ rts: true, dtr: true, delayMs: 100 },
|
|
1138
|
+
{ dtr: true, rts: false, delayMs: 200 },
|
|
1139
|
+
]);
|
|
1183
1140
|
}
|
|
1184
1141
|
|
|
1185
1142
|
/**
|
|
@@ -1188,14 +1145,11 @@ export class ESPLoader extends EventTarget {
|
|
|
1188
1145
|
* Specifically for CP2102/CH340 which may need more time
|
|
1189
1146
|
*/
|
|
1190
1147
|
async hardResetClassicLongDelayWebUSB() {
|
|
1191
|
-
await this.
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
await this.sleep(200);
|
|
1197
|
-
await this.setDTRWebUSB(false); // IO0=HIGH, done
|
|
1198
|
-
await this.sleep(500); // Extra long delay
|
|
1148
|
+
await this.runSignalSequence([
|
|
1149
|
+
{ dtr: false, rts: true, delayMs: 500 },
|
|
1150
|
+
{ dtr: true, rts: false, delayMs: 200 },
|
|
1151
|
+
{ dtr: false, delayMs: 500 },
|
|
1152
|
+
]);
|
|
1199
1153
|
}
|
|
1200
1154
|
|
|
1201
1155
|
/**
|
|
@@ -1205,12 +1159,12 @@ export class ESPLoader extends EventTarget {
|
|
|
1205
1159
|
async hardResetClassicShortDelayWebUSB() {
|
|
1206
1160
|
await this.setDTRWebUSB(false); // IO0=HIGH
|
|
1207
1161
|
await this.setRTSWebUSB(true); // EN=LOW, chip in reset
|
|
1208
|
-
await
|
|
1162
|
+
await sleep(50);
|
|
1209
1163
|
await this.setDTRWebUSB(true); // IO0=LOW
|
|
1210
1164
|
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset
|
|
1211
|
-
await
|
|
1165
|
+
await sleep(25);
|
|
1212
1166
|
await this.setDTRWebUSB(false); // IO0=HIGH, done
|
|
1213
|
-
await
|
|
1167
|
+
await sleep(100);
|
|
1214
1168
|
}
|
|
1215
1169
|
|
|
1216
1170
|
/**
|
|
@@ -1220,12 +1174,12 @@ export class ESPLoader extends EventTarget {
|
|
|
1220
1174
|
async hardResetInvertedWebUSB() {
|
|
1221
1175
|
await this.setDTRWebUSB(true); // IO0=HIGH (inverted)
|
|
1222
1176
|
await this.setRTSWebUSB(false); // EN=LOW, chip in reset (inverted)
|
|
1223
|
-
await
|
|
1177
|
+
await sleep(100);
|
|
1224
1178
|
await this.setDTRWebUSB(false); // IO0=LOW (inverted)
|
|
1225
1179
|
await this.setRTSWebUSB(true); // EN=HIGH, chip out of reset (inverted)
|
|
1226
|
-
await
|
|
1180
|
+
await sleep(50);
|
|
1227
1181
|
await this.setDTRWebUSB(true); // IO0=HIGH, done (inverted)
|
|
1228
|
-
await
|
|
1182
|
+
await sleep(200);
|
|
1229
1183
|
}
|
|
1230
1184
|
|
|
1231
1185
|
/**
|
|
@@ -1235,12 +1189,12 @@ export class ESPLoader extends EventTarget {
|
|
|
1235
1189
|
async hardResetInvertedDTRWebUSB() {
|
|
1236
1190
|
await this.setDTRWebUSB(true); // IO0=HIGH (DTR inverted)
|
|
1237
1191
|
await this.setRTSWebUSB(true); // EN=LOW, chip in reset (RTS normal)
|
|
1238
|
-
await
|
|
1192
|
+
await sleep(100);
|
|
1239
1193
|
await this.setDTRWebUSB(false); // IO0=LOW (DTR inverted)
|
|
1240
1194
|
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset (RTS normal)
|
|
1241
|
-
await
|
|
1195
|
+
await sleep(50);
|
|
1242
1196
|
await this.setDTRWebUSB(true); // IO0=HIGH, done (DTR inverted)
|
|
1243
|
-
await
|
|
1197
|
+
await sleep(200);
|
|
1244
1198
|
}
|
|
1245
1199
|
|
|
1246
1200
|
/**
|
|
@@ -1250,12 +1204,12 @@ export class ESPLoader extends EventTarget {
|
|
|
1250
1204
|
async hardResetInvertedRTSWebUSB() {
|
|
1251
1205
|
await this.setDTRWebUSB(false); // IO0=HIGH (DTR normal)
|
|
1252
1206
|
await this.setRTSWebUSB(false); // EN=LOW, chip in reset (RTS inverted)
|
|
1253
|
-
await
|
|
1207
|
+
await sleep(100);
|
|
1254
1208
|
await this.setDTRWebUSB(true); // IO0=LOW (DTR normal)
|
|
1255
1209
|
await this.setRTSWebUSB(true); // EN=HIGH, chip out of reset (RTS inverted)
|
|
1256
|
-
await
|
|
1210
|
+
await sleep(50);
|
|
1257
1211
|
await this.setDTRWebUSB(false); // IO0=HIGH, done (DTR normal)
|
|
1258
|
-
await
|
|
1212
|
+
await sleep(200);
|
|
1259
1213
|
}
|
|
1260
1214
|
|
|
1261
1215
|
/**
|
|
@@ -1312,7 +1266,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1312
1266
|
resetStrategies.push({
|
|
1313
1267
|
name: "USB-JTAG/Serial (WebUSB) - ESP32-S2",
|
|
1314
1268
|
fn: async () => {
|
|
1315
|
-
return await self.
|
|
1269
|
+
return await self.hardResetUSBJTAGSerial();
|
|
1316
1270
|
},
|
|
1317
1271
|
});
|
|
1318
1272
|
|
|
@@ -1328,7 +1282,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1328
1282
|
resetStrategies.push({
|
|
1329
1283
|
name: "UnixTight (WebUSB) - ESP32-S2 CDC",
|
|
1330
1284
|
fn: async () => {
|
|
1331
|
-
return await self.
|
|
1285
|
+
return await self.hardResetUnixTight();
|
|
1332
1286
|
},
|
|
1333
1287
|
});
|
|
1334
1288
|
|
|
@@ -1336,7 +1290,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1336
1290
|
resetStrategies.push({
|
|
1337
1291
|
name: "Classic (WebUSB) - ESP32-S2 CDC",
|
|
1338
1292
|
fn: async () => {
|
|
1339
|
-
return await self.
|
|
1293
|
+
return await self.hardResetClassic();
|
|
1340
1294
|
},
|
|
1341
1295
|
});
|
|
1342
1296
|
} else {
|
|
@@ -1350,7 +1304,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1350
1304
|
resetStrategies.push({
|
|
1351
1305
|
name: "USB-JTAG/Serial (WebUSB)",
|
|
1352
1306
|
fn: async () => {
|
|
1353
|
-
return await self.
|
|
1307
|
+
return await self.hardResetUSBJTAGSerial();
|
|
1354
1308
|
},
|
|
1355
1309
|
});
|
|
1356
1310
|
resetStrategies.push({
|
|
@@ -1369,13 +1323,13 @@ export class ESPLoader extends EventTarget {
|
|
|
1369
1323
|
resetStrategies.push({
|
|
1370
1324
|
name: "UnixTight (WebUSB) - CH34x",
|
|
1371
1325
|
fn: async () => {
|
|
1372
|
-
return await self.
|
|
1326
|
+
return await self.hardResetUnixTight();
|
|
1373
1327
|
},
|
|
1374
1328
|
});
|
|
1375
1329
|
resetStrategies.push({
|
|
1376
1330
|
name: "Classic (WebUSB) - CH34x",
|
|
1377
1331
|
fn: async () => {
|
|
1378
|
-
return await self.
|
|
1332
|
+
return await self.hardResetClassic();
|
|
1379
1333
|
},
|
|
1380
1334
|
});
|
|
1381
1335
|
resetStrategies.push({
|
|
@@ -1403,14 +1357,14 @@ export class ESPLoader extends EventTarget {
|
|
|
1403
1357
|
resetStrategies.push({
|
|
1404
1358
|
name: "UnixTight (WebUSB) - CP2102",
|
|
1405
1359
|
fn: async () => {
|
|
1406
|
-
return await self.
|
|
1360
|
+
return await self.hardResetUnixTight();
|
|
1407
1361
|
},
|
|
1408
1362
|
});
|
|
1409
1363
|
|
|
1410
1364
|
resetStrategies.push({
|
|
1411
1365
|
name: "Classic (WebUSB) - CP2102",
|
|
1412
1366
|
fn: async () => {
|
|
1413
|
-
return await self.
|
|
1367
|
+
return await self.hardResetClassic();
|
|
1414
1368
|
},
|
|
1415
1369
|
});
|
|
1416
1370
|
|
|
@@ -1439,13 +1393,13 @@ export class ESPLoader extends EventTarget {
|
|
|
1439
1393
|
resetStrategies.push({
|
|
1440
1394
|
name: "UnixTight (WebUSB)",
|
|
1441
1395
|
fn: async () => {
|
|
1442
|
-
return await self.
|
|
1396
|
+
return await self.hardResetUnixTight();
|
|
1443
1397
|
},
|
|
1444
1398
|
});
|
|
1445
1399
|
resetStrategies.push({
|
|
1446
1400
|
name: "Classic (WebUSB)",
|
|
1447
1401
|
fn: async function () {
|
|
1448
|
-
return await self.
|
|
1402
|
+
return await self.hardResetClassic();
|
|
1449
1403
|
},
|
|
1450
1404
|
});
|
|
1451
1405
|
resetStrategies.push({
|
|
@@ -1476,7 +1430,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1476
1430
|
resetStrategies.push({
|
|
1477
1431
|
name: "Classic (WebUSB)",
|
|
1478
1432
|
fn: async function () {
|
|
1479
|
-
return await self.
|
|
1433
|
+
return await self.hardResetClassic();
|
|
1480
1434
|
},
|
|
1481
1435
|
});
|
|
1482
1436
|
}
|
|
@@ -1485,7 +1439,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1485
1439
|
resetStrategies.push({
|
|
1486
1440
|
name: "UnixTight (WebUSB)",
|
|
1487
1441
|
fn: async function () {
|
|
1488
|
-
return await self.
|
|
1442
|
+
return await self.hardResetUnixTight();
|
|
1489
1443
|
},
|
|
1490
1444
|
});
|
|
1491
1445
|
|
|
@@ -1510,7 +1464,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1510
1464
|
resetStrategies.push({
|
|
1511
1465
|
name: "USB-JTAG/Serial fallback (WebUSB)",
|
|
1512
1466
|
fn: async function () {
|
|
1513
|
-
return await self.
|
|
1467
|
+
return await self.hardResetUSBJTAGSerial();
|
|
1514
1468
|
},
|
|
1515
1469
|
});
|
|
1516
1470
|
}
|
|
@@ -1708,34 +1662,152 @@ export class ESPLoader extends EventTarget {
|
|
|
1708
1662
|
await this.writeRegister(WDTWPROTECT_REG, 0, undefined, 0);
|
|
1709
1663
|
|
|
1710
1664
|
// Wait for reset to take effect
|
|
1711
|
-
await
|
|
1665
|
+
await sleep(500);
|
|
1712
1666
|
}
|
|
1713
1667
|
|
|
1714
1668
|
/**
|
|
1715
|
-
*
|
|
1716
|
-
*
|
|
1669
|
+
* Reset device from bootloader mode to firmware mode
|
|
1670
|
+
* Automatically selects the correct reset strategy based on USB connection type
|
|
1671
|
+
* @param clearForceDownloadFlag - If true, clears the force download boot flag (USB-OTG only)
|
|
1672
|
+
* @returns true if port will change (USB-OTG), false otherwise
|
|
1717
1673
|
*/
|
|
1718
|
-
|
|
1719
|
-
|
|
1674
|
+
public async resetToFirmwareMode(
|
|
1675
|
+
clearForceDownloadFlag = true,
|
|
1676
|
+
): Promise<boolean> {
|
|
1677
|
+
this.logger.debug("Resetting from bootloader to firmware mode...");
|
|
1720
1678
|
|
|
1721
|
-
|
|
1722
|
-
//
|
|
1723
|
-
await this.
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1679
|
+
try {
|
|
1680
|
+
// Detect USB connection type
|
|
1681
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
1682
|
+
|
|
1683
|
+
if (isUsbJtagOrOtg) {
|
|
1684
|
+
// USB-JTAG/OTG devices need special handling
|
|
1685
|
+
this.logger.debug("USB-JTAG/OTG detected - checking WDT reset support");
|
|
1686
|
+
|
|
1687
|
+
// Get detailed USB mode information
|
|
1688
|
+
let usbMode: {
|
|
1689
|
+
mode: "uart" | "usb-jtag-serial" | "usb-otg";
|
|
1690
|
+
uartNo: number;
|
|
1691
|
+
};
|
|
1692
|
+
try {
|
|
1693
|
+
usbMode = await this.getUsbMode();
|
|
1694
|
+
this.logger.debug(
|
|
1695
|
+
`USB mode: ${usbMode.mode} (uartNo=${usbMode.uartNo})`,
|
|
1696
|
+
);
|
|
1697
|
+
} catch (err) {
|
|
1698
|
+
this.logger.debug(`Could not get USB mode: ${err}`);
|
|
1699
|
+
// Fall back to generic USB-JTAG/OTG handling
|
|
1700
|
+
usbMode = { mode: "usb-jtag-serial", uartNo: 0 };
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
// Check if chip supports WDT reset
|
|
1704
|
+
// WDT reset is not needed for ESP32-C3
|
|
1705
|
+
// WDT reset is supported by: ESP32-S2, ESP32-S3, ESP32-P4
|
|
1706
|
+
// WDT reset is NOT supported by: ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2
|
|
1707
|
+
const supportsWdtReset =
|
|
1708
|
+
this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
1709
|
+
this.chipFamily === CHIP_FAMILY_ESP32S3 ||
|
|
1710
|
+
this.chipFamily === CHIP_FAMILY_ESP32P4;
|
|
1711
|
+
|
|
1712
|
+
if (!supportsWdtReset) {
|
|
1713
|
+
this.logger.debug(
|
|
1714
|
+
`${this.chipName} does not support WDT reset - using classic reset instead`,
|
|
1715
|
+
);
|
|
1716
|
+
|
|
1717
|
+
// Use classic reset for chips without WDT support
|
|
1718
|
+
await this.hardResetToFirmware();
|
|
1719
|
+
this.logger.debug("Classic reset to firmware complete");
|
|
1720
|
+
return false; // Port stays open
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// WDT reset is supported - proceed with WDT reset logic
|
|
1724
|
+
this.logger.debug(
|
|
1725
|
+
`${this.chipName} supports WDT reset - using WDT reset strategy`,
|
|
1726
|
+
);
|
|
1727
|
+
|
|
1728
|
+
// CRITICAL: WDT register writes require ROM (not stub) and baudrate 115200
|
|
1729
|
+
|
|
1730
|
+
// If on stub, need to return to ROM first
|
|
1731
|
+
if (this.IS_STUB) {
|
|
1732
|
+
this.logger.debug("On stub - returning to ROM before WDT reset");
|
|
1733
|
+
|
|
1734
|
+
// Change baudrate back to ROM baudrate if needed
|
|
1735
|
+
if (this.currentBaudRate !== ESP_ROM_BAUD) {
|
|
1736
|
+
this.logger.debug(
|
|
1737
|
+
`Changing baudrate from ${this.currentBaudRate} to ${ESP_ROM_BAUD}`,
|
|
1738
|
+
);
|
|
1739
|
+
await this.reconfigurePort(ESP_ROM_BAUD);
|
|
1740
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
1741
|
+
this.logger.debug("Baudrate changed to 115200");
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// CRITICAL: Temporarily clear console mode flag so hardReset(true) works
|
|
1745
|
+
const wasInConsoleMode = this._consoleMode;
|
|
1746
|
+
this._consoleMode = false;
|
|
1747
|
+
|
|
1748
|
+
// Reset to bootloader (ROM)
|
|
1749
|
+
await this.hardReset(true);
|
|
1750
|
+
await sleep(200);
|
|
1751
|
+
|
|
1752
|
+
// Restore console mode flag
|
|
1753
|
+
this._consoleMode = wasInConsoleMode;
|
|
1754
|
+
|
|
1755
|
+
// Sync with ROM
|
|
1756
|
+
await this.sync();
|
|
1757
|
+
this.IS_STUB = false;
|
|
1758
|
+
this.logger.debug("Now on ROM");
|
|
1759
|
+
} else {
|
|
1760
|
+
// Even if not on stub, ensure baudrate is 115200 for WDT register writes
|
|
1761
|
+
if (this.currentBaudRate !== ESP_ROM_BAUD) {
|
|
1762
|
+
this.logger.debug(
|
|
1763
|
+
`Not on stub, but baudrate is ${this.currentBaudRate} - changing to ${ESP_ROM_BAUD} for WDT reset`,
|
|
1764
|
+
);
|
|
1765
|
+
await this.reconfigurePort(ESP_ROM_BAUD);
|
|
1766
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
1767
|
+
this.logger.debug("Baudrate changed to 115200");
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// Clear force download boot flag if requested (USB-OTG only)
|
|
1772
|
+
if (clearForceDownloadFlag && usbMode.mode === "usb-otg") {
|
|
1773
|
+
const flagCleared = await this._clearForceDownloadBootIfNeeded();
|
|
1774
|
+
if (flagCleared) {
|
|
1775
|
+
this.logger.debug("Force download boot flag cleared");
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// Perform WDT reset to boot into firmware
|
|
1780
|
+
await this.rtcWdtResetChipSpecific();
|
|
1781
|
+
this.logger.debug("WDT reset performed - device will boot to firmware");
|
|
1782
|
+
|
|
1783
|
+
// Check if port will change after WDT reset
|
|
1784
|
+
// USB-OTG (ESP32-S2/P4): Port always changes
|
|
1785
|
+
// USB-JTAG/Serial (ESP32-S3/C3/C5/C6/C61/H2/P4): Port may change depending on platform
|
|
1786
|
+
const portWillChange =
|
|
1787
|
+
usbMode.mode === "usb-otg" || usbMode.mode === "usb-jtag-serial";
|
|
1788
|
+
|
|
1789
|
+
if (portWillChange) {
|
|
1790
|
+
this.logger.debug(
|
|
1791
|
+
`Port will change after WDT reset (${usbMode.mode}) - port reselection needed`,
|
|
1792
|
+
);
|
|
1793
|
+
return true;
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
return false;
|
|
1733
1797
|
} else {
|
|
1734
|
-
|
|
1735
|
-
this.logger.debug(
|
|
1798
|
+
// External serial chip - use classic reset to firmware
|
|
1799
|
+
this.logger.debug(
|
|
1800
|
+
"External serial chip detected - using classic reset",
|
|
1801
|
+
);
|
|
1802
|
+
|
|
1803
|
+
await this.hardResetToFirmware();
|
|
1804
|
+
this.logger.debug("Classic reset to firmware complete");
|
|
1805
|
+
return false;
|
|
1736
1806
|
}
|
|
1807
|
+
} catch (err) {
|
|
1808
|
+
this.logger.error(`Failed to reset to firmware mode: ${err}`);
|
|
1809
|
+
throw err;
|
|
1737
1810
|
}
|
|
1738
|
-
return false;
|
|
1739
1811
|
}
|
|
1740
1812
|
|
|
1741
1813
|
async hardReset(bootloader = false) {
|
|
@@ -1749,68 +1821,83 @@ export class ESPLoader extends EventTarget {
|
|
|
1749
1821
|
}
|
|
1750
1822
|
// Simple hardware reset to restart firmware (IO0=HIGH)
|
|
1751
1823
|
this.logger.debug("Performing hardware reset (console mode)...");
|
|
1752
|
-
|
|
1753
|
-
await this.hardResetToFirmwareWebUSB();
|
|
1754
|
-
} else {
|
|
1755
|
-
await this.hardResetToFirmware();
|
|
1756
|
-
}
|
|
1824
|
+
await this.resetInConsoleMode();
|
|
1757
1825
|
this.logger.debug("Hardware reset complete");
|
|
1758
1826
|
return;
|
|
1759
1827
|
}
|
|
1760
1828
|
|
|
1761
1829
|
if (bootloader) {
|
|
1762
|
-
//
|
|
1830
|
+
// Enter bootloader/flash mode
|
|
1763
1831
|
if (this.port.getInfo().usbProductId === USB_JTAG_SERIAL_PID) {
|
|
1764
1832
|
await this.hardResetUSBJTAGSerial();
|
|
1765
|
-
this.logger.debug("USB-JTAG/Serial reset.");
|
|
1833
|
+
this.logger.debug("USB-JTAG/Serial reset to bootloader.");
|
|
1766
1834
|
} else {
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
await this.hardResetClassicWebUSB();
|
|
1770
|
-
this.logger.debug("Classic reset (WebUSB/Android).");
|
|
1771
|
-
} else {
|
|
1772
|
-
await this.hardResetClassic();
|
|
1773
|
-
this.logger.debug("Classic reset.");
|
|
1774
|
-
}
|
|
1835
|
+
await this.hardResetClassic();
|
|
1836
|
+
this.logger.debug("Classic reset to bootloader.");
|
|
1775
1837
|
}
|
|
1776
1838
|
} else {
|
|
1777
|
-
//
|
|
1778
|
-
//
|
|
1779
|
-
this.logger.debug("
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
//
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1839
|
+
// Reset to firmware mode (exit bootloader)
|
|
1840
|
+
// Use intelligent reset strategy based on USB connection type
|
|
1841
|
+
this.logger.debug("Resetting to firmware mode...");
|
|
1842
|
+
|
|
1843
|
+
// Detect USB connection type to choose correct reset method
|
|
1844
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
1845
|
+
|
|
1846
|
+
if (isUsbJtagOrOtg) {
|
|
1847
|
+
// USB-JTAG/OTG devices: Use WDT reset
|
|
1848
|
+
this.logger.debug("USB-JTAG/OTG detected - using WDT reset");
|
|
1849
|
+
|
|
1850
|
+
// Get USB mode details
|
|
1851
|
+
let usbMode: {
|
|
1852
|
+
mode: "uart" | "usb-jtag-serial" | "usb-otg";
|
|
1853
|
+
uartNo: number;
|
|
1854
|
+
};
|
|
1855
|
+
try {
|
|
1856
|
+
usbMode = await this.getUsbMode();
|
|
1857
|
+
this.logger.debug(
|
|
1858
|
+
`USB mode: ${usbMode.mode} (uartNo=${usbMode.uartNo})`,
|
|
1859
|
+
);
|
|
1860
|
+
} catch (err) {
|
|
1861
|
+
this.logger.debug(`Could not get USB mode: ${err}`);
|
|
1862
|
+
usbMode = { mode: "usb-jtag-serial", uartNo: 0 };
|
|
1863
|
+
}
|
|
1799
1864
|
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1865
|
+
// Clear force download flag for USB-OTG devices
|
|
1866
|
+
if (usbMode.mode === "usb-otg") {
|
|
1867
|
+
try {
|
|
1868
|
+
const flagCleared = await this._clearForceDownloadBootIfNeeded();
|
|
1869
|
+
if (flagCleared) {
|
|
1870
|
+
this.logger.debug("Force download boot flag cleared");
|
|
1871
|
+
}
|
|
1872
|
+
} catch (err) {
|
|
1873
|
+
this.logger.debug(`Could not clear force download flag: ${err}`);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// Perform WDT reset
|
|
1878
|
+
await this.rtcWdtResetChipSpecific();
|
|
1879
|
+
this.logger.debug(`${this.chipName}: WDT reset to firmware complete`);
|
|
1880
|
+
return;
|
|
1808
1881
|
} else {
|
|
1809
|
-
//
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1882
|
+
// External serial chip: Use classic reset
|
|
1883
|
+
this.logger.debug(
|
|
1884
|
+
"External serial chip detected - using classic reset",
|
|
1885
|
+
);
|
|
1886
|
+
|
|
1887
|
+
if (this.isWebUSB()) {
|
|
1888
|
+
// WebUSB: Use longer delays for better compatibility
|
|
1889
|
+
await this.setRTSWebUSB(true); // EN->LOW
|
|
1890
|
+
await sleep(200);
|
|
1891
|
+
await this.setRTSWebUSB(false);
|
|
1892
|
+
await sleep(200);
|
|
1893
|
+
this.logger.debug("Hard reset to firmware (WebUSB).");
|
|
1894
|
+
} else {
|
|
1895
|
+
// Web Serial: Standard reset
|
|
1896
|
+
await this.setRTS(true); // EN->LOW
|
|
1897
|
+
await sleep(100);
|
|
1898
|
+
await this.setRTS(false);
|
|
1899
|
+
this.logger.debug("Hard reset to firmware.");
|
|
1900
|
+
}
|
|
1814
1901
|
}
|
|
1815
1902
|
}
|
|
1816
1903
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -3284,26 +3371,27 @@ export class ESPLoader extends EventTarget {
|
|
|
3284
3371
|
this._writer = undefined;
|
|
3285
3372
|
}
|
|
3286
3373
|
|
|
3287
|
-
// Cancel
|
|
3374
|
+
// Cancel reader - let readLoop's finally block handle releaseLock()
|
|
3288
3375
|
if (this._reader) {
|
|
3289
|
-
const reader = this._reader;
|
|
3290
3376
|
try {
|
|
3291
3377
|
// Suppress disconnect event during console mode switching
|
|
3292
3378
|
this._suppressDisconnect = true;
|
|
3293
|
-
|
|
3294
|
-
|
|
3379
|
+
|
|
3380
|
+
// Cancel will cause readLoop to exit and call releaseLock() in its finally block
|
|
3381
|
+
await this._reader.cancel();
|
|
3382
|
+
this.logger.debug("Reader cancelled - waiting for readLoop to finish");
|
|
3383
|
+
|
|
3384
|
+
// CRITICAL: Wait a bit for readLoop's finally block to complete
|
|
3385
|
+
// The finally block needs time to call releaseLock() and set _reader = undefined
|
|
3386
|
+
// This is much faster than waiting for browser to unlock (just waiting for JS execution)
|
|
3387
|
+
await sleep(50);
|
|
3388
|
+
|
|
3389
|
+
this.logger.debug("ReadLoop cleanup should be complete");
|
|
3295
3390
|
} catch (err) {
|
|
3296
3391
|
this.logger.debug(`Reader cancel error: ${err}`);
|
|
3297
|
-
} finally {
|
|
3298
|
-
try {
|
|
3299
|
-
reader.releaseLock();
|
|
3300
|
-
} catch (err) {
|
|
3301
|
-
this.logger.debug(`Reader release error: ${err}`);
|
|
3302
|
-
}
|
|
3303
|
-
}
|
|
3304
|
-
if (this._reader === reader) {
|
|
3305
|
-
this._reader = undefined;
|
|
3306
3392
|
}
|
|
3393
|
+
// Don't call releaseLock() or set _reader to undefined here
|
|
3394
|
+
// Let readLoop's finally block handle it to avoid race conditions
|
|
3307
3395
|
}
|
|
3308
3396
|
}
|
|
3309
3397
|
|
|
@@ -3352,6 +3440,155 @@ export class ESPLoader extends EventTarget {
|
|
|
3352
3440
|
return isUsbJtag;
|
|
3353
3441
|
}
|
|
3354
3442
|
|
|
3443
|
+
public async getUsbMode(): Promise<{
|
|
3444
|
+
mode: "uart" | "usb-jtag-serial" | "usb-otg";
|
|
3445
|
+
uartNo: number;
|
|
3446
|
+
}> {
|
|
3447
|
+
const family = this._parent ? this._parent.chipFamily : this.chipFamily;
|
|
3448
|
+
const revision = this._parent
|
|
3449
|
+
? (this._parent.chipRevision ?? 0)
|
|
3450
|
+
: (this.chipRevision ?? 0);
|
|
3451
|
+
|
|
3452
|
+
let bufNoAddr: number | null = null;
|
|
3453
|
+
let jtagSerialVal: number | null = null;
|
|
3454
|
+
let otgVal: number | null = null;
|
|
3455
|
+
|
|
3456
|
+
switch (family) {
|
|
3457
|
+
case CHIP_FAMILY_ESP32S2:
|
|
3458
|
+
bufNoAddr = ESP32S2_UARTDEV_BUF_NO;
|
|
3459
|
+
otgVal = ESP32S2_UARTDEV_BUF_NO_USB_OTG;
|
|
3460
|
+
break;
|
|
3461
|
+
case CHIP_FAMILY_ESP32S3:
|
|
3462
|
+
bufNoAddr = ESP32S3_UARTDEV_BUF_NO;
|
|
3463
|
+
jtagSerialVal = ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
3464
|
+
otgVal = ESP32S3_UARTDEV_BUF_NO_USB_OTG;
|
|
3465
|
+
break;
|
|
3466
|
+
case CHIP_FAMILY_ESP32C3: {
|
|
3467
|
+
const bssAddr = revision < 101 ? 0x3fcdf064 : 0x3fcdf060;
|
|
3468
|
+
bufNoAddr = bssAddr + ESP32C3_BUF_UART_NO_OFFSET;
|
|
3469
|
+
jtagSerialVal = ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
3470
|
+
break;
|
|
3471
|
+
}
|
|
3472
|
+
case CHIP_FAMILY_ESP32C5:
|
|
3473
|
+
bufNoAddr = ESP32C5_UARTDEV_BUF_NO;
|
|
3474
|
+
jtagSerialVal = ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
3475
|
+
break;
|
|
3476
|
+
case CHIP_FAMILY_ESP32C6:
|
|
3477
|
+
bufNoAddr = ESP32C6_UARTDEV_BUF_NO;
|
|
3478
|
+
jtagSerialVal = ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
3479
|
+
break;
|
|
3480
|
+
case CHIP_FAMILY_ESP32C61:
|
|
3481
|
+
bufNoAddr =
|
|
3482
|
+
revision <= 200
|
|
3483
|
+
? ESP32C61_UARTDEV_BUF_NO_REV_LE2
|
|
3484
|
+
: ESP32C61_UARTDEV_BUF_NO_REV_GT2;
|
|
3485
|
+
jtagSerialVal =
|
|
3486
|
+
revision <= 200
|
|
3487
|
+
? ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_LE2
|
|
3488
|
+
: ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_GT2;
|
|
3489
|
+
break;
|
|
3490
|
+
case CHIP_FAMILY_ESP32H2:
|
|
3491
|
+
bufNoAddr = ESP32H2_UARTDEV_BUF_NO;
|
|
3492
|
+
jtagSerialVal = ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
3493
|
+
break;
|
|
3494
|
+
case CHIP_FAMILY_ESP32H4:
|
|
3495
|
+
bufNoAddr = ESP32H4_UARTDEV_BUF_NO;
|
|
3496
|
+
jtagSerialVal = ESP32H4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
3497
|
+
break;
|
|
3498
|
+
case CHIP_FAMILY_ESP32P4:
|
|
3499
|
+
bufNoAddr =
|
|
3500
|
+
revision < 300
|
|
3501
|
+
? ESP32P4_UARTDEV_BUF_NO_REV0
|
|
3502
|
+
: ESP32P4_UARTDEV_BUF_NO_REV300;
|
|
3503
|
+
jtagSerialVal = ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
3504
|
+
otgVal = ESP32P4_UARTDEV_BUF_NO_USB_OTG;
|
|
3505
|
+
break;
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
if (bufNoAddr === null) {
|
|
3509
|
+
return { mode: "uart", uartNo: 0 };
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3512
|
+
const uartNo = (await this.readRegister(bufNoAddr)) & 0xff;
|
|
3513
|
+
|
|
3514
|
+
if (otgVal !== null && uartNo === otgVal) {
|
|
3515
|
+
this.logger.debug(`USB mode: USB-OTG (uartNo=${uartNo})`);
|
|
3516
|
+
return { mode: "usb-otg", uartNo };
|
|
3517
|
+
}
|
|
3518
|
+
if (jtagSerialVal !== null && uartNo === jtagSerialVal) {
|
|
3519
|
+
this.logger.debug(`USB mode: USB-JTAG/Serial (uartNo=${uartNo})`);
|
|
3520
|
+
return { mode: "usb-jtag-serial", uartNo };
|
|
3521
|
+
}
|
|
3522
|
+
|
|
3523
|
+
this.logger.debug(`USB mode: UART (uartNo=${uartNo})`);
|
|
3524
|
+
return { mode: "uart", uartNo };
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
/**
|
|
3528
|
+
* Check if the current chip supports USB-JTAG or USB-OTG
|
|
3529
|
+
* @returns true if chip has native USB support (JTAG or OTG)
|
|
3530
|
+
*/
|
|
3531
|
+
public supportsNativeUsb(): boolean {
|
|
3532
|
+
const family = this._parent ? this._parent.chipFamily : this.chipFamily;
|
|
3533
|
+
|
|
3534
|
+
// Chips with USB-JTAG/Serial or USB-OTG support
|
|
3535
|
+
const usbChips = [
|
|
3536
|
+
CHIP_FAMILY_ESP32S2, // USB-OTG
|
|
3537
|
+
CHIP_FAMILY_ESP32S3, // USB-OTG + USB-JTAG/Serial
|
|
3538
|
+
CHIP_FAMILY_ESP32C3, // USB-JTAG/Serial
|
|
3539
|
+
CHIP_FAMILY_ESP32C5, // USB-JTAG/Serial
|
|
3540
|
+
CHIP_FAMILY_ESP32C6, // USB-JTAG/Serial
|
|
3541
|
+
CHIP_FAMILY_ESP32C61, // USB-JTAG/Serial
|
|
3542
|
+
CHIP_FAMILY_ESP32H2, // USB-JTAG/Serial
|
|
3543
|
+
CHIP_FAMILY_ESP32H4, // USB-JTAG/Serial
|
|
3544
|
+
CHIP_FAMILY_ESP32P4, // USB-OTG + USB-JTAG/Serial
|
|
3545
|
+
];
|
|
3546
|
+
|
|
3547
|
+
return usbChips.includes(family);
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3550
|
+
/**
|
|
3551
|
+
* @name _ensureStreamsReady
|
|
3552
|
+
* After a hardware reset, ensure port streams are available.
|
|
3553
|
+
* On WebUSB, recreates streams since they break after reset.
|
|
3554
|
+
* On Web Serial, waits for streams to become available.
|
|
3555
|
+
*/
|
|
3556
|
+
private async _ensureStreamsReady(): Promise<void> {
|
|
3557
|
+
if (this.isWebUSB()) {
|
|
3558
|
+
try {
|
|
3559
|
+
await (this.port as any).recreateStreams();
|
|
3560
|
+
this.logger.debug("WebUSB streams recreated");
|
|
3561
|
+
|
|
3562
|
+
let retries = 30;
|
|
3563
|
+
while (retries > 0 && !this.port.readable) {
|
|
3564
|
+
await sleep(100);
|
|
3565
|
+
retries--;
|
|
3566
|
+
}
|
|
3567
|
+
if (!this.port.readable) {
|
|
3568
|
+
throw new Error(
|
|
3569
|
+
"Readable stream not available after recreating streams",
|
|
3570
|
+
);
|
|
3571
|
+
}
|
|
3572
|
+
this.logger.debug("WebUSB streams are ready");
|
|
3573
|
+
} catch (err) {
|
|
3574
|
+
this.logger.error(`Failed to recreate WebUSB streams: ${err}`);
|
|
3575
|
+
this._consoleMode = false;
|
|
3576
|
+
throw err;
|
|
3577
|
+
}
|
|
3578
|
+
} else {
|
|
3579
|
+
let retries = 20;
|
|
3580
|
+
while (retries > 0 && !this.port.readable) {
|
|
3581
|
+
await sleep(100);
|
|
3582
|
+
retries--;
|
|
3583
|
+
}
|
|
3584
|
+
if (!this.port.readable) {
|
|
3585
|
+
this._consoleMode = false;
|
|
3586
|
+
throw new Error("Readable stream not available after reset");
|
|
3587
|
+
}
|
|
3588
|
+
this.logger.debug("Port streams are ready");
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3355
3592
|
/**
|
|
3356
3593
|
* @name enterConsoleMode
|
|
3357
3594
|
* Prepare device for console mode by resetting to firmware
|
|
@@ -3384,8 +3621,6 @@ export class ESPLoader extends EventTarget {
|
|
|
3384
3621
|
`Cannot enter console mode: USB connection type unknown and detection failed: ${err}`,
|
|
3385
3622
|
);
|
|
3386
3623
|
}
|
|
3387
|
-
// Set console mode flag
|
|
3388
|
-
this._consoleMode = false;
|
|
3389
3624
|
|
|
3390
3625
|
this.logger.debug(
|
|
3391
3626
|
`USB detection failed, using cached value: ${this.isUsbJtagOrOtg}`,
|
|
@@ -3393,56 +3628,40 @@ export class ESPLoader extends EventTarget {
|
|
|
3393
3628
|
isUsbJtag = this.isUsbJtagOrOtg;
|
|
3394
3629
|
}
|
|
3395
3630
|
|
|
3396
|
-
//
|
|
3397
|
-
|
|
3631
|
+
// Set console mode flag BEFORE any operations
|
|
3632
|
+
this._consoleMode = true;
|
|
3633
|
+
|
|
3398
3634
|
if (isUsbJtag) {
|
|
3399
|
-
// USB-JTAG/OTG devices: Use
|
|
3635
|
+
// USB-JTAG/OTG devices: Use reset which may close port
|
|
3400
3636
|
const wasReset = await this._resetToFirmwareIfNeeded();
|
|
3401
|
-
|
|
3637
|
+
if (wasReset) {
|
|
3638
|
+
return true; // port closed, caller must reopen
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
// Port stayed open (e.g. C3/C5/C6/H2 classic reset)
|
|
3642
|
+
await this._ensureStreamsReady();
|
|
3643
|
+
return false;
|
|
3402
3644
|
} else {
|
|
3403
3645
|
// External serial chip devices: Release locks and do simple reset
|
|
3404
3646
|
try {
|
|
3405
3647
|
await this.releaseReaderWriter();
|
|
3406
|
-
await
|
|
3648
|
+
await sleep(100);
|
|
3407
3649
|
} catch (err) {
|
|
3408
3650
|
this.logger.debug(`Failed to release locks: ${err}`);
|
|
3409
3651
|
}
|
|
3410
3652
|
|
|
3411
|
-
// Hardware reset to firmware mode (IO0=HIGH)
|
|
3412
3653
|
try {
|
|
3413
|
-
await this.
|
|
3414
|
-
this.logger.
|
|
3654
|
+
await this.hardResetToFirmware();
|
|
3655
|
+
this.logger.debug("Device reset to firmware mode");
|
|
3415
3656
|
} catch (err) {
|
|
3416
3657
|
this.logger.debug(`Could not reset device: ${err}`);
|
|
3417
3658
|
}
|
|
3418
3659
|
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
try {
|
|
3422
|
-
// Use the public recreateStreams() method to safely recreate streams
|
|
3423
|
-
// without closing the port (important after hardware reset)
|
|
3424
|
-
await (this.port as any).recreateStreams();
|
|
3425
|
-
this.logger.debug("WebUSB streams recreated for console mode");
|
|
3426
|
-
} catch (err) {
|
|
3427
|
-
// Set console mode flag
|
|
3428
|
-
this._consoleMode = false;
|
|
3429
|
-
this.logger.debug(`Failed to recreate WebUSB streams: ${err}`);
|
|
3430
|
-
}
|
|
3431
|
-
}
|
|
3432
|
-
|
|
3433
|
-
// Set console mode flag
|
|
3434
|
-
this._consoleMode = true;
|
|
3435
|
-
|
|
3436
|
-
return false; // Port stays open
|
|
3660
|
+
await this._ensureStreamsReady();
|
|
3661
|
+
return false;
|
|
3437
3662
|
}
|
|
3438
3663
|
}
|
|
3439
3664
|
|
|
3440
|
-
/**
|
|
3441
|
-
* @name _resetToFirmwareIfNeeded
|
|
3442
|
-
* Reset device from bootloader to firmware when switching to console mode
|
|
3443
|
-
* Detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
3444
|
-
* @returns true if reconnect was performed, false otherwise
|
|
3445
|
-
*/
|
|
3446
3665
|
/**
|
|
3447
3666
|
* @name _clearForceDownloadBootIfNeeded
|
|
3448
3667
|
* Read and clear the force download boot flag if it is set
|
|
@@ -3503,7 +3722,15 @@ export class ESPLoader extends EventTarget {
|
|
|
3503
3722
|
}
|
|
3504
3723
|
}
|
|
3505
3724
|
|
|
3725
|
+
/**
|
|
3726
|
+
* @name _resetToFirmwareIfNeeded
|
|
3727
|
+
* Reset device from bootloader to firmware when switching to console mode
|
|
3728
|
+
* Detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
3729
|
+
* @returns true if reconnect was performed, false otherwise
|
|
3730
|
+
*/
|
|
3506
3731
|
private async _resetToFirmwareIfNeeded(): Promise<boolean> {
|
|
3732
|
+
// Detect if we need WDT reset (USB-JTAG/OTG) or classic reset
|
|
3733
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
3507
3734
|
try {
|
|
3508
3735
|
// Check if port is open - if not, assume device is already in firmware mode
|
|
3509
3736
|
if (!this.port.writable || !this.port.readable) {
|
|
@@ -3513,114 +3740,65 @@ export class ESPLoader extends EventTarget {
|
|
|
3513
3740
|
return false;
|
|
3514
3741
|
}
|
|
3515
3742
|
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
//
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
);
|
|
3531
|
-
try {
|
|
3532
|
-
await this.reconfigurePort(ESP_ROM_BAUD);
|
|
3533
|
-
this.currentBaudRate = ESP_ROM_BAUD;
|
|
3534
|
-
} catch (err) {
|
|
3535
|
-
this.logger.debug(`Baudrate change failed: ${err}`);
|
|
3536
|
-
// Continue anyway
|
|
3537
|
-
}
|
|
3538
|
-
}
|
|
3539
|
-
|
|
3540
|
-
this.logger.debug("Resetting to bootloader (ROM)...");
|
|
3541
|
-
|
|
3542
|
-
// Reset to bootloader - this will clear the stub from RAM
|
|
3543
|
-
try {
|
|
3544
|
-
await this.hardReset(true);
|
|
3545
|
-
|
|
3546
|
-
// Wait for reset to complete
|
|
3547
|
-
await sleep(200);
|
|
3548
|
-
|
|
3549
|
-
// Sync with ROM
|
|
3550
|
-
await this.sync();
|
|
3551
|
-
|
|
3552
|
-
this.logger.debug("Now on ROM after reset");
|
|
3553
|
-
|
|
3554
|
-
// Mark that we're no longer on stub
|
|
3555
|
-
this.IS_STUB = false;
|
|
3556
|
-
} catch (resetErr) {
|
|
3557
|
-
this.logger.debug(`Reset to ROM failed: ${resetErr}`);
|
|
3558
|
-
// If reset fails, we might already be in firmware mode
|
|
3559
|
-
// In this case, we don't need to do anything - just use normal reset
|
|
3560
|
-
this.logger.debug("Assuming device is already in firmware mode");
|
|
3561
|
-
|
|
3562
|
-
// Release reader/writer before returning
|
|
3563
|
-
await this.releaseReaderWriter();
|
|
3564
|
-
return false; // No port change needed
|
|
3565
|
-
}
|
|
3566
|
-
} else {
|
|
3567
|
-
this.logger.debug("Already on ROM - checking force download flag");
|
|
3568
|
-
}
|
|
3569
|
-
|
|
3570
|
-
// Now check if force download flag is set and clear it if needed
|
|
3571
|
-
const flagWasCleared = await this._clearForceDownloadBootIfNeeded();
|
|
3572
|
-
|
|
3573
|
-
if (flagWasCleared) {
|
|
3574
|
-
this.logger.debug(
|
|
3575
|
-
"Force download flag was cleared - device will boot to firmware after reset",
|
|
3576
|
-
);
|
|
3577
|
-
} else {
|
|
3578
|
-
this.logger.debug(
|
|
3579
|
-
"Force download flag already clear - device will boot to firmware after reset",
|
|
3580
|
-
);
|
|
3581
|
-
}
|
|
3582
|
-
|
|
3583
|
-
// Perform WDT reset BEFORE releasing reader/writer (needs communication)
|
|
3584
|
-
// After WDT reset, the device will reboot into firmware mode
|
|
3585
|
-
await this.hardReset(false);
|
|
3586
|
-
|
|
3587
|
-
// For USB-OTG devices (ESP32-S2, ESP32-P4), the port will change after WDT reset
|
|
3588
|
-
const portWillChange =
|
|
3589
|
-
(this.chipFamily === CHIP_FAMILY_ESP32S2 && isUsingUsbOtg) ||
|
|
3590
|
-
(this.chipFamily === CHIP_FAMILY_ESP32P4 && isUsingUsbOtg);
|
|
3743
|
+
if (isUsbJtagOrOtg) {
|
|
3744
|
+
// USB-JTAG/OTG: DON'T release reader/writer before WDT reset
|
|
3745
|
+
// The WDT reset needs active communication to send register write commands
|
|
3746
|
+
// The port will close automatically after the WDT reset anyway
|
|
3747
|
+
this.logger.debug(
|
|
3748
|
+
"USB-JTAG/OTG: Keeping reader/writer active for WDT reset",
|
|
3749
|
+
);
|
|
3750
|
+
} else {
|
|
3751
|
+
// External serial chip: Release reader/writer before classic reset
|
|
3752
|
+
await this.releaseReaderWriter();
|
|
3753
|
+
this.logger.debug(
|
|
3754
|
+
"External serial: Reader/writer released before reset",
|
|
3755
|
+
);
|
|
3756
|
+
}
|
|
3591
3757
|
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
await this.releaseReaderWriter();
|
|
3758
|
+
// Use the new resetToFirmwareMode method which handles all the logic
|
|
3759
|
+
const portWillChange = await this.resetToFirmwareMode(true);
|
|
3595
3760
|
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3761
|
+
if (portWillChange) {
|
|
3762
|
+
this.logger.debug(
|
|
3763
|
+
`${this.chipName}: Port will change after WDT reset - user must reselect port`,
|
|
3764
|
+
);
|
|
3599
3765
|
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3766
|
+
// Dispatch event to signal port change
|
|
3767
|
+
this.dispatchEvent(
|
|
3768
|
+
new CustomEvent("usb-otg-port-change", {
|
|
3769
|
+
detail: {
|
|
3770
|
+
chipName: this.chipName,
|
|
3771
|
+
message: `${this.chipName} USB port changed after reset. Please select the new port.`,
|
|
3772
|
+
reason: "wdt-reset-to-firmware",
|
|
3773
|
+
},
|
|
3774
|
+
}),
|
|
3775
|
+
);
|
|
3610
3776
|
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3777
|
+
return true;
|
|
3778
|
+
} else {
|
|
3779
|
+
// Port stays the same - release reader/writer now if not already done
|
|
3780
|
+
if (isUsbJtagOrOtg) {
|
|
3615
3781
|
await this.releaseReaderWriter();
|
|
3616
|
-
|
|
3782
|
+
this.logger.debug("Reader/writer released after reset");
|
|
3617
3783
|
}
|
|
3784
|
+
return false;
|
|
3618
3785
|
}
|
|
3619
3786
|
} catch (err) {
|
|
3620
|
-
this.logger.
|
|
3621
|
-
|
|
3787
|
+
this.logger.error(`Reset to firmware mode failed: ${err}`);
|
|
3788
|
+
|
|
3789
|
+
// For USB-JTAG/OTG, the port is likely dead after a failed reset
|
|
3790
|
+
// For external serial, the port is usually still fine
|
|
3791
|
+
if (isUsbJtagOrOtg) {
|
|
3792
|
+
this.logger.debug(
|
|
3793
|
+
"Forcing port reselection due to USB-JTAG/OTG reset failure",
|
|
3794
|
+
);
|
|
3795
|
+
return true;
|
|
3796
|
+
}
|
|
3797
|
+
this.logger.debug(
|
|
3798
|
+
"External serial reset failed, but port should still be usable",
|
|
3799
|
+
);
|
|
3800
|
+
return false;
|
|
3622
3801
|
}
|
|
3623
|
-
return false;
|
|
3624
3802
|
}
|
|
3625
3803
|
|
|
3626
3804
|
/**
|
|
@@ -3921,11 +4099,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3921
4099
|
// Perform hardware reset to bootloader (GPIO0=LOW)
|
|
3922
4100
|
// This will cause the port to change from CDC (firmware) to JTAG (bootloader)
|
|
3923
4101
|
try {
|
|
3924
|
-
|
|
3925
|
-
await this.hardResetClassicWebUSB();
|
|
3926
|
-
} else {
|
|
3927
|
-
await this.hardResetClassic();
|
|
3928
|
-
}
|
|
4102
|
+
await this.hardResetClassic();
|
|
3929
4103
|
this.logger.debug("Reset to bootloader initiated");
|
|
3930
4104
|
} catch (err) {
|
|
3931
4105
|
this.logger.debug(`Reset error: ${err}`);
|
|
@@ -3992,23 +4166,19 @@ export class ESPLoader extends EventTarget {
|
|
|
3992
4166
|
|
|
3993
4167
|
if (!this.isConsoleResetSupported()) {
|
|
3994
4168
|
this.logger.debug(
|
|
3995
|
-
"Console reset not supported for ESP32-S2 USB-JTAG/CDC",
|
|
4169
|
+
"Simple Console reset not supported for ESP32-S2 USB-JTAG/CDC - using exitConsoleMode to enter bootloader",
|
|
4170
|
+
);
|
|
4171
|
+
await this.exitConsoleMode();
|
|
4172
|
+
this.logger.debug(
|
|
4173
|
+
"S2 now in bootloader mode - caller must do syncAndWdtReset on new port, then reconnect console",
|
|
3996
4174
|
);
|
|
3997
|
-
return;
|
|
4175
|
+
return;
|
|
3998
4176
|
}
|
|
3999
4177
|
|
|
4000
4178
|
// For other devices: Use standard firmware reset
|
|
4001
|
-
const isWebUSB = (this.port as any).isWebUSB === true;
|
|
4002
|
-
|
|
4003
4179
|
try {
|
|
4004
4180
|
this.logger.debug("Resetting device in console mode");
|
|
4005
|
-
|
|
4006
|
-
if (isWebUSB) {
|
|
4007
|
-
await this.hardResetToFirmwareWebUSB();
|
|
4008
|
-
} else {
|
|
4009
|
-
await this.hardResetToFirmware();
|
|
4010
|
-
}
|
|
4011
|
-
|
|
4181
|
+
await this.hardResetToFirmware();
|
|
4012
4182
|
this.logger.debug("Device reset complete");
|
|
4013
4183
|
} catch (err) {
|
|
4014
4184
|
this.logger.error(`Reset failed: ${err}`);
|
|
@@ -4016,6 +4186,47 @@ export class ESPLoader extends EventTarget {
|
|
|
4016
4186
|
}
|
|
4017
4187
|
}
|
|
4018
4188
|
|
|
4189
|
+
/**
|
|
4190
|
+
* @name syncAndWdtReset
|
|
4191
|
+
* Open a new bootloader port, sync with ROM (no stub, no reset strategies), and fire WDT reset.
|
|
4192
|
+
* This is used for ESP32-S2 USB-OTG devices which require WDT reset to switch modes.
|
|
4193
|
+
* After WDT reset the port will re-enumerate again.
|
|
4194
|
+
* The user must select the new port after this method is called.
|
|
4195
|
+
* @param newPort - The bootloader port selected by the user
|
|
4196
|
+
*/
|
|
4197
|
+
async syncAndWdtReset(newPort: SerialPort): Promise<void> {
|
|
4198
|
+
if (this._parent) {
|
|
4199
|
+
await this._parent.syncAndWdtReset(newPort);
|
|
4200
|
+
return;
|
|
4201
|
+
}
|
|
4202
|
+
|
|
4203
|
+
this.port = newPort;
|
|
4204
|
+
this.connected = false;
|
|
4205
|
+
this.IS_STUB = false;
|
|
4206
|
+
this.__inputBuffer = [];
|
|
4207
|
+
this.__inputBufferReadIndex = 0;
|
|
4208
|
+
this.__totalBytesRead = 0;
|
|
4209
|
+
|
|
4210
|
+
this.logger.debug("Opening bootloader port at 115200...");
|
|
4211
|
+
await this.port.open({ baudRate: ESP_ROM_BAUD });
|
|
4212
|
+
this.connected = true;
|
|
4213
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
4214
|
+
|
|
4215
|
+
// Start read loop
|
|
4216
|
+
this.readLoop();
|
|
4217
|
+
await sleep(100);
|
|
4218
|
+
|
|
4219
|
+
// Sync with ROM only - no reset strategies, device is already in bootloader
|
|
4220
|
+
this.logger.debug("Syncing with bootloader ROM...");
|
|
4221
|
+
await this.sync();
|
|
4222
|
+
this.logger.debug("Bootloader sync OK, no stub");
|
|
4223
|
+
|
|
4224
|
+
// Fire WDT reset → device boots into firmware
|
|
4225
|
+
this.logger.debug("Firing WDT reset...");
|
|
4226
|
+
await this.rtcWdtResetChipSpecific();
|
|
4227
|
+
this.logger.debug("WDT reset fired - device will boot to firmware");
|
|
4228
|
+
}
|
|
4229
|
+
|
|
4019
4230
|
/**
|
|
4020
4231
|
* @name drainInputBuffer
|
|
4021
4232
|
* Actively drain the input buffer by reading data for a specified time.
|