tasmota-webserial-esptool 9.2.3 → 9.2.5

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/src/esp_loader.ts CHANGED
@@ -60,6 +60,14 @@ import {
60
60
  CHIP_ID_TO_INFO,
61
61
  ESP32P4_EFUSE_BLOCK1_ADDR,
62
62
  SlipReadError,
63
+ ESP32S2_RTC_CNTL_WDTWPROTECT_REG,
64
+ ESP32S2_RTC_CNTL_WDTCONFIG0_REG,
65
+ ESP32S2_RTC_CNTL_WDTCONFIG1_REG,
66
+ ESP32S2_RTC_CNTL_WDT_WKEY,
67
+ ESP32S3_RTC_CNTL_WDTWPROTECT_REG,
68
+ ESP32S3_RTC_CNTL_WDTCONFIG0_REG,
69
+ ESP32S3_RTC_CNTL_WDTCONFIG1_REG,
70
+ ESP32S3_RTC_CNTL_WDT_WKEY,
63
71
  } from "./const";
64
72
  import { getStubCode } from "./stubs";
65
73
  import { hexFormatter, sleep, slipEncode, toHex } from "./util";
@@ -1349,6 +1357,53 @@ export class ESPLoader extends EventTarget {
1349
1357
  );
1350
1358
  }
1351
1359
 
