esp32tool 1.3.7 → 1.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apple-touch-icon.png +0 -0
- package/dist/console.js +18 -7
- package/dist/const.d.ts +6 -0
- package/dist/const.js +8 -0
- package/dist/esp_loader.d.ts +37 -30
- package/dist/esp_loader.js +502 -367
- package/dist/web/index.js +1 -1
- package/icons/icon-128.png +0 -0
- package/icons/icon-144.png +0 -0
- package/icons/icon-152.png +0 -0
- package/icons/icon-192.png +0 -0
- package/icons/icon-384.png +0 -0
- package/icons/icon-512.png +0 -0
- package/icons/icon-72.png +0 -0
- package/icons/icon-96.png +0 -0
- package/js/console.js +60 -0
- package/js/modules/esptool.js +1 -1
- package/js/script.js +156 -113
- package/package.json +1 -1
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/console.ts +28 -11
- package/src/const.ts +8 -0
- package/src/esp_loader.ts +610 -399
- package/sw.js +1 -1
package/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, 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, 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, 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, } from "./const";
|
|
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, 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, 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
4
|
import { hexFormatter, sleep, slipEncode, toHex } from "./util";
|
|
5
5
|
import { FLASH_MANUFACTURERS, FLASH_DEVICES } from "./flash_jedec";
|
|
@@ -358,7 +358,13 @@ export class ESPLoader extends EventTarget {
|
|
|
358
358
|
}
|
|
359
359
|
catch (err) {
|
|
360
360
|
this.logger.debug(`Could not detect USB connection type: ${err}`);
|
|
361
|
-
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
const usbMode = await this.getUsbMode();
|
|
364
|
+
this.logger.debug(`USB mode (register): ${usbMode.mode} (uartNo=${usbMode.uartNo})`);
|
|
365
|
+
}
|
|
366
|
+
catch (err) {
|
|
367
|
+
this.logger.debug(`Could not detect USB mode: ${err}`);
|
|
362
368
|
}
|
|
363
369
|
// Read the OTP data for this chip and store into this.efuses array
|
|
364
370
|
const FlAddr = getSpiFlashAddresses(this.getChipFamily());
|
|
@@ -672,9 +678,6 @@ export class ESPLoader extends EventTarget {
|
|
|
672
678
|
this._suppressDisconnect = false;
|
|
673
679
|
this.logger.debug("Finished read loop");
|
|
674
680
|
}
|
|
675
|
-
sleep(ms = 100) {
|
|
676
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
677
|
-
}
|
|
678
681
|
// ============================================================================
|
|
679
682
|
// Web Serial (Desktop) - DTR/RTS Signal Handling & Reset Strategies
|
|
680
683
|
// ============================================================================
|
|
@@ -698,77 +701,82 @@ export class ESPLoader extends EventTarget {
|
|
|
698
701
|
requestToSend: rts,
|
|
699
702
|
});
|
|
700
703
|
}
|
|
704
|
+
async runSignalSequence(steps) {
|
|
705
|
+
const webusb = this.port.isWebUSB === true;
|
|
706
|
+
for (const step of steps) {
|
|
707
|
+
if (step.dtr !== undefined && step.rts !== undefined) {
|
|
708
|
+
if (webusb) {
|
|
709
|
+
await this.setDTRandRTSWebUSB(step.dtr, step.rts);
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
await this.setDTRandRTS(step.dtr, step.rts);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
if (step.dtr !== undefined) {
|
|
717
|
+
webusb
|
|
718
|
+
? await this.setDTRWebUSB(step.dtr)
|
|
719
|
+
: await this.setDTR(step.dtr);
|
|
720
|
+
}
|
|
721
|
+
if (step.rts !== undefined) {
|
|
722
|
+
webusb
|
|
723
|
+
? await this.setRTSWebUSB(step.rts)
|
|
724
|
+
: await this.setRTS(step.rts);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (step.delayMs)
|
|
728
|
+
await sleep(step.delayMs);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
701
731
|
/**
|
|
702
732
|
* @name hardResetUSBJTAGSerial
|
|
703
733
|
* USB-JTAG/Serial reset for Web Serial (Desktop)
|
|
704
734
|
*/
|
|
705
735
|
async hardResetUSBJTAGSerial() {
|
|
706
|
-
await this.
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
await this.setRTS(true);
|
|
715
|
-
await this.sleep(100);
|
|
716
|
-
await this.setDTR(false);
|
|
717
|
-
await this.setRTS(false); // Chip out of reset
|
|
718
|
-
await this.sleep(200);
|
|
736
|
+
await this.runSignalSequence([
|
|
737
|
+
{ rts: false },
|
|
738
|
+
{ dtr: false, delayMs: 100 },
|
|
739
|
+
{ dtr: true, rts: false, delayMs: 100 },
|
|
740
|
+
{ rts: true },
|
|
741
|
+
{ dtr: false, rts: true, delayMs: 100 },
|
|
742
|
+
{ dtr: false, rts: false, delayMs: 200 },
|
|
743
|
+
]);
|
|
719
744
|
}
|
|
720
745
|
/**
|
|
721
746
|
* @name hardResetClassic
|
|
722
747
|
* Classic reset for Web Serial (Desktop) DTR = IO0, RTS = EN
|
|
723
748
|
*/
|
|
724
749
|
async hardResetClassic() {
|
|
725
|
-
await this.
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
await this.sleep(50);
|
|
731
|
-
await this.setDTR(false); // IO0=HIGH, done
|
|
732
|
-
await this.sleep(200);
|
|
750
|
+
await this.runSignalSequence([
|
|
751
|
+
{ dtr: false, rts: true, delayMs: 100 },
|
|
752
|
+
{ dtr: true, rts: false, delayMs: 50 },
|
|
753
|
+
{ dtr: false, delayMs: 200 },
|
|
754
|
+
]);
|
|
733
755
|
}
|
|
734
756
|
/**
|
|
735
757
|
* Reset to firmware mode (not bootloader) for Web Serial
|
|
736
758
|
* Keeps IO0=HIGH during reset so chip boots into firmware
|
|
737
759
|
*/
|
|
738
760
|
async hardResetToFirmware() {
|
|
739
|
-
await this.
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
await this.sleep(200);
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* Reset to firmware mode (not bootloader) for WebUSB
|
|
748
|
-
* Keeps IO0=HIGH during reset so chip boots into firmware
|
|
749
|
-
*/
|
|
750
|
-
async hardResetToFirmwareWebUSB() {
|
|
751
|
-
await this.setDTRWebUSB(false); // IO0=HIGH
|
|
752
|
-
await this.setRTSWebUSB(true); // EN=LOW, chip in reset
|
|
753
|
-
await this.sleep(100);
|
|
754
|
-
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset (IO0 stays HIGH)
|
|
755
|
-
await this.sleep(50);
|
|
756
|
-
await this.sleep(200);
|
|
761
|
+
await this.runSignalSequence([
|
|
762
|
+
{ dtr: false, rts: true, delayMs: 100 },
|
|
763
|
+
{ rts: false, delayMs: 50 },
|
|
764
|
+
{ delayMs: 200 },
|
|
765
|
+
]);
|
|
757
766
|
}
|
|
758
767
|
/**
|
|
759
768
|
* @name hardResetUnixTight
|
|
760
769
|
* Unix Tight reset for Web Serial (Desktop) - sets DTR and RTS simultaneously
|
|
761
770
|
*/
|
|
762
771
|
async hardResetUnixTight() {
|
|
763
|
-
await this.
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
await this.sleep(200);
|
|
772
|
+
await this.runSignalSequence([
|
|
773
|
+
{ dtr: true, rts: true },
|
|
774
|
+
{ dtr: false, rts: false },
|
|
775
|
+
{ dtr: false, rts: true, delayMs: 100 },
|
|
776
|
+
{ dtr: true, rts: false, delayMs: 50 },
|
|
777
|
+
{ dtr: false, rts: false },
|
|
778
|
+
{ dtr: false, delayMs: 200 },
|
|
779
|
+
]);
|
|
772
780
|
}
|
|
773
781
|
// ============================================================================
|
|
774
782
|
// WebUSB (Android) - DTR/RTS Signal Handling & Reset Strategies
|
|
@@ -798,72 +806,17 @@ export class ESPLoader extends EventTarget {
|
|
|
798
806
|
requestToSend: rts,
|
|
799
807
|
});
|
|
800
808
|
}
|
|
801
|
-
/**
|
|
802
|
-
* @name hardResetUSBJTAGSerialWebUSB
|
|
803
|
-
* USB-JTAG/Serial reset for WebUSB (Android)
|
|
804
|
-
*/
|
|
805
|
-
async hardResetUSBJTAGSerialWebUSB() {
|
|
806
|
-
await this.setRTSWebUSB(false);
|
|
807
|
-
await this.setDTRWebUSB(false); // Idle
|
|
808
|
-
await this.sleep(100);
|
|
809
|
-
await this.setDTRWebUSB(true); // Set IO0
|
|
810
|
-
await this.setRTSWebUSB(false);
|
|
811
|
-
await this.sleep(100);
|
|
812
|
-
await this.setRTSWebUSB(true); // Reset
|
|
813
|
-
await this.setDTRWebUSB(false);
|
|
814
|
-
await this.setRTSWebUSB(true);
|
|
815
|
-
await this.sleep(100);
|
|
816
|
-
await this.setDTRWebUSB(false);
|
|
817
|
-
await this.setRTSWebUSB(false); // Chip out of reset
|
|
818
|
-
await this.sleep(200);
|
|
819
|
-
}
|
|
820
809
|
/**
|
|
821
810
|
* @name hardResetUSBJTAGSerialInvertedDTRWebUSB
|
|
822
811
|
* USB-JTAG/Serial reset with inverted DTR for WebUSB (Android)
|
|
823
812
|
*/
|
|
824
813
|
async hardResetUSBJTAGSerialInvertedDTRWebUSB() {
|
|
825
|
-
await this.
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
await this.setRTSWebUSB(true); // Reset
|
|
832
|
-
await this.setDTRWebUSB(true); // (DTR inverted)
|
|
833
|
-
await this.setRTSWebUSB(true);
|
|
834
|
-
await this.sleep(100);
|
|
835
|
-
await this.setDTRWebUSB(true); // (DTR inverted)
|
|
836
|
-
await this.setRTSWebUSB(false); // Chip out of reset
|
|
837
|
-
await this.sleep(200);
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* @name hardResetClassicWebUSB
|
|
841
|
-
* Classic reset for WebUSB (Android)
|
|
842
|
-
*/
|
|
843
|
-
async hardResetClassicWebUSB() {
|
|
844
|
-
await this.setDTRWebUSB(false); // IO0=HIGH
|
|
845
|
-
await this.setRTSWebUSB(true); // EN=LOW, chip in reset
|
|
846
|
-
await this.sleep(100);
|
|
847
|
-
await this.setDTRWebUSB(true); // IO0=LOW
|
|
848
|
-
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset
|
|
849
|
-
await this.sleep(50);
|
|
850
|
-
await this.setDTRWebUSB(false); // IO0=HIGH, done
|
|
851
|
-
await this.sleep(200);
|
|
852
|
-
}
|
|
853
|
-
/**
|
|
854
|
-
* @name hardResetUnixTightWebUSB
|
|
855
|
-
* Unix Tight reset for WebUSB (Android) - sets DTR and RTS simultaneously
|
|
856
|
-
*/
|
|
857
|
-
async hardResetUnixTightWebUSB() {
|
|
858
|
-
await this.setDTRandRTSWebUSB(false, false);
|
|
859
|
-
await this.setDTRandRTSWebUSB(true, true);
|
|
860
|
-
await this.setDTRandRTSWebUSB(false, true); // IO0=HIGH & EN=LOW, chip in reset
|
|
861
|
-
await this.sleep(100);
|
|
862
|
-
await this.setDTRandRTSWebUSB(true, false); // IO0=LOW & EN=HIGH, chip out of reset
|
|
863
|
-
await this.sleep(50);
|
|
864
|
-
await this.setDTRandRTSWebUSB(false, false); // IO0=HIGH, done
|
|
865
|
-
await this.setDTRWebUSB(false); // Ensure IO0=HIGH
|
|
866
|
-
await this.sleep(200);
|
|
814
|
+
await this.runSignalSequence([
|
|
815
|
+
{ rts: false, dtr: true, delayMs: 100 },
|
|
816
|
+
{ dtr: false, rts: false, delayMs: 100 },
|
|
817
|
+
{ rts: true, dtr: true, delayMs: 100 },
|
|
818
|
+
{ dtr: true, rts: false, delayMs: 200 },
|
|
819
|
+
]);
|
|
867
820
|
}
|
|
868
821
|
/**
|
|
869
822
|
* @name hardResetClassicLongDelayWebUSB
|
|
@@ -871,14 +824,11 @@ export class ESPLoader extends EventTarget {
|
|
|
871
824
|
* Specifically for CP2102/CH340 which may need more time
|
|
872
825
|
*/
|
|
873
826
|
async hardResetClassicLongDelayWebUSB() {
|
|
874
|
-
await this.
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
await this.sleep(200);
|
|
880
|
-
await this.setDTRWebUSB(false); // IO0=HIGH, done
|
|
881
|
-
await this.sleep(500); // Extra long delay
|
|
827
|
+
await this.runSignalSequence([
|
|
828
|
+
{ dtr: false, rts: true, delayMs: 500 },
|
|
829
|
+
{ dtr: true, rts: false, delayMs: 200 },
|
|
830
|
+
{ dtr: false, delayMs: 500 },
|
|
831
|
+
]);
|
|
882
832
|
}
|
|
883
833
|
/**
|
|
884
834
|
* @name hardResetClassicShortDelayWebUSB
|
|
@@ -887,12 +837,12 @@ export class ESPLoader extends EventTarget {
|
|
|
887
837
|
async hardResetClassicShortDelayWebUSB() {
|
|
888
838
|
await this.setDTRWebUSB(false); // IO0=HIGH
|
|
889
839
|
await this.setRTSWebUSB(true); // EN=LOW, chip in reset
|
|
890
|
-
await
|
|
840
|
+
await sleep(50);
|
|
891
841
|
await this.setDTRWebUSB(true); // IO0=LOW
|
|
892
842
|
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset
|
|
893
|
-
await
|
|
843
|
+
await sleep(25);
|
|
894
844
|
await this.setDTRWebUSB(false); // IO0=HIGH, done
|
|
895
|
-
await
|
|
845
|
+
await sleep(100);
|
|
896
846
|
}
|
|
897
847
|
/**
|
|
898
848
|
* @name hardResetInvertedWebUSB
|
|
@@ -901,12 +851,12 @@ export class ESPLoader extends EventTarget {
|
|
|
901
851
|
async hardResetInvertedWebUSB() {
|
|
902
852
|
await this.setDTRWebUSB(true); // IO0=HIGH (inverted)
|
|
903
853
|
await this.setRTSWebUSB(false); // EN=LOW, chip in reset (inverted)
|
|
904
|
-
await
|
|
854
|
+
await sleep(100);
|
|
905
855
|
await this.setDTRWebUSB(false); // IO0=LOW (inverted)
|
|
906
856
|
await this.setRTSWebUSB(true); // EN=HIGH, chip out of reset (inverted)
|
|
907
|
-
await
|
|
857
|
+
await sleep(50);
|
|
908
858
|
await this.setDTRWebUSB(true); // IO0=HIGH, done (inverted)
|
|
909
|
-
await
|
|
859
|
+
await sleep(200);
|
|
910
860
|
}
|
|
911
861
|
/**
|
|
912
862
|
* @name hardResetInvertedDTRWebUSB
|
|
@@ -915,12 +865,12 @@ export class ESPLoader extends EventTarget {
|
|
|
915
865
|
async hardResetInvertedDTRWebUSB() {
|
|
916
866
|
await this.setDTRWebUSB(true); // IO0=HIGH (DTR inverted)
|
|
917
867
|
await this.setRTSWebUSB(true); // EN=LOW, chip in reset (RTS normal)
|
|
918
|
-
await
|
|
868
|
+
await sleep(100);
|
|
919
869
|
await this.setDTRWebUSB(false); // IO0=LOW (DTR inverted)
|
|
920
870
|
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset (RTS normal)
|
|
921
|
-
await
|
|
871
|
+
await sleep(50);
|
|
922
872
|
await this.setDTRWebUSB(true); // IO0=HIGH, done (DTR inverted)
|
|
923
|
-
await
|
|
873
|
+
await sleep(200);
|
|
924
874
|
}
|
|
925
875
|
/**
|
|
926
876
|
* @name hardResetInvertedRTSWebUSB
|
|
@@ -929,12 +879,12 @@ export class ESPLoader extends EventTarget {
|
|
|
929
879
|
async hardResetInvertedRTSWebUSB() {
|
|
930
880
|
await this.setDTRWebUSB(false); // IO0=HIGH (DTR normal)
|
|
931
881
|
await this.setRTSWebUSB(false); // EN=LOW, chip in reset (RTS inverted)
|
|
932
|
-
await
|
|
882
|
+
await sleep(100);
|
|
933
883
|
await this.setDTRWebUSB(true); // IO0=LOW (DTR normal)
|
|
934
884
|
await this.setRTSWebUSB(true); // EN=HIGH, chip out of reset (RTS inverted)
|
|
935
|
-
await
|
|
885
|
+
await sleep(50);
|
|
936
886
|
await this.setDTRWebUSB(false); // IO0=HIGH, done (DTR normal)
|
|
937
|
-
await
|
|
887
|
+
await sleep(200);
|
|
938
888
|
}
|
|
939
889
|
/**
|
|
940
890
|
* Check if we're using WebUSB (Android) or Web Serial (Desktop)
|
|
@@ -978,7 +928,7 @@ export class ESPLoader extends EventTarget {
|
|
|
978
928
|
resetStrategies.push({
|
|
979
929
|
name: "USB-JTAG/Serial (WebUSB) - ESP32-S2",
|
|
980
930
|
fn: async () => {
|
|
981
|
-
return await self.
|
|
931
|
+
return await self.hardResetUSBJTAGSerial();
|
|
982
932
|
},
|
|
983
933
|
});
|
|
984
934
|
// Strategy 2: USB-JTAG/Serial Inverted DTR (works in JTAG mode)
|
|
@@ -992,14 +942,14 @@ export class ESPLoader extends EventTarget {
|
|
|
992
942
|
resetStrategies.push({
|
|
993
943
|
name: "UnixTight (WebUSB) - ESP32-S2 CDC",
|
|
994
944
|
fn: async () => {
|
|
995
|
-
return await self.
|
|
945
|
+
return await self.hardResetUnixTight();
|
|
996
946
|
},
|
|
997
947
|
});
|
|
998
948
|
// Strategy 4: Classic reset (CDC fallback)
|
|
999
949
|
resetStrategies.push({
|
|
1000
950
|
name: "Classic (WebUSB) - ESP32-S2 CDC",
|
|
1001
951
|
fn: async () => {
|
|
1002
|
-
return await self.
|
|
952
|
+
return await self.hardResetClassic();
|
|
1003
953
|
},
|
|
1004
954
|
});
|
|
1005
955
|
}
|
|
@@ -1014,7 +964,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1014
964
|
resetStrategies.push({
|
|
1015
965
|
name: "USB-JTAG/Serial (WebUSB)",
|
|
1016
966
|
fn: async () => {
|
|
1017
|
-
return await self.
|
|
967
|
+
return await self.hardResetUSBJTAGSerial();
|
|
1018
968
|
},
|
|
1019
969
|
});
|
|
1020
970
|
resetStrategies.push({
|
|
@@ -1032,13 +982,13 @@ export class ESPLoader extends EventTarget {
|
|
|
1032
982
|
resetStrategies.push({
|
|
1033
983
|
name: "UnixTight (WebUSB) - CH34x",
|
|
1034
984
|
fn: async () => {
|
|
1035
|
-
return await self.
|
|
985
|
+
return await self.hardResetUnixTight();
|
|
1036
986
|
},
|
|
1037
987
|
});
|
|
1038
988
|
resetStrategies.push({
|
|
1039
989
|
name: "Classic (WebUSB) - CH34x",
|
|
1040
990
|
fn: async () => {
|
|
1041
|
-
return await self.
|
|
991
|
+
return await self.hardResetClassic();
|
|
1042
992
|
},
|
|
1043
993
|
});
|
|
1044
994
|
resetStrategies.push({
|
|
@@ -1066,13 +1016,13 @@ export class ESPLoader extends EventTarget {
|
|
|
1066
1016
|
resetStrategies.push({
|
|
1067
1017
|
name: "UnixTight (WebUSB) - CP2102",
|
|
1068
1018
|
fn: async () => {
|
|
1069
|
-
return await self.
|
|
1019
|
+
return await self.hardResetUnixTight();
|
|
1070
1020
|
},
|
|
1071
1021
|
});
|
|
1072
1022
|
resetStrategies.push({
|
|
1073
1023
|
name: "Classic (WebUSB) - CP2102",
|
|
1074
1024
|
fn: async () => {
|
|
1075
|
-
return await self.
|
|
1025
|
+
return await self.hardResetClassic();
|
|
1076
1026
|
},
|
|
1077
1027
|
});
|
|
1078
1028
|
resetStrategies.push({
|
|
@@ -1099,13 +1049,13 @@ export class ESPLoader extends EventTarget {
|
|
|
1099
1049
|
resetStrategies.push({
|
|
1100
1050
|
name: "UnixTight (WebUSB)",
|
|
1101
1051
|
fn: async () => {
|
|
1102
|
-
return await self.
|
|
1052
|
+
return await self.hardResetUnixTight();
|
|
1103
1053
|
},
|
|
1104
1054
|
});
|
|
1105
1055
|
resetStrategies.push({
|
|
1106
1056
|
name: "Classic (WebUSB)",
|
|
1107
1057
|
fn: async function () {
|
|
1108
|
-
return await self.
|
|
1058
|
+
return await self.hardResetClassic();
|
|
1109
1059
|
},
|
|
1110
1060
|
});
|
|
1111
1061
|
resetStrategies.push({
|
|
@@ -1135,7 +1085,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1135
1085
|
resetStrategies.push({
|
|
1136
1086
|
name: "Classic (WebUSB)",
|
|
1137
1087
|
fn: async function () {
|
|
1138
|
-
return await self.
|
|
1088
|
+
return await self.hardResetClassic();
|
|
1139
1089
|
},
|
|
1140
1090
|
});
|
|
1141
1091
|
}
|
|
@@ -1143,7 +1093,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1143
1093
|
resetStrategies.push({
|
|
1144
1094
|
name: "UnixTight (WebUSB)",
|
|
1145
1095
|
fn: async function () {
|
|
1146
|
-
return await self.
|
|
1096
|
+
return await self.hardResetUnixTight();
|
|
1147
1097
|
},
|
|
1148
1098
|
});
|
|
1149
1099
|
// WebUSB Strategy: Classic with long delays
|
|
@@ -1165,7 +1115,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1165
1115
|
resetStrategies.push({
|
|
1166
1116
|
name: "USB-JTAG/Serial fallback (WebUSB)",
|
|
1167
1117
|
fn: async function () {
|
|
1168
|
-
return await self.
|
|
1118
|
+
return await self.hardResetUSBJTAGSerial();
|
|
1169
1119
|
},
|
|
1170
1120
|
});
|
|
1171
1121
|
}
|
|
@@ -1334,32 +1284,114 @@ export class ESPLoader extends EventTarget {
|
|
|
1334
1284
|
// Lock watchdog registers
|
|
1335
1285
|
await this.writeRegister(WDTWPROTECT_REG, 0, undefined, 0);
|
|
1336
1286
|
// Wait for reset to take effect
|
|
1337
|
-
await
|
|
1287
|
+
await sleep(500);
|
|
1338
1288
|
}
|
|
1339
1289
|
/**
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1290
|
+
* Reset device from bootloader mode to firmware mode
|
|
1291
|
+
* Automatically selects the correct reset strategy based on USB connection type
|
|
1292
|
+
* @param clearForceDownloadFlag - If true, clears the force download boot flag (USB-OTG only)
|
|
1293
|
+
* @returns true if port will change (USB-OTG), false otherwise
|
|
1342
1294
|
*/
|
|
1343
|
-
async
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
//
|
|
1347
|
-
await this.
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1295
|
+
async resetToFirmwareMode(clearForceDownloadFlag = true) {
|
|
1296
|
+
this.logger.debug("Resetting from bootloader to firmware mode...");
|
|
1297
|
+
try {
|
|
1298
|
+
// Detect USB connection type
|
|
1299
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
1300
|
+
if (isUsbJtagOrOtg) {
|
|
1301
|
+
// USB-JTAG/OTG devices need special handling
|
|
1302
|
+
this.logger.debug("USB-JTAG/OTG detected - checking WDT reset support");
|
|
1303
|
+
// Get detailed USB mode information
|
|
1304
|
+
let usbMode;
|
|
1305
|
+
try {
|
|
1306
|
+
usbMode = await this.getUsbMode();
|
|
1307
|
+
this.logger.debug(`USB mode: ${usbMode.mode} (uartNo=${usbMode.uartNo})`);
|
|
1308
|
+
}
|
|
1309
|
+
catch (err) {
|
|
1310
|
+
this.logger.debug(`Could not get USB mode: ${err}`);
|
|
1311
|
+
// Fall back to generic USB-JTAG/OTG handling
|
|
1312
|
+
usbMode = { mode: "usb-jtag-serial", uartNo: 0 };
|
|
1313
|
+
}
|
|
1314
|
+
// Check if chip supports WDT reset
|
|
1315
|
+
// WDT reset is not needed for ESP32-C3
|
|
1316
|
+
// WDT reset is supported by: ESP32-S2, ESP32-S3, ESP32-P4
|
|
1317
|
+
// WDT reset is NOT supported by: ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2
|
|
1318
|
+
const supportsWdtReset = this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
1319
|
+
this.chipFamily === CHIP_FAMILY_ESP32S3 ||
|
|
1320
|
+
this.chipFamily === CHIP_FAMILY_ESP32P4;
|
|
1321
|
+
if (!supportsWdtReset) {
|
|
1322
|
+
this.logger.debug(`${this.chipName} does not support WDT reset - using classic reset instead`);
|
|
1323
|
+
// Use classic reset for chips without WDT support
|
|
1324
|
+
await this.hardResetToFirmware();
|
|
1325
|
+
this.logger.debug("Classic reset to firmware complete");
|
|
1326
|
+
return false; // Port stays open
|
|
1327
|
+
}
|
|
1328
|
+
// WDT reset is supported - proceed with WDT reset logic
|
|
1329
|
+
this.logger.debug(`${this.chipName} supports WDT reset - using WDT reset strategy`);
|
|
1330
|
+
// CRITICAL: WDT register writes require ROM (not stub) and baudrate 115200
|
|
1331
|
+
// If on stub, need to return to ROM first
|
|
1332
|
+
if (this.IS_STUB) {
|
|
1333
|
+
this.logger.debug("On stub - returning to ROM before WDT reset");
|
|
1334
|
+
// Change baudrate back to ROM baudrate if needed
|
|
1335
|
+
if (this.currentBaudRate !== ESP_ROM_BAUD) {
|
|
1336
|
+
this.logger.debug(`Changing baudrate from ${this.currentBaudRate} to ${ESP_ROM_BAUD}`);
|
|
1337
|
+
await this.reconfigurePort(ESP_ROM_BAUD);
|
|
1338
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
1339
|
+
this.logger.debug("Baudrate changed to 115200");
|
|
1340
|
+
}
|
|
1341
|
+
// CRITICAL: Temporarily clear console mode flag so hardReset(true) works
|
|
1342
|
+
const wasInConsoleMode = this._consoleMode;
|
|
1343
|
+
this._consoleMode = false;
|
|
1344
|
+
// Reset to bootloader (ROM)
|
|
1345
|
+
await this.hardReset(true);
|
|
1346
|
+
await sleep(200);
|
|
1347
|
+
// Restore console mode flag
|
|
1348
|
+
this._consoleMode = wasInConsoleMode;
|
|
1349
|
+
// Sync with ROM
|
|
1350
|
+
await this.sync();
|
|
1351
|
+
this.IS_STUB = false;
|
|
1352
|
+
this.logger.debug("Now on ROM");
|
|
1353
|
+
}
|
|
1354
|
+
else {
|
|
1355
|
+
// Even if not on stub, ensure baudrate is 115200 for WDT register writes
|
|
1356
|
+
if (this.currentBaudRate !== ESP_ROM_BAUD) {
|
|
1357
|
+
this.logger.debug(`Not on stub, but baudrate is ${this.currentBaudRate} - changing to ${ESP_ROM_BAUD} for WDT reset`);
|
|
1358
|
+
await this.reconfigurePort(ESP_ROM_BAUD);
|
|
1359
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
1360
|
+
this.logger.debug("Baudrate changed to 115200");
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
// Clear force download boot flag if requested (USB-OTG only)
|
|
1364
|
+
if (clearForceDownloadFlag && usbMode.mode === "usb-otg") {
|
|
1365
|
+
const flagCleared = await this._clearForceDownloadBootIfNeeded();
|
|
1366
|
+
if (flagCleared) {
|
|
1367
|
+
this.logger.debug("Force download boot flag cleared");
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
// Perform WDT reset to boot into firmware
|
|
1371
|
+
await this.rtcWdtResetChipSpecific();
|
|
1372
|
+
this.logger.debug("WDT reset performed - device will boot to firmware");
|
|
1373
|
+
// Check if port will change after WDT reset
|
|
1374
|
+
// USB-OTG (ESP32-S2/P4): Port always changes
|
|
1375
|
+
// USB-JTAG/Serial (ESP32-S3/C3/C5/C6/C61/H2/P4): Port may change depending on platform
|
|
1376
|
+
const portWillChange = usbMode.mode === "usb-otg" || usbMode.mode === "usb-jtag-serial";
|
|
1377
|
+
if (portWillChange) {
|
|
1378
|
+
this.logger.debug(`Port will change after WDT reset (${usbMode.mode}) - port reselection needed`);
|
|
1379
|
+
return true;
|
|
1380
|
+
}
|
|
1381
|
+
return false;
|
|
1356
1382
|
}
|
|
1357
1383
|
else {
|
|
1358
|
-
|
|
1359
|
-
this.logger.debug("
|
|
1384
|
+
// External serial chip - use classic reset to firmware
|
|
1385
|
+
this.logger.debug("External serial chip detected - using classic reset");
|
|
1386
|
+
await this.hardResetToFirmware();
|
|
1387
|
+
this.logger.debug("Classic reset to firmware complete");
|
|
1388
|
+
return false;
|
|
1360
1389
|
}
|
|
1361
1390
|
}
|
|
1362
|
-
|
|
1391
|
+
catch (err) {
|
|
1392
|
+
this.logger.error(`Failed to reset to firmware mode: ${err}`);
|
|
1393
|
+
throw err;
|
|
1394
|
+
}
|
|
1363
1395
|
}
|
|
1364
1396
|
async hardReset(bootloader = false) {
|
|
1365
1397
|
// In console mode, only allow simple hardware reset (no bootloader entry)
|
|
@@ -1370,78 +1402,75 @@ export class ESPLoader extends EventTarget {
|
|
|
1370
1402
|
}
|
|
1371
1403
|
// Simple hardware reset to restart firmware (IO0=HIGH)
|
|
1372
1404
|
this.logger.debug("Performing hardware reset (console mode)...");
|
|
1373
|
-
|
|
1374
|
-
await this.hardResetToFirmwareWebUSB();
|
|
1375
|
-
}
|
|
1376
|
-
else {
|
|
1377
|
-
await this.hardResetToFirmware();
|
|
1378
|
-
}
|
|
1405
|
+
await this.resetInConsoleMode();
|
|
1379
1406
|
this.logger.debug("Hardware reset complete");
|
|
1380
1407
|
return;
|
|
1381
1408
|
}
|
|
1382
1409
|
if (bootloader) {
|
|
1383
|
-
//
|
|
1410
|
+
// Enter bootloader/flash mode
|
|
1384
1411
|
if (this.port.getInfo().usbProductId === USB_JTAG_SERIAL_PID) {
|
|
1385
1412
|
await this.hardResetUSBJTAGSerial();
|
|
1386
|
-
this.logger.debug("USB-JTAG/Serial reset.");
|
|
1413
|
+
this.logger.debug("USB-JTAG/Serial reset to bootloader.");
|
|
1387
1414
|
}
|
|
1388
1415
|
else {
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
await this.hardResetClassicWebUSB();
|
|
1392
|
-
this.logger.debug("Classic reset (WebUSB/Android).");
|
|
1393
|
-
}
|
|
1394
|
-
else {
|
|
1395
|
-
await this.hardResetClassic();
|
|
1396
|
-
this.logger.debug("Classic reset.");
|
|
1397
|
-
}
|
|
1416
|
+
await this.hardResetClassic();
|
|
1417
|
+
this.logger.debug("Classic reset to bootloader.");
|
|
1398
1418
|
}
|
|
1399
1419
|
}
|
|
1400
1420
|
else {
|
|
1401
|
-
//
|
|
1402
|
-
//
|
|
1403
|
-
this.logger.debug("
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
//
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
await this.setRTSWebUSB(false);
|
|
1436
|
-
await this.sleep(200);
|
|
1437
|
-
this.logger.debug("Hard reset (WebUSB).");
|
|
1421
|
+
// Reset to firmware mode (exit bootloader)
|
|
1422
|
+
// Use intelligent reset strategy based on USB connection type
|
|
1423
|
+
this.logger.debug("Resetting to firmware mode...");
|
|
1424
|
+
// Detect USB connection type to choose correct reset method
|
|
1425
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
1426
|
+
if (isUsbJtagOrOtg) {
|
|
1427
|
+
// USB-JTAG/OTG devices: Use WDT reset
|
|
1428
|
+
this.logger.debug("USB-JTAG/OTG detected - using WDT reset");
|
|
1429
|
+
// Get USB mode details
|
|
1430
|
+
let usbMode;
|
|
1431
|
+
try {
|
|
1432
|
+
usbMode = await this.getUsbMode();
|
|
1433
|
+
this.logger.debug(`USB mode: ${usbMode.mode} (uartNo=${usbMode.uartNo})`);
|
|
1434
|
+
}
|
|
1435
|
+
catch (err) {
|
|
1436
|
+
this.logger.debug(`Could not get USB mode: ${err}`);
|
|
1437
|
+
usbMode = { mode: "usb-jtag-serial", uartNo: 0 };
|
|
1438
|
+
}
|
|
1439
|
+
// Clear force download flag for USB-OTG devices
|
|
1440
|
+
if (usbMode.mode === "usb-otg") {
|
|
1441
|
+
try {
|
|
1442
|
+
const flagCleared = await this._clearForceDownloadBootIfNeeded();
|
|
1443
|
+
if (flagCleared) {
|
|
1444
|
+
this.logger.debug("Force download boot flag cleared");
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
catch (err) {
|
|
1448
|
+
this.logger.debug(`Could not clear force download flag: ${err}`);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
// Perform WDT reset
|
|
1452
|
+
await this.rtcWdtResetChipSpecific();
|
|
1453
|
+
this.logger.debug(`${this.chipName}: WDT reset to firmware complete`);
|
|
1454
|
+
return;
|
|
1438
1455
|
}
|
|
1439
1456
|
else {
|
|
1440
|
-
//
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1457
|
+
// External serial chip: Use classic reset
|
|
1458
|
+
this.logger.debug("External serial chip detected - using classic reset");
|
|
1459
|
+
if (this.isWebUSB()) {
|
|
1460
|
+
// WebUSB: Use longer delays for better compatibility
|
|
1461
|
+
await this.setRTSWebUSB(true); // EN->LOW
|
|
1462
|
+
await sleep(200);
|
|
1463
|
+
await this.setRTSWebUSB(false);
|
|
1464
|
+
await sleep(200);
|
|
1465
|
+
this.logger.debug("Hard reset to firmware (WebUSB).");
|
|
1466
|
+
}
|
|
1467
|
+
else {
|
|
1468
|
+
// Web Serial: Standard reset
|
|
1469
|
+
await this.setRTS(true); // EN->LOW
|
|
1470
|
+
await sleep(100);
|
|
1471
|
+
await this.setRTS(false);
|
|
1472
|
+
this.logger.debug("Hard reset to firmware.");
|
|
1473
|
+
}
|
|
1445
1474
|
}
|
|
1446
1475
|
}
|
|
1447
1476
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2655,29 +2684,25 @@ export class ESPLoader extends EventTarget {
|
|
|
2655
2684
|
}
|
|
2656
2685
|
this._writer = undefined;
|
|
2657
2686
|
}
|
|
2658
|
-
// Cancel
|
|
2687
|
+
// Cancel reader - let readLoop's finally block handle releaseLock()
|
|
2659
2688
|
if (this._reader) {
|
|
2660
|
-
const reader = this._reader;
|
|
2661
2689
|
try {
|
|
2662
2690
|
// Suppress disconnect event during console mode switching
|
|
2663
2691
|
this._suppressDisconnect = true;
|
|
2664
|
-
|
|
2665
|
-
this.
|
|
2692
|
+
// Cancel will cause readLoop to exit and call releaseLock() in its finally block
|
|
2693
|
+
await this._reader.cancel();
|
|
2694
|
+
this.logger.debug("Reader cancelled - waiting for readLoop to finish");
|
|
2695
|
+
// CRITICAL: Wait a bit for readLoop's finally block to complete
|
|
2696
|
+
// The finally block needs time to call releaseLock() and set _reader = undefined
|
|
2697
|
+
// This is much faster than waiting for browser to unlock (just waiting for JS execution)
|
|
2698
|
+
await sleep(50);
|
|
2699
|
+
this.logger.debug("ReadLoop cleanup should be complete");
|
|
2666
2700
|
}
|
|
2667
2701
|
catch (err) {
|
|
2668
2702
|
this.logger.debug(`Reader cancel error: ${err}`);
|
|
2669
2703
|
}
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
reader.releaseLock();
|
|
2673
|
-
}
|
|
2674
|
-
catch (err) {
|
|
2675
|
-
this.logger.debug(`Reader release error: ${err}`);
|
|
2676
|
-
}
|
|
2677
|
-
}
|
|
2678
|
-
if (this._reader === reader) {
|
|
2679
|
-
this._reader = undefined;
|
|
2680
|
-
}
|
|
2704
|
+
// Don't call releaseLock() or set _reader to undefined here
|
|
2705
|
+
// Let readLoop's finally block handle it to avoid race conditions
|
|
2681
2706
|
}
|
|
2682
2707
|
}
|
|
2683
2708
|
/**
|
|
@@ -2716,6 +2741,141 @@ export class ESPLoader extends EventTarget {
|
|
|
2716
2741
|
this.logger.debug(`USB-JTAG/OTG detection: ${isUsbJtag ? "YES" : "NO"} (PID=0x${pid === null || pid === void 0 ? void 0 : pid.toString(16)})`);
|
|
2717
2742
|
return isUsbJtag;
|
|
2718
2743
|
}
|
|
2744
|
+
async getUsbMode() {
|
|
2745
|
+
var _a, _b;
|
|
2746
|
+
const family = this._parent ? this._parent.chipFamily : this.chipFamily;
|
|
2747
|
+
const revision = this._parent
|
|
2748
|
+
? ((_a = this._parent.chipRevision) !== null && _a !== void 0 ? _a : 0)
|
|
2749
|
+
: ((_b = this.chipRevision) !== null && _b !== void 0 ? _b : 0);
|
|
2750
|
+
let bufNoAddr = null;
|
|
2751
|
+
let jtagSerialVal = null;
|
|
2752
|
+
let otgVal = null;
|
|
2753
|
+
switch (family) {
|
|
2754
|
+
case CHIP_FAMILY_ESP32S2:
|
|
2755
|
+
bufNoAddr = ESP32S2_UARTDEV_BUF_NO;
|
|
2756
|
+
otgVal = ESP32S2_UARTDEV_BUF_NO_USB_OTG;
|
|
2757
|
+
break;
|
|
2758
|
+
case CHIP_FAMILY_ESP32S3:
|
|
2759
|
+
bufNoAddr = ESP32S3_UARTDEV_BUF_NO;
|
|
2760
|
+
jtagSerialVal = ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2761
|
+
otgVal = ESP32S3_UARTDEV_BUF_NO_USB_OTG;
|
|
2762
|
+
break;
|
|
2763
|
+
case CHIP_FAMILY_ESP32C3: {
|
|
2764
|
+
const bssAddr = revision < 101 ? 0x3fcdf064 : 0x3fcdf060;
|
|
2765
|
+
bufNoAddr = bssAddr + ESP32C3_BUF_UART_NO_OFFSET;
|
|
2766
|
+
jtagSerialVal = ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2767
|
+
break;
|
|
2768
|
+
}
|
|
2769
|
+
case CHIP_FAMILY_ESP32C5:
|
|
2770
|
+
bufNoAddr = ESP32C5_UARTDEV_BUF_NO;
|
|
2771
|
+
jtagSerialVal = ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2772
|
+
break;
|
|
2773
|
+
case CHIP_FAMILY_ESP32C6:
|
|
2774
|
+
bufNoAddr = ESP32C6_UARTDEV_BUF_NO;
|
|
2775
|
+
jtagSerialVal = ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2776
|
+
break;
|
|
2777
|
+
case CHIP_FAMILY_ESP32C61:
|
|
2778
|
+
bufNoAddr =
|
|
2779
|
+
revision <= 200
|
|
2780
|
+
? ESP32C61_UARTDEV_BUF_NO_REV_LE2
|
|
2781
|
+
: ESP32C61_UARTDEV_BUF_NO_REV_GT2;
|
|
2782
|
+
jtagSerialVal =
|
|
2783
|
+
revision <= 200
|
|
2784
|
+
? ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_LE2
|
|
2785
|
+
: ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_GT2;
|
|
2786
|
+
break;
|
|
2787
|
+
case CHIP_FAMILY_ESP32H2:
|
|
2788
|
+
bufNoAddr = ESP32H2_UARTDEV_BUF_NO;
|
|
2789
|
+
jtagSerialVal = ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2790
|
+
break;
|
|
2791
|
+
case CHIP_FAMILY_ESP32H4:
|
|
2792
|
+
bufNoAddr = ESP32H4_UARTDEV_BUF_NO;
|
|
2793
|
+
jtagSerialVal = ESP32H4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2794
|
+
break;
|
|
2795
|
+
case CHIP_FAMILY_ESP32P4:
|
|
2796
|
+
bufNoAddr =
|
|
2797
|
+
revision < 300
|
|
2798
|
+
? ESP32P4_UARTDEV_BUF_NO_REV0
|
|
2799
|
+
: ESP32P4_UARTDEV_BUF_NO_REV300;
|
|
2800
|
+
jtagSerialVal = ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
2801
|
+
otgVal = ESP32P4_UARTDEV_BUF_NO_USB_OTG;
|
|
2802
|
+
break;
|
|
2803
|
+
}
|
|
2804
|
+
if (bufNoAddr === null) {
|
|
2805
|
+
return { mode: "uart", uartNo: 0 };
|
|
2806
|
+
}
|
|
2807
|
+
const uartNo = (await this.readRegister(bufNoAddr)) & 0xff;
|
|
2808
|
+
if (otgVal !== null && uartNo === otgVal) {
|
|
2809
|
+
this.logger.debug(`USB mode: USB-OTG (uartNo=${uartNo})`);
|
|
2810
|
+
return { mode: "usb-otg", uartNo };
|
|
2811
|
+
}
|
|
2812
|
+
if (jtagSerialVal !== null && uartNo === jtagSerialVal) {
|
|
2813
|
+
this.logger.debug(`USB mode: USB-JTAG/Serial (uartNo=${uartNo})`);
|
|
2814
|
+
return { mode: "usb-jtag-serial", uartNo };
|
|
2815
|
+
}
|
|
2816
|
+
this.logger.debug(`USB mode: UART (uartNo=${uartNo})`);
|
|
2817
|
+
return { mode: "uart", uartNo };
|
|
2818
|
+
}
|
|
2819
|
+
/**
|
|
2820
|
+
* Check if the current chip supports USB-JTAG or USB-OTG
|
|
2821
|
+
* @returns true if chip has native USB support (JTAG or OTG)
|
|
2822
|
+
*/
|
|
2823
|
+
supportsNativeUsb() {
|
|
2824
|
+
const family = this._parent ? this._parent.chipFamily : this.chipFamily;
|
|
2825
|
+
// Chips with USB-JTAG/Serial or USB-OTG support
|
|
2826
|
+
const usbChips = [
|
|
2827
|
+
CHIP_FAMILY_ESP32S2, // USB-OTG
|
|
2828
|
+
CHIP_FAMILY_ESP32S3, // USB-OTG + USB-JTAG/Serial
|
|
2829
|
+
CHIP_FAMILY_ESP32C3, // USB-JTAG/Serial
|
|
2830
|
+
CHIP_FAMILY_ESP32C5, // USB-JTAG/Serial
|
|
2831
|
+
CHIP_FAMILY_ESP32C6, // USB-JTAG/Serial
|
|
2832
|
+
CHIP_FAMILY_ESP32C61, // USB-JTAG/Serial
|
|
2833
|
+
CHIP_FAMILY_ESP32H2, // USB-JTAG/Serial
|
|
2834
|
+
CHIP_FAMILY_ESP32H4, // USB-JTAG/Serial
|
|
2835
|
+
CHIP_FAMILY_ESP32P4, // USB-OTG + USB-JTAG/Serial
|
|
2836
|
+
];
|
|
2837
|
+
return usbChips.includes(family);
|
|
2838
|
+
}
|
|
2839
|
+
/**
|
|
2840
|
+
* @name _ensureStreamsReady
|
|
2841
|
+
* After a hardware reset, ensure port streams are available.
|
|
2842
|
+
* On WebUSB, recreates streams since they break after reset.
|
|
2843
|
+
* On Web Serial, waits for streams to become available.
|
|
2844
|
+
*/
|
|
2845
|
+
async _ensureStreamsReady() {
|
|
2846
|
+
if (this.isWebUSB()) {
|
|
2847
|
+
try {
|
|
2848
|
+
await this.port.recreateStreams();
|
|
2849
|
+
this.logger.debug("WebUSB streams recreated");
|
|
2850
|
+
let retries = 30;
|
|
2851
|
+
while (retries > 0 && !this.port.readable) {
|
|
2852
|
+
await sleep(100);
|
|
2853
|
+
retries--;
|
|
2854
|
+
}
|
|
2855
|
+
if (!this.port.readable) {
|
|
2856
|
+
throw new Error("Readable stream not available after recreating streams");
|
|
2857
|
+
}
|
|
2858
|
+
this.logger.debug("WebUSB streams are ready");
|
|
2859
|
+
}
|
|
2860
|
+
catch (err) {
|
|
2861
|
+
this.logger.error(`Failed to recreate WebUSB streams: ${err}`);
|
|
2862
|
+
this._consoleMode = false;
|
|
2863
|
+
throw err;
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
else {
|
|
2867
|
+
let retries = 20;
|
|
2868
|
+
while (retries > 0 && !this.port.readable) {
|
|
2869
|
+
await sleep(100);
|
|
2870
|
+
retries--;
|
|
2871
|
+
}
|
|
2872
|
+
if (!this.port.readable) {
|
|
2873
|
+
this._consoleMode = false;
|
|
2874
|
+
throw new Error("Readable stream not available after reset");
|
|
2875
|
+
}
|
|
2876
|
+
this.logger.debug("Port streams are ready");
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2719
2879
|
/**
|
|
2720
2880
|
* @name enterConsoleMode
|
|
2721
2881
|
* Prepare device for console mode by resetting to firmware
|
|
@@ -2743,60 +2903,41 @@ export class ESPLoader extends EventTarget {
|
|
|
2743
2903
|
if (this.isUsbJtagOrOtg === undefined) {
|
|
2744
2904
|
throw new Error(`Cannot enter console mode: USB connection type unknown and detection failed: ${err}`);
|
|
2745
2905
|
}
|
|
2746
|
-
// Set console mode flag
|
|
2747
|
-
this._consoleMode = false;
|
|
2748
2906
|
this.logger.debug(`USB detection failed, using cached value: ${this.isUsbJtagOrOtg}`);
|
|
2749
2907
|
isUsbJtag = this.isUsbJtagOrOtg;
|
|
2750
2908
|
}
|
|
2751
|
-
//
|
|
2752
|
-
|
|
2909
|
+
// Set console mode flag BEFORE any operations
|
|
2910
|
+
this._consoleMode = true;
|
|
2753
2911
|
if (isUsbJtag) {
|
|
2754
|
-
// USB-JTAG/OTG devices: Use
|
|
2912
|
+
// USB-JTAG/OTG devices: Use reset which may close port
|
|
2755
2913
|
const wasReset = await this._resetToFirmwareIfNeeded();
|
|
2756
|
-
|
|
2914
|
+
if (wasReset) {
|
|
2915
|
+
return true; // port closed, caller must reopen
|
|
2916
|
+
}
|
|
2917
|
+
// Port stayed open (e.g. C3/C5/C6/H2 classic reset)
|
|
2918
|
+
await this._ensureStreamsReady();
|
|
2919
|
+
return false;
|
|
2757
2920
|
}
|
|
2758
2921
|
else {
|
|
2759
2922
|
// External serial chip devices: Release locks and do simple reset
|
|
2760
2923
|
try {
|
|
2761
2924
|
await this.releaseReaderWriter();
|
|
2762
|
-
await
|
|
2925
|
+
await sleep(100);
|
|
2763
2926
|
}
|
|
2764
2927
|
catch (err) {
|
|
2765
2928
|
this.logger.debug(`Failed to release locks: ${err}`);
|
|
2766
2929
|
}
|
|
2767
|
-
// Hardware reset to firmware mode (IO0=HIGH)
|
|
2768
2930
|
try {
|
|
2769
|
-
await this.
|
|
2770
|
-
this.logger.
|
|
2931
|
+
await this.hardResetToFirmware();
|
|
2932
|
+
this.logger.debug("Device reset to firmware mode");
|
|
2771
2933
|
}
|
|
2772
2934
|
catch (err) {
|
|
2773
2935
|
this.logger.debug(`Could not reset device: ${err}`);
|
|
2774
2936
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
try {
|
|
2778
|
-
// Use the public recreateStreams() method to safely recreate streams
|
|
2779
|
-
// without closing the port (important after hardware reset)
|
|
2780
|
-
await this.port.recreateStreams();
|
|
2781
|
-
this.logger.debug("WebUSB streams recreated for console mode");
|
|
2782
|
-
}
|
|
2783
|
-
catch (err) {
|
|
2784
|
-
// Set console mode flag
|
|
2785
|
-
this._consoleMode = false;
|
|
2786
|
-
this.logger.debug(`Failed to recreate WebUSB streams: ${err}`);
|
|
2787
|
-
}
|
|
2788
|
-
}
|
|
2789
|
-
// Set console mode flag
|
|
2790
|
-
this._consoleMode = true;
|
|
2791
|
-
return false; // Port stays open
|
|
2937
|
+
await this._ensureStreamsReady();
|
|
2938
|
+
return false;
|
|
2792
2939
|
}
|
|
2793
2940
|
}
|
|
2794
|
-
/**
|
|
2795
|
-
* @name _resetToFirmwareIfNeeded
|
|
2796
|
-
* Reset device from bootloader to firmware when switching to console mode
|
|
2797
|
-
* Detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
2798
|
-
* @returns true if reconnect was performed, false otherwise
|
|
2799
|
-
*/
|
|
2800
2941
|
/**
|
|
2801
2942
|
* @name _clearForceDownloadBootIfNeeded
|
|
2802
2943
|
* Read and clear the force download boot flag if it is set
|
|
@@ -2851,98 +2992,66 @@ export class ESPLoader extends EventTarget {
|
|
|
2851
2992
|
return false;
|
|
2852
2993
|
}
|
|
2853
2994
|
}
|
|
2995
|
+
/**
|
|
2996
|
+
* @name _resetToFirmwareIfNeeded
|
|
2997
|
+
* Reset device from bootloader to firmware when switching to console mode
|
|
2998
|
+
* Detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
2999
|
+
* @returns true if reconnect was performed, false otherwise
|
|
3000
|
+
*/
|
|
2854
3001
|
async _resetToFirmwareIfNeeded() {
|
|
3002
|
+
// Detect if we need WDT reset (USB-JTAG/OTG) or classic reset
|
|
3003
|
+
const isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
2855
3004
|
try {
|
|
2856
3005
|
// Check if port is open - if not, assume device is already in firmware mode
|
|
2857
3006
|
if (!this.port.writable || !this.port.readable) {
|
|
2858
3007
|
this.logger.debug("Port is not open - assuming device is already in firmware mode");
|
|
2859
3008
|
return false;
|
|
2860
3009
|
}
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
//
|
|
2864
|
-
//
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
// Mark that we're no longer on stub
|
|
2890
|
-
this.IS_STUB = false;
|
|
2891
|
-
}
|
|
2892
|
-
catch (resetErr) {
|
|
2893
|
-
this.logger.debug(`Reset to ROM failed: ${resetErr}`);
|
|
2894
|
-
// If reset fails, we might already be in firmware mode
|
|
2895
|
-
// In this case, we don't need to do anything - just use normal reset
|
|
2896
|
-
this.logger.debug("Assuming device is already in firmware mode");
|
|
2897
|
-
// Release reader/writer before returning
|
|
2898
|
-
await this.releaseReaderWriter();
|
|
2899
|
-
return false; // No port change needed
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
else {
|
|
2903
|
-
this.logger.debug("Already on ROM - checking force download flag");
|
|
2904
|
-
}
|
|
2905
|
-
// Now check if force download flag is set and clear it if needed
|
|
2906
|
-
const flagWasCleared = await this._clearForceDownloadBootIfNeeded();
|
|
2907
|
-
if (flagWasCleared) {
|
|
2908
|
-
this.logger.debug("Force download flag was cleared - device will boot to firmware after reset");
|
|
2909
|
-
}
|
|
2910
|
-
else {
|
|
2911
|
-
this.logger.debug("Force download flag already clear - device will boot to firmware after reset");
|
|
2912
|
-
}
|
|
2913
|
-
// Perform WDT reset BEFORE releasing reader/writer (needs communication)
|
|
2914
|
-
// After WDT reset, the device will reboot into firmware mode
|
|
2915
|
-
await this.hardReset(false);
|
|
2916
|
-
// For USB-OTG devices (ESP32-S2, ESP32-P4), the port will change after WDT reset
|
|
2917
|
-
const portWillChange = (this.chipFamily === CHIP_FAMILY_ESP32S2 && isUsingUsbOtg) ||
|
|
2918
|
-
(this.chipFamily === CHIP_FAMILY_ESP32P4 && isUsingUsbOtg);
|
|
2919
|
-
if (portWillChange) {
|
|
2920
|
-
// Port will change - release reader/writer and let the port become invalid
|
|
2921
|
-
await this.releaseReaderWriter();
|
|
2922
|
-
this.logger.debug(`${this.chipName} USB-OTG: Port will change after WDT reset`);
|
|
2923
|
-
// Dispatch event to signal port change
|
|
2924
|
-
this.dispatchEvent(new CustomEvent("usb-otg-port-change", {
|
|
2925
|
-
detail: {
|
|
2926
|
-
chipName: this.chipName,
|
|
2927
|
-
message: `${this.chipName} USB port changed after reset. Please select the new port.`,
|
|
2928
|
-
reason: "wdt-reset-to-firmware",
|
|
2929
|
-
},
|
|
2930
|
-
}));
|
|
2931
|
-
// Return true to indicate port selection is needed
|
|
2932
|
-
return true;
|
|
2933
|
-
}
|
|
2934
|
-
else {
|
|
2935
|
-
// Port stays the same - release reader/writer so console can use the stream
|
|
3010
|
+
if (isUsbJtagOrOtg) {
|
|
3011
|
+
// USB-JTAG/OTG: DON'T release reader/writer before WDT reset
|
|
3012
|
+
// The WDT reset needs active communication to send register write commands
|
|
3013
|
+
// The port will close automatically after the WDT reset anyway
|
|
3014
|
+
this.logger.debug("USB-JTAG/OTG: Keeping reader/writer active for WDT reset");
|
|
3015
|
+
}
|
|
3016
|
+
else {
|
|
3017
|
+
// External serial chip: Release reader/writer before classic reset
|
|
3018
|
+
await this.releaseReaderWriter();
|
|
3019
|
+
this.logger.debug("External serial: Reader/writer released before reset");
|
|
3020
|
+
}
|
|
3021
|
+
// Use the new resetToFirmwareMode method which handles all the logic
|
|
3022
|
+
const portWillChange = await this.resetToFirmwareMode(true);
|
|
3023
|
+
if (portWillChange) {
|
|
3024
|
+
this.logger.debug(`${this.chipName}: Port will change after WDT reset - user must reselect port`);
|
|
3025
|
+
// Dispatch event to signal port change
|
|
3026
|
+
this.dispatchEvent(new CustomEvent("usb-otg-port-change", {
|
|
3027
|
+
detail: {
|
|
3028
|
+
chipName: this.chipName,
|
|
3029
|
+
message: `${this.chipName} USB port changed after reset. Please select the new port.`,
|
|
3030
|
+
reason: "wdt-reset-to-firmware",
|
|
3031
|
+
},
|
|
3032
|
+
}));
|
|
3033
|
+
return true;
|
|
3034
|
+
}
|
|
3035
|
+
else {
|
|
3036
|
+
// Port stays the same - release reader/writer now if not already done
|
|
3037
|
+
if (isUsbJtagOrOtg) {
|
|
2936
3038
|
await this.releaseReaderWriter();
|
|
2937
|
-
|
|
3039
|
+
this.logger.debug("Reader/writer released after reset");
|
|
2938
3040
|
}
|
|
3041
|
+
return false;
|
|
2939
3042
|
}
|
|
2940
3043
|
}
|
|
2941
3044
|
catch (err) {
|
|
2942
|
-
this.logger.
|
|
2943
|
-
//
|
|
3045
|
+
this.logger.error(`Reset to firmware mode failed: ${err}`);
|
|
3046
|
+
// For USB-JTAG/OTG, the port is likely dead after a failed reset
|
|
3047
|
+
// For external serial, the port is usually still fine
|
|
3048
|
+
if (isUsbJtagOrOtg) {
|
|
3049
|
+
this.logger.debug("Forcing port reselection due to USB-JTAG/OTG reset failure");
|
|
3050
|
+
return true;
|
|
3051
|
+
}
|
|
3052
|
+
this.logger.debug("External serial reset failed, but port should still be usable");
|
|
3053
|
+
return false;
|
|
2944
3054
|
}
|
|
2945
|
-
return false;
|
|
2946
3055
|
}
|
|
2947
3056
|
/**
|
|
2948
3057
|
* @name reconnectAndResume
|
|
@@ -3197,12 +3306,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3197
3306
|
// Perform hardware reset to bootloader (GPIO0=LOW)
|
|
3198
3307
|
// This will cause the port to change from CDC (firmware) to JTAG (bootloader)
|
|
3199
3308
|
try {
|
|
3200
|
-
|
|
3201
|
-
await this.hardResetClassicWebUSB();
|
|
3202
|
-
}
|
|
3203
|
-
else {
|
|
3204
|
-
await this.hardResetClassic();
|
|
3205
|
-
}
|
|
3309
|
+
await this.hardResetClassic();
|
|
3206
3310
|
this.logger.debug("Reset to bootloader initiated");
|
|
3207
3311
|
}
|
|
3208
3312
|
catch (err) {
|
|
@@ -3255,19 +3359,15 @@ export class ESPLoader extends EventTarget {
|
|
|
3255
3359
|
return await this._parent.resetInConsoleMode();
|
|
3256
3360
|
}
|
|
3257
3361
|
if (!this.isConsoleResetSupported()) {
|
|
3258
|
-
this.logger.debug("Console reset not supported for ESP32-S2 USB-JTAG/CDC");
|
|
3259
|
-
|
|
3362
|
+
this.logger.debug("Simple Console reset not supported for ESP32-S2 USB-JTAG/CDC - using exitConsoleMode to enter bootloader");
|
|
3363
|
+
await this.exitConsoleMode();
|
|
3364
|
+
this.logger.debug("S2 now in bootloader mode - caller must do syncAndWdtReset on new port, then reconnect console");
|
|
3365
|
+
return;
|
|
3260
3366
|
}
|
|
3261
3367
|
// For other devices: Use standard firmware reset
|
|
3262
|
-
const isWebUSB = this.port.isWebUSB === true;
|
|
3263
3368
|
try {
|
|
3264
3369
|
this.logger.debug("Resetting device in console mode");
|
|
3265
|
-
|
|
3266
|
-
await this.hardResetToFirmwareWebUSB();
|
|
3267
|
-
}
|
|
3268
|
-
else {
|
|
3269
|
-
await this.hardResetToFirmware();
|
|
3270
|
-
}
|
|
3370
|
+
await this.hardResetToFirmware();
|
|
3271
3371
|
this.logger.debug("Device reset complete");
|
|
3272
3372
|
}
|
|
3273
3373
|
catch (err) {
|
|
@@ -3275,6 +3375,41 @@ export class ESPLoader extends EventTarget {
|
|
|
3275
3375
|
throw err;
|
|
3276
3376
|
}
|
|
3277
3377
|
}
|
|
3378
|
+
/**
|
|
3379
|
+
* @name syncAndWdtReset
|
|
3380
|
+
* Open a new bootloader port, sync with ROM (no stub, no reset strategies), and fire WDT reset.
|
|
3381
|
+
* This is used for ESP32-S2 USB-OTG devices which require WDT reset to switch modes.
|
|
3382
|
+
* After WDT reset the port will re-enumerate again.
|
|
3383
|
+
* The user must select the new port after this method is called.
|
|
3384
|
+
* @param newPort - The bootloader port selected by the user
|
|
3385
|
+
*/
|
|
3386
|
+
async syncAndWdtReset(newPort) {
|
|
3387
|
+
if (this._parent) {
|
|
3388
|
+
await this._parent.syncAndWdtReset(newPort);
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
this.port = newPort;
|
|
3392
|
+
this.connected = false;
|
|
3393
|
+
this.IS_STUB = false;
|
|
3394
|
+
this.__inputBuffer = [];
|
|
3395
|
+
this.__inputBufferReadIndex = 0;
|
|
3396
|
+
this.__totalBytesRead = 0;
|
|
3397
|
+
this.logger.debug("Opening bootloader port at 115200...");
|
|
3398
|
+
await this.port.open({ baudRate: ESP_ROM_BAUD });
|
|
3399
|
+
this.connected = true;
|
|
3400
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
3401
|
+
// Start read loop
|
|
3402
|
+
this.readLoop();
|
|
3403
|
+
await sleep(100);
|
|
3404
|
+
// Sync with ROM only - no reset strategies, device is already in bootloader
|
|
3405
|
+
this.logger.debug("Syncing with bootloader ROM...");
|
|
3406
|
+
await this.sync();
|
|
3407
|
+
this.logger.debug("Bootloader sync OK, no stub");
|
|
3408
|
+
// Fire WDT reset → device boots into firmware
|
|
3409
|
+
this.logger.debug("Firing WDT reset...");
|
|
3410
|
+
await this.rtcWdtResetChipSpecific();
|
|
3411
|
+
this.logger.debug("WDT reset fired - device will boot to firmware");
|
|
3412
|
+
}
|
|
3278
3413
|
/**
|
|
3279
3414
|
* @name drainInputBuffer
|
|
3280
3415
|
* Actively drain the input buffer by reading data for a specified time.
|