esp32tool 1.3.1 → 1.3.2

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
@@ -522,28 +522,7 @@ export class ESPLoader extends EventTarget {
522
522
  // Detect if device is using USB-JTAG/Serial or USB-OTG (not external serial chip)
523
523
  // This is needed to determine the correct reset strategy for console mode
524
524
  try {
525
- if (
526
- this.chipFamily === CHIP_FAMILY_ESP32S2 ||
527
- this.chipFamily === CHIP_FAMILY_ESP32S3
528
- ) {
529
- const isUsingUsbOtg = await this.usingUsbOtg();
530
- const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
531
- this._isUsbJtagOrOtg = isUsingUsbOtg || isUsingUsbJtagSerial;
532
- } else if (
533
- this.chipFamily === CHIP_FAMILY_ESP32C3 ||
534
- this.chipFamily === CHIP_FAMILY_ESP32C5 ||
535
- this.chipFamily === CHIP_FAMILY_ESP32C6
536
- ) {
537
- const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
538
- this._isUsbJtagOrOtg = isUsingUsbJtagSerial;
539
- } else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
540
- const isUsingUsbOtg = await this.usingUsbOtg();
541
- const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
542
- this._isUsbJtagOrOtg = isUsingUsbOtg || isUsingUsbJtagSerial;
543
- } else {
544
- // Other chips don't have USB-JTAG/OTG
545
- this._isUsbJtagOrOtg = false;
546
- }
525
+ this._isUsbJtagOrOtg = await this.detectUsbConnectionType();
547
526
  this.logger.debug(
548
527
  `USB connection type: ${this._isUsbJtagOrOtg ? "USB-JTAG/OTG" : "External Serial Chip"}`,
549
528
  );
@@ -647,7 +626,6 @@ export class ESPLoader extends EventTarget {
647
626
 
648
627
  if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
649
628
  this.chipRevision = await this.getChipRevision();
650
- this.logger.debug(`ESP32-P4 revision: ${this.chipRevision}`);
651
629
 
652
630
  if (this.chipRevision >= 300) {
653
631
  this.chipVariant = "rev300";
@@ -657,7 +635,6 @@ export class ESPLoader extends EventTarget {
657
635
  this.logger.debug(`ESP32-P4 variant: ${this.chipVariant}`);
658
636
  } else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
659
637
  this.chipRevision = await this.getChipRevision();
660
- this.logger.debug(`ESP32-C3 revision: ${this.chipRevision}`);
661
638
  }
662
639
 
663
640
  this.logger.debug(
@@ -787,10 +764,7 @@ export class ESPLoader extends EventTarget {
787
764
  this._totalBytesRead += value.length;
788
765
  }
789
766
  } catch {
790
- // Don't log error if this is an expected disconnect during console mode transition
791
- if (!this._consoleMode) {
792
- this.logger.error("Read loop got disconnected");
793
- }
767
+ // this.logger.error("Read loop got disconnected");
794
768
  } finally {
795
769
  // Always reset reconfiguring flag when read loop ends
796
770
  // This prevents "Cannot write during port reconfiguration" errors
@@ -1485,9 +1459,9 @@ export class ESPLoader extends EventTarget {
1485
1459
  }
1486
1460
  } catch (error) {
1487
1461
  lastError = error as Error;
1488
- this.logger.debug(
1489
- `${strategy.name} reset failed: ${(error as Error).message}`,
1490
- );
1462
+ // this.logger.debug(
1463
+ // `${strategy.name} reset failed: ${(error as Error).message}`,
1464
+ // );
1491
1465
 
1492
1466
  // Set abandon flag to stop any in-flight operations
1493
1467
  this._abandonCurrentOperation = true;
@@ -1641,8 +1615,6 @@ export class ESPLoader extends EventTarget {
1641
1615
  // Combine: upper 3 bits from word5, lower 3 bits from word3
1642
1616
  const revision = (hi << 3) | low;
1643
1617
 
1644
- this.logger.debug(`ESP32-C3 revision: ${revision}`);
1645
-
1646
1618
  return revision;
1647
1619
  }
1648
1620
 
@@ -2454,8 +2426,8 @@ export class ESPLoader extends EventTarget {
2454
2426
  // Restart Readloop
2455
2427
  this.readLoop();
2456
2428
  } catch (e) {
2457
- this.logger.error(`Reconfigure port error: ${e}`);
2458
- throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
2429
+ // this.logger.error(`Reconfigure port error: ${e}`);
2430
+ // throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
2459
2431
  } finally {
2460
2432
  // Always reset flag, even on error or early return
2461
2433
  this._isReconfiguring = false;
@@ -3228,7 +3200,7 @@ export class ESPLoader extends EventTarget {
3228
3200
  return;
3229
3201
  }
3230
3202
  if (!this.port.writable) {
3231
- this.logger.debug("Port already closed, skipping disconnect");
3203
+ // this.logger.debug("Port already closed, skipping disconnect");
3232
3204
  return;
3233
3205
  }
3234
3206
 
@@ -3236,7 +3208,7 @@ export class ESPLoader extends EventTarget {
3236
3208
  try {
3237
3209
  await this._writeChain;
3238
3210
  } catch (err) {
3239
- this.logger.debug(`Pending write error during disconnect: ${err}`);
3211
+ // this.logger.debug(`Pending write error during disconnect: ${err}`);
3240
3212
  }
3241
3213
 
3242
3214
  // Release persistent writer before closing
@@ -3245,7 +3217,7 @@ export class ESPLoader extends EventTarget {
3245
3217
  await this._writer.close();
3246
3218
  this._writer.releaseLock();
3247
3219
  } catch (err) {
3248
- this.logger.debug(`Writer close/release error: ${err}`);
3220
+ // this.logger.debug(`Writer close/release error: ${err}`);
3249
3221
  }
3250
3222
  this._writer = undefined;
3251
3223
  } else {
@@ -3256,7 +3228,7 @@ export class ESPLoader extends EventTarget {
3256
3228
  await writer.close();
3257
3229
  writer.releaseLock();
3258
3230
  } catch (err) {
3259
- this.logger.debug(`Direct writer close error: ${err}`);
3231
+ // this.logger.debug(`Direct writer close error: ${err}`);
3260
3232
  }
3261
3233
  }
3262
3234
 
@@ -3285,7 +3257,7 @@ export class ESPLoader extends EventTarget {
3285
3257
  try {
3286
3258
  this._reader.cancel();
3287
3259
  } catch (err) {
3288
- this.logger.debug(`Reader cancel error: ${err}`);
3260
+ // this.logger.debug(`Reader cancel error: ${err}`);
3289
3261
  // Reader already released, resolve immediately
3290
3262
  clearTimeout(timeout);
3291
3263
  resolve(undefined);
@@ -3325,7 +3297,7 @@ export class ESPLoader extends EventTarget {
3325
3297
  try {
3326
3298
  await this._writeChain;
3327
3299
  } catch (err) {
3328
- this.logger.debug(`Pending write error during release: ${err}`);
3300
+ // this.logger.debug(`Pending write error during release: ${err}`);
3329
3301
  }
3330
3302
 
3331
3303
  // Release writer
@@ -3372,6 +3344,42 @@ export class ESPLoader extends EventTarget {
3372
3344
  return await this._resetToFirmwareIfNeeded();
3373
3345
  }
3374
3346
 
3347
+ /**
3348
+ * @name detectUsbConnectionType
3349
+ * Detect if device is using USB-JTAG/Serial or USB-OTG (not external serial chip)
3350
+ * This helper extracts the detection logic from initialize() for reuse
3351
+ * @returns true if USB-JTAG or USB-OTG, false if external serial chip
3352
+ * @throws Error if detection fails and chipFamily is not set
3353
+ */
3354
+ private async detectUsbConnectionType(): Promise<boolean> {
3355
+ if (!this.chipFamily) {
3356
+ throw new Error("Cannot detect USB connection type: chipFamily not set");
3357
+ }
3358
+
3359
+ if (
3360
+ this.chipFamily === CHIP_FAMILY_ESP32S2 ||
3361
+ this.chipFamily === CHIP_FAMILY_ESP32S3
3362
+ ) {
3363
+ const isUsingUsbOtg = await this.usingUsbOtg();
3364
+ const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
3365
+ return isUsingUsbOtg || isUsingUsbJtagSerial;
3366
+ } else if (
3367
+ this.chipFamily === CHIP_FAMILY_ESP32C3 ||
3368
+ this.chipFamily === CHIP_FAMILY_ESP32C5 ||
3369
+ this.chipFamily === CHIP_FAMILY_ESP32C6
3370
+ ) {
3371
+ const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
3372
+ return isUsingUsbJtagSerial;
3373
+ } else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
3374
+ const isUsingUsbOtg = await this.usingUsbOtg();
3375
+ const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
3376
+ return isUsingUsbOtg || isUsingUsbJtagSerial;
3377
+ } else {
3378
+ // Other chips don't have USB-JTAG/OTG
3379
+ return false;
3380
+ }
3381
+ }
3382
+
3375
3383
  /**
3376
3384
  * @name enterConsoleMode
3377
3385
  * Prepare device for console mode by resetting to firmware
@@ -3382,8 +3390,26 @@ export class ESPLoader extends EventTarget {
3382
3390
  // Set console mode flag
3383
3391
  this._consoleMode = true;
3384
3392
 
3385
- // Check device type
3386
- const isUsbJtag = this.isUsbJtagOrOtg === true;
3393
+ // Re-detect USB connection type to ensure we have a definitive value
3394
+ // This handles cases where isUsbJtagOrOtg might be undefined
3395
+ let isUsbJtag: boolean;
3396
+ try {
3397
+ isUsbJtag = await this.detectUsbConnectionType();
3398
+ this.logger.debug(
3399
+ `USB connection type detected: ${isUsbJtag ? "USB-JTAG/OTG" : "External Serial Chip"}`,
3400
+ );
3401
+ } catch (err) {
3402
+ // If detection fails, fall back to cached value or fail-fast
3403
+ if (this.isUsbJtagOrOtg === undefined) {
3404
+ throw new Error(
3405
+ `Cannot enter console mode: USB connection type unknown and detection failed: ${err}`,
3406
+ );
3407
+ }
3408
+ this.logger.debug(
3409
+ `USB detection failed, using cached value: ${this.isUsbJtagOrOtg}`,
3410
+ );
3411
+ isUsbJtag = this.isUsbJtagOrOtg;
3412
+ }
3387
3413
 
3388
3414
  if (isUsbJtag) {
3389
3415
  // USB-JTAG/OTG devices: Use watchdog reset which closes port
@@ -3431,7 +3457,7 @@ export class ESPLoader extends EventTarget {
3431
3457
  : "USB-JTAG/Serial";
3432
3458
 
3433
3459
  this.logger.log(
3434
- `Resetting ${this.chipFamily} (${resetMethod}) to boot into firmware...`,
3460
+ `Resetting ${this.chipName || "device"} (${resetMethod}) to boot into firmware...`,
3435
3461
  );
3436
3462
 
3437
3463
  // Set console mode flag before reset to prevent subsequent hardReset calls
@@ -3748,6 +3774,115 @@ export class ESPLoader extends EventTarget {
3748
3774
  }
3749
3775
  }
3750
3776
 
3777
+ /**
3778
+ * @name exitConsoleMode
3779
+ * Exit console mode and return to bootloader
3780
+ * For ESP32-S2, uses reconnectToBootloader which will trigger port change
3781
+ * @returns true if manual reconnection is needed (ESP32-S2), false otherwise
3782
+ */
3783
+ async exitConsoleMode(): Promise<boolean> {
3784
+ if (this._parent) {
3785
+ return await this._parent.exitConsoleMode();
3786
+ }
3787
+
3788
+ // Clear console mode flag
3789
+ this._consoleMode = false;
3790
+
3791
+ // Check if this is ESP32-S2 with USB-JTAG/OTG
3792
+ const isESP32S2 = this.chipFamily === CHIP_FAMILY_ESP32S2;
3793
+
3794
+ // For ESP32-S2: if _isUsbJtagOrOtg is undefined, try to detect it
3795
+ // If detection fails or is undefined, assume USB-JTAG/OTG (conservative/safe path)
3796
+ let isUsbJtagOrOtg = this._isUsbJtagOrOtg;
3797
+ if (isESP32S2 && isUsbJtagOrOtg === undefined) {
3798
+ try {
3799
+ isUsbJtagOrOtg = await this.detectUsbConnectionType();
3800
+ } catch (err) {
3801
+ this.logger.debug(
3802
+ `USB detection failed, assuming USB-JTAG/OTG for ESP32-S2: ${err}`,
3803
+ );
3804
+ isUsbJtagOrOtg = true; // Conservative fallback for ESP32-S2
3805
+ }
3806
+ }
3807
+
3808
+ if (isESP32S2 && isUsbJtagOrOtg) {
3809
+ // ESP32-S2 USB: Use reconnectToBootloader which handles the mode switch
3810
+ // This will close the port and the device will reboot to bootloader
3811
+ this.logger.log("ESP32-S2 USB detected - reconnecting to bootloader");
3812
+
3813
+ try {
3814
+ await this.reconnectToBootloader();
3815
+ } catch (err) {
3816
+ this.logger.debug(`Reconnect error (expected for ESP32-S2): ${err}`);
3817
+ }
3818
+
3819
+ // For ESP32-S2, port will change, so return true to indicate manual reconnection needed
3820
+ return true;
3821
+ }
3822
+
3823
+ // For other devices, use standard reconnectToBootloader
3824
+ await this.reconnectToBootloader();
3825
+ return false; // No manual reconnection needed
3826
+ }
3827
+
3828
+ /**
3829
+ * @name isConsoleResetSupported
3830
+ * Check if console reset is supported for this device
3831
+ * ESP32-S2 USB-JTAG/CDC does not support reset in console mode
3832
+ * because any reset causes USB port to be lost (hardware limitation)
3833
+ */
3834
+ isConsoleResetSupported(): boolean {
3835
+ if (this._parent) {
3836
+ return this._parent.isConsoleResetSupported();
3837
+ }
3838
+
3839
+ // For ESP32-S2: if _isUsbJtagOrOtg is undefined, assume USB-JTAG/OTG (conservative)
3840
+ // This means console reset is NOT supported (safer default)
3841
+ const isS2UsbJtag =
3842
+ this.chipFamily === CHIP_FAMILY_ESP32S2 &&
3843
+ (this._isUsbJtagOrOtg === true || this._isUsbJtagOrOtg === undefined);
3844
+ return !isS2UsbJtag; // Not supported for ESP32-S2 USB-JTAG/CDC
3845
+ }
3846
+
3847
+ /**
3848
+ * @name resetInConsoleMode
3849
+ * Reset device while in console mode (firmware mode)
3850
+ *
3851
+ * NOTE: For ESP32-S2 USB-JTAG/CDC, ANY reset (hardware or software) causes
3852
+ * the USB port to be lost because the device switches USB modes during reset.
3853
+ * This is a hardware limitation - use isConsoleResetSupported() to check first.
3854
+ */
3855
+ async resetInConsoleMode(): Promise<void> {
3856
+ if (this._parent) {
3857
+ return await this._parent.resetInConsoleMode();
3858
+ }
3859
+
3860
+ if (!this.isConsoleResetSupported()) {
3861
+ this.logger.debug(
3862
+ "Console reset not supported for ESP32-S2 USB-JTAG/CDC",
3863
+ );
3864
+ return; // Do nothing
3865
+ }
3866
+
3867
+ // For other devices: Use standard firmware reset
3868
+ const isWebUSB = (this.port as any).isWebUSB === true;
3869
+
3870
+ try {
3871
+ this.logger.debug("Resetting device in console mode");
3872
+
3873
+ if (isWebUSB) {
3874
+ await this.hardResetToFirmwareWebUSB();
3875
+ } else {
3876
+ await this.hardResetToFirmware();
3877
+ }
3878
+
3879
+ this.logger.debug("Device reset complete");
3880
+ } catch (err) {
3881
+ this.logger.error(`Reset failed: ${err}`);
3882
+ throw err;
3883
+ }
3884
+ }
3885
+
3751
3886
  /**
3752
3887
  * @name drainInputBuffer
3753
3888
  * Actively drain the input buffer by reading data for a specified time.
package/src/index.ts CHANGED
@@ -107,6 +107,9 @@ export {
107
107
  } from "./partition";
108
108
  export type { Partition } from "./partition";
109
109
 
110
+ // Export utility functions for use in UI code
111
+ export { toHex, sleep, hexFormatter, formatMacAddr } from "./util";
112
+
110
113
  export {
111
114
  FilesystemType,
112
115
  detectFilesystemType,
package/src/util.ts CHANGED
@@ -45,5 +45,14 @@ export const toHex = (value: number, size = 2) => {
45
45
  }
46
46
  };
47
47
 
48
+ /**
49
+ * Format MAC address array to string (e.g., [0xAA, 0xBB, 0xCC] -> "AA:BB:CC:DD:EE:FF")
50
+ */
51
+ export const formatMacAddr = (macAddr: number[]): string => {
52
+ return macAddr
53
+ .map((value) => value.toString(16).toUpperCase().padStart(2, "0"))
54
+ .join(":");
55
+ };
56
+
48
57
  export const sleep = (ms: number) =>
49
58
  new Promise((resolve) => setTimeout(resolve, ms));
package/script/build DELETED
@@ -1,12 +0,0 @@
1
- # Stop on errors
2
- set -e
3
-
4
- cd "$(dirname "$0")/.."
5
-
6
- rm -rf dist
7
- rm -rf js/modules
8
- mkdir js/modules
9
- NODE_ENV=production npm exec -- tsc
10
- NODE_ENV=production npm exec -- rollup -c
11
- cp dist/web/*.js js/modules/
12
- mv js/modules/index.js js/modules/esptool.js
package/script/develop DELETED
@@ -1,17 +0,0 @@
1
- # Stop on errors
2
- set -e
3
-
4
- cd "$(dirname "$0")/.."
5
-
6
- rm -rf dist
7
-
8
- # Quit all background tasks when script exits
9
- trap "kill 0" EXIT
10
-
11
- # Run tsc once as rollup expects those files
12
- npm exec -- tsc || true
13
-
14
- npm exec -- serve -p 5004 &
15
- npm exec -- tsc --watch &
16
- npm exec -- rollup -c --watch &
17
- wait