1360
+ /**
1361
+ * @name watchdogReset
1362
+ * Watchdog reset for ESP32-S2/S3 with USB-OTG
1363
+ * Uses RTC watchdog timer to reset the chip - works when DTR/RTS signals are not available
1364
+ */
1365
+ async watchdogReset() {
1366
+ this.logger.log("Hard resetting with watchdog timer...");
1367
+
1368
+ // Select correct register addresses based on chip family
1369
+ let WDTWPROTECT_REG: number;
1370
+ let WDTCONFIG0_REG: number;
1371
+ let WDTCONFIG1_REG: number;
1372
+ let WDT_WKEY: number;
1373
+
1374
+ if (this.chipFamily === CHIP_FAMILY_ESP32S2) {
1375
+ WDTWPROTECT_REG = ESP32S2_RTC_CNTL_WDTWPROTECT_REG;
1376
+ WDTCONFIG0_REG = ESP32S2_RTC_CNTL_WDTCONFIG0_REG;
1377
+ WDTCONFIG1_REG = ESP32S2_RTC_CNTL_WDTCONFIG1_REG;
1378
+ WDT_WKEY = ESP32S2_RTC_CNTL_WDT_WKEY;
1379
+ } else if (this.chipFamily === CHIP_FAMILY_ESP32S3) {
1380
+ WDTWPROTECT_REG = ESP32S3_RTC_CNTL_WDTWPROTECT_REG;
1381
+ WDTCONFIG0_REG = ESP32S3_RTC_CNTL_WDTCONFIG0_REG;
1382
+ WDTCONFIG1_REG = ESP32S3_RTC_CNTL_WDTCONFIG1_REG;
1383
+ WDT_WKEY = ESP32S3_RTC_CNTL_WDT_WKEY;
1384
+ } else {
1385
+ throw new Error(
1386
+ `watchdogReset() is only supported for ESP32-S2 and ESP32-S3, not ${this.chipFamily}`,
1387
+ );
1388
+ }
1389
+
1390
+ // Unlock watchdog registers
1391
+ await this.writeRegister(WDTWPROTECT_REG, WDT_WKEY, undefined, 0);
1392
+
1393
+ // Set WDT timeout to 2000ms
1394
+ await this.writeRegister(WDTCONFIG1_REG, 2000, undefined, 0);
1395
+
1396
+ // Enable WDT: bit 31 = enable, bits 28-30 = stage, bit 8 = sys reset, bits 0-2 = prescaler
1397
+ const wdtConfig = (1 << 31) | (5 << 28) | (1 << 8) | 2;
1398
+ await this.writeRegister(WDTCONFIG0_REG, wdtConfig, undefined, 0);
1399
+
1400
+ // Lock watchdog registers
1401
+ await this.writeRegister(WDTWPROTECT_REG, 0, undefined, 0);
1402
+
1403
+ // Wait for reset to take effect
1404
+ await this.sleep(500);
1405
+ }
1406
+
1352
1407
  async hardReset(bootloader = false) {
1353
1408
  if (bootloader) {
1354
1409
  // enter flash mode
@@ -1367,7 +1422,15 @@ export class ESPLoader extends EventTarget {
1367
1422
  }
1368
1423
  } else {
1369
1424
  // just reset (no bootloader mode)
1370
- if (this.isWebUSB()) {
1425
+ // For ESP32-S2/S3 with USB-OTG, use watchdog reset instead of DTR/RTS
1426
+ if (
1427
+ this.port.getInfo().usbProductId === USB_JTAG_SERIAL_PID &&
1428
+ (this.chipFamily === CHIP_FAMILY_ESP32S2 ||
1429
+ this.chipFamily === CHIP_FAMILY_ESP32S3)
1430
+ ) {
1431
+ await this.watchdogReset();
1432
+ this.logger.log("Watchdog reset (USB-OTG).");
1433
+ } else if (this.isWebUSB()) {
1371
1434
  // WebUSB: Use longer delays for better compatibility
1372
1435
  await this.setRTSWebUSB(true); // EN->LOW
1373
1436
  await this.sleep(200);
@@ -2634,7 +2697,9 @@ export class ESPLoader extends EventTarget {
2634
2697
  }
2635
2698
 
2636
2699
  private get _currentBaudRate(): number {
2637
- return this._parent ? this._parent._currentBaudRate : this.__currentBaudRate;
2700
+ return this._parent
2701
+ ? this._parent._currentBaudRate
2702
+ : this.__currentBaudRate;
2638
2703
  }
2639
2704
 
2640
2705
  private set _currentBaudRate(value: number) {
@@ -2939,6 +3004,111 @@ export class ESPLoader extends EventTarget {
2939
3004
  }
2940
3005
  }
2941
3006
 
3007
+ /**
3008
+ * @name reconnectToBootloader
3009
+ * Close and reopen the port, then reset ESP to bootloader mode
3010
+ * This is needed after Improv or other operations that leave ESP in firmware mode
3011
+ */
3012
+ async reconnectToBootloader(): Promise<void> {
3013
+ if (this._parent) {
3014
+ await this._parent.reconnectToBootloader();
3015
+ return;
3016
+ }
3017
+
3018
+ try {
3019
+ this.logger.log("Reconnecting to bootloader mode...");
3020
+
3021
+ this.connected = false;
3022
+ this.__inputBuffer = [];
3023
+ this.__inputBufferReadIndex = 0;
3024
+
3025
+ // Wait for pending writes to complete
3026
+ try {
3027
+ await this._writeChain;
3028
+ } catch (err) {
3029
+ this.logger.debug(`Pending write error during reconnect: ${err}`);
3030
+ }
3031
+
3032
+ // Block new writes during port close/open
3033
+ this._isReconfiguring = true;
3034
+
3035
+ // Release persistent writer
3036
+ if (this._writer) {
3037
+ try {
3038
+ this._writer.releaseLock();
3039
+ } catch (err) {
3040
+ this.logger.debug(`Writer release error during reconnect: ${err}`);
3041
+ }
3042
+ this._writer = undefined;
3043
+ }
3044
+
3045
+ // Cancel reader
3046
+ if (this._reader) {
3047
+ try {
3048
+ await this._reader.cancel();
3049
+ } catch (err) {
3050
+ this.logger.debug(`Reader cancel error: ${err}`);
3051
+ }
3052
+ this._reader = undefined;
3053
+ }
3054
+
3055
+ // Close port
3056
+ try {
3057
+ await this.port.close();
3058
+ this.logger.log("Port closed");
3059
+ } catch (err) {
3060
+ this.logger.debug(`Port close error: ${err}`);
3061
+ }
3062
+
3063
+ // Open the port
3064
+ this.logger.debug("Opening port...");
3065
+ try {
3066
+ await this.port.open({ baudRate: ESP_ROM_BAUD });
3067
+ this.connected = true;
3068
+ } catch (err) {
3069
+ throw new Error(`Failed to open port: ${err}`);
3070
+ }
3071
+
3072
+ // Verify port streams are available
3073
+ if (!this.port.readable || !this.port.writable) {
3074
+ throw new Error(
3075
+ `Port streams not available after open (readable: ${!!this.port.readable}, writable: ${!!this.port.writable})`,
3076
+ );
3077
+ }
3078
+
3079
+ // Port is now open and ready - allow writes for initialization
3080
+ this._isReconfiguring = false;
3081
+
3082
+ // Reset chip info and stub state
3083
+ this.__chipFamily = undefined;
3084
+ this.chipName = "Unknown Chip";
3085
+ this.IS_STUB = false;
3086
+
3087
+ // Start read loop
3088
+ if (!this._parent) {
3089
+ this.__inputBuffer = [];
3090
+ this.__inputBufferReadIndex = 0;
3091
+ this.__totalBytesRead = 0;
3092
+ this.readLoop();
3093
+ }
3094
+
3095
+ // Wait for readLoop to start
3096
+ await sleep(100);
3097
+
3098
+ // Reset to bootloader mode using multiple strategies
3099
+ await this.connectWithResetStrategies();
3100
+
3101
+ // Detect chip type
3102
+ await this.detectChip();
3103
+
3104
+ this.logger.log(`Reconnected to bootloader: ${this.chipName}`);
3105
+ } catch (err) {
3106
+ // Ensure flag is reset on error
3107
+ this._isReconfiguring = false;
3108
+ throw err;
3109
+ }
3110
+ }
3111
+
2942
3112
  /**
2943
3113
  * @name drainInputBuffer
2944
3114
  * Actively drain the input buffer by reading data for a specified time.