tasmota-webserial-esptool 9.2.19 → 9.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/const.d.ts +13 -3
- package/dist/const.js +10 -0
- package/dist/esp_loader.d.ts +42 -30
- package/dist/esp_loader.js +593 -414
- package/dist/util.d.ts +5 -0
- package/dist/util.js +15 -0
- package/dist/web/index.js +1 -1
- package/js/modules/esptool.js +1 -1
- package/package.json +19 -8
- package/src/const.ts +13 -5
- package/src/esp_loader.ts +727 -488
- package/src/util.ts +20 -0
package/dist/esp_loader.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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, 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,
|
|
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, ESP32_BASEFUSEADDR, ESP32_APB_CTL_DATE_ADDR, ESP32S2_EFUSE_BLOCK1_ADDR, ESP32S3_EFUSE_BLOCK1_ADDR, ESP32C2_EFUSE_BLOCK2_ADDR, ESP32C5_EFUSE_BLOCK1_ADDR, ESP32C6_EFUSE_BLOCK1_ADDR, ESP32C61_EFUSE_BLOCK1_ADDR, ESP32H2_EFUSE_BLOCK1_ADDR, ESP32P4_EFUSE_BLOCK1_ADDR, ESP32S31_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, ESP32C5_UART_CLKDIV_REG, ESP32C5_PCR_SYSCLK_CONF_REG, ESP32C5_PCR_SYSCLK_XTAL_FREQ_V, ESP32C5_PCR_SYSCLK_XTAL_FREQ_S, 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, ESP32P4_LP_SYSTEM_REG_ANA_XPD_PAD_GROUP_REG, ESP32P4_PMU_EXT_LDO_P0_0P1A_ANA_REG, ESP32P4_PMU_ANA_0P1A_EN_CUR_LIM_0, ESP32P4_PMU_EXT_LDO_P0_0P1A_REG, ESP32P4_PMU_0P1A_TARGET0_0, ESP32P4_PMU_0P1A_FORCE_TIEH_SEL_0, ESP32P4_PMU_DATE_REG, ESP32S2_UARTDEV_BUF_NO, ESP32S2_UARTDEV_BUF_NO_USB_OTG, ESP32S3_UARTDEV_BUF_NO, ESP32S3_UARTDEV_BUF_NO_USB_OTG, ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C3_BUF_UART_NO_OFFSET, ESP32C5_UARTDEV_BUF_NO, ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C6_UARTDEV_BUF_NO, ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C61_UARTDEV_BUF_NO_REV_LE2, ESP32C61_UARTDEV_BUF_NO_REV_GT2, ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_LE2, ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_GT2, ESP32H2_UARTDEV_BUF_NO, ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32H4_UARTDEV_BUF_NO, ESP32H4_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32P4_UARTDEV_BUF_NO_REV0, ESP32P4_UARTDEV_BUF_NO_REV300, ESP32P4_UARTDEV_BUF_NO_USB_OTG, ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL, } from "./const";
|
|
3
3
|
import { getStubCode } from "./stubs";
|
|
4
|
-
import { hexFormatter, sleep, slipEncode, toHex } from "./util";
|
|
4
|
+
import { hexFormatter, padTo, sleep, slipEncode, toHex } from "./util";
|
|
5
5
|
import { deflate } from "pako";
|
|
6
6
|
import { pack, unpack } from "./struct";
|
|
7
7
|
export class ESPLoader extends EventTarget {
|
|
@@ -357,7 +357,13 @@ export class ESPLoader extends EventTarget {
|
|
|
357
357
|
}
|
|
358
358
|
catch (err) {
|
|
359
359
|
this.logger.debug(`Could not detect USB connection type: ${err}`);
|
|
360
|
-
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
const usbMode = await this.getUsbMode();
|
|
363
|
+
this.logger.debug(`USB mode (register): ${usbMode.mode} (uartNo=${usbMode.uartNo})`);
|
|
364
|
+
}
|
|
365
|
+
catch (err) {
|
|
366
|
+
this.logger.debug(`Could not detect USB mode: ${err}`);
|
|
361
367
|
}
|
|
362
368
|
// Read the OTP data for this chip and store into this.efuses array
|
|
363
369
|
const FlAddr = getSpiFlashAddresses(this.getChipFamily());
|
|
@@ -365,7 +371,10 @@ export class ESPLoader extends EventTarget {
|
|
|
365
371
|
for (let i = 0; i < 4; i++) {
|
|
366
372
|
this._efuses[i] = await this.readRegister(AddrMAC + 4 * i);
|
|
367
373
|
}
|
|
368
|
-
this.
|
|
374
|
+
const revisionInfo = this.chipRevision !== null && this.chipRevision !== undefined
|
|
375
|
+
? ` (revision ${this.chipRevision})`
|
|
376
|
+
: "";
|
|
377
|
+
this.logger.log(`Connected to ${this.chipName}${revisionInfo}`);
|
|
369
378
|
this.logger.debug(`Bootloader flash offset: 0x${FlAddr.flashOffs.toString(16)}`);
|
|
370
379
|
// Mark initialization as successful
|
|
371
380
|
this._initializationSucceeded = true;
|
|
@@ -382,22 +391,14 @@ export class ESPLoader extends EventTarget {
|
|
|
382
391
|
if (chipInfo) {
|
|
383
392
|
this.chipName = chipInfo.name;
|
|
384
393
|
this.chipFamily = chipInfo.family;
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
this.
|
|
389
|
-
|
|
390
|
-
if (this.chipRevision >= 300) {
|
|
391
|
-
this.chipVariant = "rev300";
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
this.chipVariant = "rev0";
|
|
395
|
-
}
|
|
396
|
-
this.logger.debug(`ESP32-P4 variant: ${this.chipVariant}`);
|
|
394
|
+
this.chipRevision = await this.getChipRevision();
|
|
395
|
+
this.logger.debug(`${this.chipName} revision: ${this.chipRevision}`);
|
|
396
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32P4 &&
|
|
397
|
+
this.chipRevision >= 300) {
|
|
398
|
+
this.chipVariant = "rev300";
|
|
397
399
|
}
|
|
398
|
-
else if (this.chipFamily ===
|
|
399
|
-
this.
|
|
400
|
-
this.logger.debug(`ESP32-C3 revision: ${this.chipRevision}`);
|
|
400
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
401
|
+
this.chipVariant = "rev0";
|
|
401
402
|
}
|
|
402
403
|
this.logger.debug(`Detected chip via IMAGE_CHIP_ID: ${chipId} (${this.chipName})`);
|
|
403
404
|
return;
|
|
@@ -429,40 +430,109 @@ export class ESPLoader extends EventTarget {
|
|
|
429
430
|
}
|
|
430
431
|
this.chipName = chip.name;
|
|
431
432
|
this.chipFamily = chip.family;
|
|
433
|
+
this.chipRevision = await this.getChipRevision();
|
|
434
|
+
this.logger.debug(`${this.chipName} revision: ${this.chipRevision}`);
|
|
432
435
|
if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
433
|
-
this.
|
|
434
|
-
if (this.chipRevision >= 300) {
|
|
435
|
-
this.chipVariant = "rev300";
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
this.chipVariant = "rev0";
|
|
439
|
-
}
|
|
436
|
+
this.chipVariant = this.chipRevision >= 300 ? "rev300" : "rev0";
|
|
440
437
|
this.logger.debug(`ESP32-P4 variant: ${this.chipVariant}`);
|
|
441
438
|
}
|
|
442
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
443
|
-
this.chipRevision = await this.getChipRevision();
|
|
444
|
-
}
|
|
445
439
|
this.logger.debug(`Detected chip via magic value: ${toHex(chipMagicValue >>> 0, 8)} (${this.chipName})`);
|
|
446
440
|
}
|
|
447
|
-
/**
|
|
448
|
-
* Get chip revision for ESP32-P4
|
|
449
|
-
*/
|
|
450
441
|
async getChipRevision() {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
442
|
+
var _a;
|
|
443
|
+
let minor = 0;
|
|
444
|
+
let major = 0;
|
|
445
|
+
switch (this.chipFamily) {
|
|
446
|
+
case CHIP_FAMILY_ESP32: {
|
|
447
|
+
const efuse3 = await this.readRegister(ESP32_BASEFUSEADDR + 4 * 3);
|
|
448
|
+
const efuse5 = await this.readRegister(ESP32_BASEFUSEADDR + 4 * 5);
|
|
449
|
+
minor = (efuse5 >> 24) & 0x3;
|
|
450
|
+
const revBit0 = (efuse3 >> 15) & 0x1;
|
|
451
|
+
const revBit1 = (efuse5 >> 20) & 0x1;
|
|
452
|
+
const apb = await this.readRegister(ESP32_APB_CTL_DATE_ADDR);
|
|
453
|
+
const revBit2 = (apb >> 31) & 0x1;
|
|
454
|
+
const combined = (revBit2 << 2) | (revBit1 << 1) | revBit0;
|
|
455
|
+
major =
|
|
456
|
+
(_a = { 0: 0, 1: 1, 3: 2, 7: 3 }[combined]) !== null && _a !== void 0 ? _a : 0;
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
case CHIP_FAMILY_ESP32S2: {
|
|
460
|
+
const w3 = await this.readRegister(ESP32S2_EFUSE_BLOCK1_ADDR + 4 * 3);
|
|
461
|
+
const w4 = await this.readRegister(ESP32S2_EFUSE_BLOCK1_ADDR + 4 * 4);
|
|
462
|
+
const hi = (w3 >> 20) & 0x01;
|
|
463
|
+
const lo = (w4 >> 4) & 0x07;
|
|
464
|
+
minor = (hi << 3) + lo;
|
|
465
|
+
major = (w3 >> 18) & 0x03;
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
case CHIP_FAMILY_ESP32S3: {
|
|
469
|
+
const w3 = await this.readRegister(ESP32S3_EFUSE_BLOCK1_ADDR + 4 * 3);
|
|
470
|
+
const w5 = await this.readRegister(ESP32S3_EFUSE_BLOCK1_ADDR + 4 * 5);
|
|
471
|
+
const hi = (w5 >> 23) & 0x01;
|
|
472
|
+
const lo = (w3 >> 18) & 0x07;
|
|
473
|
+
minor = (hi << 3) + lo;
|
|
474
|
+
major = (w5 >> 24) & 0x03;
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
case CHIP_FAMILY_ESP32C2: {
|
|
478
|
+
const w1 = await this.readRegister(ESP32C2_EFUSE_BLOCK2_ADDR + 4 * 1);
|
|
479
|
+
minor = (w1 >> 16) & 0x0f;
|
|
480
|
+
major = (w1 >> 20) & 0x03;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
case CHIP_FAMILY_ESP32C3: {
|
|
484
|
+
const w3 = await this.readRegister(ESP32C3_EFUSE_RD_MAC_SPI_SYS_3_REG);
|
|
485
|
+
const w5 = await this.readRegister(ESP32C3_EFUSE_RD_MAC_SPI_SYS_5_REG);
|
|
486
|
+
const hi = (w5 >> 23) & 0x01;
|
|
487
|
+
const lo = (w3 >> 18) & 0x07;
|
|
488
|
+
minor = (hi << 3) + lo;
|
|
489
|
+
major = (w5 >> 24) & 0x03;
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
case CHIP_FAMILY_ESP32C5: {
|
|
493
|
+
const w2 = await this.readRegister(ESP32C5_EFUSE_BLOCK1_ADDR + 4 * 2);
|
|
494
|
+
minor = w2 & 0x0f;
|
|
495
|
+
major = (w2 >> 4) & 0x03;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
case CHIP_FAMILY_ESP32C6: {
|
|
499
|
+
const w3 = await this.readRegister(ESP32C6_EFUSE_BLOCK1_ADDR + 4 * 3);
|
|
500
|
+
minor = (w3 >> 18) & 0x0f;
|
|
501
|
+
major = (w3 >> 22) & 0x03;
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
case CHIP_FAMILY_ESP32C61: {
|
|
505
|
+
const w2 = await this.readRegister(ESP32C61_EFUSE_BLOCK1_ADDR + 4 * 2);
|
|
506
|
+
minor = w2 & 0x0f;
|
|
507
|
+
major = (w2 >> 4) & 0x03;
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case CHIP_FAMILY_ESP32H2: {
|
|
511
|
+
const w3 = await this.readRegister(ESP32H2_EFUSE_BLOCK1_ADDR + 4 * 3);
|
|
512
|
+
minor = (w3 >> 18) & 0x07;
|
|
513
|
+
major = (w3 >> 21) & 0x03;
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
case CHIP_FAMILY_ESP32H4: {
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
case CHIP_FAMILY_ESP32H21: {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
case CHIP_FAMILY_ESP32P4: {
|
|
523
|
+
const w2 = await this.readRegister(ESP32P4_EFUSE_BLOCK1_ADDR + 4 * 2);
|
|
524
|
+
minor = w2 & 0x0f;
|
|
525
|
+
major = (((w2 >> 23) & 1) << 2) | ((w2 >> 4) & 0x03);
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
case CHIP_FAMILY_ESP32S31: {
|
|
529
|
+
const w2 = await this.readRegister(ESP32S31_EFUSE_BLOCK1_ADDR + 4 * 2);
|
|
530
|
+
minor = w2 & 0x0f;
|
|
531
|
+
major = (w2 >> 4) & 0x03;
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
464
534
|
}
|
|
465
|
-
return
|
|
535
|
+
return major * 100 + minor;
|
|
466
536
|
}
|
|
467
537
|
/**
|
|
468
538
|
* Power on the flash chip for ESP32-P4 Rev 301 (ECO6)
|
|
@@ -607,9 +677,6 @@ export class ESPLoader extends EventTarget {
|
|
|
607
677
|
this._suppressDisconnect = false;
|
|
608
678
|
this.logger.debug("Finished read loop");
|
|
609
679
|
}
|
|
610
|
-
sleep(ms = 100) {
|
|
611
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
612
|
-
}
|
|
613
680
|
// ============================================================================
|
|
614
681
|
// Web Serial (Desktop) - DTR/RTS Signal Handling & Reset Strategies
|
|
615
682
|
// ============================================================================
|
|
@@ -633,10 +700,6 @@ export class ESPLoader extends EventTarget {
|
|
|
633
700
|
requestToSend: rts,
|
|
634
701
|
});
|
|
635
702
|
}
|
|
636
|
-
/**
|
|
637
|
-
* Helper function to run a sequence of signal changes
|
|
638
|
-
* Automatically detects WebUSB vs Web Serial and calls appropriate methods
|
|
639
|
-
*/
|
|
640
703
|
async runSignalSequence(steps) {
|
|
641
704
|
const webusb = this.port.isWebUSB === true;
|
|
642
705
|
for (const step of steps) {
|
|
@@ -650,14 +713,20 @@ export class ESPLoader extends EventTarget {
|
|
|
650
713
|
}
|
|
651
714
|
else {
|
|
652
715
|
if (step.dtr !== undefined) {
|
|
653
|
-
webusb
|
|
654
|
-
|
|
655
|
-
|
|
716
|
+
if (webusb) {
|
|
717
|
+
await this.setDTRWebUSB(step.dtr);
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
await this.setDTR(step.dtr);
|
|
721
|
+
}
|
|
656
722
|
}
|
|
657
723
|
if (step.rts !== undefined) {
|
|
658
|
-
webusb
|
|
659
|
-
|
|
660
|
-
|
|
724
|
+
if (webusb) {
|
|
725
|
+
await this.setRTSWebUSB(step.rts);
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
await this.setRTS(step.rts);
|
|
729
|
+
}
|
|
661
730
|
}
|
|
662
731
|
}
|
|
663
732
|
if (step.delayMs)
|
|
@@ -1002,8 +1071,12 @@ export class ESPLoader extends EventTarget {
|
|
|
1002
1071
|
});
|
|
1003
1072
|
}
|
|
1004
1073
|
}
|
|
1005
|
-
// Add general fallback strategies only for
|
|
1006
|
-
|
|
1074
|
+
// Add general fallback strategies only for Native USB chips (not USB-Serial)
|
|
1075
|
+
// and only for chips not already handled by specific blocks above
|
|
1076
|
+
if (!isUSBSerialChip &&
|
|
1077
|
+
!isCP2102 &&
|
|
1078
|
+
!isESP32S2NativeUSB &&
|
|
1079
|
+
!isUSBJTAGSerial) {
|
|
1007
1080
|
// Classic reset (for chips not handled above)
|
|
1008
1081
|
if (portInfo.usbVendorId !== 0x1a86) {
|
|
1009
1082
|
resetStrategies.push({
|
|
@@ -1035,7 +1108,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1035
1108
|
},
|
|
1036
1109
|
});
|
|
1037
1110
|
// WebUSB Strategy: USB-JTAG/Serial fallback
|
|
1038
|
-
if (!
|
|
1111
|
+
if (!isEspressifUSB) {
|
|
1039
1112
|
resetStrategies.push({
|
|
1040
1113
|
name: "USB-JTAG/Serial fallback (WebUSB)",
|
|
1041
1114
|
fn: async function () {
|
|
@@ -1110,10 +1183,10 @@ export class ESPLoader extends EventTarget {
|
|
|
1110
1183
|
try {
|
|
1111
1184
|
await Promise.race([syncPromise, timeoutPromise]);
|
|
1112
1185
|
// Sync succeeded
|
|
1113
|
-
this.logger.
|
|
1186
|
+
this.logger.debug(`Connected CDC/JTAG successfully with ${strategy.name} reset.`);
|
|
1114
1187
|
return;
|
|
1115
1188
|
}
|
|
1116
|
-
catch
|
|
1189
|
+
catch {
|
|
1117
1190
|
throw new Error("Sync timeout or abandoned");
|
|
1118
1191
|
}
|
|
1119
1192
|
}
|
|
@@ -1144,35 +1217,19 @@ export class ESPLoader extends EventTarget {
|
|
|
1144
1217
|
}
|
|
1145
1218
|
/**
|
|
1146
1219
|
* @name watchdogReset
|
|
1147
|
-
* Watchdog reset for ESP32-S2/S3/
|
|
1220
|
+
* Watchdog reset for ESP32-S2/S3/P4 with USB-OTG or USB-JTAG/Serial
|
|
1148
1221
|
* Uses RTC watchdog timer to reset the chip - works when DTR/RTS signals are not available
|
|
1149
1222
|
* This is an alias for rtcWdtResetChipSpecific() for backwards compatibility
|
|
1223
|
+
* Note: ESP32-C3, ESP32-C5, ESP32-C6 do NOT boot correctly after WDT reset
|
|
1150
1224
|
*/
|
|
1151
1225
|
async watchdogReset() {
|
|
1152
1226
|
await this.rtcWdtResetChipSpecific();
|
|
1153
1227
|
}
|
|
1154
1228
|
/**
|
|
1155
|
-
*
|
|
1156
|
-
* Reads from EFUSE registers and calculates revision
|
|
1157
|
-
*/
|
|
1158
|
-
async getChipRevisionC3() {
|
|
1159
|
-
if (this.chipFamily !== CHIP_FAMILY_ESP32C3) {
|
|
1160
|
-
return 0;
|
|
1161
|
-
}
|
|
1162
|
-
// Read EFUSE_RD_MAC_SPI_SYS_3_REG (bits [20:18] = lower 3 bits of revision)
|
|
1163
|
-
const word3 = await this.readRegister(ESP32C3_EFUSE_RD_MAC_SPI_SYS_3_REG);
|
|
1164
|
-
const low = (word3 >> 18) & 0x07;
|
|
1165
|
-
// Read EFUSE_RD_MAC_SPI_SYS_5_REG (bits [25:23] = upper 3 bits of revision)
|
|
1166
|
-
const word5 = await this.readRegister(ESP32C3_EFUSE_RD_MAC_SPI_SYS_5_REG);
|
|
1167
|
-
const hi = (word5 >> 23) & 0x07;
|
|
1168
|
-
// Combine: upper 3 bits from word5, lower 3 bits from word3
|
|
1169
|
-
const revision = (hi << 3) | low;
|
|
1170
|
-
return revision;
|
|
1171
|
-
}
|
|
1172
|
-
/**
|
|
1173
|
-
* RTC watchdog timer reset for ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C5, ESP32-C6, and ESP32-P4
|
|
1229
|
+
* RTC watchdog timer reset for ESP32-S2, ESP32-S3, and ESP32-P4
|
|
1174
1230
|
* Uses specific registers for each chip family
|
|
1175
|
-
* Note: ESP32-
|
|
1231
|
+
* Note: ESP32-C3 does NOT boot correctly after WDT reset
|
|
1232
|
+
* Note: ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2 do NOT support WDT reset (no usable RTC WDT path)
|
|
1176
1233
|
*/
|
|
1177
1234
|
async rtcWdtResetChipSpecific() {
|
|
1178
1235
|
this.logger.debug("Hard resetting with watchdog timer...");
|
|
@@ -1192,20 +1249,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1192
1249
|
WDTCONFIG1_REG = ESP32S3_RTC_CNTL_WDTCONFIG1_REG;
|
|
1193
1250
|
WDT_WKEY = ESP32S3_RTC_CNTL_WDT_WKEY;
|
|
1194
1251
|
}
|
|
1195
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
1196
|
-
WDTWPROTECT_REG = ESP32C3_RTC_CNTL_WDTWPROTECT_REG;
|
|
1197
|
-
WDTCONFIG0_REG = ESP32C3_RTC_CNTL_WDTCONFIG0_REG;
|
|
1198
|
-
WDTCONFIG1_REG = ESP32C3_RTC_CNTL_WDTCONFIG1_REG;
|
|
1199
|
-
WDT_WKEY = ESP32C3_RTC_CNTL_WDT_WKEY;
|
|
1200
|
-
}
|
|
1201
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32C5 ||
|
|
1202
|
-
this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
1203
|
-
// C5 and C6 use LP_WDT (Low Power Watchdog Timer)
|
|
1204
|
-
WDTWPROTECT_REG = ESP32C5_C6_RTC_CNTL_WDTWPROTECT_REG;
|
|
1205
|
-
WDTCONFIG0_REG = ESP32C5_C6_RTC_CNTL_WDTCONFIG0_REG;
|
|
1206
|
-
WDTCONFIG1_REG = ESP32C5_C6_RTC_CNTL_WDTCONFIG1_REG;
|
|
1207
|
-
WDT_WKEY = ESP32C5_C6_RTC_CNTL_WDT_WKEY;
|
|
1208
|
-
}
|
|
1209
1252
|
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1210
1253
|
// P4 uses LP_WDT (Low Power Watchdog Timer)
|
|
1211
1254
|
WDTWPROTECT_REG = ESP32P4_RTC_CNTL_WDTWPROTECT_REG;
|
|
@@ -1226,26 +1269,113 @@ export class ESPLoader extends EventTarget {
|
|
|
1226
1269
|
// Lock watchdog registers
|
|
1227
1270
|
await this.writeRegister(WDTWPROTECT_REG, 0, undefined, 0);
|
|
1228
1271
|
// Wait for reset to take effect
|
|
1229
|
-
await
|
|
1272
|
+
await sleep(500);
|
|
1230
1273
|
}
|
|
1231
1274
|
/**
|
|
1232
|
-
*
|
|
1233
|
-
*
|
|
1275
|
+
* Reset device from bootloader mode to firmware mode
|
|
1276
|
+
* Automatically selects the correct reset strategy based on USB connection type
|
|
1277
|
+
* @param clearForceDownloadFlag - If true, clears the force download boot flag (USB-OTG only)
|
|
1278
|
+
* @returns true if port will change (USB-OTG), false otherwise
|
|
1234
1279
|
*/
|
|
1235
|
-
async
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
//
|
|
1239
|
-
await this.
|
|
1240
|
-
|
|
1241
|
-
|
|
1280
|
+
async resetToFirmwareMode(clearForceDownloadFlag = true) {
|
|
1281
|
+
this.logger.debug("Resetting from bootloader to firmware mode...");
|
|
1282
|
+
try {
|
|
1283
|
+
// Detect USB connection type
|
|
1284
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
1285
|
+
if (isUsbJtagOrOtg) {
|
|
1286
|
+
// USB-JTAG/OTG devices need special handling
|
|
1287
|
+
this.logger.debug("USB-JTAG/OTG detected - checking WDT reset support");
|
|
1288
|
+
// Get detailed USB mode information
|
|
1289
|
+
let usbMode;
|
|
1290
|
+
try {
|
|
1291
|
+
usbMode = await this.getUsbMode();
|
|
1292
|
+
this.logger.debug(`USB mode: ${usbMode.mode} (uartNo=${usbMode.uartNo})`);
|
|
1293
|
+
}
|
|
1294
|
+
catch (err) {
|
|
1295
|
+
this.logger.debug(`Could not get USB mode: ${err}`);
|
|
1296
|
+
// Fall back to generic USB-JTAG/OTG handling
|
|
1297
|
+
usbMode = { mode: "usb-jtag-serial", uartNo: 0 };
|
|
1298
|
+
}
|
|
1299
|
+
// WDT reset is not needed for ESP32-C3
|
|
1300
|
+
// WDT reset is supported by: ESP32-S2, ESP32-S3, ESP32-P4
|
|
1301
|
+
// WDT reset is NOT supported by: ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2
|
|
1302
|
+
const supportsWdtReset = this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
1303
|
+
this.chipFamily === CHIP_FAMILY_ESP32S3 ||
|
|
1304
|
+
this.chipFamily === CHIP_FAMILY_ESP32P4;
|
|
1305
|
+
if (!supportsWdtReset) {
|
|
1306
|
+
this.logger.debug(`${this.chipName} does not support WDT reset - using classic reset instead`);
|
|
1307
|
+
// Use classic reset for chips without WDT support
|
|
1308
|
+
await this.hardResetToFirmware();
|
|
1309
|
+
this.logger.debug("Classic reset to firmware complete");
|
|
1310
|
+
return false; // Port stays open
|
|
1311
|
+
}
|
|
1312
|
+
// WDT reset is supported - proceed with WDT reset logic
|
|
1313
|
+
this.logger.debug(`${this.chipName} supports WDT reset - using WDT reset strategy`);
|
|
1314
|
+
// CRITICAL: WDT register writes require ROM (not stub) and baudrate 115200
|
|
1315
|
+
// If on stub, need to return to ROM first
|
|
1316
|
+
if (this.IS_STUB) {
|
|
1317
|
+
this.logger.debug("On stub - returning to ROM before WDT reset");
|
|
1318
|
+
// Change baudrate back to ROM baudrate if needed
|
|
1319
|
+
if (this.currentBaudRate !== ESP_ROM_BAUD) {
|
|
1320
|
+
this.logger.debug(`Changing baudrate from ${this.currentBaudRate} to ${ESP_ROM_BAUD}`);
|
|
1321
|
+
await this.reconfigurePort(ESP_ROM_BAUD);
|
|
1322
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
1323
|
+
this.logger.debug("Baudrate changed to 115200");
|
|
1324
|
+
}
|
|
1325
|
+
// CRITICAL: Temporarily clear console mode flag so hardReset(true) works
|
|
1326
|
+
const wasInConsoleMode = this._consoleMode;
|
|
1327
|
+
this._consoleMode = false;
|
|
1328
|
+
// Reset to bootloader (ROM)
|
|
1329
|
+
await this.hardReset(true);
|
|
1330
|
+
await sleep(200);
|
|
1331
|
+
// Restore console mode flag
|
|
1332
|
+
this._consoleMode = wasInConsoleMode;
|
|
1333
|
+
// Sync with ROM
|
|
1334
|
+
await this.sync();
|
|
1335
|
+
this.IS_STUB = false;
|
|
1336
|
+
this.logger.debug("Now on ROM");
|
|
1337
|
+
}
|
|
1338
|
+
else {
|
|
1339
|
+
// Even if not on stub, ensure baudrate is 115200 for WDT register writes
|
|
1340
|
+
if (this.currentBaudRate !== ESP_ROM_BAUD) {
|
|
1341
|
+
this.logger.debug(`Not on stub, but baudrate is ${this.currentBaudRate} - changing to ${ESP_ROM_BAUD} for WDT reset`);
|
|
1342
|
+
await this.reconfigurePort(ESP_ROM_BAUD);
|
|
1343
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
1344
|
+
this.logger.debug("Baudrate changed to 115200");
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
// Clear force download boot flag if requested (USB-OTG only)
|
|
1348
|
+
if (clearForceDownloadFlag && usbMode.mode === "usb-otg") {
|
|
1349
|
+
const flagCleared = await this._clearForceDownloadBootIfNeeded();
|
|
1350
|
+
if (flagCleared) {
|
|
1351
|
+
this.logger.debug("Force download boot flag cleared");
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
// Perform WDT reset to boot into firmware
|
|
1355
|
+
await this.rtcWdtResetChipSpecific();
|
|
1356
|
+
this.logger.debug("WDT reset performed - device will boot to firmware");
|
|
1357
|
+
// Check if port will change after WDT reset
|
|
1358
|
+
// USB-OTG (ESP32-S2/P4): Port always changes
|
|
1359
|
+
// USB-JTAG/Serial (ESP32-S3/C3/C5/C6/C61/H2/P4): Port may change depending on platform
|
|
1360
|
+
const portWillChange = usbMode.mode === "usb-otg" || usbMode.mode === "usb-jtag-serial";
|
|
1361
|
+
if (portWillChange) {
|
|
1362
|
+
this.logger.debug(`Port will change after WDT reset (${usbMode.mode}) - port reselection needed`);
|
|
1363
|
+
return true;
|
|
1364
|
+
}
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
else {
|
|
1368
|
+
// External serial chip - use classic reset to firmware
|
|
1369
|
+
this.logger.debug("External serial chip detected - using classic reset");
|
|
1370
|
+
await this.hardResetToFirmware();
|
|
1371
|
+
this.logger.debug("Classic reset to firmware complete");
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1242
1374
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
this.logger.debug("Classic reset.");
|
|
1375
|
+
catch (err) {
|
|
1376
|
+
this.logger.error(`Failed to reset to firmware mode: ${err}`);
|
|
1377
|
+
throw err;
|
|
1247
1378
|
}
|
|
1248
|
-
return false;
|
|
1249
1379
|
}
|
|
1250
1380
|
async hardReset(bootloader = false) {
|
|
1251
1381
|
// In console mode, only allow simple hardware reset (no bootloader entry)
|
|
@@ -1256,66 +1386,87 @@ export class ESPLoader extends EventTarget {
|
|
|
1256
1386
|
}
|
|
1257
1387
|
// Simple hardware reset to restart firmware (IO0=HIGH)
|
|
1258
1388
|
this.logger.debug("Performing hardware reset (console mode)...");
|
|
1259
|
-
await this.
|
|
1389
|
+
await this.resetInConsoleMode();
|
|
1260
1390
|
this.logger.debug("Hardware reset complete");
|
|
1261
1391
|
return;
|
|
1262
1392
|
}
|
|
1263
1393
|
if (bootloader) {
|
|
1264
|
-
//
|
|
1394
|
+
// Enter bootloader/flash mode
|
|
1265
1395
|
if (this.port.getInfo().usbProductId === USB_JTAG_SERIAL_PID) {
|
|
1266
1396
|
await this.hardResetUSBJTAGSerial();
|
|
1267
|
-
this.logger.debug("USB-JTAG/Serial reset.");
|
|
1397
|
+
this.logger.debug("USB-JTAG/Serial reset to bootloader.");
|
|
1268
1398
|
}
|
|
1269
1399
|
else {
|
|
1270
1400
|
await this.hardResetClassic();
|
|
1271
|
-
this.logger.debug("Classic reset.");
|
|
1401
|
+
this.logger.debug("Classic reset to bootloader.");
|
|
1272
1402
|
}
|
|
1273
1403
|
}
|
|
1274
1404
|
else {
|
|
1275
|
-
//
|
|
1276
|
-
//
|
|
1277
|
-
this.logger.debug("
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
//
|
|
1283
|
-
//
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1405
|
+
// Reset to firmware mode (exit bootloader)
|
|
1406
|
+
// Use intelligent reset strategy based on USB connection type
|
|
1407
|
+
this.logger.debug("Resetting to firmware mode...");
|
|
1408
|
+
// Detect USB connection type to choose correct reset method
|
|
1409
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
1410
|
+
if (isUsbJtagOrOtg) {
|
|
1411
|
+
// USB-JTAG/OTG devices: Check if chip supports WDT reset
|
|
1412
|
+
// Only S2, S3, P4 support WDT reset correctly
|
|
1413
|
+
// C3, C5, C6, C61, H2 do NOT boot correctly after WDT reset
|
|
1414
|
+
const supportsWdtReset = this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
1415
|
+
this.chipFamily === CHIP_FAMILY_ESP32S3 ||
|
|
1416
|
+
this.chipFamily === CHIP_FAMILY_ESP32P4;
|
|
1417
|
+
if (supportsWdtReset) {
|
|
1418
|
+
this.logger.debug("USB-JTAG/OTG detected - using WDT reset");
|
|
1419
|
+
// Get USB mode details
|
|
1420
|
+
let usbMode;
|
|
1421
|
+
try {
|
|
1422
|
+
usbMode = await this.getUsbMode();
|
|
1423
|
+
this.logger.debug(`USB mode: ${usbMode.mode} (uartNo=${usbMode.uartNo})`);
|
|
1424
|
+
}
|
|
1425
|
+
catch (err) {
|
|
1426
|
+
this.logger.debug(`Could not get USB mode: ${err}`);
|
|
1427
|
+
usbMode = { mode: "usb-jtag-serial", uartNo: 0 };
|
|
1428
|
+
}
|
|
1429
|
+
// Clear force download flag for USB-OTG devices
|
|
1430
|
+
if (usbMode.mode === "usb-otg") {
|
|
1431
|
+
try {
|
|
1432
|
+
const flagCleared = await this._clearForceDownloadBootIfNeeded();
|
|
1433
|
+
if (flagCleared) {
|
|
1434
|
+
this.logger.debug("Force download boot flag cleared");
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
catch (err) {
|
|
1438
|
+
this.logger.debug(`Could not clear force download flag: ${err}`);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
// Perform WDT reset
|
|
1442
|
+
await this.rtcWdtResetChipSpecific();
|
|
1443
|
+
this.logger.debug(`${this.chipName}: WDT reset to firmware complete`);
|
|
1297
1444
|
return;
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
// C3, C5, C6, etc. - use classic reset (like external serial chips)
|
|
1448
|
+
this.logger.debug(`${this.chipName} does not support WDT reset - using classic reset instead`);
|
|
1449
|
+
}
|
|
1298
1450
|
}
|
|
1299
|
-
else
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
return;
|
|
1451
|
+
else {
|
|
1452
|
+
// External serial chip: Use classic reset
|
|
1453
|
+
this.logger.debug("External serial chip detected - using classic reset");
|
|
1303
1454
|
}
|
|
1304
|
-
//
|
|
1455
|
+
// Classic reset: used for external serial chips and USB-JTAG chips that do not support WDT reset
|
|
1305
1456
|
if (this.isWebUSB()) {
|
|
1306
1457
|
// WebUSB: Use longer delays for better compatibility
|
|
1307
1458
|
await this.setRTSWebUSB(true); // EN->LOW
|
|
1308
|
-
await
|
|
1459
|
+
await sleep(200);
|
|
1309
1460
|
await this.setRTSWebUSB(false);
|
|
1310
|
-
await
|
|
1311
|
-
this.logger.debug("Hard reset (WebUSB).");
|
|
1461
|
+
await sleep(200);
|
|
1462
|
+
this.logger.debug("Hard reset to firmware (WebUSB).");
|
|
1312
1463
|
}
|
|
1313
1464
|
else {
|
|
1314
1465
|
// Web Serial: Standard reset
|
|
1315
1466
|
await this.setRTS(true); // EN->LOW
|
|
1316
|
-
await
|
|
1467
|
+
await sleep(100);
|
|
1317
1468
|
await this.setRTS(false);
|
|
1318
|
-
this.logger.debug("Hard reset.");
|
|
1469
|
+
this.logger.debug("Hard reset to firmware.");
|
|
1319
1470
|
}
|
|
1320
1471
|
}
|
|
1321
1472
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -1715,28 +1866,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1715
1866
|
return 40;
|
|
1716
1867
|
return 26;
|
|
1717
1868
|
}
|
|
1718
|
-
async setBaudrateC5Rom(baud) {
|
|
1719
|
-
const crystalFreqRomExpect = await this.getC5CrystalFreqRomExpect();
|
|
1720
|
-
const crystalFreqDetect = await this.getC5CrystalFreqDetected();
|
|
1721
|
-
this.logger.log(`ROM expects crystal freq: ${crystalFreqRomExpect} MHz, detected ${crystalFreqDetect} MHz.`);
|
|
1722
|
-
let baudRate = baud;
|
|
1723
|
-
if (crystalFreqDetect === 48 && crystalFreqRomExpect === 40) {
|
|
1724
|
-
baudRate = Math.trunc((baud * 40) / 48);
|
|
1725
|
-
}
|
|
1726
|
-
else if (crystalFreqDetect === 40 && crystalFreqRomExpect === 48) {
|
|
1727
|
-
baudRate = Math.trunc((baud * 48) / 40);
|
|
1728
|
-
}
|
|
1729
|
-
this.logger.log(`Changing baud rate to ${baudRate}...`);
|
|
1730
|
-
try {
|
|
1731
|
-
const buffer = pack("<II", baudRate, 0);
|
|
1732
|
-
await this.checkCommand(ESP_CHANGE_BAUDRATE, buffer);
|
|
1733
|
-
}
|
|
1734
|
-
catch (e) {
|
|
1735
|
-
this.logger.error(`Baudrate change error: ${e}`);
|
|
1736
|
-
throw new Error(`Unable to change the baud rate to ${baudRate}: No response from set baud rate command.`);
|
|
1737
|
-
}
|
|
1738
|
-
this.logger.log("Changed.");
|
|
1739
|
-
}
|
|
1740
1869
|
async setBaudrate(baud) {
|
|
1741
1870
|
const chipFamily = this._parent ? this._parent.chipFamily : this.chipFamily;
|
|
1742
1871
|
if (!this.IS_STUB && chipFamily === CHIP_FAMILY_ESP32C5) {
|
|
@@ -1744,7 +1873,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1744
1873
|
}
|
|
1745
1874
|
else {
|
|
1746
1875
|
try {
|
|
1747
|
-
// Send ESP_ROM_BAUD(115200) as the old one if running STUB otherwise 0
|
|
1748
1876
|
const buffer = pack("<II", baud, this.IS_STUB ? ESP_ROM_BAUD : 0);
|
|
1749
1877
|
await this.checkCommand(ESP_CHANGE_BAUDRATE, buffer);
|
|
1750
1878
|
}
|
|
@@ -1776,7 +1904,29 @@ export class ESPLoader extends EventTarget {
|
|
|
1776
1904
|
this.logger.log(`⚠️ WARNING: Baudrate ${baud} exceeds USB-Serial chip limit (${maxBaud})!`);
|
|
1777
1905
|
this.logger.log(`⚠️ This may cause data corruption or connection failures!`);
|
|
1778
1906
|
}
|
|
1779
|
-
this.logger.
|
|
1907
|
+
this.logger.debug(`Changed baud rate to ${baud}`);
|
|
1908
|
+
}
|
|
1909
|
+
async setBaudrateC5Rom(baud) {
|
|
1910
|
+
const crystalFreqRomExpect = await this.getC5CrystalFreqRomExpect();
|
|
1911
|
+
const crystalFreqDetect = await this.getC5CrystalFreqDetected();
|
|
1912
|
+
this.logger.log(`ROM expects crystal freq: ${crystalFreqRomExpect} MHz, detected ${crystalFreqDetect} MHz.`);
|
|
1913
|
+
let baudRate = baud;
|
|
1914
|
+
if (crystalFreqDetect === 48 && crystalFreqRomExpect === 40) {
|
|
1915
|
+
baudRate = Math.trunc((baud * 40) / 48);
|
|
1916
|
+
}
|
|
1917
|
+
else if (crystalFreqDetect === 40 && crystalFreqRomExpect === 48) {
|
|
1918
|
+
baudRate = Math.trunc((baud * 48) / 40);
|
|
1919
|
+
}
|
|
1920
|
+
this.logger.log(`Changing baud rate to ${baudRate}...`);
|
|
1921
|
+
try {
|
|
1922
|
+
const buffer = pack("<II", baudRate, 0);
|
|
1923
|
+
await this.checkCommand(ESP_CHANGE_BAUDRATE, buffer);
|
|
1924
|
+
}
|
|
1925
|
+
catch (e) {
|
|
1926
|
+
this.logger.error(`Baudrate change error: ${e}`);
|
|
1927
|
+
throw new Error(`Unable to change the baud rate to ${baudRate}: No response from set baud rate command.`);
|
|
1928
|
+
}
|
|
1929
|
+
this.logger.log("Changed.");
|
|
1780
1930
|
}
|
|
1781
1931
|
async reconfigurePort(baud) {
|
|
1782
1932
|
var _a;
|
|
@@ -1837,9 +1987,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1837
1987
|
// Restart Readloop
|
|
1838
1988
|
this.readLoop();
|
|
1839
1989
|
}
|
|
1840
|
-
catch
|
|
1841
|
-
// this.logger.error(`Reconfigure port error
|
|
1842
|
-
// throw new Error(`Unable to change the baud rate to ${baud}
|
|
1990
|
+
catch {
|
|
1991
|
+
// this.logger.error(`Reconfigure port error`);
|
|
1992
|
+
// throw new Error(`Unable to change the baud rate to ${baud}`);
|
|
1843
1993
|
}
|
|
1844
1994
|
finally {
|
|
1845
1995
|
// Always reset flag, even on error or early return
|
|
@@ -1946,6 +2096,8 @@ export class ESPLoader extends EventTarget {
|
|
|
1946
2096
|
const headerFlashSizeFreq = header[3];
|
|
1947
2097
|
this.logger.log(`Image header, Magic=${toHex(headerMagic)}, FlashMode=${toHex(headerFlashMode)}, FlashSizeFreq=${toHex(headerFlashSizeFreq)}`);
|
|
1948
2098
|
}
|
|
2099
|
+
const paddedData = padTo(new Uint8Array(binaryData), 4);
|
|
2100
|
+
binaryData = paddedData.buffer;
|
|
1949
2101
|
const uncompressedFilesize = binaryData.byteLength;
|
|
1950
2102
|
let compressedFilesize = 0;
|
|
1951
2103
|
let dataToFlash;
|
|
@@ -2242,13 +2394,9 @@ export class ESPLoader extends EventTarget {
|
|
|
2242
2394
|
return status;
|
|
2243
2395
|
}
|
|
2244
2396
|
async detectFlashSize() {
|
|
2245
|
-
this.logger.
|
|
2397
|
+
this.logger.debug("Detecting Flash Size");
|
|
2246
2398
|
const flashId = await this.flashId();
|
|
2247
|
-
const manufacturer = flashId & 0xff;
|
|
2248
2399
|
const flashIdLowbyte = (flashId >> 16) & 0xff;
|
|
2249
|
-
this.logger.log(`FlashId: ${toHex(flashId)}`);
|
|
2250
|
-
this.logger.log(`Flash Manufacturer: ${manufacturer.toString(16)}`);
|
|
2251
|
-
this.logger.log(`Flash Device: ${((flashId >> 8) & 0xff).toString(16)}${flashIdLowbyte.toString(16)}`);
|
|
2252
2400
|
this.flashSize = DETECTED_FLASH_SIZES[flashIdLowbyte];
|
|
2253
2401
|
this.logger.log(`Auto-detected Flash size: ${this.flashSize}`);
|
|
2254
2402
|
}
|
|
@@ -2310,7 +2458,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2310
2458
|
// We're transferring over USB, right?
|
|
2311
2459
|
const ramBlock = USB_RAM_BLOCK;
|
|
2312
2460
|
// Upload
|
|
2313
|
-
this.logger.
|
|
2461
|
+
this.logger.debug("Uploading stub...");
|
|
2314
2462
|
for (const field of ["text", "data"]) {
|
|
2315
2463
|
const fieldData = stub[field];
|
|
2316
2464
|
const offset = stub[`${field}_start`];
|
|
@@ -2332,7 +2480,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2332
2480
|
if (pChar != "OHAI") {
|
|
2333
2481
|
throw new Error("Failed to start stub. Unexpected response: " + pChar);
|
|
2334
2482
|
}
|
|
2335
|
-
this.logger.
|
|
2483
|
+
this.logger.debug("Stub is now running...");
|
|
2336
2484
|
const espStubLoader = new EspStubLoader(this.port, this.logger, this);
|
|
2337
2485
|
// Try to autodetect the flash size.
|
|
2338
2486
|
if (!skipFlashDetection) {
|
|
@@ -2436,81 +2584,6 @@ export class ESPLoader extends EventTarget {
|
|
|
2436
2584
|
// Always await the write chain to ensure errors are caught
|
|
2437
2585
|
await this._writeChain;
|
|
2438
2586
|
}
|
|
2439
|
-
async getUsbMode() {
|
|
2440
|
-
var _a, _b;
|
|
2441
|
-
const family = this._parent ? this._parent.chipFamily : this.chipFamily;
|
|
2442
|
-
const revision = this._parent
|
|
2443
|
-
? ((_a = this._parent.chipRevision) !== null && _a !== void 0 ? _a : 0)
|
|
2444
|
-
: ((_b = this.chipRevision) !== null && _b !== void 0 ? _b : 0);
|
|
2445
|
-
let bufNoAddr = null;
|
|
2446
|
-
let jtagSerialVal = null;
|
|
2447
|
-
let otgVal = null;
|
|
2448
|
-
switch (family) {
|
|
2449
|
-
case CHIP_FAMILY_ESP32S2:
|
|
2450
|
-
bufNoAddr = ESP32S2_UARTDEV_BUF_NO;
|
|
2451
|
-
otgVal = ESP32S2_UARTDEV_BUF_NO_USB_OTG;
|
|
2452
|
-
break;
|
|
2453
|
-
case CHIP_FAMILY_ESP32S3:
|
|
2454
|
-
bufNoAddr = ESP32S3_UARTDEV_BUF_NO;
|
|
2455
|
-
jtagSerialVal = ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2456
|
-
otgVal = ESP32S3_UARTDEV_BUF_NO_USB_OTG;
|
|
2457
|
-
break;
|
|
2458
|
-
case CHIP_FAMILY_ESP32C3: {
|
|
2459
|
-
const bssAddr = revision < 101 ? 0x3fcdf064 : 0x3fcdf060;
|
|
2460
|
-
bufNoAddr = bssAddr + ESP32C3_BUF_UART_NO_OFFSET;
|
|
2461
|
-
jtagSerialVal = ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2462
|
-
break;
|
|
2463
|
-
}
|
|
2464
|
-
case CHIP_FAMILY_ESP32C5:
|
|
2465
|
-
bufNoAddr = ESP32C5_UARTDEV_BUF_NO;
|
|
2466
|
-
jtagSerialVal = ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2467
|
-
break;
|
|
2468
|
-
case CHIP_FAMILY_ESP32C6:
|
|
2469
|
-
bufNoAddr = ESP32C6_UARTDEV_BUF_NO;
|
|
2470
|
-
jtagSerialVal = ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2471
|
-
break;
|
|
2472
|
-
case CHIP_FAMILY_ESP32C61:
|
|
2473
|
-
bufNoAddr =
|
|
2474
|
-
revision <= 200
|
|
2475
|
-
? ESP32C61_UARTDEV_BUF_NO_REV_LE2
|
|
2476
|
-
: ESP32C61_UARTDEV_BUF_NO_REV_GT2;
|
|
2477
|
-
jtagSerialVal =
|
|
2478
|
-
revision <= 200
|
|
2479
|
-
? ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_LE2
|
|
2480
|
-
: ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_GT2;
|
|
2481
|
-
break;
|
|
2482
|
-
case CHIP_FAMILY_ESP32H2:
|
|
2483
|
-
bufNoAddr = ESP32H2_UARTDEV_BUF_NO;
|
|
2484
|
-
jtagSerialVal = ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2485
|
-
break;
|
|
2486
|
-
case CHIP_FAMILY_ESP32H4:
|
|
2487
|
-
bufNoAddr = ESP32H4_UARTDEV_BUF_NO;
|
|
2488
|
-
jtagSerialVal = ESP32H4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2489
|
-
break;
|
|
2490
|
-
case CHIP_FAMILY_ESP32P4:
|
|
2491
|
-
bufNoAddr =
|
|
2492
|
-
revision < 300
|
|
2493
|
-
? ESP32P4_UARTDEV_BUF_NO_REV0
|
|
2494
|
-
: ESP32P4_UARTDEV_BUF_NO_REV300;
|
|
2495
|
-
jtagSerialVal = ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2496
|
-
otgVal = ESP32P4_UARTDEV_BUF_NO_USB_OTG;
|
|
2497
|
-
break;
|
|
2498
|
-
}
|
|
2499
|
-
if (bufNoAddr === null) {
|
|
2500
|
-
return { mode: "uart", uartNo: 0 };
|
|
2501
|
-
}
|
|
2502
|
-
const uartNo = (await this.readRegister(bufNoAddr)) & 0xff;
|
|
2503
|
-
if (otgVal !== null && uartNo === otgVal) {
|
|
2504
|
-
this.logger.debug(`USB mode: USB-OTG (uartNo=${uartNo})`);
|
|
2505
|
-
return { mode: "usb-otg", uartNo };
|
|
2506
|
-
}
|
|
2507
|
-
if (jtagSerialVal !== null && uartNo === jtagSerialVal) {
|
|
2508
|
-
this.logger.debug(`USB mode: USB-JTAG/Serial (uartNo=${uartNo})`);
|
|
2509
|
-
return { mode: "usb-jtag-serial", uartNo };
|
|
2510
|
-
}
|
|
2511
|
-
this.logger.debug(`USB mode: UART (uartNo=${uartNo})`);
|
|
2512
|
-
return { mode: "uart", uartNo };
|
|
2513
|
-
}
|
|
2514
2587
|
async disconnect() {
|
|
2515
2588
|
if (this._parent) {
|
|
2516
2589
|
await this._parent.disconnect();
|
|
@@ -2524,8 +2597,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2524
2597
|
try {
|
|
2525
2598
|
await this._writeChain;
|
|
2526
2599
|
}
|
|
2527
|
-
catch
|
|
2528
|
-
// this.logger.debug(
|
|
2600
|
+
catch {
|
|
2601
|
+
// this.logger.debug("Pending write error during disconnect");
|
|
2529
2602
|
}
|
|
2530
2603
|
// Release persistent writer before closing
|
|
2531
2604
|
if (this._writer) {
|
|
@@ -2533,8 +2606,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2533
2606
|
await this._writer.close();
|
|
2534
2607
|
this._writer.releaseLock();
|
|
2535
2608
|
}
|
|
2536
|
-
catch
|
|
2537
|
-
// this.logger.debug(
|
|
2609
|
+
catch {
|
|
2610
|
+
// this.logger.debug("Writer close/release error");
|
|
2538
2611
|
}
|
|
2539
2612
|
this._writer = undefined;
|
|
2540
2613
|
}
|
|
@@ -2546,8 +2619,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2546
2619
|
await writer.close();
|
|
2547
2620
|
writer.releaseLock();
|
|
2548
2621
|
}
|
|
2549
|
-
catch
|
|
2550
|
-
// this.logger.debug(
|
|
2622
|
+
catch {
|
|
2623
|
+
// this.logger.debug("Direct writer close error");
|
|
2551
2624
|
}
|
|
2552
2625
|
}
|
|
2553
2626
|
await new Promise((resolve) => {
|
|
@@ -2568,7 +2641,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2568
2641
|
try {
|
|
2569
2642
|
this._reader.cancel();
|
|
2570
2643
|
}
|
|
2571
|
-
catch
|
|
2644
|
+
catch {
|
|
2572
2645
|
// Reader already released, resolve immediately
|
|
2573
2646
|
clearTimeout(timeout);
|
|
2574
2647
|
resolve(undefined);
|
|
@@ -2598,8 +2671,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2598
2671
|
try {
|
|
2599
2672
|
await this._writeChain;
|
|
2600
2673
|
}
|
|
2601
|
-
catch
|
|
2602
|
-
// this.logger.debug(
|
|
2674
|
+
catch {
|
|
2675
|
+
// this.logger.debug("Pending write error during release");
|
|
2603
2676
|
}
|
|
2604
2677
|
// Release writer
|
|
2605
2678
|
if (this._writer) {
|
|
@@ -2612,29 +2685,25 @@ export class ESPLoader extends EventTarget {
|
|
|
2612
2685
|
}
|
|
2613
2686
|
this._writer = undefined;
|
|
2614
2687
|
}
|
|
2615
|
-
// Cancel
|
|
2688
|
+
// Cancel reader - let readLoop's finally block handle releaseLock()
|
|
2616
2689
|
if (this._reader) {
|
|
2617
|
-
const reader = this._reader;
|
|
2618
2690
|
try {
|
|
2619
2691
|
// Suppress disconnect event during console mode switching
|
|
2620
2692
|
this._suppressDisconnect = true;
|
|
2621
|
-
|
|
2622
|
-
this.
|
|
2693
|
+
// Cancel will cause readLoop to exit and call releaseLock() in its finally block
|
|
2694
|
+
await this._reader.cancel();
|
|
2695
|
+
this.logger.debug("Reader cancelled - waiting for readLoop to finish");
|
|
2696
|
+
// CRITICAL: Wait a bit for readLoop's finally block to complete
|
|
2697
|
+
// The finally block needs time to call releaseLock() and set _reader = undefined
|
|
2698
|
+
// This is much faster than waiting for browser to unlock (just waiting for JS execution)
|
|
2699
|
+
await sleep(50);
|
|
2700
|
+
this.logger.debug("ReadLoop cleanup should be complete");
|
|
2623
2701
|
}
|
|
2624
2702
|
catch (err) {
|
|
2625
2703
|
this.logger.debug(`Reader cancel error: ${err}`);
|
|
2626
2704
|
}
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
reader.releaseLock();
|
|
2630
|
-
}
|
|
2631
|
-
catch (err) {
|
|
2632
|
-
this.logger.debug(`Reader release error: ${err}`);
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
if (this._reader === reader) {
|
|
2636
|
-
this._reader = undefined;
|
|
2637
|
-
}
|
|
2705
|
+
// Don't call releaseLock() or set _reader to undefined here
|
|
2706
|
+
// Let readLoop's finally block handle it to avoid race conditions
|
|
2638
2707
|
}
|
|
2639
2708
|
}
|
|
2640
2709
|
/**
|
|
@@ -2673,6 +2742,141 @@ export class ESPLoader extends EventTarget {
|
|
|
2673
2742
|
this.logger.debug(`USB-JTAG/OTG detection: ${isUsbJtag ? "YES" : "NO"} (PID=0x${pid === null || pid === void 0 ? void 0 : pid.toString(16)})`);
|
|
2674
2743
|
return isUsbJtag;
|
|
2675
2744
|
}
|
|
2745
|
+
async getUsbMode() {
|
|
2746
|
+
var _a, _b;
|
|
2747
|
+
const family = this._parent ? this._parent.chipFamily : this.chipFamily;
|
|
2748
|
+
const revision = this._parent
|
|
2749
|
+
? ((_a = this._parent.chipRevision) !== null && _a !== void 0 ? _a : 0)
|
|
2750
|
+
: ((_b = this.chipRevision) !== null && _b !== void 0 ? _b : 0);
|
|
2751
|
+
let bufNoAddr = null;
|
|
2752
|
+
let jtagSerialVal = null;
|
|
2753
|
+
let otgVal = null;
|
|
2754
|
+
switch (family) {
|
|
2755
|
+
case CHIP_FAMILY_ESP32S2:
|
|
2756
|
+
bufNoAddr = ESP32S2_UARTDEV_BUF_NO;
|
|
2757
|
+
otgVal = ESP32S2_UARTDEV_BUF_NO_USB_OTG;
|
|
2758
|
+
break;
|
|
2759
|
+
case CHIP_FAMILY_ESP32S3:
|
|
2760
|
+
bufNoAddr = ESP32S3_UARTDEV_BUF_NO;
|
|
2761
|
+
jtagSerialVal = ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2762
|
+
otgVal = ESP32S3_UARTDEV_BUF_NO_USB_OTG;
|
|
2763
|
+
break;
|
|
2764
|
+
case CHIP_FAMILY_ESP32C3: {
|
|
2765
|
+
const bssAddr = revision < 101 ? 0x3fcdf064 : 0x3fcdf060;
|
|
2766
|
+
bufNoAddr = bssAddr + ESP32C3_BUF_UART_NO_OFFSET;
|
|
2767
|
+
jtagSerialVal = ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2768
|
+
break;
|
|
2769
|
+
}
|
|
2770
|
+
case CHIP_FAMILY_ESP32C5:
|
|
2771
|
+
bufNoAddr = ESP32C5_UARTDEV_BUF_NO;
|
|
2772
|
+
jtagSerialVal = ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2773
|
+
break;
|
|
2774
|
+
case CHIP_FAMILY_ESP32C6:
|
|
2775
|
+
bufNoAddr = ESP32C6_UARTDEV_BUF_NO;
|
|
2776
|
+
jtagSerialVal = ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2777
|
+
break;
|
|
2778
|
+
case CHIP_FAMILY_ESP32C61:
|
|
2779
|
+
bufNoAddr =
|
|
2780
|
+
revision <= 200
|
|
2781
|
+
? ESP32C61_UARTDEV_BUF_NO_REV_LE2
|
|
2782
|
+
: ESP32C61_UARTDEV_BUF_NO_REV_GT2;
|
|
2783
|
+
jtagSerialVal =
|
|
2784
|
+
revision <= 200
|
|
2785
|
+
? ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_LE2
|
|
2786
|
+
: ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_GT2;
|
|
2787
|
+
break;
|
|
2788
|
+
case CHIP_FAMILY_ESP32H2:
|
|
2789
|
+
bufNoAddr = ESP32H2_UARTDEV_BUF_NO;
|
|
2790
|
+
jtagSerialVal = ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2791
|
+
break;
|
|
2792
|
+
case CHIP_FAMILY_ESP32H4:
|
|
2793
|
+
bufNoAddr = ESP32H4_UARTDEV_BUF_NO;
|
|
2794
|
+
jtagSerialVal = ESP32H4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2795
|
+
break;
|
|
2796
|
+
case CHIP_FAMILY_ESP32P4:
|
|
2797
|
+
bufNoAddr =
|
|
2798
|
+
revision < 300
|
|
2799
|
+
? ESP32P4_UARTDEV_BUF_NO_REV0
|
|
2800
|
+
: ESP32P4_UARTDEV_BUF_NO_REV300;
|
|
2801
|
+
jtagSerialVal = ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2802
|
+
otgVal = ESP32P4_UARTDEV_BUF_NO_USB_OTG;
|
|
2803
|
+
break;
|
|
2804
|
+
}
|
|
2805
|
+
if (bufNoAddr === null) {
|
|
2806
|
+
return { mode: "uart", uartNo: 0 };
|
|
2807
|
+
}
|
|
2808
|
+
const uartNo = (await this.readRegister(bufNoAddr)) & 0xff;
|
|
2809
|
+
if (otgVal !== null && uartNo === otgVal) {
|
|
2810
|
+
this.logger.debug(`USB mode: USB-OTG (uartNo=${uartNo})`);
|
|
2811
|
+
return { mode: "usb-otg", uartNo };
|
|
2812
|
+
}
|
|
2813
|
+
if (jtagSerialVal !== null && uartNo === jtagSerialVal) {
|
|
2814
|
+
this.logger.debug(`USB mode: USB-JTAG/Serial (uartNo=${uartNo})`);
|
|
2815
|
+
return { mode: "usb-jtag-serial", uartNo };
|
|
2816
|
+
}
|
|
2817
|
+
this.logger.debug(`USB mode: UART (uartNo=${uartNo})`);
|
|
2818
|
+
return { mode: "uart", uartNo };
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Check if the current chip supports USB-JTAG or USB-OTG
|
|
2822
|
+
* @returns true if chip has native USB support (JTAG or OTG)
|
|
2823
|
+
*/
|
|
2824
|
+
supportsNativeUsb() {
|
|
2825
|
+
const family = this._parent ? this._parent.chipFamily : this.chipFamily;
|
|
2826
|
+
// Chips with USB-JTAG/Serial or USB-OTG support
|
|
2827
|
+
const usbChips = [
|
|
2828
|
+
CHIP_FAMILY_ESP32S2, // USB-OTG
|
|
2829
|
+
CHIP_FAMILY_ESP32S3, // USB-OTG + USB-JTAG/Serial
|
|
2830
|
+
CHIP_FAMILY_ESP32C3, // USB-JTAG/Serial
|
|
2831
|
+
CHIP_FAMILY_ESP32C5, // USB-JTAG/Serial
|
|
2832
|
+
CHIP_FAMILY_ESP32C6, // USB-JTAG/Serial
|
|
2833
|
+
CHIP_FAMILY_ESP32C61, // USB-JTAG/Serial
|
|
2834
|
+
CHIP_FAMILY_ESP32H2, // USB-JTAG/Serial
|
|
2835
|
+
CHIP_FAMILY_ESP32H4, // USB-JTAG/Serial
|
|
2836
|
+
CHIP_FAMILY_ESP32P4, // USB-OTG + USB-JTAG/Serial
|
|
2837
|
+
];
|
|
2838
|
+
return usbChips.includes(family);
|
|
2839
|
+
}
|
|
2840
|
+
/**
|
|
2841
|
+
* @name _ensureStreamsReady
|
|
2842
|
+
* After a hardware reset, ensure port streams are available.
|
|
2843
|
+
* On WebUSB, recreates streams since they break after reset.
|
|
2844
|
+
* On Web Serial, waits for streams to become available.
|
|
2845
|
+
*/
|
|
2846
|
+
async _ensureStreamsReady() {
|
|
2847
|
+
if (this.isWebUSB()) {
|
|
2848
|
+
try {
|
|
2849
|
+
await this.port.recreateStreams();
|
|
2850
|
+
this.logger.debug("WebUSB streams recreated");
|
|
2851
|
+
let retries = 30;
|
|
2852
|
+
while (retries > 0 && !this.port.readable) {
|
|
2853
|
+
await sleep(100);
|
|
2854
|
+
retries--;
|
|
2855
|
+
}
|
|
2856
|
+
if (!this.port.readable) {
|
|
2857
|
+
throw new Error("Readable stream not available after recreating streams");
|
|
2858
|
+
}
|
|
2859
|
+
this.logger.debug("WebUSB streams are ready");
|
|
2860
|
+
}
|
|
2861
|
+
catch (err) {
|
|
2862
|
+
this.logger.error(`Failed to recreate WebUSB streams: ${err}`);
|
|
2863
|
+
this._consoleMode = false;
|
|
2864
|
+
throw err;
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
else {
|
|
2868
|
+
let retries = 20;
|
|
2869
|
+
while (retries > 0 && !this.port.readable) {
|
|
2870
|
+
await sleep(100);
|
|
2871
|
+
retries--;
|
|
2872
|
+
}
|
|
2873
|
+
if (!this.port.readable) {
|
|
2874
|
+
this._consoleMode = false;
|
|
2875
|
+
throw new Error("Readable stream not available after reset");
|
|
2876
|
+
}
|
|
2877
|
+
this.logger.debug("Port streams are ready");
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2676
2880
|
/**
|
|
2677
2881
|
* @name enterConsoleMode
|
|
2678
2882
|
* Prepare device for console mode by resetting to firmware
|
|
@@ -2700,60 +2904,41 @@ export class ESPLoader extends EventTarget {
|
|
|
2700
2904
|
if (this.isUsbJtagOrOtg === undefined) {
|
|
2701
2905
|
throw new Error(`Cannot enter console mode: USB connection type unknown and detection failed: ${err}`);
|
|
2702
2906
|
}
|
|
2703
|
-
// Set console mode flag
|
|
2704
|
-
this._consoleMode = false;
|
|
2705
2907
|
this.logger.debug(`USB detection failed, using cached value: ${this.isUsbJtagOrOtg}`);
|
|
2706
2908
|
isUsbJtag = this.isUsbJtagOrOtg;
|
|
2707
2909
|
}
|
|
2708
|
-
//
|
|
2709
|
-
|
|
2910
|
+
// Set console mode flag BEFORE any operations
|
|
2911
|
+
this._consoleMode = true;
|
|
2710
2912
|
if (isUsbJtag) {
|
|
2711
|
-
// USB-JTAG/OTG devices: Use
|
|
2913
|
+
// USB-JTAG/OTG devices: Use reset which may close port
|
|
2712
2914
|
const wasReset = await this._resetToFirmwareIfNeeded();
|
|
2713
|
-
|
|
2915
|
+
if (wasReset) {
|
|
2916
|
+
return true; // port closed, caller must reopen
|
|
2917
|
+
}
|
|
2918
|
+
// Port stayed open (e.g. C3/C5/C6/H2 classic reset)
|
|
2919
|
+
await this._ensureStreamsReady();
|
|
2920
|
+
return false;
|
|
2714
2921
|
}
|
|
2715
2922
|
else {
|
|
2716
2923
|
// External serial chip devices: Release locks and do simple reset
|
|
2717
2924
|
try {
|
|
2718
2925
|
await this.releaseReaderWriter();
|
|
2719
|
-
await
|
|
2926
|
+
await sleep(100);
|
|
2720
2927
|
}
|
|
2721
2928
|
catch (err) {
|
|
2722
2929
|
this.logger.debug(`Failed to release locks: ${err}`);
|
|
2723
2930
|
}
|
|
2724
|
-
// Hardware reset to firmware mode (IO0=HIGH)
|
|
2725
2931
|
try {
|
|
2726
|
-
await this.
|
|
2727
|
-
this.logger.
|
|
2932
|
+
await this.hardResetToFirmware();
|
|
2933
|
+
this.logger.debug("Device reset to firmware mode");
|
|
2728
2934
|
}
|
|
2729
2935
|
catch (err) {
|
|
2730
2936
|
this.logger.debug(`Could not reset device: ${err}`);
|
|
2731
2937
|
}
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
try {
|
|
2735
|
-
// Use the public recreateStreams() method to safely recreate streams
|
|
2736
|
-
// without closing the port (important after hardware reset)
|
|
2737
|
-
await this.port.recreateStreams();
|
|
2738
|
-
this.logger.debug("WebUSB streams recreated for console mode");
|
|
2739
|
-
}
|
|
2740
|
-
catch (err) {
|
|
2741
|
-
// Set console mode flag
|
|
2742
|
-
this._consoleMode = false;
|
|
2743
|
-
this.logger.debug(`Failed to recreate WebUSB streams: ${err}`);
|
|
2744
|
-
}
|
|
2745
|
-
}
|
|
2746
|
-
// Set console mode flag
|
|
2747
|
-
this._consoleMode = true;
|
|
2748
|
-
return false; // Port stays open
|
|
2938
|
+
await this._ensureStreamsReady();
|
|
2939
|
+
return false;
|
|
2749
2940
|
}
|
|
2750
2941
|
}
|
|
2751
|
-
/**
|
|
2752
|
-
* @name _resetToFirmwareIfNeeded
|
|
2753
|
-
* Reset device from bootloader to firmware when switching to console mode
|
|
2754
|
-
* Detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
2755
|
-
* @returns true if reconnect was performed, false otherwise
|
|
2756
|
-
*/
|
|
2757
2942
|
/**
|
|
2758
2943
|
* @name _clearForceDownloadBootIfNeeded
|
|
2759
2944
|
* Read and clear the force download boot flag if it is set
|
|
@@ -2808,99 +2993,66 @@ export class ESPLoader extends EventTarget {
|
|
|
2808
2993
|
return false;
|
|
2809
2994
|
}
|
|
2810
2995
|
}
|
|
2996
|
+
/**
|
|
2997
|
+
* @name _resetToFirmwareIfNeeded
|
|
2998
|
+
* Reset device from bootloader to firmware when switching to console mode
|
|
2999
|
+
* Detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
3000
|
+
* @returns true if reconnect was performed, false otherwise
|
|
3001
|
+
*/
|
|
2811
3002
|
async _resetToFirmwareIfNeeded() {
|
|
3003
|
+
// Detect if we need WDT reset (USB-JTAG/OTG) or classic reset
|
|
3004
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
2812
3005
|
try {
|
|
2813
3006
|
// Check if port is open - if not, assume device is already in firmware mode
|
|
2814
3007
|
if (!this.port.writable || !this.port.readable) {
|
|
2815
3008
|
this.logger.debug("Port is not open - assuming device is already in firmware mode");
|
|
2816
3009
|
return false;
|
|
2817
3010
|
}
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
//
|
|
2821
|
-
//
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
// Mark that we're no longer on stub
|
|
2847
|
-
this.IS_STUB = false;
|
|
2848
|
-
}
|
|
2849
|
-
catch (resetErr) {
|
|
2850
|
-
this.logger.debug(`Reset to ROM failed: ${resetErr}`);
|
|
2851
|
-
// If reset fails, we might already be in firmware mode
|
|
2852
|
-
// In this case, we don't need to do anything - just use normal reset
|
|
2853
|
-
this.logger.debug("Assuming device is already in firmware mode");
|
|
2854
|
-
// Release reader/writer before returning
|
|
2855
|
-
await this.releaseReaderWriter();
|
|
2856
|
-
return false; // No port change needed
|
|
2857
|
-
}
|
|
2858
|
-
}
|
|
2859
|
-
else {
|
|
2860
|
-
this.logger.debug("Already on ROM - checking force download flag");
|
|
2861
|
-
}
|
|
2862
|
-
// Now check if force download flag is set and clear it if needed
|
|
2863
|
-
const flagWasCleared = await this._clearForceDownloadBootIfNeeded();
|
|
2864
|
-
if (flagWasCleared) {
|
|
2865
|
-
this.logger.debug("Force download flag was cleared - device will boot to firmware after reset");
|
|
2866
|
-
}
|
|
2867
|
-
else {
|
|
2868
|
-
this.logger.debug("Force download flag already clear - device will boot to firmware after reset");
|
|
2869
|
-
}
|
|
2870
|
-
// Perform WDT reset BEFORE releasing reader/writer (needs communication)
|
|
2871
|
-
// After WDT reset, the device will reboot into firmware mode
|
|
2872
|
-
await this.hardReset(false);
|
|
2873
|
-
// For USB-OTG devices (ESP32-S2, ESP32-P4), the port will change after WDT reset
|
|
2874
|
-
const portWillChange = (this.chipFamily === CHIP_FAMILY_ESP32S2 && isUsingUsbOtg) ||
|
|
2875
|
-
(this.chipFamily === CHIP_FAMILY_ESP32P4 && isUsingUsbOtg);
|
|
2876
|
-
if (portWillChange) {
|
|
2877
|
-
// Port will change - release reader/writer and let the port become invalid
|
|
2878
|
-
await this.releaseReaderWriter();
|
|
2879
|
-
this.logger.log(`${this.chipName} USB-OTG: Port will change after WDT reset`);
|
|
2880
|
-
this.logger.log("Please select the new port for console mode");
|
|
2881
|
-
// Dispatch event to signal port change
|
|
2882
|
-
this.dispatchEvent(new CustomEvent("usb-otg-port-change", {
|
|
2883
|
-
detail: {
|
|
2884
|
-
chipName: this.chipName,
|
|
2885
|
-
message: `${this.chipName} USB port changed after reset. Please select the new port.`,
|
|
2886
|
-
reason: "wdt-reset-to-firmware",
|
|
2887
|
-
},
|
|
2888
|
-
}));
|
|
2889
|
-
// Return true to indicate port selection is needed
|
|
2890
|
-
return true;
|
|
2891
|
-
}
|
|
2892
|
-
else {
|
|
2893
|
-
// Port stays the same - release reader/writer so console can use the stream
|
|
3011
|
+
if (isUsbJtagOrOtg) {
|
|
3012
|
+
// USB-JTAG/OTG: DON'T release reader/writer before WDT reset
|
|
3013
|
+
// The WDT reset needs active communication to send register write commands
|
|
3014
|
+
// The port will close automatically after the WDT reset anyway
|
|
3015
|
+
this.logger.debug("USB-JTAG/OTG: Keeping reader/writer active for WDT reset");
|
|
3016
|
+
}
|
|
3017
|
+
else {
|
|
3018
|
+
// External serial chip: Release reader/writer before classic reset
|
|
3019
|
+
await this.releaseReaderWriter();
|
|
3020
|
+
this.logger.debug("External serial: Reader/writer released before reset");
|
|
3021
|
+
}
|
|
3022
|
+
// Use the new resetToFirmwareMode method which handles all the logic
|
|
3023
|
+
const portWillChange = await this.resetToFirmwareMode(true);
|
|
3024
|
+
if (portWillChange) {
|
|
3025
|
+
this.logger.debug(`${this.chipName}: Port will change after WDT reset - user must reselect port`);
|
|
3026
|
+
// Dispatch event to signal port change
|
|
3027
|
+
this.dispatchEvent(new CustomEvent("usb-otg-port-change", {
|
|
3028
|
+
detail: {
|
|
3029
|
+
chipName: this.chipName,
|
|
3030
|
+
message: `${this.chipName} USB port changed after reset. Please select the new port.`,
|
|
3031
|
+
reason: "wdt-reset-to-firmware",
|
|
3032
|
+
},
|
|
3033
|
+
}));
|
|
3034
|
+
return true;
|
|
3035
|
+
}
|
|
3036
|
+
else {
|
|
3037
|
+
// Port stays the same - release reader/writer now if not already done
|
|
3038
|
+
if (isUsbJtagOrOtg) {
|
|
2894
3039
|
await this.releaseReaderWriter();
|
|
2895
|
-
|
|
3040
|
+
this.logger.debug("Reader/writer released after reset");
|
|
2896
3041
|
}
|
|
3042
|
+
return false;
|
|
2897
3043
|
}
|
|
2898
3044
|
}
|
|
2899
3045
|
catch (err) {
|
|
2900
|
-
this.logger.
|
|
2901
|
-
//
|
|
3046
|
+
this.logger.error(`Reset to firmware mode failed: ${err}`);
|
|
3047
|
+
// For USB-JTAG/OTG, the port is likely dead after a failed reset
|
|
3048
|
+
// For external serial, the port is usually still fine
|
|
3049
|
+
if (isUsbJtagOrOtg) {
|
|
3050
|
+
this.logger.debug("Forcing port reselection due to USB-JTAG/OTG reset failure");
|
|
3051
|
+
return true;
|
|
3052
|
+
}
|
|
3053
|
+
this.logger.debug("External serial reset failed, but port should still be usable");
|
|
3054
|
+
return false;
|
|
2902
3055
|
}
|
|
2903
|
-
return false;
|
|
2904
3056
|
}
|
|
2905
3057
|
/**
|
|
2906
3058
|
* @name reconnectAndResume
|
|
@@ -3114,7 +3266,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3114
3266
|
await this.connectWithResetStrategies();
|
|
3115
3267
|
// Detect chip type
|
|
3116
3268
|
await this.detectChip();
|
|
3117
|
-
this.logger.
|
|
3269
|
+
this.logger.debug(`Reconnected to bootloader: ${this.chipName}`);
|
|
3118
3270
|
}
|
|
3119
3271
|
catch (err) {
|
|
3120
3272
|
// Ensure flag is reset on error
|
|
@@ -3151,7 +3303,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3151
3303
|
}
|
|
3152
3304
|
if (isUsbOtgChip && isUsbJtagOrOtg) {
|
|
3153
3305
|
// USB-OTG devices: Need to reset to bootloader, which will cause port change
|
|
3154
|
-
this.logger.
|
|
3306
|
+
this.logger.debug(`${this.chipName} USB: Resetting to bootloader mode`);
|
|
3155
3307
|
// Perform hardware reset to bootloader (GPIO0=LOW)
|
|
3156
3308
|
// This will cause the port to change from CDC (firmware) to JTAG (bootloader)
|
|
3157
3309
|
try {
|
|
@@ -3163,7 +3315,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3163
3315
|
}
|
|
3164
3316
|
// Wait for reset to complete and port to change
|
|
3165
3317
|
await sleep(500);
|
|
3166
|
-
this.logger.
|
|
3318
|
+
this.logger.debug(`${this.chipName}: Port changed. Please select the bootloader port.`);
|
|
3167
3319
|
// Dispatch event to signal port change
|
|
3168
3320
|
this.dispatchEvent(new CustomEvent("usb-otg-port-change", {
|
|
3169
3321
|
detail: {
|
|
@@ -3208,8 +3360,10 @@ export class ESPLoader extends EventTarget {
|
|
|
3208
3360
|
return await this._parent.resetInConsoleMode();
|
|
3209
3361
|
}
|
|
3210
3362
|
if (!this.isConsoleResetSupported()) {
|
|
3211
|
-
this.logger.debug("Console reset not supported for ESP32-S2 USB-JTAG/CDC");
|
|
3212
|
-
|
|
3363
|
+
this.logger.debug("Simple Console reset not supported for ESP32-S2 USB-JTAG/CDC - using exitConsoleMode to enter bootloader");
|
|
3364
|
+
await this.exitConsoleMode();
|
|
3365
|
+
this.logger.debug("S2 now in bootloader mode - caller must do syncAndWdtReset on new port, then reconnect console");
|
|
3366
|
+
return;
|
|
3213
3367
|
}
|
|
3214
3368
|
// For other devices: Use standard firmware reset
|
|
3215
3369
|
try {
|
|
@@ -3222,6 +3376,41 @@ export class ESPLoader extends EventTarget {
|
|
|
3222
3376
|
throw err;
|
|
3223
3377
|
}
|
|
3224
3378
|
}
|
|
3379
|
+
/**
|
|
3380
|
+
* @name syncAndWdtReset
|
|
3381
|
+
* Open a new bootloader port, sync with ROM (no stub, no reset strategies), and fire WDT reset.
|
|
3382
|
+
* This is used for ESP32-S2 USB-OTG devices which require WDT reset to switch modes.
|
|
3383
|
+
* After WDT reset the port will re-enumerate again.
|
|
3384
|
+
* The user must select the new port after this method is called.
|
|
3385
|
+
* @param newPort - The bootloader port selected by the user
|
|
3386
|
+
*/
|
|
3387
|
+
async syncAndWdtReset(newPort) {
|
|
3388
|
+
if (this._parent) {
|
|
3389
|
+
await this._parent.syncAndWdtReset(newPort);
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
3392
|
+
this.port = newPort;
|
|
3393
|
+
this.connected = false;
|
|
3394
|
+
this.IS_STUB = false;
|
|
3395
|
+
this.__inputBuffer = [];
|
|
3396
|
+
this.__inputBufferReadIndex = 0;
|
|
3397
|
+
this.__totalBytesRead = 0;
|
|
3398
|
+
this.logger.debug("Opening bootloader port at 115200...");
|
|
3399
|
+
await this.port.open({ baudRate: ESP_ROM_BAUD });
|
|
3400
|
+
this.connected = true;
|
|
3401
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
3402
|
+
// Start read loop
|
|
3403
|
+
this.readLoop();
|
|
3404
|
+
await sleep(100);
|
|
3405
|
+
// Sync with ROM only - no reset strategies, device is already in bootloader
|
|
3406
|
+
this.logger.debug("Syncing with bootloader ROM...");
|
|
3407
|
+
await this.sync();
|
|
3408
|
+
this.logger.debug("Bootloader sync OK, no stub");
|
|
3409
|
+
// Fire WDT reset → device boots into firmware
|
|
3410
|
+
this.logger.debug("Firing WDT reset...");
|
|
3411
|
+
await this.rtcWdtResetChipSpecific();
|
|
3412
|
+
this.logger.debug("WDT reset fired - device will boot to firmware");
|
|
3413
|
+
}
|
|
3225
3414
|
/**
|
|
3226
3415
|
* @name drainInputBuffer
|
|
3227
3416
|
* Actively drain the input buffer by reading data for a specified time.
|
|
@@ -3395,7 +3584,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3395
3584
|
}
|
|
3396
3585
|
catch (err) {
|
|
3397
3586
|
if (err instanceof SlipReadError) {
|
|
3398
|
-
this.logger.debug(
|
|
3587
|
+
this.logger.debug(`${err.message} at byte 0x${resp.length.toString(16)}`);
|
|
3399
3588
|
// Send empty SLIP frame to abort the stub's read operation
|
|
3400
3589
|
// The stub expects 4 bytes (ACK), if we send less it will break out
|
|
3401
3590
|
try {
|
|
@@ -3505,18 +3694,8 @@ export class ESPLoader extends EventTarget {
|
|
|
3505
3694
|
// Check if it's a timeout error or SLIP error
|
|
3506
3695
|
if (err instanceof SlipReadError) {
|
|
3507
3696
|
if (retryCount <= MAX_RETRIES) {
|
|
3508
|
-
this.logger.debug(
|
|
3509
|
-
|
|
3510
|
-
await this.drainInputBuffer(200);
|
|
3511
|
-
// Clear application buffer
|
|
3512
|
-
await this.flushSerialBuffers();
|
|
3513
|
-
// Wait before retry to let hardware settle
|
|
3514
|
-
await sleep(SYNC_TIMEOUT);
|
|
3515
|
-
// Continue to retry the same chunk (will send NEW read command)
|
|
3516
|
-
}
|
|
3517
|
-
catch (drainErr) {
|
|
3518
|
-
this.logger.debug(`Buffer drain error: ${drainErr}`);
|
|
3519
|
-
}
|
|
3697
|
+
this.logger.debug(`Cleared buffer and retrying (attempt ${retryCount}/${MAX_RETRIES})...`);
|
|
3698
|
+
// Continue to retry the same chunk (will send NEW read command)
|
|
3520
3699
|
}
|
|
3521
3700
|
else {
|
|
3522
3701
|
// All retries exhausted - attempt recovery by reloading stub
|