tasmota-webserial-esptool 9.2.8 → 9.2.9
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 +89 -6
- package/dist/const.js +115 -14
- package/dist/esp_loader.d.ts +108 -2
- package/dist/esp_loader.js +633 -97
- package/dist/web/index.js +1 -1
- package/js/modules/esptool.js +1 -1
- package/package.json +1 -1
- package/src/const.ts +151 -18
- package/src/esp_loader.ts +765 -117
package/dist/esp_loader.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
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_GPIO_STRAP_REG, ESP32S2_GPIO_STRAP_SPI_BOOT_MASK, 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_GPIO_STRAP_REG, ESP32S3_GPIO_STRAP_SPI_BOOT_MASK, ESP32S3_RTC_CNTL_OPTION1_REG, ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, } 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, 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_GPIO_STRAP_REG, ESP32S2_GPIO_STRAP_SPI_BOOT_MASK, 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_GPIO_STRAP_REG, ESP32S3_GPIO_STRAP_SPI_BOOT_MASK, ESP32S3_RTC_CNTL_OPTION1_REG, ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, 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, 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_UARTDEV_BUF_NO, ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C6_UARTDEV_BUF_NO, ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32P4_RTC_CNTL_WDTWPROTECT_REG, ESP32P4_RTC_CNTL_WDTCONFIG0_REG, ESP32P4_RTC_CNTL_WDTCONFIG1_REG, ESP32P4_RTC_CNTL_WDT_WKEY, ESP32P4_UARTDEV_BUF_NO_REV0, ESP32P4_UARTDEV_BUF_NO_REV300, ESP32P4_UARTDEV_BUF_NO_USB_OTG, ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32P4_RTC_CNTL_OPTION1_REG, ESP32P4_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32H2_UARTDEV_BUF_NO, ESP32H2_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 { deflate } from "pako";
|
|
6
6
|
import { pack, unpack } from "./struct";
|
|
7
7
|
export class ESPLoader extends EventTarget {
|
|
8
|
+
/**
|
|
9
|
+
* Check if device is using USB-JTAG or USB-OTG (not external serial chip)
|
|
10
|
+
* Returns undefined if not yet determined
|
|
11
|
+
*/
|
|
12
|
+
get isUsbJtagOrOtg() {
|
|
13
|
+
return this._parent ? this._parent._isUsbJtagOrOtg : this._isUsbJtagOrOtg;
|
|
14
|
+
}
|
|
8
15
|
constructor(port, logger, _parent) {
|
|
9
16
|
super();
|
|
10
17
|
this.port = port;
|
|
@@ -25,6 +32,9 @@ export class ESPLoader extends EventTarget {
|
|
|
25
32
|
this.__commandLock = Promise.resolve([0, []]);
|
|
26
33
|
this.__isReconfiguring = false;
|
|
27
34
|
this.__abandonCurrentOperation = false;
|
|
35
|
+
this._suppressDisconnect = false;
|
|
36
|
+
this.__consoleMode = false;
|
|
37
|
+
this._isUsbJtagOrOtg = undefined;
|
|
28
38
|
// Adaptive speed adjustment for flash read operations
|
|
29
39
|
this.__adaptiveBlockMultiplier = 1;
|
|
30
40
|
this.__adaptiveMaxInFlightMultiplier = 1;
|
|
@@ -81,6 +91,22 @@ export class ESPLoader extends EventTarget {
|
|
|
81
91
|
this.__chipVariant = value;
|
|
82
92
|
}
|
|
83
93
|
}
|
|
94
|
+
// Console mode with parent delegation
|
|
95
|
+
get _consoleMode() {
|
|
96
|
+
return this._parent ? this._parent._consoleMode : this.__consoleMode;
|
|
97
|
+
}
|
|
98
|
+
set _consoleMode(value) {
|
|
99
|
+
if (this._parent) {
|
|
100
|
+
this._parent._consoleMode = value;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
this.__consoleMode = value;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Public setter for console mode (used by script.js)
|
|
107
|
+
setConsoleMode(value) {
|
|
108
|
+
this._consoleMode = value;
|
|
109
|
+
}
|
|
84
110
|
get _inputBuffer() {
|
|
85
111
|
if (this._parent) {
|
|
86
112
|
return this._parent._inputBuffer;
|
|
@@ -317,6 +343,16 @@ export class ESPLoader extends EventTarget {
|
|
|
317
343
|
await this.connectWithResetStrategies();
|
|
318
344
|
// Detect chip type
|
|
319
345
|
await this.detectChip();
|
|
346
|
+
// Detect if device is using USB-JTAG/Serial or USB-OTG (not external serial chip)
|
|
347
|
+
// This is needed to determine the correct reset strategy for console mode
|
|
348
|
+
try {
|
|
349
|
+
this._isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
350
|
+
this.logger.debug(`USB connection type: ${this._isUsbJtagOrOtg ? "USB-JTAG/OTG" : "External Serial Chip"}`);
|
|
351
|
+
}
|
|
352
|
+
catch (err) {
|
|
353
|
+
this.logger.debug(`Could not detect USB connection type: ${err}`);
|
|
354
|
+
// Leave as undefined if detection fails
|
|
355
|
+
}
|
|
320
356
|
// Read the OTP data for this chip and store into this.efuses array
|
|
321
357
|
const FlAddr = getSpiFlashAddresses(this.getChipFamily());
|
|
322
358
|
const AddrMAC = FlAddr.macFuse;
|
|
@@ -340,7 +376,7 @@ export class ESPLoader extends EventTarget {
|
|
|
340
376
|
if (chipInfo) {
|
|
341
377
|
this.chipName = chipInfo.name;
|
|
342
378
|
this.chipFamily = chipInfo.family;
|
|
343
|
-
// Get chip revision for ESP32-P4
|
|
379
|
+
// Get chip revision for ESP32-P4 and ESP32-C3
|
|
344
380
|
if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
345
381
|
this.chipRevision = await this.getChipRevision();
|
|
346
382
|
this.logger.debug(`ESP32-P4 revision: ${this.chipRevision}`);
|
|
@@ -353,6 +389,10 @@ export class ESPLoader extends EventTarget {
|
|
|
353
389
|
}
|
|
354
390
|
this.logger.debug(`ESP32-P4 variant: ${this.chipVariant}`);
|
|
355
391
|
}
|
|
392
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
393
|
+
this.chipRevision = await this.getChipRevision();
|
|
394
|
+
this.logger.debug(`ESP32-C3 revision: ${this.chipRevision}`);
|
|
395
|
+
}
|
|
356
396
|
this.logger.debug(`Detected chip via IMAGE_CHIP_ID: ${chipId} (${this.chipName})`);
|
|
357
397
|
return;
|
|
358
398
|
}
|
|
@@ -385,7 +425,6 @@ export class ESPLoader extends EventTarget {
|
|
|
385
425
|
this.chipFamily = chip.family;
|
|
386
426
|
if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
387
427
|
this.chipRevision = await this.getChipRevision();
|
|
388
|
-
this.logger.debug(`ESP32-P4 revision: ${this.chipRevision}`);
|
|
389
428
|
if (this.chipRevision >= 300) {
|
|
390
429
|
this.chipVariant = "rev300";
|
|
391
430
|
}
|
|
@@ -394,24 +433,30 @@ export class ESPLoader extends EventTarget {
|
|
|
394
433
|
}
|
|
395
434
|
this.logger.debug(`ESP32-P4 variant: ${this.chipVariant}`);
|
|
396
435
|
}
|
|
436
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
437
|
+
this.chipRevision = await this.getChipRevision();
|
|
438
|
+
}
|
|
397
439
|
this.logger.debug(`Detected chip via magic value: ${toHex(chipMagicValue >>> 0, 8)} (${this.chipName})`);
|
|
398
440
|
}
|
|
399
441
|
/**
|
|
400
442
|
* Get chip revision for ESP32-P4
|
|
401
443
|
*/
|
|
402
444
|
async getChipRevision() {
|
|
403
|
-
if (this.chipFamily
|
|
404
|
-
|
|
445
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
446
|
+
// Read from EFUSE_BLOCK1 to get chip revision
|
|
447
|
+
// Word 2 contains revision info for ESP32-P4
|
|
448
|
+
const word2 = await this.readRegister(ESP32P4_EFUSE_BLOCK1_ADDR + 8);
|
|
449
|
+
// Minor revision: bits [3:0]
|
|
450
|
+
const minorRev = word2 & 0x0f;
|
|
451
|
+
// Major revision: bits [23] << 2 | bits [5:4]
|
|
452
|
+
const majorRev = (((word2 >> 23) & 1) << 2) | ((word2 >> 4) & 0x03);
|
|
453
|
+
// Revision is major * 100 + minor
|
|
454
|
+
return majorRev * 100 + minorRev;
|
|
405
455
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const minorRev = word2 & 0x0f;
|
|
411
|
-
// Major revision: bits [23] << 2 | bits [5:4]
|
|
412
|
-
const majorRev = (((word2 >> 23) & 1) << 2) | ((word2 >> 4) & 0x03);
|
|
413
|
-
// Revision is major * 100 + minor
|
|
414
|
-
return majorRev * 100 + minorRev;
|
|
456
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
457
|
+
return await this.getChipRevisionC3();
|
|
458
|
+
}
|
|
459
|
+
return 0;
|
|
415
460
|
}
|
|
416
461
|
/**
|
|
417
462
|
* Get security info including chip ID (ESP32-C3 and later)
|
|
@@ -484,13 +529,27 @@ export class ESPLoader extends EventTarget {
|
|
|
484
529
|
}
|
|
485
530
|
}
|
|
486
531
|
catch {
|
|
487
|
-
|
|
532
|
+
// Don't log error if this is an expected disconnect during console mode transition
|
|
533
|
+
if (!this._consoleMode) {
|
|
534
|
+
this.logger.error("Read loop got disconnected");
|
|
535
|
+
}
|
|
488
536
|
}
|
|
489
537
|
finally {
|
|
490
538
|
// Always reset reconfiguring flag when read loop ends
|
|
491
539
|
// This prevents "Cannot write during port reconfiguration" errors
|
|
492
540
|
// when the read loop dies unexpectedly
|
|
493
541
|
this._isReconfiguring = false;
|
|
542
|
+
// Release reader if still locked
|
|
543
|
+
if (this._reader) {
|
|
544
|
+
try {
|
|
545
|
+
this._reader.releaseLock();
|
|
546
|
+
this.logger.debug("Reader released in readLoop cleanup");
|
|
547
|
+
}
|
|
548
|
+
catch (err) {
|
|
549
|
+
this.logger.debug(`Reader release error in readLoop: ${err}`);
|
|
550
|
+
}
|
|
551
|
+
this._reader = undefined;
|
|
552
|
+
}
|
|
494
553
|
}
|
|
495
554
|
// Disconnected!
|
|
496
555
|
this.connected = false;
|
|
@@ -502,7 +561,11 @@ export class ESPLoader extends EventTarget {
|
|
|
502
561
|
detail: { message: "ESP32-S2 Native USB requires port reselection" },
|
|
503
562
|
}));
|
|
504
563
|
}
|
|
505
|
-
|
|
564
|
+
// Only dispatch disconnect event if not suppressed
|
|
565
|
+
if (!this._suppressDisconnect) {
|
|
566
|
+
this.dispatchEvent(new Event("disconnect"));
|
|
567
|
+
}
|
|
568
|
+
this._suppressDisconnect = false;
|
|
506
569
|
this.logger.debug("Finished read loop");
|
|
507
570
|
}
|
|
508
571
|
sleep(ms = 100) {
|
|
@@ -564,6 +627,30 @@ export class ESPLoader extends EventTarget {
|
|
|
564
627
|
await this.setDTR(false); // IO0=HIGH, done
|
|
565
628
|
await this.sleep(200);
|
|
566
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Reset to firmware mode (not bootloader) for Web Serial
|
|
632
|
+
* Keeps IO0=HIGH during reset so chip boots into firmware
|
|
633
|
+
*/
|
|
634
|
+
async hardResetToFirmware() {
|
|
635
|
+
await this.setDTR(false); // IO0=HIGH
|
|
636
|
+
await this.setRTS(true); // EN=LOW, chip in reset
|
|
637
|
+
await this.sleep(100);
|
|
638
|
+
await this.setRTS(false); // EN=HIGH, chip out of reset (IO0 stays HIGH)
|
|
639
|
+
await this.sleep(50);
|
|
640
|
+
await this.sleep(200);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Reset to firmware mode (not bootloader) for WebUSB
|
|
644
|
+
* Keeps IO0=HIGH during reset so chip boots into firmware
|
|
645
|
+
*/
|
|
646
|
+
async hardResetToFirmwareWebUSB() {
|
|
647
|
+
await this.setDTRWebUSB(false); // IO0=HIGH
|
|
648
|
+
await this.setRTSWebUSB(true); // EN=LOW, chip in reset
|
|
649
|
+
await this.sleep(100);
|
|
650
|
+
await this.setRTSWebUSB(false); // EN=HIGH, chip out of reset (IO0 stays HIGH)
|
|
651
|
+
await this.sleep(50);
|
|
652
|
+
await this.sleep(200);
|
|
653
|
+
}
|
|
567
654
|
/**
|
|
568
655
|
* @name hardResetUnixTight
|
|
569
656
|
* Unix Tight reset for Web Serial (Desktop) - sets DTR and RTS simultaneously
|
|
@@ -1013,7 +1100,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1013
1100
|
try {
|
|
1014
1101
|
// Check if port is still open, if not, skip this strategy
|
|
1015
1102
|
if (!this.connected || !this.port.writable) {
|
|
1016
|
-
this.logger.
|
|
1103
|
+
this.logger.debug(`Port disconnected, skipping ${strategy.name} reset`);
|
|
1017
1104
|
continue;
|
|
1018
1105
|
}
|
|
1019
1106
|
// Clear abandon flag before starting new strategy
|
|
@@ -1055,7 +1142,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1055
1142
|
}
|
|
1056
1143
|
catch (error) {
|
|
1057
1144
|
lastError = error;
|
|
1058
|
-
this.logger.
|
|
1145
|
+
this.logger.debug(`${strategy.name} reset failed: ${error.message}`);
|
|
1059
1146
|
// Set abandon flag to stop any in-flight operations
|
|
1060
1147
|
this._abandonCurrentOperation = true;
|
|
1061
1148
|
// Wait a bit for in-flight operations to abort
|
|
@@ -1077,12 +1164,134 @@ export class ESPLoader extends EventTarget {
|
|
|
1077
1164
|
}
|
|
1078
1165
|
/**
|
|
1079
1166
|
* @name watchdogReset
|
|
1080
|
-
* Watchdog reset for ESP32-S2/S3 with USB-OTG
|
|
1167
|
+
* Watchdog reset for ESP32-S2/S3/C3 with USB-OTG or USB-JTAG/Serial
|
|
1081
1168
|
* Uses RTC watchdog timer to reset the chip - works when DTR/RTS signals are not available
|
|
1169
|
+
* This is an alias for rtcWdtResetChipSpecific() for backwards compatibility
|
|
1082
1170
|
*/
|
|
1083
1171
|
async watchdogReset() {
|
|
1084
|
-
this.
|
|
1085
|
-
|
|
1172
|
+
await this.rtcWdtResetChipSpecific();
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Check if current chip is using USB-OTG
|
|
1176
|
+
* Supports ESP32-S2 and ESP32-S3
|
|
1177
|
+
*/
|
|
1178
|
+
async usingUsbOtg() {
|
|
1179
|
+
let uartDevBufNo;
|
|
1180
|
+
let usbOtgValue;
|
|
1181
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S2) {
|
|
1182
|
+
uartDevBufNo = ESP32S2_UARTDEV_BUF_NO;
|
|
1183
|
+
usbOtgValue = ESP32S2_UARTDEV_BUF_NO_USB_OTG;
|
|
1184
|
+
}
|
|
1185
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
1186
|
+
uartDevBufNo = ESP32S3_UARTDEV_BUF_NO;
|
|
1187
|
+
usbOtgValue = ESP32S3_UARTDEV_BUF_NO_USB_OTG;
|
|
1188
|
+
}
|
|
1189
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1190
|
+
// P4: UARTDEV_BUF_NO depends on chip revision
|
|
1191
|
+
if (this.chipRevision === null) {
|
|
1192
|
+
this.chipRevision = await this.getChipRevision();
|
|
1193
|
+
}
|
|
1194
|
+
if (this.chipRevision < 300) {
|
|
1195
|
+
uartDevBufNo = ESP32P4_UARTDEV_BUF_NO_REV0;
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
uartDevBufNo = ESP32P4_UARTDEV_BUF_NO_REV300;
|
|
1199
|
+
}
|
|
1200
|
+
usbOtgValue = ESP32P4_UARTDEV_BUF_NO_USB_OTG;
|
|
1201
|
+
}
|
|
1202
|
+
else {
|
|
1203
|
+
return false;
|
|
1204
|
+
}
|
|
1205
|
+
const uartNo = (await this.readRegister(uartDevBufNo)) & 0xff;
|
|
1206
|
+
return uartNo === usbOtgValue;
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Check if current chip is using USB-JTAG/Serial
|
|
1210
|
+
* Supports ESP32-S3 and ESP32-C3
|
|
1211
|
+
*/
|
|
1212
|
+
async usingUsbJtagSerial() {
|
|
1213
|
+
let uartDevBufNo;
|
|
1214
|
+
let usbJtagSerialValue;
|
|
1215
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
1216
|
+
uartDevBufNo = ESP32S3_UARTDEV_BUF_NO;
|
|
1217
|
+
usbJtagSerialValue = ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1218
|
+
}
|
|
1219
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
1220
|
+
// ESP32-C3: BSS_UART_DEV_ADDR depends on chip revision
|
|
1221
|
+
// Revision < 101: 0x3FCDF064
|
|
1222
|
+
// Revision >= 101: 0x3FCDF060
|
|
1223
|
+
let bssUartDevAddr;
|
|
1224
|
+
// Get chip revision if not already set
|
|
1225
|
+
if (this.chipRevision === null) {
|
|
1226
|
+
this.chipRevision = await this.getChipRevisionC3();
|
|
1227
|
+
}
|
|
1228
|
+
if (this.chipRevision < 101) {
|
|
1229
|
+
bssUartDevAddr = 0x3fcdf064;
|
|
1230
|
+
}
|
|
1231
|
+
else {
|
|
1232
|
+
bssUartDevAddr = 0x3fcdf060;
|
|
1233
|
+
}
|
|
1234
|
+
uartDevBufNo = bssUartDevAddr + ESP32C3_BUF_UART_NO_OFFSET;
|
|
1235
|
+
usbJtagSerialValue = ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1236
|
+
}
|
|
1237
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C5) {
|
|
1238
|
+
uartDevBufNo = ESP32C5_UARTDEV_BUF_NO;
|
|
1239
|
+
usbJtagSerialValue = ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1240
|
+
}
|
|
1241
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
1242
|
+
uartDevBufNo = ESP32C6_UARTDEV_BUF_NO;
|
|
1243
|
+
usbJtagSerialValue = ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1244
|
+
}
|
|
1245
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1246
|
+
// P4: UARTDEV_BUF_NO depends on chip revision
|
|
1247
|
+
// Revision < 300: 0x4FF3FEC8
|
|
1248
|
+
// Revision >= 300: 0x4FFBFEC8
|
|
1249
|
+
if (this.chipRevision === null) {
|
|
1250
|
+
this.chipRevision = await this.getChipRevision();
|
|
1251
|
+
}
|
|
1252
|
+
if (this.chipRevision < 300) {
|
|
1253
|
+
uartDevBufNo = ESP32P4_UARTDEV_BUF_NO_REV0;
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
uartDevBufNo = ESP32P4_UARTDEV_BUF_NO_REV300;
|
|
1257
|
+
}
|
|
1258
|
+
usbJtagSerialValue = ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1259
|
+
}
|
|
1260
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32H2) {
|
|
1261
|
+
uartDevBufNo = ESP32H2_UARTDEV_BUF_NO;
|
|
1262
|
+
usbJtagSerialValue = ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL;
|
|
1263
|
+
}
|
|
1264
|
+
else {
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
const uartNo = (await this.readRegister(uartDevBufNo)) & 0xff;
|
|
1268
|
+
return uartNo === usbJtagSerialValue;
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Get chip revision for ESP32-C3
|
|
1272
|
+
* Reads from EFUSE registers and calculates revision
|
|
1273
|
+
*/
|
|
1274
|
+
async getChipRevisionC3() {
|
|
1275
|
+
if (this.chipFamily !== CHIP_FAMILY_ESP32C3) {
|
|
1276
|
+
return 0;
|
|
1277
|
+
}
|
|
1278
|
+
// Read EFUSE_RD_MAC_SPI_SYS_3_REG (bits [20:18] = lower 3 bits of revision)
|
|
1279
|
+
const word3 = await this.readRegister(ESP32C3_EFUSE_RD_MAC_SPI_SYS_3_REG);
|
|
1280
|
+
const low = (word3 >> 18) & 0x07;
|
|
1281
|
+
// Read EFUSE_RD_MAC_SPI_SYS_5_REG (bits [25:23] = upper 3 bits of revision)
|
|
1282
|
+
const word5 = await this.readRegister(ESP32C3_EFUSE_RD_MAC_SPI_SYS_5_REG);
|
|
1283
|
+
const hi = (word5 >> 23) & 0x07;
|
|
1284
|
+
// Combine: upper 3 bits from word5, lower 3 bits from word3
|
|
1285
|
+
const revision = (hi << 3) | low;
|
|
1286
|
+
return revision;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* RTC watchdog timer reset for ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C5, ESP32-C6, and ESP32-P4
|
|
1290
|
+
* Uses specific registers for each chip family
|
|
1291
|
+
* Note: ESP32-H2 does NOT support WDT reset
|
|
1292
|
+
*/
|
|
1293
|
+
async rtcWdtResetChipSpecific() {
|
|
1294
|
+
this.logger.debug("Hard resetting with watchdog timer...");
|
|
1086
1295
|
let WDTWPROTECT_REG;
|
|
1087
1296
|
let WDTCONFIG0_REG;
|
|
1088
1297
|
let WDTCONFIG1_REG;
|
|
@@ -1099,12 +1308,62 @@ export class ESPLoader extends EventTarget {
|
|
|
1099
1308
|
WDTCONFIG1_REG = ESP32S3_RTC_CNTL_WDTCONFIG1_REG;
|
|
1100
1309
|
WDT_WKEY = ESP32S3_RTC_CNTL_WDT_WKEY;
|
|
1101
1310
|
}
|
|
1311
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
1312
|
+
WDTWPROTECT_REG = ESP32C3_RTC_CNTL_WDTWPROTECT_REG;
|
|
1313
|
+
WDTCONFIG0_REG = ESP32C3_RTC_CNTL_WDTCONFIG0_REG;
|
|
1314
|
+
WDTCONFIG1_REG = ESP32C3_RTC_CNTL_WDTCONFIG1_REG;
|
|
1315
|
+
WDT_WKEY = ESP32C3_RTC_CNTL_WDT_WKEY;
|
|
1316
|
+
}
|
|
1317
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C5 ||
|
|
1318
|
+
this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
1319
|
+
// C5 and C6 use LP_WDT (Low Power Watchdog Timer)
|
|
1320
|
+
WDTWPROTECT_REG = ESP32C5_C6_RTC_CNTL_WDTWPROTECT_REG;
|
|
1321
|
+
WDTCONFIG0_REG = ESP32C5_C6_RTC_CNTL_WDTCONFIG0_REG;
|
|
1322
|
+
WDTCONFIG1_REG = ESP32C5_C6_RTC_CNTL_WDTCONFIG1_REG;
|
|
1323
|
+
WDT_WKEY = ESP32C5_C6_RTC_CNTL_WDT_WKEY;
|
|
1324
|
+
}
|
|
1325
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1326
|
+
// P4 uses LP_WDT (Low Power Watchdog Timer)
|
|
1327
|
+
WDTWPROTECT_REG = ESP32P4_RTC_CNTL_WDTWPROTECT_REG;
|
|
1328
|
+
WDTCONFIG0_REG = ESP32P4_RTC_CNTL_WDTCONFIG0_REG;
|
|
1329
|
+
WDTCONFIG1_REG = ESP32P4_RTC_CNTL_WDTCONFIG1_REG;
|
|
1330
|
+
WDT_WKEY = ESP32P4_RTC_CNTL_WDT_WKEY;
|
|
1331
|
+
}
|
|
1102
1332
|
else {
|
|
1103
|
-
throw new Error(`
|
|
1333
|
+
throw new Error(`rtcWdtResetChipSpecific() is not supported for ${this.chipFamily}`);
|
|
1104
1334
|
}
|
|
1105
1335
|
// Unlock watchdog registers
|
|
1106
1336
|
await this.writeRegister(WDTWPROTECT_REG, WDT_WKEY, undefined, 0);
|
|
1107
|
-
//
|
|
1337
|
+
// Clear force download boot register (if applicable) BEFORE triggering WDT reset
|
|
1338
|
+
// This ensures the chip boots into firmware mode after reset
|
|
1339
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S2) {
|
|
1340
|
+
try {
|
|
1341
|
+
await this.writeRegister(ESP32S2_RTC_CNTL_OPTION1_REG, 0, ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, 0);
|
|
1342
|
+
this.logger.debug("Cleared force download boot mask");
|
|
1343
|
+
}
|
|
1344
|
+
catch (err) {
|
|
1345
|
+
this.logger.debug(`Expected error clearing force download boot mask: ${err}`);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
1349
|
+
try {
|
|
1350
|
+
await this.writeRegister(ESP32S3_RTC_CNTL_OPTION1_REG, 0, ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, 0);
|
|
1351
|
+
this.logger.debug("Cleared force download boot mask");
|
|
1352
|
+
}
|
|
1353
|
+
catch (err) {
|
|
1354
|
+
this.logger.debug(`Expected error clearing force download boot mask: ${err}`);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
1358
|
+
try {
|
|
1359
|
+
await this.writeRegister(ESP32P4_RTC_CNTL_OPTION1_REG, 0, ESP32P4_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, 0);
|
|
1360
|
+
this.logger.debug("Cleared force download boot mask");
|
|
1361
|
+
}
|
|
1362
|
+
catch (err) {
|
|
1363
|
+
this.logger.debug(`Expected error clearing force download boot mask: ${err}`);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
// Set WDT timeout to 2000ms (matches Python esptool)
|
|
1108
1367
|
await this.writeRegister(WDTCONFIG1_REG, 2000, undefined, 0);
|
|
1109
1368
|
// Enable WDT: bit 31 = enable, bits 28-30 = stage, bit 8 = sys reset, bits 0-2 = prescaler
|
|
1110
1369
|
const wdtConfig = (1 << 31) | (5 << 28) | (1 << 8) | 2;
|
|
@@ -1114,102 +1373,139 @@ export class ESPLoader extends EventTarget {
|
|
|
1114
1373
|
// Wait for reset to take effect
|
|
1115
1374
|
await this.sleep(500);
|
|
1116
1375
|
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Helper: Check if USB-based WDT reset should be used for S2/S3
|
|
1378
|
+
* Returns true if WDT reset was performed, false otherwise
|
|
1379
|
+
*/
|
|
1380
|
+
async tryUsbWdtReset(chipName, GPIO_STRAP_REG, GPIO_STRAP_SPI_BOOT_MASK, RTC_CNTL_OPTION1_REG, RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK) {
|
|
1381
|
+
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
1382
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
1383
|
+
if (isUsingUsbOtg || isUsingUsbJtagSerial) {
|
|
1384
|
+
const strapReg = await this.readRegister(GPIO_STRAP_REG);
|
|
1385
|
+
const forceDlReg = await this.readRegister(RTC_CNTL_OPTION1_REG);
|
|
1386
|
+
// Only use watchdog reset if GPIO0 is low AND force download boot mode is not set
|
|
1387
|
+
if ((strapReg & GPIO_STRAP_SPI_BOOT_MASK) === 0 &&
|
|
1388
|
+
(forceDlReg & RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK) === 0) {
|
|
1389
|
+
await this.rtcWdtResetChipSpecific();
|
|
1390
|
+
this.logger.debug(`${chipName}: RTC WDT reset (USB detected, GPIO0 low)`);
|
|
1391
|
+
return true;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
return false;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Chip-specific hard reset for ESP32-S2
|
|
1398
|
+
* Checks if using USB-JTAG/Serial and uses watchdog reset if necessary
|
|
1399
|
+
*/
|
|
1400
|
+
async hardResetS2() {
|
|
1401
|
+
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
1402
|
+
if (isUsingUsbOtg) {
|
|
1403
|
+
await this.rtcWdtResetChipSpecific();
|
|
1404
|
+
this.logger.debug("ESP32-S2: RTC WDT reset (USB-OTG detected)");
|
|
1405
|
+
}
|
|
1406
|
+
else {
|
|
1407
|
+
// Use standard hardware reset
|
|
1408
|
+
await this.hardResetClassic();
|
|
1409
|
+
this.logger.debug("ESP32-S2: Classic reset");
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Chip-specific hard reset for ESP32-S3
|
|
1414
|
+
* Checks if using USB-JTAG/Serial and uses watchdog reset if necessary
|
|
1415
|
+
*/
|
|
1416
|
+
async hardResetS3() {
|
|
1417
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
1418
|
+
if (isUsingUsbJtagSerial) {
|
|
1419
|
+
await this.rtcWdtResetChipSpecific();
|
|
1420
|
+
this.logger.debug("ESP32-S3: RTC WDT reset (USB-JTAG/Serial detected)");
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
// Use standard hardware reset
|
|
1424
|
+
await this.hardResetClassic();
|
|
1425
|
+
this.logger.debug("ESP32-S3: Classic reset");
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Chip-specific hard reset for ESP32-C3
|
|
1430
|
+
* Checks if using USB-JTAG/Serial and uses watchdog reset if necessary
|
|
1431
|
+
*/
|
|
1432
|
+
async hardResetC3() {
|
|
1433
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
1434
|
+
if (isUsingUsbJtagSerial) {
|
|
1435
|
+
await this.rtcWdtResetChipSpecific();
|
|
1436
|
+
this.logger.debug("ESP32-C3: RTC WDT reset (USB-JTAG/Serial detected)");
|
|
1437
|
+
}
|
|
1438
|
+
else {
|
|
1439
|
+
// Use standard hardware reset
|
|
1440
|
+
await this.hardResetClassic();
|
|
1441
|
+
this.logger.debug("ESP32-C3: Classic reset");
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1117
1444
|
async hardReset(bootloader = false) {
|
|
1445
|
+
// In console mode, only allow simple hardware reset (no bootloader entry)
|
|
1446
|
+
if (this._consoleMode) {
|
|
1447
|
+
if (bootloader) {
|
|
1448
|
+
this.logger.debug("Skipping bootloader reset - device is in console mode");
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
// Simple hardware reset to restart firmware (IO0=HIGH)
|
|
1452
|
+
this.logger.debug("Performing hardware reset (console mode)...");
|
|
1453
|
+
if (this.isWebUSB()) {
|
|
1454
|
+
await this.hardResetToFirmwareWebUSB();
|
|
1455
|
+
}
|
|
1456
|
+
else {
|
|
1457
|
+
await this.hardResetToFirmware();
|
|
1458
|
+
}
|
|
1459
|
+
this.logger.debug("Hardware reset complete");
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1118
1462
|
if (bootloader) {
|
|
1119
1463
|
// enter flash mode
|
|
1120
1464
|
if (this.port.getInfo().usbProductId === USB_JTAG_SERIAL_PID) {
|
|
1121
1465
|
await this.hardResetUSBJTAGSerial();
|
|
1122
|
-
this.logger.
|
|
1466
|
+
this.logger.debug("USB-JTAG/Serial reset.");
|
|
1123
1467
|
}
|
|
1124
1468
|
else {
|
|
1125
1469
|
// Use different reset strategy for WebUSB (Android) vs Web Serial (Desktop)
|
|
1126
1470
|
if (this.isWebUSB()) {
|
|
1127
1471
|
await this.hardResetClassicWebUSB();
|
|
1128
|
-
this.logger.
|
|
1472
|
+
this.logger.debug("Classic reset (WebUSB/Android).");
|
|
1129
1473
|
}
|
|
1130
1474
|
else {
|
|
1131
1475
|
await this.hardResetClassic();
|
|
1132
|
-
this.logger.
|
|
1476
|
+
this.logger.debug("Classic reset.");
|
|
1133
1477
|
}
|
|
1134
1478
|
}
|
|
1135
1479
|
}
|
|
1136
1480
|
else {
|
|
1137
1481
|
// just reset (no bootloader mode)
|
|
1138
|
-
// For ESP32-S2/S3 with USB-OTG, check if watchdog reset is needed
|
|
1139
|
-
if (this.
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
try {
|
|
1144
|
-
// Clear force download boot mode to avoid chip being stuck in download mode
|
|
1145
|
-
// after reset. Workaround for issue:
|
|
1146
|
-
// https://github.com/espressif/arduino-esp32/issues/6762
|
|
1147
|
-
const RTC_CNTL_OPTION1_REG = this.chipFamily === CHIP_FAMILY_ESP32S2
|
|
1148
|
-
? ESP32S2_RTC_CNTL_OPTION1_REG
|
|
1149
|
-
: ESP32S3_RTC_CNTL_OPTION1_REG;
|
|
1150
|
-
const RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = this.chipFamily === CHIP_FAMILY_ESP32S2
|
|
1151
|
-
? ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK
|
|
1152
|
-
: ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK;
|
|
1153
|
-
await this.writeRegister(RTC_CNTL_OPTION1_REG, 0, RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, 0);
|
|
1154
|
-
}
|
|
1155
|
-
catch (e) {
|
|
1156
|
-
// Skip invalid response and continue reset (can happen when monitoring during reset)
|
|
1157
|
-
this.logger.log("Warning: Could not clear force download boot mode:", e);
|
|
1158
|
-
}
|
|
1159
|
-
// Check the strapping register to see if we can perform a watchdog reset
|
|
1160
|
-
// Only use watchdog reset if GPIO0 is low AND force download boot mode is not set
|
|
1161
|
-
let useWatchdogReset = false;
|
|
1162
|
-
try {
|
|
1163
|
-
const GPIO_STRAP_REG = this.chipFamily === CHIP_FAMILY_ESP32S2
|
|
1164
|
-
? ESP32S2_GPIO_STRAP_REG
|
|
1165
|
-
: ESP32S3_GPIO_STRAP_REG;
|
|
1166
|
-
const GPIO_STRAP_SPI_BOOT_MASK = this.chipFamily === CHIP_FAMILY_ESP32S2
|
|
1167
|
-
? ESP32S2_GPIO_STRAP_SPI_BOOT_MASK
|
|
1168
|
-
: ESP32S3_GPIO_STRAP_SPI_BOOT_MASK;
|
|
1169
|
-
const RTC_CNTL_OPTION1_REG = this.chipFamily === CHIP_FAMILY_ESP32S2
|
|
1170
|
-
? ESP32S2_RTC_CNTL_OPTION1_REG
|
|
1171
|
-
: ESP32S3_RTC_CNTL_OPTION1_REG;
|
|
1172
|
-
const RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = this.chipFamily === CHIP_FAMILY_ESP32S2
|
|
1173
|
-
? ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK
|
|
1174
|
-
: ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK;
|
|
1175
|
-
const strapReg = await this.readRegister(GPIO_STRAP_REG);
|
|
1176
|
-
const forceDlReg = await this.readRegister(RTC_CNTL_OPTION1_REG);
|
|
1177
|
-
// GPIO0 low (download mode) AND force download boot not set
|
|
1178
|
-
if ((strapReg & GPIO_STRAP_SPI_BOOT_MASK) === 0 &&
|
|
1179
|
-
(forceDlReg & RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK) === 0) {
|
|
1180
|
-
useWatchdogReset = true;
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
catch (e) {
|
|
1184
|
-
// If we can't read the registers, use watchdog reset as fallback
|
|
1185
|
-
this.logger.log("Warning: Could not read strap/option registers, using watchdog reset:", e);
|
|
1186
|
-
useWatchdogReset = true;
|
|
1187
|
-
}
|
|
1188
|
-
if (useWatchdogReset) {
|
|
1189
|
-
await this.watchdogReset();
|
|
1190
|
-
this.logger.log("Watchdog reset (USB-OTG).");
|
|
1191
|
-
}
|
|
1192
|
-
else {
|
|
1193
|
-
// Not in download mode, can use DTR/RTS reset
|
|
1194
|
-
// But USB-OTG doesn't have DTR/RTS, so fall back to watchdog
|
|
1195
|
-
await this.watchdogReset();
|
|
1196
|
-
this.logger.log("Watchdog reset (USB-OTG, normal boot).");
|
|
1197
|
-
}
|
|
1482
|
+
// For ESP32-S2/S3 with USB-OTG or USB-JTAG/Serial, check if watchdog reset is needed
|
|
1483
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S2 && !this._consoleMode) {
|
|
1484
|
+
const wdtResetUsed = await this.tryUsbWdtReset("ESP32-S2", ESP32S2_GPIO_STRAP_REG, ESP32S2_GPIO_STRAP_SPI_BOOT_MASK, ESP32S2_RTC_CNTL_OPTION1_REG, ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK);
|
|
1485
|
+
if (wdtResetUsed)
|
|
1486
|
+
return;
|
|
1198
1487
|
}
|
|
1199
|
-
else if (this.
|
|
1488
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32S3 &&
|
|
1489
|
+
!this._consoleMode) {
|
|
1490
|
+
const wdtResetUsed = await this.tryUsbWdtReset("ESP32-S3", ESP32S3_GPIO_STRAP_REG, ESP32S3_GPIO_STRAP_SPI_BOOT_MASK, ESP32S3_RTC_CNTL_OPTION1_REG, ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK);
|
|
1491
|
+
if (wdtResetUsed)
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
// Standard reset for all other cases
|
|
1495
|
+
if (this.isWebUSB()) {
|
|
1200
1496
|
// WebUSB: Use longer delays for better compatibility
|
|
1201
1497
|
await this.setRTSWebUSB(true); // EN->LOW
|
|
1202
1498
|
await this.sleep(200);
|
|
1203
1499
|
await this.setRTSWebUSB(false);
|
|
1204
1500
|
await this.sleep(200);
|
|
1205
|
-
this.logger.
|
|
1501
|
+
this.logger.debug("Hard reset (WebUSB).");
|
|
1206
1502
|
}
|
|
1207
1503
|
else {
|
|
1208
1504
|
// Web Serial: Standard reset
|
|
1209
1505
|
await this.setRTS(true); // EN->LOW
|
|
1210
1506
|
await this.sleep(100);
|
|
1211
1507
|
await this.setRTS(false);
|
|
1212
|
-
this.logger.
|
|
1508
|
+
this.logger.debug("Hard reset.");
|
|
1213
1509
|
}
|
|
1214
1510
|
}
|
|
1215
1511
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
@@ -2374,6 +2670,221 @@ export class ESPLoader extends EventTarget {
|
|
|
2374
2670
|
this.logger.debug(`Port close error: ${err}`);
|
|
2375
2671
|
}
|
|
2376
2672
|
}
|
|
2673
|
+
/**
|
|
2674
|
+
* @name releaseReaderWriter
|
|
2675
|
+
* Release reader and writer locks without closing the port
|
|
2676
|
+
* Used when switching to console mode
|
|
2677
|
+
*/
|
|
2678
|
+
async releaseReaderWriter() {
|
|
2679
|
+
if (this._parent) {
|
|
2680
|
+
await this._parent.releaseReaderWriter();
|
|
2681
|
+
return;
|
|
2682
|
+
}
|
|
2683
|
+
// Check if device is in JTAG mode and needs reset to boot into firmware
|
|
2684
|
+
const didReconnect = await this._resetToFirmwareIfNeeded();
|
|
2685
|
+
// If we reconnected for console, the reader/writer are already released and restarted
|
|
2686
|
+
if (didReconnect) {
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
// Wait for pending writes to complete
|
|
2690
|
+
try {
|
|
2691
|
+
await this._writeChain;
|
|
2692
|
+
}
|
|
2693
|
+
catch (err) {
|
|
2694
|
+
this.logger.debug(`Pending write error during release: ${err}`);
|
|
2695
|
+
}
|
|
2696
|
+
// Release writer
|
|
2697
|
+
if (this._writer) {
|
|
2698
|
+
try {
|
|
2699
|
+
this._writer.releaseLock();
|
|
2700
|
+
this.logger.debug("Writer released");
|
|
2701
|
+
}
|
|
2702
|
+
catch (err) {
|
|
2703
|
+
this.logger.debug(`Writer release error: ${err}`);
|
|
2704
|
+
}
|
|
2705
|
+
this._writer = undefined;
|
|
2706
|
+
}
|
|
2707
|
+
// Cancel and release reader
|
|
2708
|
+
if (this._reader) {
|
|
2709
|
+
const reader = this._reader;
|
|
2710
|
+
try {
|
|
2711
|
+
// Suppress disconnect event during console mode switching
|
|
2712
|
+
this._suppressDisconnect = true;
|
|
2713
|
+
await reader.cancel();
|
|
2714
|
+
this.logger.debug("Reader cancelled");
|
|
2715
|
+
}
|
|
2716
|
+
catch (err) {
|
|
2717
|
+
this.logger.debug(`Reader cancel error: ${err}`);
|
|
2718
|
+
}
|
|
2719
|
+
finally {
|
|
2720
|
+
try {
|
|
2721
|
+
reader.releaseLock();
|
|
2722
|
+
}
|
|
2723
|
+
catch (err) {
|
|
2724
|
+
this.logger.debug(`Reader release error: ${err}`);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
if (this._reader === reader) {
|
|
2728
|
+
this._reader = undefined;
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
/**
|
|
2733
|
+
* @name resetToFirmware
|
|
2734
|
+
* Public method to reset device from bootloader to firmware for console mode
|
|
2735
|
+
* Automatically detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
2736
|
+
* @returns true if reset was performed, false if not needed
|
|
2737
|
+
*/
|
|
2738
|
+
async resetToFirmware() {
|
|
2739
|
+
return await this._resetToFirmwareIfNeeded();
|
|
2740
|
+
}
|
|
2741
|
+
/**
|
|
2742
|
+
* @name detectUsbConnectionType
|
|
2743
|
+
* Detect if device is using USB-JTAG/Serial or USB-OTG (not external serial chip)
|
|
2744
|
+
* This helper extracts the detection logic from initialize() for reuse
|
|
2745
|
+
* @returns true if USB-JTAG or USB-OTG, false if external serial chip
|
|
2746
|
+
* @throws Error if detection fails and chipFamily is not set
|
|
2747
|
+
*/
|
|
2748
|
+
async detectUsbConnectionType() {
|
|
2749
|
+
if (!this.chipFamily) {
|
|
2750
|
+
throw new Error("Cannot detect USB connection type: chipFamily not set");
|
|
2751
|
+
}
|
|
2752
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
2753
|
+
this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
2754
|
+
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
2755
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
2756
|
+
return isUsingUsbOtg || isUsingUsbJtagSerial;
|
|
2757
|
+
}
|
|
2758
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C3 ||
|
|
2759
|
+
this.chipFamily === CHIP_FAMILY_ESP32C5 ||
|
|
2760
|
+
this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
2761
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
2762
|
+
return isUsingUsbJtagSerial;
|
|
2763
|
+
}
|
|
2764
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
2765
|
+
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
2766
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
2767
|
+
return isUsingUsbOtg || isUsingUsbJtagSerial;
|
|
2768
|
+
}
|
|
2769
|
+
else {
|
|
2770
|
+
// Other chips don't have USB-JTAG/OTG
|
|
2771
|
+
return false;
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* @name enterConsoleMode
|
|
2776
|
+
* Prepare device for console mode by resetting to firmware
|
|
2777
|
+
* Handles both USB-JTAG/OTG devices (closes port) and external serial chips (keeps port open)
|
|
2778
|
+
* @returns true if port was closed (USB-JTAG), false if port stays open (serial chip)
|
|
2779
|
+
*/
|
|
2780
|
+
async enterConsoleMode() {
|
|
2781
|
+
// Set console mode flag
|
|
2782
|
+
this._consoleMode = true;
|
|
2783
|
+
// Re-detect USB connection type to ensure we have a definitive value
|
|
2784
|
+
// This handles cases where isUsbJtagOrOtg might be undefined
|
|
2785
|
+
let isUsbJtag;
|
|
2786
|
+
try {
|
|
2787
|
+
isUsbJtag = await this.detectUsbConnectionType();
|
|
2788
|
+
this.logger.debug(`USB connection type detected: ${isUsbJtag ? "USB-JTAG/OTG" : "External Serial Chip"}`);
|
|
2789
|
+
}
|
|
2790
|
+
catch (err) {
|
|
2791
|
+
// If detection fails, fall back to cached value or fail-fast
|
|
2792
|
+
if (this.isUsbJtagOrOtg === undefined) {
|
|
2793
|
+
throw new Error(`Cannot enter console mode: USB connection type unknown and detection failed: ${err}`);
|
|
2794
|
+
}
|
|
2795
|
+
this.logger.debug(`USB detection failed, using cached value: ${this.isUsbJtagOrOtg}`);
|
|
2796
|
+
isUsbJtag = this.isUsbJtagOrOtg;
|
|
2797
|
+
}
|
|
2798
|
+
if (isUsbJtag) {
|
|
2799
|
+
// USB-JTAG/OTG devices: Use watchdog reset which closes port
|
|
2800
|
+
const wasReset = await this._resetToFirmwareIfNeeded();
|
|
2801
|
+
return wasReset; // true = port closed, caller must reopen
|
|
2802
|
+
}
|
|
2803
|
+
else {
|
|
2804
|
+
// External serial chip devices: Release locks and do simple reset
|
|
2805
|
+
try {
|
|
2806
|
+
await this.releaseReaderWriter();
|
|
2807
|
+
await this.sleep(100);
|
|
2808
|
+
}
|
|
2809
|
+
catch (err) {
|
|
2810
|
+
this.logger.debug(`Failed to release locks: ${err}`);
|
|
2811
|
+
}
|
|
2812
|
+
// Hardware reset to firmware mode (IO0=HIGH)
|
|
2813
|
+
try {
|
|
2814
|
+
await this.hardReset(false);
|
|
2815
|
+
this.logger.log("Device reset to firmware mode");
|
|
2816
|
+
}
|
|
2817
|
+
catch (err) {
|
|
2818
|
+
this.logger.debug(`Could not reset device: ${err}`);
|
|
2819
|
+
}
|
|
2820
|
+
return false; // Port stays open
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
/**
|
|
2824
|
+
* @name _resetToFirmwareIfNeeded
|
|
2825
|
+
* Reset device from bootloader to firmware when switching to console mode
|
|
2826
|
+
* Detects USB-JTAG/Serial and USB-OTG devices and performs appropriate reset
|
|
2827
|
+
* @returns true if reconnect was performed, false otherwise
|
|
2828
|
+
*/
|
|
2829
|
+
async _resetToFirmwareIfNeeded() {
|
|
2830
|
+
try {
|
|
2831
|
+
// Check if device is using USB-JTAG/Serial or USB-OTG
|
|
2832
|
+
// Value should already be set during main() connection
|
|
2833
|
+
// Use getter to access parent's value if this is a stub
|
|
2834
|
+
const needsReset = this.isUsbJtagOrOtg === true;
|
|
2835
|
+
if (needsReset) {
|
|
2836
|
+
const resetMethod = this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
2837
|
+
this.chipFamily === CHIP_FAMILY_ESP32S3
|
|
2838
|
+
? "USB-JTAG/Serial or USB-OTG"
|
|
2839
|
+
: "USB-JTAG/Serial";
|
|
2840
|
+
this.logger.log(`Resetting ${this.chipFamily} (${resetMethod}) to boot into firmware...`);
|
|
2841
|
+
// Set console mode flag before reset to prevent subsequent hardReset calls
|
|
2842
|
+
this._consoleMode = true;
|
|
2843
|
+
// For S2/S3: Clear force download boot mask before WDT reset
|
|
2844
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
2845
|
+
this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
2846
|
+
const OPTION1_REG = this.chipFamily === CHIP_FAMILY_ESP32S2
|
|
2847
|
+
? ESP32S2_RTC_CNTL_OPTION1_REG
|
|
2848
|
+
: ESP32S3_RTC_CNTL_OPTION1_REG;
|
|
2849
|
+
const FORCE_DOWNLOAD_BOOT_MASK = this.chipFamily === CHIP_FAMILY_ESP32S2
|
|
2850
|
+
? ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK
|
|
2851
|
+
: ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK;
|
|
2852
|
+
try {
|
|
2853
|
+
// Clear force download boot mode to avoid chip being stuck in download mode
|
|
2854
|
+
await this.writeRegister(OPTION1_REG, 0, FORCE_DOWNLOAD_BOOT_MASK, 0);
|
|
2855
|
+
this.logger.debug("Cleared force download boot mask");
|
|
2856
|
+
}
|
|
2857
|
+
catch (err) {
|
|
2858
|
+
this.logger.debug(`Expected error clearing force download boot mask: ${err}`);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
// Perform watchdog reset to reboot into firmware
|
|
2862
|
+
try {
|
|
2863
|
+
await this.rtcWdtResetChipSpecific();
|
|
2864
|
+
this.logger.debug("Watchdog reset triggered successfully");
|
|
2865
|
+
}
|
|
2866
|
+
catch (err) {
|
|
2867
|
+
// Error is expected - device resets before responding
|
|
2868
|
+
this.logger.debug(`Watchdog reset initiated (connection lost as expected: ${err})`);
|
|
2869
|
+
}
|
|
2870
|
+
// Wait for device to fully boot into firmware
|
|
2871
|
+
this.logger.log("Waiting for device to boot into firmware...");
|
|
2872
|
+
await this.sleep(1000);
|
|
2873
|
+
// After WDT reset, streams are dead/locked - don't try to manipulate them
|
|
2874
|
+
// Just mark everything as disconnected and let browser clean up
|
|
2875
|
+
this.connected = false;
|
|
2876
|
+
this._writer = undefined;
|
|
2877
|
+
this._reader = undefined;
|
|
2878
|
+
this.logger.debug("Device reset to firmware mode (port closed)");
|
|
2879
|
+
return true;
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
catch (err) {
|
|
2883
|
+
this.logger.debug(`Could not reset device to firmware mode: ${err}`);
|
|
2884
|
+
// Continue anyway - console mode might still work
|
|
2885
|
+
}
|
|
2886
|
+
return false;
|
|
2887
|
+
}
|
|
2377
2888
|
/**
|
|
2378
2889
|
* @name reconnectAndResume
|
|
2379
2890
|
* Reconnect the serial port to flush browser buffers and reload stub
|
|
@@ -2385,6 +2896,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2385
2896
|
}
|
|
2386
2897
|
try {
|
|
2387
2898
|
this.logger.log("Reconnecting serial port...");
|
|
2899
|
+
const savedBaudRate = this._currentBaudRate;
|
|
2388
2900
|
this.connected = false;
|
|
2389
2901
|
this.__inputBuffer = [];
|
|
2390
2902
|
this.__inputBufferReadIndex = 0;
|
|
@@ -2420,7 +2932,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2420
2932
|
// Close port
|
|
2421
2933
|
try {
|
|
2422
2934
|
await this.port.close();
|
|
2423
|
-
this.logger.
|
|
2935
|
+
this.logger.debug("Port closed");
|
|
2424
2936
|
}
|
|
2425
2937
|
catch (err) {
|
|
2426
2938
|
this.logger.debug(`Port close error: ${err}`);
|
|
@@ -2430,6 +2942,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2430
2942
|
try {
|
|
2431
2943
|
await this.port.open({ baudRate: ESP_ROM_BAUD });
|
|
2432
2944
|
this.connected = true;
|
|
2945
|
+
this._currentBaudRate = ESP_ROM_BAUD;
|
|
2433
2946
|
}
|
|
2434
2947
|
catch (err) {
|
|
2435
2948
|
throw new Error(`Failed to open port: ${err}`);
|
|
@@ -2471,8 +2984,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2471
2984
|
const stubLoader = await this.runStub(true);
|
|
2472
2985
|
this.logger.debug("Stub loaded");
|
|
2473
2986
|
// Restore baudrate if it was changed
|
|
2474
|
-
if (
|
|
2475
|
-
await stubLoader.setBaudrate(
|
|
2987
|
+
if (savedBaudRate !== ESP_ROM_BAUD) {
|
|
2988
|
+
await stubLoader.setBaudrate(savedBaudRate);
|
|
2476
2989
|
// Verify port is still ready after baudrate change
|
|
2477
2990
|
if (!this.port.writable || !this.port.readable) {
|
|
2478
2991
|
throw new Error(`Port not ready after baudrate change (readable: ${!!this.port.readable}, writable: ${!!this.port.writable})`);
|
|
@@ -2502,6 +3015,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2502
3015
|
}
|
|
2503
3016
|
try {
|
|
2504
3017
|
this.logger.log("Reconnecting to bootloader mode...");
|
|
3018
|
+
// Clear console mode flag when reconnecting to bootloader
|
|
3019
|
+
this._consoleMode = false;
|
|
2505
3020
|
this.connected = false;
|
|
2506
3021
|
this.__inputBuffer = [];
|
|
2507
3022
|
this.__inputBufferReadIndex = 0;
|
|
@@ -2537,7 +3052,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2537
3052
|
// Close port
|
|
2538
3053
|
try {
|
|
2539
3054
|
await this.port.close();
|
|
2540
|
-
this.logger.
|
|
3055
|
+
this.logger.debug("Port closed");
|
|
2541
3056
|
}
|
|
2542
3057
|
catch (err) {
|
|
2543
3058
|
this.logger.debug(`Port close error: ${err}`);
|
|
@@ -2547,6 +3062,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2547
3062
|
try {
|
|
2548
3063
|
await this.port.open({ baudRate: ESP_ROM_BAUD });
|
|
2549
3064
|
this.connected = true;
|
|
3065
|
+
this._currentBaudRate = ESP_ROM_BAUD;
|
|
2550
3066
|
}
|
|
2551
3067
|
catch (err) {
|
|
2552
3068
|
throw new Error(`Failed to open port: ${err}`);
|
|
@@ -2560,6 +3076,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2560
3076
|
// Reset chip info and stub state
|
|
2561
3077
|
this.__chipFamily = undefined;
|
|
2562
3078
|
this.chipName = "Unknown Chip";
|
|
3079
|
+
this.chipRevision = null;
|
|
3080
|
+
this.chipVariant = null;
|
|
2563
3081
|
this.IS_STUB = false;
|
|
2564
3082
|
// Start read loop
|
|
2565
3083
|
if (!this._parent) {
|
|
@@ -2651,9 +3169,13 @@ export class ESPLoader extends EventTarget {
|
|
|
2651
3169
|
* @param addr - Address to read from
|
|
2652
3170
|
* @param size - Number of bytes to read
|
|
2653
3171
|
* @param onPacketReceived - Optional callback function called when packet is received
|
|
3172
|
+
* @param options - Optional parameters for advanced control
|
|
3173
|
+
* - chunkSize: Amount of data to request from ESP in one command (bytes)
|
|
3174
|
+
* - blockSize: Size of each data block sent by ESP (bytes)
|
|
3175
|
+
* - maxInFlight: Maximum unacknowledged bytes (bytes)
|
|
2654
3176
|
* @returns Uint8Array containing the flash data
|
|
2655
3177
|
*/
|
|
2656
|
-
async readFlash(addr, size, onPacketReceived) {
|
|
3178
|
+
async readFlash(addr, size, onPacketReceived, options) {
|
|
2657
3179
|
if (!this.IS_STUB) {
|
|
2658
3180
|
throw new Error("Reading flash is only supported in stub mode. Please run runStub() first.");
|
|
2659
3181
|
}
|
|
@@ -2681,7 +3203,12 @@ export class ESPLoader extends EventTarget {
|
|
|
2681
3203
|
// For WebUSB (Android), use smaller chunks to avoid timeouts and buffer issues
|
|
2682
3204
|
// For Web Serial (Desktop), use larger chunks for better performance
|
|
2683
3205
|
let CHUNK_SIZE;
|
|
2684
|
-
if (
|
|
3206
|
+
if ((options === null || options === void 0 ? void 0 : options.chunkSize) !== undefined) {
|
|
3207
|
+
// Use user-provided chunkSize if in advanced mode
|
|
3208
|
+
CHUNK_SIZE = options.chunkSize;
|
|
3209
|
+
this.logger.log(`Using custom chunk size: 0x${CHUNK_SIZE.toString(16)} bytes`);
|
|
3210
|
+
}
|
|
3211
|
+
else if (this.isWebUSB()) {
|
|
2685
3212
|
// WebUSB: Use smaller chunks to avoid SLIP timeout issues
|
|
2686
3213
|
CHUNK_SIZE = 0x4 * 0x1000; // 4KB = 16384 bytes
|
|
2687
3214
|
}
|
|
@@ -2709,7 +3236,16 @@ export class ESPLoader extends EventTarget {
|
|
|
2709
3236
|
}
|
|
2710
3237
|
let blockSize;
|
|
2711
3238
|
let maxInFlight;
|
|
2712
|
-
if (
|
|
3239
|
+
if ((options === null || options === void 0 ? void 0 : options.blockSize) !== undefined &&
|
|
3240
|
+
(options === null || options === void 0 ? void 0 : options.maxInFlight) !== undefined) {
|
|
3241
|
+
// Use user-provided values if in advanced mode
|
|
3242
|
+
blockSize = options.blockSize;
|
|
3243
|
+
maxInFlight = options.maxInFlight;
|
|
3244
|
+
if (retryCount === 0) {
|
|
3245
|
+
this.logger.debug(`Using custom parameters: blockSize=${blockSize}, maxInFlight=${maxInFlight}`);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
else if (this.isWebUSB()) {
|
|
2713
3249
|
// WebUSB (Android): All devices use adaptive speed
|
|
2714
3250
|
// All have maxTransferSize=64, baseBlockSize=31
|
|
2715
3251
|
const maxTransferSize = this.port.maxTransferSize || 64;
|
|
@@ -2847,7 +3383,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2847
3383
|
// Check if it's a timeout error or SLIP error
|
|
2848
3384
|
if (err instanceof SlipReadError) {
|
|
2849
3385
|
if (retryCount <= MAX_RETRIES) {
|
|
2850
|
-
this.logger.
|
|
3386
|
+
this.logger.debug(`${err.message} at 0x${currentAddr.toString(16)}. Draining buffer and retrying (attempt ${retryCount}/${MAX_RETRIES})...`);
|
|
2851
3387
|
try {
|
|
2852
3388
|
await this.drainInputBuffer(200);
|
|
2853
3389
|
// Clear application buffer
|