esp32tool 1.3.2 → 1.3.4
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/css/style.css +47 -35
- package/dist/const.js +1 -1
- package/dist/esp_loader.d.ts +15 -29
- package/dist/esp_loader.js +289 -314
- 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 +12 -2
- package/js/modules/esptool.js +1 -1
- package/js/script.js +112 -160
- package/js/util/console-color.js +2 -1
- package/js/webusb-serial.js +42 -7
- package/package.json +3 -3
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/const.ts +1 -1
- package/src/esp_loader.ts +321 -393
- package/sw.js +1 -1
package/dist/esp_loader.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="@types/w3c-web-serial" />
|
|
2
|
-
import { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, 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, CHIP_FAMILY_ESP8266, MAX_TIMEOUT, DEFAULT_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, ESP_CHANGE_BAUDRATE, ESP_CHECKSUM_MAGIC, ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_DATA, ESP_MEM_END, ESP_READ_REG, ESP_WRITE_REG, ESP_SPI_ATTACH, ESP_SYNC, ESP_GET_SECURITY_INFO, FLASH_SECTOR_SIZE, FLASH_WRITE_SIZE, STUB_FLASH_WRITE_SIZE, MEM_END_ROM_TIMEOUT, ROM_INVALID_RECV_MSG, SYNC_PACKET, SYNC_TIMEOUT, USB_RAM_BLOCK, ESP_ERASE_FLASH, ESP_ERASE_REGION, ESP_READ_FLASH, CHIP_ERASE_TIMEOUT, FLASH_READ_TIMEOUT, timeoutPerMb, ESP_ROM_BAUD, USB_JTAG_SERIAL_PID, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, getSpiFlashAddresses, DETECTED_FLASH_SIZES, CHIP_DETECT_MAGIC_REG_ADDR, CHIP_DETECT_MAGIC_VALUES, CHIP_ID_TO_INFO, ESP32P4_EFUSE_BLOCK1_ADDR, SlipReadError, ESP32S2_RTC_CNTL_WDTWPROTECT_REG, ESP32S2_RTC_CNTL_WDTCONFIG0_REG, ESP32S2_RTC_CNTL_WDTCONFIG1_REG, ESP32S2_RTC_CNTL_WDT_WKEY,
|
|
2
|
+
import { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, 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, CHIP_FAMILY_ESP8266, MAX_TIMEOUT, DEFAULT_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, ESP_CHANGE_BAUDRATE, ESP_CHECKSUM_MAGIC, ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_DATA, ESP_MEM_END, ESP_READ_REG, ESP_WRITE_REG, ESP_SPI_ATTACH, ESP_SYNC, ESP_GET_SECURITY_INFO, FLASH_SECTOR_SIZE, FLASH_WRITE_SIZE, STUB_FLASH_WRITE_SIZE, MEM_END_ROM_TIMEOUT, ROM_INVALID_RECV_MSG, SYNC_PACKET, SYNC_TIMEOUT, USB_RAM_BLOCK, ESP_ERASE_FLASH, ESP_ERASE_REGION, ESP_READ_FLASH, CHIP_ERASE_TIMEOUT, FLASH_READ_TIMEOUT, timeoutPerMb, ESP_ROM_BAUD, USB_JTAG_SERIAL_PID, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, getSpiFlashAddresses, DETECTED_FLASH_SIZES, CHIP_DETECT_MAGIC_REG_ADDR, CHIP_DETECT_MAGIC_VALUES, CHIP_ID_TO_INFO, ESP32P4_EFUSE_BLOCK1_ADDR, SlipReadError, ESP32S2_RTC_CNTL_WDTWPROTECT_REG, ESP32S2_RTC_CNTL_WDTCONFIG0_REG, ESP32S2_RTC_CNTL_WDTCONFIG1_REG, ESP32S2_RTC_CNTL_WDT_WKEY, ESP32S2_RTC_CNTL_OPTION1_REG, ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32S3_RTC_CNTL_WDTWPROTECT_REG, ESP32S3_RTC_CNTL_WDTCONFIG0_REG, ESP32S3_RTC_CNTL_WDTCONFIG1_REG, ESP32S3_RTC_CNTL_WDT_WKEY, ESP32S3_RTC_CNTL_OPTION1_REG, ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32C3_EFUSE_RD_MAC_SPI_SYS_3_REG, ESP32C3_EFUSE_RD_MAC_SPI_SYS_5_REG, ESP32C3_RTC_CNTL_WDTWPROTECT_REG, ESP32C3_RTC_CNTL_WDTCONFIG0_REG, ESP32C3_RTC_CNTL_WDTCONFIG1_REG, ESP32C3_RTC_CNTL_WDT_WKEY, ESP32C5_C6_RTC_CNTL_WDTWPROTECT_REG, ESP32C5_C6_RTC_CNTL_WDTCONFIG0_REG, ESP32C5_C6_RTC_CNTL_WDTCONFIG1_REG, ESP32C5_C6_RTC_CNTL_WDT_WKEY, ESP32P4_RTC_CNTL_WDTWPROTECT_REG, ESP32P4_RTC_CNTL_WDTCONFIG0_REG, ESP32P4_RTC_CNTL_WDTCONFIG1_REG, ESP32P4_RTC_CNTL_WDT_WKEY, ESP32P4_RTC_CNTL_OPTION1_REG, ESP32P4_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, } from "./const";
|
|
3
3
|
import { getStubCode } from "./stubs";
|
|
4
4
|
import { hexFormatter, sleep, slipEncode, toHex } from "./util";
|
|
5
5
|
import { deflate } from "pako";
|
|
@@ -27,6 +27,10 @@ export class ESPLoader extends EventTarget {
|
|
|
27
27
|
this.connected = true;
|
|
28
28
|
this.flashSize = null;
|
|
29
29
|
this.currentBaudRate = ESP_ROM_BAUD;
|
|
30
|
+
this.SLIP_END = 0xc0;
|
|
31
|
+
this.SLIP_ESC = 0xdb;
|
|
32
|
+
this.SLIP_ESC_END = 0xdc;
|
|
33
|
+
this.SLIP_ESC_ESC = 0xdd;
|
|
30
34
|
this._isESP32S2NativeUSB = false;
|
|
31
35
|
this._initializationSucceeded = false;
|
|
32
36
|
this.__commandLock = Promise.resolve([0, []]);
|
|
@@ -297,10 +301,8 @@ export class ESPLoader extends EventTarget {
|
|
|
297
301
|
0x303a: {
|
|
298
302
|
// Espressif (native USB)
|
|
299
303
|
0x2: { name: "ESP32-S2 Native USB", maxBaudrate: 2000000 },
|
|
304
|
+
0x12: { name: "ESP32-P4 Native USB", maxBaudrate: 2000000 },
|
|
300
305
|
0x1001: { name: "ESP32 Native USB", maxBaudrate: 2000000 },
|
|
301
|
-
0x1002: { name: "ESP32 Native USB", maxBaudrate: 2000000 },
|
|
302
|
-
0x4002: { name: "ESP32 Native USB", maxBaudrate: 2000000 },
|
|
303
|
-
0x1000: { name: "ESP32 Native USB", maxBaudrate: 2000000 },
|
|
304
306
|
},
|
|
305
307
|
};
|
|
306
308
|
const vendor = chips[vendorId];
|
|
@@ -1170,102 +1172,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1170
1172
|
async watchdogReset() {
|
|
1171
1173
|
await this.rtcWdtResetChipSpecific();
|
|
1172
1174
|
}
|
|
1173
|
-
/**
|
|
1174
|
-
* Check if current chip is using USB-OTG
|
|
1175
|
-
* Supports ESP32-S2 and ESP32-S3
|
|
1176
|
-
*/
|
|
1177
|
-
async usingUsbOtg() {
|
|
1178
|
-
let uartDevBufNo;
|
|
1179
|
-
let usbOtgValue;
|
|
1180
|
-
if (this.chipFamily === CHIP_FAMILY_ESP32S2) {
|
|
1181
|
-
uartDevBufNo = ESP32S2_UARTDEV_BUF_NO;
|
|
1182
|
-
usbOtgValue = ESP32S2_UARTDEV_BUF_NO_USB_OTG;
|
|
1183
|
-
}
|
|
1184
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
1185
|
-
uartDevBufNo = ESP32S3_UARTDEV_BUF_NO;
|
|
1186
|
-
usbOtgValue = ESP32S3_UARTDEV_BUF_NO_USB_OTG;
|
|
1187
|
-
}
|
|
1188
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1189
|
-
// P4: UARTDEV_BUF_NO depends on chip revision
|
|
1190
|
-
if (this.chipRevision === null) {
|
|
1191
|
-
this.chipRevision = await this.getChipRevision();
|
|
1192
|
-
}
|
|
1193
|
-
if (this.chipRevision < 300) {
|
|
1194
|
-
uartDevBufNo = ESP32P4_UARTDEV_BUF_NO_REV0;
|
|
1195
|
-
}
|
|
1196
|
-
else {
|
|
1197
|
-
uartDevBufNo = ESP32P4_UARTDEV_BUF_NO_REV300;
|
|
1198
|
-
}
|
|
1199
|
-
usbOtgValue = ESP32P4_UARTDEV_BUF_NO_USB_OTG;
|
|
1200
|
-
}
|
|
1201
|
-
else {
|
|
1202
|
-
return false;
|
|
1203
|
-
}
|
|
1204
|
-
const uartNo = (await this.readRegister(uartDevBufNo)) & 0xff;
|
|
1205
|
-
return uartNo === usbOtgValue;
|
|
1206
|
-
}
|
|
1207
|
-
/**
|
|
1208
|
-
* Check if current chip is using USB-JTAG/Serial
|
|
1209
|
-
* Supports ESP32-S3 and ESP32-C3
|
|
1210
|
-
*/
|
|
1211
|
-
async usingUsbJtagSerial() {
|
|
1212
|
-
let uartDevBufNo;
|
|
1213
|
-
let usbJtagSerialValue;
|
|
1214
|
-
if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
1215
|
-
uartDevBufNo = ESP32S3_UARTDEV_BUF_NO;
|
|
1216
|
-
usbJtagSerialValue = ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1217
|
-
}
|
|
1218
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
1219
|
-
// ESP32-C3: BSS_UART_DEV_ADDR depends on chip revision
|
|
1220
|
-
// Revision < 101: 0x3FCDF064
|
|
1221
|
-
// Revision >= 101: 0x3FCDF060
|
|
1222
|
-
let bssUartDevAddr;
|
|
1223
|
-
// Get chip revision if not already set
|
|
1224
|
-
if (this.chipRevision === null) {
|
|
1225
|
-
this.chipRevision = await this.getChipRevisionC3();
|
|
1226
|
-
}
|
|
1227
|
-
if (this.chipRevision < 101) {
|
|
1228
|
-
bssUartDevAddr = 0x3fcdf064;
|
|
1229
|
-
}
|
|
1230
|
-
else {
|
|
1231
|
-
bssUartDevAddr = 0x3fcdf060;
|
|
1232
|
-
}
|
|
1233
|
-
uartDevBufNo = bssUartDevAddr + ESP32C3_BUF_UART_NO_OFFSET;
|
|
1234
|
-
usbJtagSerialValue = ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1235
|
-
}
|
|
1236
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32C5) {
|
|
1237
|
-
uartDevBufNo = ESP32C5_UARTDEV_BUF_NO;
|
|
1238
|
-
usbJtagSerialValue = ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1239
|
-
}
|
|
1240
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
1241
|
-
uartDevBufNo = ESP32C6_UARTDEV_BUF_NO;
|
|
1242
|
-
usbJtagSerialValue = ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1243
|
-
}
|
|
1244
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1245
|
-
// P4: UARTDEV_BUF_NO depends on chip revision
|
|
1246
|
-
// Revision < 300: 0x4FF3FEC8
|
|
1247
|
-
// Revision >= 300: 0x4FFBFEC8
|
|
1248
|
-
if (this.chipRevision === null) {
|
|
1249
|
-
this.chipRevision = await this.getChipRevision();
|
|
1250
|
-
}
|
|
1251
|
-
if (this.chipRevision < 300) {
|
|
1252
|
-
uartDevBufNo = ESP32P4_UARTDEV_BUF_NO_REV0;
|
|
1253
|
-
}
|
|
1254
|
-
else {
|
|
1255
|
-
uartDevBufNo = ESP32P4_UARTDEV_BUF_NO_REV300;
|
|
1256
|
-
}
|
|
1257
|
-
usbJtagSerialValue = ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1258
|
-
}
|
|
1259
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32H2) {
|
|
1260
|
-
uartDevBufNo = ESP32H2_UARTDEV_BUF_NO;
|
|
1261
|
-
usbJtagSerialValue = ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1262
|
-
}
|
|
1263
|
-
else {
|
|
1264
|
-
return false;
|
|
1265
|
-
}
|
|
1266
|
-
const uartNo = (await this.readRegister(uartDevBufNo)) & 0xff;
|
|
1267
|
-
return uartNo === usbJtagSerialValue;
|
|
1268
|
-
}
|
|
1269
1175
|
/**
|
|
1270
1176
|
* Get chip revision for ESP32-C3
|
|
1271
1177
|
* Reads from EFUSE registers and calculates revision
|
|
@@ -1333,35 +1239,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1333
1239
|
}
|
|
1334
1240
|
// Unlock watchdog registers
|
|
1335
1241
|
await this.writeRegister(WDTWPROTECT_REG, WDT_WKEY, undefined, 0);
|
|
1336
|
-
// Clear force download boot register (if applicable) BEFORE triggering WDT reset
|
|
1337
|
-
// This ensures the chip boots into firmware mode after reset
|
|
1338
|
-
if (this.chipFamily === CHIP_FAMILY_ESP32S2) {
|
|
1339
|
-
try {
|
|
1340
|
-
await this.writeRegister(ESP32S2_RTC_CNTL_OPTION1_REG, 0, ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, 0);
|
|
1341
|
-
this.logger.debug("Cleared force download boot mask");
|
|
1342
|
-
}
|
|
1343
|
-
catch (err) {
|
|
1344
|
-
this.logger.debug(`Expected error clearing force download boot mask: ${err}`);
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
1348
|
-
try {
|
|
1349
|
-
await this.writeRegister(ESP32S3_RTC_CNTL_OPTION1_REG, 0, ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, 0);
|
|
1350
|
-
this.logger.debug("Cleared force download boot mask");
|
|
1351
|
-
}
|
|
1352
|
-
catch (err) {
|
|
1353
|
-
this.logger.debug(`Expected error clearing force download boot mask: ${err}`);
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1357
|
-
try {
|
|
1358
|
-
await this.writeRegister(ESP32P4_RTC_CNTL_OPTION1_REG, 0, ESP32P4_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, 0);
|
|
1359
|
-
this.logger.debug("Cleared force download boot mask");
|
|
1360
|
-
}
|
|
1361
|
-
catch (err) {
|
|
1362
|
-
this.logger.debug(`Expected error clearing force download boot mask: ${err}`);
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
1242
|
// Set WDT timeout to 2000ms (matches Python esptool)
|
|
1366
1243
|
await this.writeRegister(WDTCONFIG1_REG, 2000, undefined, 0);
|
|
1367
1244
|
// Enable WDT: bit 31 = enable, bits 28-30 = stage, bit 8 = sys reset, bits 0-2 = prescaler
|
|
@@ -1373,72 +1250,29 @@ export class ESPLoader extends EventTarget {
|
|
|
1373
1250
|
await this.sleep(500);
|
|
1374
1251
|
}
|
|
1375
1252
|
/**
|
|
1376
|
-
* Helper:
|
|
1253
|
+
* Helper: USB-based WDT reset
|
|
1377
1254
|
* Returns true if WDT reset was performed, false otherwise
|
|
1378
1255
|
*/
|
|
1379
|
-
async tryUsbWdtReset(chipName
|
|
1380
|
-
const isUsingUsbOtg = await this.
|
|
1381
|
-
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
1382
|
-
if (isUsingUsbOtg || isUsingUsbJtagSerial) {
|
|
1383
|
-
const strapReg = await this.readRegister(GPIO_STRAP_REG);
|
|
1384
|
-
const forceDlReg = await this.readRegister(RTC_CNTL_OPTION1_REG);
|
|
1385
|
-
// Only use watchdog reset if GPIO0 is low AND force download boot mode is not set
|
|
1386
|
-
if ((strapReg & GPIO_STRAP_SPI_BOOT_MASK) === 0 &&
|
|
1387
|
-
(forceDlReg & RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK) === 0) {
|
|
1388
|
-
await this.rtcWdtResetChipSpecific();
|
|
1389
|
-
this.logger.debug(`${chipName}: RTC WDT reset (USB detected, GPIO0 low)`);
|
|
1390
|
-
return true;
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
return false;
|
|
1394
|
-
}
|
|
1395
|
-
/**
|
|
1396
|
-
* Chip-specific hard reset for ESP32-S2
|
|
1397
|
-
* Checks if using USB-JTAG/Serial and uses watchdog reset if necessary
|
|
1398
|
-
*/
|
|
1399
|
-
async hardResetS2() {
|
|
1400
|
-
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
1256
|
+
async tryUsbWdtReset(chipName) {
|
|
1257
|
+
const isUsingUsbOtg = await this.detectUsbConnectionType();
|
|
1401
1258
|
if (isUsingUsbOtg) {
|
|
1259
|
+
// Use WDT reset for USB-OTG devices
|
|
1402
1260
|
await this.rtcWdtResetChipSpecific();
|
|
1403
|
-
this.logger.debug(
|
|
1404
|
-
|
|
1405
|
-
else {
|
|
1406
|
-
// Use standard hardware reset
|
|
1407
|
-
await this.hardResetClassic();
|
|
1408
|
-
this.logger.debug("ESP32-S2: Classic reset");
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
/**
|
|
1412
|
-
* Chip-specific hard reset for ESP32-S3
|
|
1413
|
-
* Checks if using USB-JTAG/Serial and uses watchdog reset if necessary
|
|
1414
|
-
*/
|
|
1415
|
-
async hardResetS3() {
|
|
1416
|
-
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
1417
|
-
if (isUsingUsbJtagSerial) {
|
|
1418
|
-
await this.rtcWdtResetChipSpecific();
|
|
1419
|
-
this.logger.debug("ESP32-S3: RTC WDT reset (USB-JTAG/Serial detected)");
|
|
1420
|
-
}
|
|
1421
|
-
else {
|
|
1422
|
-
// Use standard hardware reset
|
|
1423
|
-
await this.hardResetClassic();
|
|
1424
|
-
this.logger.debug("ESP32-S3: Classic reset");
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
/**
|
|
1428
|
-
* Chip-specific hard reset for ESP32-C3
|
|
1429
|
-
* Checks if using USB-JTAG/Serial and uses watchdog reset if necessary
|
|
1430
|
-
*/
|
|
1431
|
-
async hardResetC3() {
|
|
1432
|
-
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
1433
|
-
if (isUsingUsbJtagSerial) {
|
|
1434
|
-
await this.rtcWdtResetChipSpecific();
|
|
1435
|
-
this.logger.debug("ESP32-C3: RTC WDT reset (USB-JTAG/Serial detected)");
|
|
1261
|
+
this.logger.debug(`${chipName}: RTC WDT reset (USB-JTAG/Serial or USB-OTG detected)`);
|
|
1262
|
+
return true;
|
|
1436
1263
|
}
|
|
1437
1264
|
else {
|
|
1438
|
-
// Use
|
|
1439
|
-
|
|
1440
|
-
|
|
1265
|
+
// Use classic reset for non-USB devices
|
|
1266
|
+
if (this.isWebUSB()) {
|
|
1267
|
+
await this.hardResetClassicWebUSB();
|
|
1268
|
+
this.logger.debug("Classic reset (WebUSB/Android).");
|
|
1269
|
+
}
|
|
1270
|
+
else {
|
|
1271
|
+
await this.hardResetClassic();
|
|
1272
|
+
this.logger.debug("Classic reset.");
|
|
1273
|
+
}
|
|
1441
1274
|
}
|
|
1275
|
+
return false;
|
|
1442
1276
|
}
|
|
1443
1277
|
async hardReset(bootloader = false) {
|
|
1444
1278
|
// In console mode, only allow simple hardware reset (no bootloader entry)
|
|
@@ -1478,15 +1312,35 @@ export class ESPLoader extends EventTarget {
|
|
|
1478
1312
|
}
|
|
1479
1313
|
else {
|
|
1480
1314
|
// just reset (no bootloader mode)
|
|
1481
|
-
// For ESP32-S2/S3 with USB-OTG or USB-JTAG/Serial, check if watchdog reset is needed
|
|
1482
|
-
|
|
1483
|
-
|
|
1315
|
+
// For ESP32-S2/S3/P4 with USB-OTG or USB-JTAG/Serial, check if watchdog reset is needed
|
|
1316
|
+
this.logger.debug("*** Performing WDT reset strategy ***");
|
|
1317
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S2) {
|
|
1318
|
+
const wdtResetUsed = await this.tryUsbWdtReset("ESP32-S2");
|
|
1319
|
+
if (wdtResetUsed)
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
1323
|
+
const wdtResetUsed = await this.tryUsbWdtReset("ESP32-S3");
|
|
1484
1324
|
if (wdtResetUsed)
|
|
1485
1325
|
return;
|
|
1486
1326
|
}
|
|
1487
|
-
else if (this.chipFamily ===
|
|
1488
|
-
|
|
1489
|
-
|
|
1327
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1328
|
+
const wdtResetUsed = await this.tryUsbWdtReset("ESP32-P4");
|
|
1329
|
+
if (wdtResetUsed)
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
1333
|
+
const wdtResetUsed = await this.tryUsbWdtReset("ESP32-C3");
|
|
1334
|
+
if (wdtResetUsed)
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C5) {
|
|
1338
|
+
const wdtResetUsed = await this.tryUsbWdtReset("ESP32-C5");
|
|
1339
|
+
if (wdtResetUsed)
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
1343
|
+
const wdtResetUsed = await this.tryUsbWdtReset("ESP32-C6");
|
|
1490
1344
|
if (wdtResetUsed)
|
|
1491
1345
|
return;
|
|
1492
1346
|
}
|
|
@@ -1720,44 +1574,44 @@ export class ESPLoader extends EventTarget {
|
|
|
1720
1574
|
const waitingFor = partialPacket === null ? "header" : "content";
|
|
1721
1575
|
throw new SlipReadError("Timed out waiting for packet " + waitingFor);
|
|
1722
1576
|
}
|
|
1723
|
-
const
|
|
1577
|
+
const byte = this._readByte();
|
|
1724
1578
|
if (partialPacket === null) {
|
|
1725
1579
|
// waiting for packet header
|
|
1726
|
-
if (
|
|
1580
|
+
if (byte == this.SLIP_END) {
|
|
1727
1581
|
partialPacket = [];
|
|
1728
1582
|
}
|
|
1729
1583
|
else {
|
|
1730
1584
|
if (this.debug) {
|
|
1731
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
1585
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
1732
1586
|
this.logger.debug("Remaining data in serial buffer: " +
|
|
1733
1587
|
hexFormatter(this._inputBuffer));
|
|
1734
1588
|
}
|
|
1735
|
-
throw new SlipReadError("Invalid head of packet (" + toHex(
|
|
1589
|
+
throw new SlipReadError("Invalid head of packet (" + toHex(byte) + ")");
|
|
1736
1590
|
}
|
|
1737
1591
|
}
|
|
1738
1592
|
else if (inEscape) {
|
|
1739
1593
|
// part-way through escape sequence
|
|
1740
1594
|
inEscape = false;
|
|
1741
|
-
if (
|
|
1742
|
-
partialPacket.push(
|
|
1595
|
+
if (byte == this.SLIP_ESC_END) {
|
|
1596
|
+
partialPacket.push(this.SLIP_END);
|
|
1743
1597
|
}
|
|
1744
|
-
else if (
|
|
1745
|
-
partialPacket.push(
|
|
1598
|
+
else if (byte == this.SLIP_ESC_ESC) {
|
|
1599
|
+
partialPacket.push(this.SLIP_ESC);
|
|
1746
1600
|
}
|
|
1747
1601
|
else {
|
|
1748
1602
|
if (this.debug) {
|
|
1749
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
1603
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
1750
1604
|
this.logger.debug("Remaining data in serial buffer: " +
|
|
1751
1605
|
hexFormatter(this._inputBuffer));
|
|
1752
1606
|
}
|
|
1753
|
-
throw new SlipReadError("Invalid SLIP escape (0xdb, " + toHex(
|
|
1607
|
+
throw new SlipReadError("Invalid SLIP escape (0xdb, " + toHex(byte) + ")");
|
|
1754
1608
|
}
|
|
1755
1609
|
}
|
|
1756
|
-
else if (
|
|
1610
|
+
else if (byte == this.SLIP_ESC) {
|
|
1757
1611
|
// start of escape sequence
|
|
1758
1612
|
inEscape = true;
|
|
1759
1613
|
}
|
|
1760
|
-
else if (
|
|
1614
|
+
else if (byte == this.SLIP_END) {
|
|
1761
1615
|
// end of packet
|
|
1762
1616
|
if (this.debug)
|
|
1763
1617
|
this.logger.debug("Received full packet: " + hexFormatter(partialPacket));
|
|
@@ -1767,7 +1621,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1767
1621
|
}
|
|
1768
1622
|
else {
|
|
1769
1623
|
// normal byte in packet
|
|
1770
|
-
partialPacket.push(
|
|
1624
|
+
partialPacket.push(byte);
|
|
1771
1625
|
}
|
|
1772
1626
|
}
|
|
1773
1627
|
}
|
|
@@ -1798,44 +1652,44 @@ export class ESPLoader extends EventTarget {
|
|
|
1798
1652
|
}
|
|
1799
1653
|
if (this.debug)
|
|
1800
1654
|
this.logger.debug("Read " + readBytes.length + " bytes: " + hexFormatter(readBytes));
|
|
1801
|
-
for (const
|
|
1655
|
+
for (const byte of readBytes) {
|
|
1802
1656
|
if (partialPacket === null) {
|
|
1803
1657
|
// waiting for packet header
|
|
1804
|
-
if (
|
|
1658
|
+
if (byte == this.SLIP_END) {
|
|
1805
1659
|
partialPacket = [];
|
|
1806
1660
|
}
|
|
1807
1661
|
else {
|
|
1808
1662
|
if (this.debug) {
|
|
1809
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
1663
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
1810
1664
|
this.logger.debug("Remaining data in serial buffer: " +
|
|
1811
1665
|
hexFormatter(this._inputBuffer));
|
|
1812
1666
|
}
|
|
1813
|
-
throw new SlipReadError("Invalid head of packet (" + toHex(
|
|
1667
|
+
throw new SlipReadError("Invalid head of packet (" + toHex(byte) + ")");
|
|
1814
1668
|
}
|
|
1815
1669
|
}
|
|
1816
1670
|
else if (inEscape) {
|
|
1817
1671
|
// part-way through escape sequence
|
|
1818
1672
|
inEscape = false;
|
|
1819
|
-
if (
|
|
1820
|
-
partialPacket.push(
|
|
1673
|
+
if (byte == this.SLIP_ESC_END) {
|
|
1674
|
+
partialPacket.push(this.SLIP_END);
|
|
1821
1675
|
}
|
|
1822
|
-
else if (
|
|
1823
|
-
partialPacket.push(
|
|
1676
|
+
else if (byte == this.SLIP_ESC_ESC) {
|
|
1677
|
+
partialPacket.push(this.SLIP_ESC);
|
|
1824
1678
|
}
|
|
1825
1679
|
else {
|
|
1826
1680
|
if (this.debug) {
|
|
1827
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
1681
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
1828
1682
|
this.logger.debug("Remaining data in serial buffer: " +
|
|
1829
1683
|
hexFormatter(this._inputBuffer));
|
|
1830
1684
|
}
|
|
1831
|
-
throw new SlipReadError("Invalid SLIP escape (0xdb, " + toHex(
|
|
1685
|
+
throw new SlipReadError("Invalid SLIP escape (0xdb, " + toHex(byte) + ")");
|
|
1832
1686
|
}
|
|
1833
1687
|
}
|
|
1834
|
-
else if (
|
|
1688
|
+
else if (byte == this.SLIP_ESC) {
|
|
1835
1689
|
// start of escape sequence
|
|
1836
1690
|
inEscape = true;
|
|
1837
1691
|
}
|
|
1838
|
-
else if (
|
|
1692
|
+
else if (byte == this.SLIP_END) {
|
|
1839
1693
|
// end of packet
|
|
1840
1694
|
if (this.debug)
|
|
1841
1695
|
this.logger.debug("Received full packet: " + hexFormatter(partialPacket));
|
|
@@ -1845,7 +1699,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1845
1699
|
}
|
|
1846
1700
|
else {
|
|
1847
1701
|
// normal byte in packet
|
|
1848
|
-
partialPacket.push(
|
|
1702
|
+
partialPacket.push(byte);
|
|
1849
1703
|
}
|
|
1850
1704
|
}
|
|
1851
1705
|
}
|
|
@@ -2629,7 +2483,6 @@ export class ESPLoader extends EventTarget {
|
|
|
2629
2483
|
this._reader.cancel();
|
|
2630
2484
|
}
|
|
2631
2485
|
catch (err) {
|
|
2632
|
-
// this.logger.debug(`Reader cancel error: ${err}`);
|
|
2633
2486
|
// Reader already released, resolve immediately
|
|
2634
2487
|
clearTimeout(timeout);
|
|
2635
2488
|
resolve(undefined);
|
|
@@ -2655,12 +2508,6 @@ export class ESPLoader extends EventTarget {
|
|
|
2655
2508
|
await this._parent.releaseReaderWriter();
|
|
2656
2509
|
return;
|
|
2657
2510
|
}
|
|
2658
|
-
// Check if device is in JTAG mode and needs reset to boot into firmware
|
|
2659
|
-
const didReconnect = await this._resetToFirmwareIfNeeded();
|
|
2660
|
-
// If we reconnected for console, the reader/writer are already released and restarted
|
|
2661
|
-
if (didReconnect) {
|
|
2662
|
-
return;
|
|
2663
|
-
}
|
|
2664
2511
|
// Wait for pending writes to complete
|
|
2665
2512
|
try {
|
|
2666
2513
|
await this._writeChain;
|
|
@@ -2716,35 +2563,29 @@ export class ESPLoader extends EventTarget {
|
|
|
2716
2563
|
/**
|
|
2717
2564
|
* @name detectUsbConnectionType
|
|
2718
2565
|
* Detect if device is using USB-JTAG/Serial or USB-OTG (not external serial chip)
|
|
2719
|
-
*
|
|
2566
|
+
* Uses USB PID (Product ID) for reliable detection - does NOT require chipFamily
|
|
2720
2567
|
* @returns true if USB-JTAG or USB-OTG, false if external serial chip
|
|
2721
|
-
* @throws Error if detection fails and chipFamily is not set
|
|
2722
2568
|
*/
|
|
2723
2569
|
async detectUsbConnectionType() {
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
}
|
|
2733
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32C3 ||
|
|
2734
|
-
this.chipFamily === CHIP_FAMILY_ESP32C5 ||
|
|
2735
|
-
this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
2736
|
-
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
2737
|
-
return isUsingUsbJtagSerial;
|
|
2738
|
-
}
|
|
2739
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
2740
|
-
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
2741
|
-
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
2742
|
-
return isUsingUsbOtg || isUsingUsbJtagSerial;
|
|
2743
|
-
}
|
|
2744
|
-
else {
|
|
2745
|
-
// Other chips don't have USB-JTAG/OTG
|
|
2570
|
+
// Use PID-based detection
|
|
2571
|
+
const portInfo = this.port.getInfo();
|
|
2572
|
+
const pid = portInfo.usbProductId;
|
|
2573
|
+
const vid = portInfo.usbVendorId;
|
|
2574
|
+
// Check if this is an Espressif device
|
|
2575
|
+
const isEspressif = vid === 0x303a;
|
|
2576
|
+
if (!isEspressif) {
|
|
2577
|
+
this.logger.debug("Not Espressif VID - external serial chip");
|
|
2746
2578
|
return false;
|
|
2747
2579
|
}
|
|
2580
|
+
// ESP32-S2/S3/C3/C5/C6/C61/H2/P4 USB-JTAG/OTG PIDs
|
|
2581
|
+
// According to official Espressif documentation:
|
|
2582
|
+
// https://docs.espressif.com/projects/esp-iot-solution/en/latest/usb/usb_overview/usb_device_const_COM.html
|
|
2583
|
+
// 0x0002 = ESP32-S2 USB-OTG, 0x0012 = ESP32-P4 USB-Serial-JTAG
|
|
2584
|
+
// 0x1001 = ESP32-S3, C3, C5, C6, C61, H2 USB-Serial-JTAG
|
|
2585
|
+
const usbJtagPids = [0x0002, 0x0012, 0x1001];
|
|
2586
|
+
const isUsbJtag = usbJtagPids.includes(pid || 0);
|
|
2587
|
+
this.logger.debug(`USB-JTAG/OTG detection: ${isUsbJtag ? "YES" : "NO"} (PID=0x${pid === null || pid === void 0 ? void 0 : pid.toString(16)})`);
|
|
2588
|
+
return isUsbJtag;
|
|
2748
2589
|
}
|
|
2749
2590
|
/**
|
|
2750
2591
|
* @name enterConsoleMode
|
|
@@ -2753,23 +2594,33 @@ export class ESPLoader extends EventTarget {
|
|
|
2753
2594
|
* @returns true if port was closed (USB-JTAG), false if port stays open (serial chip)
|
|
2754
2595
|
*/
|
|
2755
2596
|
async enterConsoleMode() {
|
|
2756
|
-
//
|
|
2757
|
-
this.
|
|
2597
|
+
// Check if port is open - if not, we need a new port selection
|
|
2598
|
+
if (!this.port.writable || !this.port.readable) {
|
|
2599
|
+
this.logger.debug("Port is not open - port selection needed");
|
|
2600
|
+
// Return true to signal that port selection is needed
|
|
2601
|
+
// The caller should handle port selection and try again
|
|
2602
|
+
return true;
|
|
2603
|
+
}
|
|
2758
2604
|
// Re-detect USB connection type to ensure we have a definitive value
|
|
2759
|
-
// This handles cases where isUsbJtagOrOtg might be undefined
|
|
2760
2605
|
let isUsbJtag;
|
|
2761
2606
|
try {
|
|
2762
2607
|
isUsbJtag = await this.detectUsbConnectionType();
|
|
2763
2608
|
this.logger.debug(`USB connection type detected: ${isUsbJtag ? "USB-JTAG/OTG" : "External Serial Chip"}`);
|
|
2609
|
+
// CRITICAL: Set the cached value so _resetToFirmwareIfNeeded() can use it
|
|
2610
|
+
this._isUsbJtagOrOtg = isUsbJtag;
|
|
2764
2611
|
}
|
|
2765
2612
|
catch (err) {
|
|
2766
2613
|
// If detection fails, fall back to cached value or fail-fast
|
|
2767
2614
|
if (this.isUsbJtagOrOtg === undefined) {
|
|
2768
2615
|
throw new Error(`Cannot enter console mode: USB connection type unknown and detection failed: ${err}`);
|
|
2769
2616
|
}
|
|
2617
|
+
// Set console mode flag
|
|
2618
|
+
this._consoleMode = false;
|
|
2770
2619
|
this.logger.debug(`USB detection failed, using cached value: ${this.isUsbJtagOrOtg}`);
|
|
2771
2620
|
isUsbJtag = this.isUsbJtagOrOtg;
|
|
2772
2621
|
}
|
|
2622
|
+
// Release reader/writer so console can create new ones
|
|
2623
|
+
// This is needed for Desktop (Web Serial) to unlock streams
|
|
2773
2624
|
if (isUsbJtag) {
|
|
2774
2625
|
// USB-JTAG/OTG devices: Use watchdog reset which closes port
|
|
2775
2626
|
const wasReset = await this._resetToFirmwareIfNeeded();
|
|
@@ -2792,6 +2643,22 @@ export class ESPLoader extends EventTarget {
|
|
|
2792
2643
|
catch (err) {
|
|
2793
2644
|
this.logger.debug(`Could not reset device: ${err}`);
|
|
2794
2645
|
}
|
|
2646
|
+
// For WebUSB (Android), recreate streams after hardware reset
|
|
2647
|
+
if (this.isWebUSB()) {
|
|
2648
|
+
try {
|
|
2649
|
+
// Use the public recreateStreams() method to safely recreate streams
|
|
2650
|
+
// without closing the port (important after hardware reset)
|
|
2651
|
+
await this.port.recreateStreams();
|
|
2652
|
+
this.logger.debug("WebUSB streams recreated for console mode");
|
|
2653
|
+
}
|
|
2654
|
+
catch (err) {
|
|
2655
|
+
// Set console mode flag
|
|
2656
|
+
this._consoleMode = false;
|
|
2657
|
+
this.logger.debug(`Failed to recreate WebUSB streams: ${err}`);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
// Set console mode flag
|
|
2661
|
+
this._consoleMode = true;
|
|
2795
2662
|
return false; // Port stays open
|
|
2796
2663
|
}
|
|
2797
2664
|
}
|
|
@@ -2801,57 +2668,146 @@ export class ESPLoader extends EventTarget {
|
|
|
2801
2668
|
* Detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
2802
2669
|
* @returns true if reconnect was performed, false otherwise
|
|
2803
2670
|
*/
|
|
2671
|
+
/**
|
|
2672
|
+
* @name _clearForceDownloadBootIfNeeded
|
|
2673
|
+
* Read and clear the force download boot flag if it is set
|
|
2674
|
+
* This should ONLY be called when on ROM (not stub) and before WDT reset
|
|
2675
|
+
* Clearing it on every connect causes issues with flash operations
|
|
2676
|
+
* Returns true if the flag was cleared, false if it was already clear
|
|
2677
|
+
*/
|
|
2678
|
+
async _clearForceDownloadBootIfNeeded() {
|
|
2679
|
+
try {
|
|
2680
|
+
let regAddr;
|
|
2681
|
+
let mask;
|
|
2682
|
+
let chipName;
|
|
2683
|
+
// Get register address and mask for this chip
|
|
2684
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S2) {
|
|
2685
|
+
regAddr = ESP32S2_RTC_CNTL_OPTION1_REG;
|
|
2686
|
+
mask = ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK;
|
|
2687
|
+
chipName = "ESP32-S2";
|
|
2688
|
+
}
|
|
2689
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
2690
|
+
regAddr = ESP32S3_RTC_CNTL_OPTION1_REG;
|
|
2691
|
+
mask = ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK;
|
|
2692
|
+
chipName = "ESP32-S3";
|
|
2693
|
+
}
|
|
2694
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
2695
|
+
regAddr = ESP32P4_RTC_CNTL_OPTION1_REG;
|
|
2696
|
+
mask = ESP32P4_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK;
|
|
2697
|
+
chipName = "ESP32-P4";
|
|
2698
|
+
}
|
|
2699
|
+
else {
|
|
2700
|
+
// Not a chip that needs this
|
|
2701
|
+
return false;
|
|
2702
|
+
}
|
|
2703
|
+
// Read current register value
|
|
2704
|
+
const currentValue = await this.readRegister(regAddr);
|
|
2705
|
+
this.logger.debug(`${chipName} force download boot register: 0x${currentValue.toString(16)} (mask: 0x${mask.toString(16)})`);
|
|
2706
|
+
// Check if the flag is set
|
|
2707
|
+
const isFlagSet = (currentValue & mask) !== 0;
|
|
2708
|
+
if (isFlagSet) {
|
|
2709
|
+
this.logger.debug(`${chipName} force download boot flag is SET - clearing it`);
|
|
2710
|
+
// Clear the flag by writing 0 to the masked bits
|
|
2711
|
+
await this.writeRegister(regAddr, 0, mask, 0);
|
|
2712
|
+
this.logger.debug(`${chipName} force download boot flag cleared`);
|
|
2713
|
+
return true;
|
|
2714
|
+
}
|
|
2715
|
+
else {
|
|
2716
|
+
this.logger.debug(`${chipName} force download boot flag is already CLEAR - no action needed`);
|
|
2717
|
+
return false;
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
catch (err) {
|
|
2721
|
+
this.logger.debug(`Error checking/clearing force download flag: ${err}`);
|
|
2722
|
+
return false;
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2804
2725
|
async _resetToFirmwareIfNeeded() {
|
|
2805
2726
|
try {
|
|
2806
|
-
// Check if
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2727
|
+
// Check if port is open - if not, assume device is already in firmware mode
|
|
2728
|
+
if (!this.port.writable || !this.port.readable) {
|
|
2729
|
+
this.logger.debug("Port is not open - assuming device is already in firmware mode");
|
|
2730
|
+
return false;
|
|
2731
|
+
}
|
|
2732
|
+
const isUsingUsbOtg = await this.detectUsbConnectionType();
|
|
2733
|
+
if (isUsingUsbOtg) {
|
|
2734
|
+
// For USB-OTG devices, we need to check if force download flag is set
|
|
2735
|
+
// Only if it's set, we need WDT reset (which causes port change)
|
|
2736
|
+
// If it's clear, we can use normal reset (no port change)
|
|
2737
|
+
if (this.IS_STUB) {
|
|
2738
|
+
this.logger.debug("On stub - need to get back to ROM to check flag");
|
|
2739
|
+
// If we're running at higher baudrate, we need to change back to ROM baudrate
|
|
2740
|
+
if (this.currentBaudRate !== ESP_ROM_BAUD) {
|
|
2741
|
+
this.logger.debug(`Changing baudrate from ${this.currentBaudRate} to ${ESP_ROM_BAUD} for ROM`);
|
|
2742
|
+
try {
|
|
2743
|
+
await this.reconfigurePort(ESP_ROM_BAUD);
|
|
2744
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
2745
|
+
}
|
|
2746
|
+
catch (err) {
|
|
2747
|
+
this.logger.debug(`Baudrate change failed: ${err}`);
|
|
2748
|
+
// Continue anyway
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
this.logger.debug("Resetting to bootloader (ROM)...");
|
|
2752
|
+
// Reset to bootloader - this will clear the stub from RAM
|
|
2827
2753
|
try {
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2754
|
+
await this.hardReset(true);
|
|
2755
|
+
// Wait for reset to complete
|
|
2756
|
+
await sleep(200);
|
|
2757
|
+
// Sync with ROM
|
|
2758
|
+
await this.sync();
|
|
2759
|
+
this.logger.debug("Now on ROM after reset");
|
|
2760
|
+
// Mark that we're no longer on stub
|
|
2761
|
+
this.IS_STUB = false;
|
|
2831
2762
|
}
|
|
2832
|
-
catch (
|
|
2833
|
-
this.logger.debug(`
|
|
2763
|
+
catch (resetErr) {
|
|
2764
|
+
this.logger.debug(`Reset to ROM failed: ${resetErr}`);
|
|
2765
|
+
// If reset fails, we might already be in firmware mode
|
|
2766
|
+
// In this case, we don't need to do anything - just use normal reset
|
|
2767
|
+
this.logger.debug("Assuming device is already in firmware mode");
|
|
2768
|
+
// Release reader/writer before returning
|
|
2769
|
+
await this.releaseReaderWriter();
|
|
2770
|
+
return false; // No port change needed
|
|
2834
2771
|
}
|
|
2835
2772
|
}
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2773
|
+
else {
|
|
2774
|
+
this.logger.debug("Already on ROM - checking force download flag");
|
|
2775
|
+
}
|
|
2776
|
+
// Now check if force download flag is set and clear it if needed
|
|
2777
|
+
const flagWasCleared = await this._clearForceDownloadBootIfNeeded();
|
|
2778
|
+
if (flagWasCleared) {
|
|
2779
|
+
this.logger.debug("Force download flag was cleared - device will boot to firmware after reset");
|
|
2780
|
+
}
|
|
2781
|
+
else {
|
|
2782
|
+
this.logger.debug("Force download flag already clear - device will boot to firmware after reset");
|
|
2783
|
+
}
|
|
2784
|
+
// Perform WDT reset BEFORE releasing reader/writer (needs communication)
|
|
2785
|
+
// After WDT reset, the device will reboot into firmware mode
|
|
2786
|
+
await this.hardReset(false);
|
|
2787
|
+
// For USB-OTG devices (ESP32-S2, ESP32-P4), the port will change after WDT reset
|
|
2788
|
+
const portWillChange = (this.chipFamily === CHIP_FAMILY_ESP32S2 && isUsingUsbOtg) ||
|
|
2789
|
+
(this.chipFamily === CHIP_FAMILY_ESP32P4 && isUsingUsbOtg);
|
|
2790
|
+
if (portWillChange) {
|
|
2791
|
+
// Port will change - release reader/writer and let the port become invalid
|
|
2792
|
+
await this.releaseReaderWriter();
|
|
2793
|
+
this.logger.log(`${this.chipName} USB-OTG: Port will change after WDT reset`);
|
|
2794
|
+
this.logger.log("Please select the new port for console mode");
|
|
2795
|
+
// Dispatch event to signal port change
|
|
2796
|
+
this.dispatchEvent(new CustomEvent("usb-otg-port-change", {
|
|
2797
|
+
detail: {
|
|
2798
|
+
chipName: this.chipName,
|
|
2799
|
+
message: `${this.chipName} USB port changed after reset. Please select the new port.`,
|
|
2800
|
+
reason: "wdt-reset-to-firmware",
|
|
2801
|
+
},
|
|
2802
|
+
}));
|
|
2803
|
+
// Return true to indicate port selection is needed
|
|
2804
|
+
return true;
|
|
2805
|
+
}
|
|
2806
|
+
else {
|
|
2807
|
+
// Port stays the same - release reader/writer so console can use the stream
|
|
2808
|
+
await this.releaseReaderWriter();
|
|
2809
|
+
return false;
|
|
2840
2810
|
}
|
|
2841
|
-
catch (err) {
|
|
2842
|
-
// Error is expected - device resets before responding
|
|
2843
|
-
this.logger.debug(`Watchdog reset initiated (connection lost as expected: ${err})`);
|
|
2844
|
-
}
|
|
2845
|
-
// Wait for device to fully boot into firmware
|
|
2846
|
-
this.logger.log("Waiting for device to boot into firmware...");
|
|
2847
|
-
await this.sleep(1000);
|
|
2848
|
-
// After WDT reset, streams are dead/locked - don't try to manipulate them
|
|
2849
|
-
// Just mark everything as disconnected and let browser clean up
|
|
2850
|
-
this.connected = false;
|
|
2851
|
-
this._writer = undefined;
|
|
2852
|
-
this._reader = undefined;
|
|
2853
|
-
this.logger.debug("Device reset to firmware mode (port closed)");
|
|
2854
|
-
return true;
|
|
2855
2811
|
}
|
|
2856
2812
|
}
|
|
2857
2813
|
catch (err) {
|
|
@@ -3087,31 +3043,50 @@ export class ESPLoader extends EventTarget {
|
|
|
3087
3043
|
}
|
|
3088
3044
|
// Clear console mode flag
|
|
3089
3045
|
this._consoleMode = false;
|
|
3090
|
-
// Check if this is ESP32-S2
|
|
3091
|
-
const
|
|
3092
|
-
|
|
3046
|
+
// Check if this is a USB-OTG device (ESP32-S2 or ESP32-P4)
|
|
3047
|
+
const isUsbOtgChip = this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
3048
|
+
this.chipFamily === CHIP_FAMILY_ESP32P4;
|
|
3049
|
+
// For USB-OTG chips: if _isUsbJtagOrOtg is undefined, try to detect it
|
|
3093
3050
|
// If detection fails or is undefined, assume USB-JTAG/OTG (conservative/safe path)
|
|
3094
3051
|
let isUsbJtagOrOtg = this._isUsbJtagOrOtg;
|
|
3095
|
-
if (
|
|
3052
|
+
if (isUsbOtgChip && isUsbJtagOrOtg === undefined) {
|
|
3096
3053
|
try {
|
|
3097
3054
|
isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
3098
3055
|
}
|
|
3099
3056
|
catch (err) {
|
|
3100
|
-
this.logger.debug(`USB detection failed, assuming USB-JTAG/OTG for
|
|
3101
|
-
isUsbJtagOrOtg = true; // Conservative fallback
|
|
3057
|
+
this.logger.debug(`USB detection failed, assuming USB-JTAG/OTG for ${this.chipName}: ${err}`);
|
|
3058
|
+
isUsbJtagOrOtg = true; // Conservative fallback
|
|
3102
3059
|
}
|
|
3103
3060
|
}
|
|
3104
|
-
if (
|
|
3105
|
-
//
|
|
3106
|
-
|
|
3107
|
-
|
|
3061
|
+
if (isUsbOtgChip && isUsbJtagOrOtg) {
|
|
3062
|
+
// USB-OTG devices: Need to reset to bootloader, which will cause port change
|
|
3063
|
+
this.logger.log(`${this.chipName} USB: Resetting to bootloader mode`);
|
|
3064
|
+
// Perform hardware reset to bootloader (GPIO0=LOW)
|
|
3065
|
+
// This will cause the port to change from CDC (firmware) to JTAG (bootloader)
|
|
3108
3066
|
try {
|
|
3109
|
-
|
|
3067
|
+
if (this.isWebUSB()) {
|
|
3068
|
+
await this.hardResetClassicWebUSB();
|
|
3069
|
+
}
|
|
3070
|
+
else {
|
|
3071
|
+
await this.hardResetClassic();
|
|
3072
|
+
}
|
|
3073
|
+
this.logger.debug("Reset to bootloader initiated");
|
|
3110
3074
|
}
|
|
3111
3075
|
catch (err) {
|
|
3112
|
-
this.logger.debug(`
|
|
3113
|
-
}
|
|
3114
|
-
//
|
|
3076
|
+
this.logger.debug(`Reset error: ${err}`);
|
|
3077
|
+
}
|
|
3078
|
+
// Wait for reset to complete and port to change
|
|
3079
|
+
await sleep(500);
|
|
3080
|
+
this.logger.log(`${this.chipName}: Port changed. Please select the bootloader port.`);
|
|
3081
|
+
// Dispatch event to signal port change
|
|
3082
|
+
this.dispatchEvent(new CustomEvent("usb-otg-port-change", {
|
|
3083
|
+
detail: {
|
|
3084
|
+
chipName: this.chipName,
|
|
3085
|
+
message: `${this.chipName}: Port changed. Please select the bootloader port.`,
|
|
3086
|
+
reason: "exit-console-to-bootloader",
|
|
3087
|
+
},
|
|
3088
|
+
}));
|
|
3089
|
+
// Port will change, so return true to indicate manual reconnection needed
|
|
3115
3090
|
return true;
|
|
3116
3091
|
}
|
|
3117
3092
|
// For other devices, use standard reconnectToBootloader
|
|
@@ -3181,7 +3156,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3181
3156
|
// Wait for the buffer to fill
|
|
3182
3157
|
await sleep(bufferingTime);
|
|
3183
3158
|
// Unsupported command response is sent 8 times and has
|
|
3184
|
-
// 14 bytes length including delimiter 0xC0 bytes.
|
|
3159
|
+
// 14 bytes length including delimiter SLIP_END (0xC0) bytes.
|
|
3185
3160
|
// At least part of it is read as a command response,
|
|
3186
3161
|
// but to be safe, read it all.
|
|
3187
3162
|
const bytesToDrain = 14 * 8;
|
|
@@ -3345,7 +3320,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3345
3320
|
// The stub expects 4 bytes (ACK), if we send less it will break out
|
|
3346
3321
|
try {
|
|
3347
3322
|
// Send SLIP frame with no data (just delimiters)
|
|
3348
|
-
const abortFrame = [
|
|
3323
|
+
const abortFrame = [this.SLIP_END, this.SLIP_END]; // Empty SLIP frame
|
|
3349
3324
|
await this.writeToStream(abortFrame);
|
|
3350
3325
|
this.logger.debug(`Sent abort frame to stub`);
|
|
3351
3326
|
// Give stub time to process abort
|