esp32tool 1.3.1 → 1.3.3
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/css/style.css +47 -35
- package/dist/const.js +1 -1
- package/dist/esp_loader.d.ts +35 -0
- package/dist/esp_loader.js +201 -68
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/util.d.ts +4 -0
- package/dist/util.js +8 -0
- 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 +12 -2
- package/js/modules/esptool.js +1 -1
- package/js/script.js +300 -285
- package/js/util/console-color.js +2 -1
- package/js/webusb-serial.js +42 -7
- package/package.cli.json +1 -1
- package/package.json +11 -5
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/const.ts +1 -1
- package/src/esp_loader.ts +227 -73
- package/src/index.ts +3 -0
- package/src/util.ts +9 -0
- package/sw.js +1 -1
- package/script/build +0 -12
- package/script/develop +0 -17
package/apple-touch-icon.png
CHANGED
|
Binary file
|
package/css/style.css
CHANGED
|
@@ -179,36 +179,7 @@ div.clear {
|
|
|
179
179
|
display: none;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
/* Console Container */
|
|
183
|
-
.console-container {
|
|
184
|
-
height: 500px;
|
|
185
|
-
overflow: hidden;
|
|
186
|
-
transition: height 0.3s ease-in-out, visibility 0.3s ease-in-out;
|
|
187
|
-
margin: 0;
|
|
188
|
-
padding: 0;
|
|
189
|
-
visibility: visible;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.console-container.hidden {
|
|
193
|
-
height: 0;
|
|
194
|
-
visibility: hidden;
|
|
195
|
-
max-height: 0;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/* Mobile console optimization */
|
|
199
|
-
@media (max-width: 768px) {
|
|
200
|
-
.console-container {
|
|
201
|
-
height: 350px;
|
|
202
|
-
font-size: 13px;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@media (max-width: 480px) {
|
|
207
|
-
.console-container {
|
|
208
|
-
height: 300px;
|
|
209
|
-
font-size: 12px;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
182
|
+
/* Console Container - OLD DEFINITION REMOVED - See bottom of file for new fullscreen version */
|
|
212
183
|
|
|
213
184
|
.notSupported {
|
|
214
185
|
padding: 1em;
|
|
@@ -2189,11 +2160,7 @@ div.clear {
|
|
|
2189
2160
|
padding-right: 10px;
|
|
2190
2161
|
}
|
|
2191
2162
|
|
|
2192
|
-
/* Console optimization for small screens */
|
|
2193
|
-
.console-container {
|
|
2194
|
-
height: 300px;
|
|
2195
|
-
font-size: 12px;
|
|
2196
|
-
}
|
|
2163
|
+
/* Console optimization for small screens - REMOVED - Using fullscreen console now */
|
|
2197
2164
|
|
|
2198
2165
|
/* Filesystem manager for small screens */
|
|
2199
2166
|
.littlefs-manager {
|
|
@@ -2257,3 +2224,48 @@ div.clear {
|
|
|
2257
2224
|
-webkit-tap-highlight-color: transparent;
|
|
2258
2225
|
}
|
|
2259
2226
|
}
|
|
2227
|
+
|
|
2228
|
+
/* Console Container Styling */
|
|
2229
|
+
.console-container {
|
|
2230
|
+
position: fixed;
|
|
2231
|
+
top: 0;
|
|
2232
|
+
left: 0;
|
|
2233
|
+
right: 0;
|
|
2234
|
+
bottom: 0;
|
|
2235
|
+
z-index: 1001; /* Above header and main content */
|
|
2236
|
+
background-color: #1c1c1c;
|
|
2237
|
+
margin: 0;
|
|
2238
|
+
padding: 0;
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
.console-container.hidden {
|
|
2242
|
+
display: none;
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
/* Ensure console wrapper fills the container */
|
|
2246
|
+
.console-container .esp32tool-console-wrapper {
|
|
2247
|
+
position: absolute;
|
|
2248
|
+
top: 0;
|
|
2249
|
+
left: 0;
|
|
2250
|
+
right: 0;
|
|
2251
|
+
bottom: 0;
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
/* Hide header and commands when console is active */
|
|
2255
|
+
body.console-active .header {
|
|
2256
|
+
display: none !important;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
body.console-active #commands {
|
|
2260
|
+
display: none !important;
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
body.console-active #notSupported {
|
|
2264
|
+
display: none !important;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
/* Remove padding from main when console is active */
|
|
2268
|
+
body.console-active .main {
|
|
2269
|
+
padding-top: 0 !important;
|
|
2270
|
+
overflow: hidden !important;
|
|
2271
|
+
}
|
package/dist/const.js
CHANGED
|
@@ -128,7 +128,7 @@ export const ESP32S3_UARTDEV_BUF_NO_USB_OTG = 3; // The above var when USB-OTG i
|
|
|
128
128
|
export const ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL = 4; // The above var when USB-JTAG/Serial is used
|
|
129
129
|
export const ESP32C2_SPI_REG_BASE = 0x60002000;
|
|
130
130
|
export const ESP32C2_BASEFUSEADDR = 0x60008800;
|
|
131
|
-
export const ESP32C2_MACFUSEADDR =
|
|
131
|
+
export const ESP32C2_MACFUSEADDR = ESP32C2_BASEFUSEADDR + 0x040;
|
|
132
132
|
export const ESP32C2_SPI_USR_OFFS = 0x18;
|
|
133
133
|
export const ESP32C2_SPI_USR1_OFFS = 0x1c;
|
|
134
134
|
export const ESP32C2_SPI_USR2_OFFS = 0x20;
|
package/dist/esp_loader.d.ts
CHANGED
|
@@ -19,6 +19,10 @@ export declare class ESPLoader extends EventTarget {
|
|
|
19
19
|
currentBaudRate: number;
|
|
20
20
|
private _maxUSBSerialBaudrate?;
|
|
21
21
|
private _reader?;
|
|
22
|
+
private SLIP_END;
|
|
23
|
+
private SLIP_ESC;
|
|
24
|
+
private SLIP_ESC_END;
|
|
25
|
+
private SLIP_ESC_ESC;
|
|
22
26
|
private _isESP32S2NativeUSB;
|
|
23
27
|
private _initializationSucceeded;
|
|
24
28
|
private __commandLock;
|
|
@@ -394,6 +398,14 @@ export declare class ESPLoader extends EventTarget {
|
|
|
394
398
|
* @returns true if reset was performed, false if not needed
|
|
395
399
|
*/
|
|
396
400
|
resetToFirmware(): Promise<boolean>;
|
|
401
|
+
/**
|
|
402
|
+
* @name detectUsbConnectionType
|
|
403
|
+
* Detect if device is using USB-JTAG/Serial or USB-OTG (not external serial chip)
|
|
404
|
+
* This helper extracts the detection logic from initialize() for reuse
|
|
405
|
+
* @returns true if USB-JTAG or USB-OTG, false if external serial chip
|
|
406
|
+
* @throws Error if detection fails and chipFamily is not set
|
|
407
|
+
*/
|
|
408
|
+
private detectUsbConnectionType;
|
|
397
409
|
/**
|
|
398
410
|
* @name enterConsoleMode
|
|
399
411
|
* Prepare device for console mode by resetting to firmware
|
|
@@ -419,6 +431,29 @@ export declare class ESPLoader extends EventTarget {
|
|
|
419
431
|
* This is needed after Improv or other operations that leave ESP in firmware mode
|
|
420
432
|
*/
|
|
421
433
|
reconnectToBootloader(): Promise<void>;
|
|
434
|
+
/**
|
|
435
|
+
* @name exitConsoleMode
|
|
436
|
+
* Exit console mode and return to bootloader
|
|
437
|
+
* For ESP32-S2, uses reconnectToBootloader which will trigger port change
|
|
438
|
+
* @returns true if manual reconnection is needed (ESP32-S2), false otherwise
|
|
439
|
+
*/
|
|
440
|
+
exitConsoleMode(): Promise<boolean>;
|
|
441
|
+
/**
|
|
442
|
+
* @name isConsoleResetSupported
|
|
443
|
+
* Check if console reset is supported for this device
|
|
444
|
+
* ESP32-S2 USB-JTAG/CDC does not support reset in console mode
|
|
445
|
+
* because any reset causes USB port to be lost (hardware limitation)
|
|
446
|
+
*/
|
|
447
|
+
isConsoleResetSupported(): boolean;
|
|
448
|
+
/**
|
|
449
|
+
* @name resetInConsoleMode
|
|
450
|
+
* Reset device while in console mode (firmware mode)
|
|
451
|
+
*
|
|
452
|
+
* NOTE: For ESP32-S2 USB-JTAG/CDC, ANY reset (hardware or software) causes
|
|
453
|
+
* the USB port to be lost because the device switches USB modes during reset.
|
|
454
|
+
* This is a hardware limitation - use isConsoleResetSupported() to check first.
|
|
455
|
+
*/
|
|
456
|
+
resetInConsoleMode(): Promise<void>;
|
|
422
457
|
/**
|
|
423
458
|
* @name drainInputBuffer
|
|
424
459
|
* Actively drain the input buffer by reading data for a specified time.
|
package/dist/esp_loader.js
CHANGED
|
@@ -27,6 +27,10 @@ export class ESPLoader extends EventTarget {
|
|
|
27
27
|
this.connected = true;
|
|
28
28
|
this.flashSize = null;
|
|
29
29
|
this.currentBaudRate = ESP_ROM_BAUD;
|
|
30
|
+
this.SLIP_END = 0xc0;
|
|
31
|
+
this.SLIP_ESC = 0xdb;
|
|
32
|
+
this.SLIP_ESC_END = 0xdc;
|
|
33
|
+
this.SLIP_ESC_ESC = 0xdd;
|
|
30
34
|
this._isESP32S2NativeUSB = false;
|
|
31
35
|
this._initializationSucceeded = false;
|
|
32
36
|
this.__commandLock = Promise.resolve([0, []]);
|
|
@@ -346,27 +350,7 @@ export class ESPLoader extends EventTarget {
|
|
|
346
350
|
// Detect if device is using USB-JTAG/Serial or USB-OTG (not external serial chip)
|
|
347
351
|
// This is needed to determine the correct reset strategy for console mode
|
|
348
352
|
try {
|
|
349
|
-
|
|
350
|
-
this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
351
|
-
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
352
|
-
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
353
|
-
this._isUsbJtagOrOtg = isUsingUsbOtg || isUsingUsbJtagSerial;
|
|
354
|
-
}
|
|
355
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32C3 ||
|
|
356
|
-
this.chipFamily === CHIP_FAMILY_ESP32C5 ||
|
|
357
|
-
this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
358
|
-
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
359
|
-
this._isUsbJtagOrOtg = isUsingUsbJtagSerial;
|
|
360
|
-
}
|
|
361
|
-
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
362
|
-
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
363
|
-
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
364
|
-
this._isUsbJtagOrOtg = isUsingUsbOtg || isUsingUsbJtagSerial;
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
// Other chips don't have USB-JTAG/OTG
|
|
368
|
-
this._isUsbJtagOrOtg = false;
|
|
369
|
-
}
|
|
353
|
+
this._isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
370
354
|
this.logger.debug(`USB connection type: ${this._isUsbJtagOrOtg ? "USB-JTAG/OTG" : "External Serial Chip"}`);
|
|
371
355
|
}
|
|
372
356
|
catch (err) {
|
|
@@ -445,7 +429,6 @@ export class ESPLoader extends EventTarget {
|
|
|
445
429
|
this.chipFamily = chip.family;
|
|
446
430
|
if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
447
431
|
this.chipRevision = await this.getChipRevision();
|
|
448
|
-
this.logger.debug(`ESP32-P4 revision: ${this.chipRevision}`);
|
|
449
432
|
if (this.chipRevision >= 300) {
|
|
450
433
|
this.chipVariant = "rev300";
|
|
451
434
|
}
|
|
@@ -456,7 +439,6 @@ export class ESPLoader extends EventTarget {
|
|
|
456
439
|
}
|
|
457
440
|
else if (this.chipFamily === CHIP_FAMILY_ESP32C3) {
|
|
458
441
|
this.chipRevision = await this.getChipRevision();
|
|
459
|
-
this.logger.debug(`ESP32-C3 revision: ${this.chipRevision}`);
|
|
460
442
|
}
|
|
461
443
|
this.logger.debug(`Detected chip via magic value: ${toHex(chipMagicValue >>> 0, 8)} (${this.chipName})`);
|
|
462
444
|
}
|
|
@@ -551,10 +533,7 @@ export class ESPLoader extends EventTarget {
|
|
|
551
533
|
}
|
|
552
534
|
}
|
|
553
535
|
catch {
|
|
554
|
-
//
|
|
555
|
-
if (!this._consoleMode) {
|
|
556
|
-
this.logger.error("Read loop got disconnected");
|
|
557
|
-
}
|
|
536
|
+
// this.logger.error("Read loop got disconnected");
|
|
558
537
|
}
|
|
559
538
|
finally {
|
|
560
539
|
// Always reset reconfiguring flag when read loop ends
|
|
@@ -1164,7 +1143,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1164
1143
|
}
|
|
1165
1144
|
catch (error) {
|
|
1166
1145
|
lastError = error;
|
|
1167
|
-
this.logger.debug(
|
|
1146
|
+
// this.logger.debug(
|
|
1147
|
+
// `${strategy.name} reset failed: ${(error as Error).message}`,
|
|
1148
|
+
// );
|
|
1168
1149
|
// Set abandon flag to stop any in-flight operations
|
|
1169
1150
|
this._abandonCurrentOperation = true;
|
|
1170
1151
|
// Wait a bit for in-flight operations to abort
|
|
@@ -1305,7 +1286,6 @@ export class ESPLoader extends EventTarget {
|
|
|
1305
1286
|
const hi = (word5 >> 23) & 0x07;
|
|
1306
1287
|
// Combine: upper 3 bits from word5, lower 3 bits from word3
|
|
1307
1288
|
const revision = (hi << 3) | low;
|
|
1308
|
-
this.logger.debug(`ESP32-C3 revision: ${revision}`);
|
|
1309
1289
|
return revision;
|
|
1310
1290
|
}
|
|
1311
1291
|
/**
|
|
@@ -1744,44 +1724,44 @@ export class ESPLoader extends EventTarget {
|
|
|
1744
1724
|
const waitingFor = partialPacket === null ? "header" : "content";
|
|
1745
1725
|
throw new SlipReadError("Timed out waiting for packet " + waitingFor);
|
|
1746
1726
|
}
|
|
1747
|
-
const
|
|
1727
|
+
const byte = this._readByte();
|
|
1748
1728
|
if (partialPacket === null) {
|
|
1749
1729
|
// waiting for packet header
|
|
1750
|
-
if (
|
|
1730
|
+
if (byte == this.SLIP_END) {
|
|
1751
1731
|
partialPacket = [];
|
|
1752
1732
|
}
|
|
1753
1733
|
else {
|
|
1754
1734
|
if (this.debug) {
|
|
1755
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
1735
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
1756
1736
|
this.logger.debug("Remaining data in serial buffer: " +
|
|
1757
1737
|
hexFormatter(this._inputBuffer));
|
|
1758
1738
|
}
|
|
1759
|
-
throw new SlipReadError("Invalid head of packet (" + toHex(
|
|
1739
|
+
throw new SlipReadError("Invalid head of packet (" + toHex(byte) + ")");
|
|
1760
1740
|
}
|
|
1761
1741
|
}
|
|
1762
1742
|
else if (inEscape) {
|
|
1763
1743
|
// part-way through escape sequence
|
|
1764
1744
|
inEscape = false;
|
|
1765
|
-
if (
|
|
1766
|
-
partialPacket.push(
|
|
1745
|
+
if (byte == this.SLIP_ESC_END) {
|
|
1746
|
+
partialPacket.push(this.SLIP_END);
|
|
1767
1747
|
}
|
|
1768
|
-
else if (
|
|
1769
|
-
partialPacket.push(
|
|
1748
|
+
else if (byte == this.SLIP_ESC_ESC) {
|
|
1749
|
+
partialPacket.push(this.SLIP_ESC);
|
|
1770
1750
|
}
|
|
1771
1751
|
else {
|
|
1772
1752
|
if (this.debug) {
|
|
1773
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
1753
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
1774
1754
|
this.logger.debug("Remaining data in serial buffer: " +
|
|
1775
1755
|
hexFormatter(this._inputBuffer));
|
|
1776
1756
|
}
|
|
1777
|
-
throw new SlipReadError("Invalid SLIP escape (0xdb, " + toHex(
|
|
1757
|
+
throw new SlipReadError("Invalid SLIP escape (0xdb, " + toHex(byte) + ")");
|
|
1778
1758
|
}
|
|
1779
1759
|
}
|
|
1780
|
-
else if (
|
|
1760
|
+
else if (byte == this.SLIP_ESC) {
|
|
1781
1761
|
// start of escape sequence
|
|
1782
1762
|
inEscape = true;
|
|
1783
1763
|
}
|
|
1784
|
-
else if (
|
|
1764
|
+
else if (byte == this.SLIP_END) {
|
|
1785
1765
|
// end of packet
|
|
1786
1766
|
if (this.debug)
|
|
1787
1767
|
this.logger.debug("Received full packet: " + hexFormatter(partialPacket));
|
|
@@ -1791,7 +1771,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1791
1771
|
}
|
|
1792
1772
|
else {
|
|
1793
1773
|
// normal byte in packet
|
|
1794
|
-
partialPacket.push(
|
|
1774
|
+
partialPacket.push(byte);
|
|
1795
1775
|
}
|
|
1796
1776
|
}
|
|
1797
1777
|
}
|
|
@@ -1822,44 +1802,44 @@ export class ESPLoader extends EventTarget {
|
|
|
1822
1802
|
}
|
|
1823
1803
|
if (this.debug)
|
|
1824
1804
|
this.logger.debug("Read " + readBytes.length + " bytes: " + hexFormatter(readBytes));
|
|
1825
|
-
for (const
|
|
1805
|
+
for (const byte of readBytes) {
|
|
1826
1806
|
if (partialPacket === null) {
|
|
1827
1807
|
// waiting for packet header
|
|
1828
|
-
if (
|
|
1808
|
+
if (byte == this.SLIP_END) {
|
|
1829
1809
|
partialPacket = [];
|
|
1830
1810
|
}
|
|
1831
1811
|
else {
|
|
1832
1812
|
if (this.debug) {
|
|
1833
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
1813
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
1834
1814
|
this.logger.debug("Remaining data in serial buffer: " +
|
|
1835
1815
|
hexFormatter(this._inputBuffer));
|
|
1836
1816
|
}
|
|
1837
|
-
throw new SlipReadError("Invalid head of packet (" + toHex(
|
|
1817
|
+
throw new SlipReadError("Invalid head of packet (" + toHex(byte) + ")");
|
|
1838
1818
|
}
|
|
1839
1819
|
}
|
|
1840
1820
|
else if (inEscape) {
|
|
1841
1821
|
// part-way through escape sequence
|
|
1842
1822
|
inEscape = false;
|
|
1843
|
-
if (
|
|
1844
|
-
partialPacket.push(
|
|
1823
|
+
if (byte == this.SLIP_ESC_END) {
|
|
1824
|
+
partialPacket.push(this.SLIP_END);
|
|
1845
1825
|
}
|
|
1846
|
-
else if (
|
|
1847
|
-
partialPacket.push(
|
|
1826
|
+
else if (byte == this.SLIP_ESC_ESC) {
|
|
1827
|
+
partialPacket.push(this.SLIP_ESC);
|
|
1848
1828
|
}
|
|
1849
1829
|
else {
|
|
1850
1830
|
if (this.debug) {
|
|
1851
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
1831
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
1852
1832
|
this.logger.debug("Remaining data in serial buffer: " +
|
|
1853
1833
|
hexFormatter(this._inputBuffer));
|
|
1854
1834
|
}
|
|
1855
|
-
throw new SlipReadError("Invalid SLIP escape (0xdb, " + toHex(
|
|
1835
|
+
throw new SlipReadError("Invalid SLIP escape (0xdb, " + toHex(byte) + ")");
|
|
1856
1836
|
}
|
|
1857
1837
|
}
|
|
1858
|
-
else if (
|
|
1838
|
+
else if (byte == this.SLIP_ESC) {
|
|
1859
1839
|
// start of escape sequence
|
|
1860
1840
|
inEscape = true;
|
|
1861
1841
|
}
|
|
1862
|
-
else if (
|
|
1842
|
+
else if (byte == this.SLIP_END) {
|
|
1863
1843
|
// end of packet
|
|
1864
1844
|
if (this.debug)
|
|
1865
1845
|
this.logger.debug("Received full packet: " + hexFormatter(partialPacket));
|
|
@@ -1869,7 +1849,7 @@ export class ESPLoader extends EventTarget {
|
|
|
1869
1849
|
}
|
|
1870
1850
|
else {
|
|
1871
1851
|
// normal byte in packet
|
|
1872
|
-
partialPacket.push(
|
|
1852
|
+
partialPacket.push(byte);
|
|
1873
1853
|
}
|
|
1874
1854
|
}
|
|
1875
1855
|
}
|
|
@@ -2009,8 +1989,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2009
1989
|
this.readLoop();
|
|
2010
1990
|
}
|
|
2011
1991
|
catch (e) {
|
|
2012
|
-
this.logger.error(`Reconfigure port error: ${e}`);
|
|
2013
|
-
throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
|
|
1992
|
+
// this.logger.error(`Reconfigure port error: ${e}`);
|
|
1993
|
+
// throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
|
|
2014
1994
|
}
|
|
2015
1995
|
finally {
|
|
2016
1996
|
// Always reset flag, even on error or early return
|
|
@@ -2601,7 +2581,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2601
2581
|
return;
|
|
2602
2582
|
}
|
|
2603
2583
|
if (!this.port.writable) {
|
|
2604
|
-
this.logger.debug("Port already closed, skipping disconnect");
|
|
2584
|
+
// this.logger.debug("Port already closed, skipping disconnect");
|
|
2605
2585
|
return;
|
|
2606
2586
|
}
|
|
2607
2587
|
// Wait for pending writes to complete
|
|
@@ -2609,7 +2589,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2609
2589
|
await this._writeChain;
|
|
2610
2590
|
}
|
|
2611
2591
|
catch (err) {
|
|
2612
|
-
this.logger.debug(`Pending write error during disconnect: ${err}`);
|
|
2592
|
+
// this.logger.debug(`Pending write error during disconnect: ${err}`);
|
|
2613
2593
|
}
|
|
2614
2594
|
// Release persistent writer before closing
|
|
2615
2595
|
if (this._writer) {
|
|
@@ -2618,7 +2598,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2618
2598
|
this._writer.releaseLock();
|
|
2619
2599
|
}
|
|
2620
2600
|
catch (err) {
|
|
2621
|
-
this.logger.debug(`Writer close/release error: ${err}`);
|
|
2601
|
+
// this.logger.debug(`Writer close/release error: ${err}`);
|
|
2622
2602
|
}
|
|
2623
2603
|
this._writer = undefined;
|
|
2624
2604
|
}
|
|
@@ -2631,7 +2611,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2631
2611
|
writer.releaseLock();
|
|
2632
2612
|
}
|
|
2633
2613
|
catch (err) {
|
|
2634
|
-
this.logger.debug(`Direct writer close error: ${err}`);
|
|
2614
|
+
// this.logger.debug(`Direct writer close error: ${err}`);
|
|
2635
2615
|
}
|
|
2636
2616
|
}
|
|
2637
2617
|
await new Promise((resolve) => {
|
|
@@ -2653,7 +2633,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2653
2633
|
this._reader.cancel();
|
|
2654
2634
|
}
|
|
2655
2635
|
catch (err) {
|
|
2656
|
-
this.logger.debug(`Reader cancel error: ${err}`);
|
|
2636
|
+
// this.logger.debug(`Reader cancel error: ${err}`);
|
|
2657
2637
|
// Reader already released, resolve immediately
|
|
2658
2638
|
clearTimeout(timeout);
|
|
2659
2639
|
resolve(undefined);
|
|
@@ -2690,7 +2670,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2690
2670
|
await this._writeChain;
|
|
2691
2671
|
}
|
|
2692
2672
|
catch (err) {
|
|
2693
|
-
this.logger.debug(`Pending write error during release: ${err}`);
|
|
2673
|
+
// this.logger.debug(`Pending write error during release: ${err}`);
|
|
2694
2674
|
}
|
|
2695
2675
|
// Release writer
|
|
2696
2676
|
if (this._writer) {
|
|
@@ -2737,6 +2717,39 @@ export class ESPLoader extends EventTarget {
|
|
|
2737
2717
|
async resetToFirmware() {
|
|
2738
2718
|
return await this._resetToFirmwareIfNeeded();
|
|
2739
2719
|
}
|
|
2720
|
+
/**
|
|
2721
|
+
* @name detectUsbConnectionType
|
|
2722
|
+
* Detect if device is using USB-JTAG/Serial or USB-OTG (not external serial chip)
|
|
2723
|
+
* This helper extracts the detection logic from initialize() for reuse
|
|
2724
|
+
* @returns true if USB-JTAG or USB-OTG, false if external serial chip
|
|
2725
|
+
* @throws Error if detection fails and chipFamily is not set
|
|
2726
|
+
*/
|
|
2727
|
+
async detectUsbConnectionType() {
|
|
2728
|
+
if (!this.chipFamily) {
|
|
2729
|
+
throw new Error("Cannot detect USB connection type: chipFamily not set");
|
|
2730
|
+
}
|
|
2731
|
+
if (this.chipFamily === CHIP_FAMILY_ESP32S2 ||
|
|
2732
|
+
this.chipFamily === CHIP_FAMILY_ESP32S3) {
|
|
2733
|
+
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
2734
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
2735
|
+
return isUsingUsbOtg || isUsingUsbJtagSerial;
|
|
2736
|
+
}
|
|
2737
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32C3 ||
|
|
2738
|
+
this.chipFamily === CHIP_FAMILY_ESP32C5 ||
|
|
2739
|
+
this.chipFamily === CHIP_FAMILY_ESP32C6) {
|
|
2740
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
2741
|
+
return isUsingUsbJtagSerial;
|
|
2742
|
+
}
|
|
2743
|
+
else if (this.chipFamily === CHIP_FAMILY_ESP32P4) {
|
|
2744
|
+
const isUsingUsbOtg = await this.usingUsbOtg();
|
|
2745
|
+
const isUsingUsbJtagSerial = await this.usingUsbJtagSerial();
|
|
2746
|
+
return isUsingUsbOtg || isUsingUsbJtagSerial;
|
|
2747
|
+
}
|
|
2748
|
+
else {
|
|
2749
|
+
// Other chips don't have USB-JTAG/OTG
|
|
2750
|
+
return false;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2740
2753
|
/**
|
|
2741
2754
|
* @name enterConsoleMode
|
|
2742
2755
|
* Prepare device for console mode by resetting to firmware
|
|
@@ -2746,8 +2759,23 @@ export class ESPLoader extends EventTarget {
|
|
|
2746
2759
|
async enterConsoleMode() {
|
|
2747
2760
|
// Set console mode flag
|
|
2748
2761
|
this._consoleMode = true;
|
|
2749
|
-
//
|
|
2750
|
-
|
|
2762
|
+
// Re-detect USB connection type to ensure we have a definitive value
|
|
2763
|
+
// This handles cases where isUsbJtagOrOtg might be undefined
|
|
2764
|
+
let isUsbJtag;
|
|
2765
|
+
try {
|
|
2766
|
+
isUsbJtag = await this.detectUsbConnectionType();
|
|
2767
|
+
this.logger.debug(`USB connection type detected: ${isUsbJtag ? "USB-JTAG/OTG" : "External Serial Chip"}`);
|
|
2768
|
+
}
|
|
2769
|
+
catch (err) {
|
|
2770
|
+
// If detection fails, fall back to cached value or fail-fast
|
|
2771
|
+
if (this.isUsbJtagOrOtg === undefined) {
|
|
2772
|
+
throw new Error(`Cannot enter console mode: USB connection type unknown and detection failed: ${err}`);
|
|
2773
|
+
}
|
|
2774
|
+
this.logger.debug(`USB detection failed, using cached value: ${this.isUsbJtagOrOtg}`);
|
|
2775
|
+
isUsbJtag = this.isUsbJtagOrOtg;
|
|
2776
|
+
}
|
|
2777
|
+
// Release reader/writer so console can create new ones
|
|
2778
|
+
// This is needed for Desktop (Web Serial) to unlock streams
|
|
2751
2779
|
if (isUsbJtag) {
|
|
2752
2780
|
// USB-JTAG/OTG devices: Use watchdog reset which closes port
|
|
2753
2781
|
const wasReset = await this._resetToFirmwareIfNeeded();
|
|
@@ -2770,6 +2798,19 @@ export class ESPLoader extends EventTarget {
|
|
|
2770
2798
|
catch (err) {
|
|
2771
2799
|
this.logger.debug(`Could not reset device: ${err}`);
|
|
2772
2800
|
}
|
|
2801
|
+
// For WebUSB (Android), recreate streams after hardware reset
|
|
2802
|
+
const isWebUSB = this.port.isWebUSB === true;
|
|
2803
|
+
if (isWebUSB) {
|
|
2804
|
+
try {
|
|
2805
|
+
// Use the public recreateStreams() method to safely recreate streams
|
|
2806
|
+
// without closing the port (important after hardware reset)
|
|
2807
|
+
await this.port.recreateStreams();
|
|
2808
|
+
this.logger.debug("WebUSB streams recreated for console mode");
|
|
2809
|
+
}
|
|
2810
|
+
catch (err) {
|
|
2811
|
+
this.logger.debug(`Failed to recreate WebUSB streams: ${err}`);
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2773
2814
|
return false; // Port stays open
|
|
2774
2815
|
}
|
|
2775
2816
|
}
|
|
@@ -2790,7 +2831,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2790
2831
|
this.chipFamily === CHIP_FAMILY_ESP32S3
|
|
2791
2832
|
? "USB-JTAG/Serial or USB-OTG"
|
|
2792
2833
|
: "USB-JTAG/Serial";
|
|
2793
|
-
this.logger.log(`Resetting ${this.
|
|
2834
|
+
this.logger.log(`Resetting ${this.chipName || "device"} (${resetMethod}) to boot into firmware...`);
|
|
2794
2835
|
// Set console mode flag before reset to prevent subsequent hardReset calls
|
|
2795
2836
|
this._consoleMode = true;
|
|
2796
2837
|
// For S2/S3: Clear force download boot mask before WDT reset
|
|
@@ -3053,6 +3094,98 @@ export class ESPLoader extends EventTarget {
|
|
|
3053
3094
|
throw err;
|
|
3054
3095
|
}
|
|
3055
3096
|
}
|
|
3097
|
+
/**
|
|
3098
|
+
* @name exitConsoleMode
|
|
3099
|
+
* Exit console mode and return to bootloader
|
|
3100
|
+
* For ESP32-S2, uses reconnectToBootloader which will trigger port change
|
|
3101
|
+
* @returns true if manual reconnection is needed (ESP32-S2), false otherwise
|
|
3102
|
+
*/
|
|
3103
|
+
async exitConsoleMode() {
|
|
3104
|
+
if (this._parent) {
|
|
3105
|
+
return await this._parent.exitConsoleMode();
|
|
3106
|
+
}
|
|
3107
|
+
// Clear console mode flag
|
|
3108
|
+
this._consoleMode = false;
|
|
3109
|
+
// Check if this is ESP32-S2 with USB-JTAG/OTG
|
|
3110
|
+
const isESP32S2 = this.chipFamily === CHIP_FAMILY_ESP32S2;
|
|
3111
|
+
// For ESP32-S2: if _isUsbJtagOrOtg is undefined, try to detect it
|
|
3112
|
+
// If detection fails or is undefined, assume USB-JTAG/OTG (conservative/safe path)
|
|
3113
|
+
let isUsbJtagOrOtg = this._isUsbJtagOrOtg;
|
|
3114
|
+
if (isESP32S2 && isUsbJtagOrOtg === undefined) {
|
|
3115
|
+
try {
|
|
3116
|
+
isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
3117
|
+
}
|
|
3118
|
+
catch (err) {
|
|
3119
|
+
this.logger.debug(`USB detection failed, assuming USB-JTAG/OTG for ESP32-S2: ${err}`);
|
|
3120
|
+
isUsbJtagOrOtg = true; // Conservative fallback for ESP32-S2
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
if (isESP32S2 && isUsbJtagOrOtg) {
|
|
3124
|
+
// ESP32-S2 USB: Use reconnectToBootloader which handles the mode switch
|
|
3125
|
+
// This will close the port and the device will reboot to bootloader
|
|
3126
|
+
this.logger.log("ESP32-S2 USB detected - reconnecting to bootloader");
|
|
3127
|
+
try {
|
|
3128
|
+
await this.reconnectToBootloader();
|
|
3129
|
+
}
|
|
3130
|
+
catch (err) {
|
|
3131
|
+
this.logger.debug(`Reconnect error (expected for ESP32-S2): ${err}`);
|
|
3132
|
+
}
|
|
3133
|
+
// For ESP32-S2, port will change, so return true to indicate manual reconnection needed
|
|
3134
|
+
return true;
|
|
3135
|
+
}
|
|
3136
|
+
// For other devices, use standard reconnectToBootloader
|
|
3137
|
+
await this.reconnectToBootloader();
|
|
3138
|
+
return false; // No manual reconnection needed
|
|
3139
|
+
}
|
|
3140
|
+
/**
|
|
3141
|
+
* @name isConsoleResetSupported
|
|
3142
|
+
* Check if console reset is supported for this device
|
|
3143
|
+
* ESP32-S2 USB-JTAG/CDC does not support reset in console mode
|
|
3144
|
+
* because any reset causes USB port to be lost (hardware limitation)
|
|
3145
|
+
*/
|
|
3146
|
+
isConsoleResetSupported() {
|
|
3147
|
+
if (this._parent) {
|
|
3148
|
+
return this._parent.isConsoleResetSupported();
|
|
3149
|
+
}
|
|
3150
|
+
// For ESP32-S2: if _isUsbJtagOrOtg is undefined, assume USB-JTAG/OTG (conservative)
|
|
3151
|
+
// This means console reset is NOT supported (safer default)
|
|
3152
|
+
const isS2UsbJtag = this.chipFamily === CHIP_FAMILY_ESP32S2 &&
|
|
3153
|
+
(this._isUsbJtagOrOtg === true || this._isUsbJtagOrOtg === undefined);
|
|
3154
|
+
return !isS2UsbJtag; // Not supported for ESP32-S2 USB-JTAG/CDC
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* @name resetInConsoleMode
|
|
3158
|
+
* Reset device while in console mode (firmware mode)
|
|
3159
|
+
*
|
|
3160
|
+
* NOTE: For ESP32-S2 USB-JTAG/CDC, ANY reset (hardware or software) causes
|
|
3161
|
+
* the USB port to be lost because the device switches USB modes during reset.
|
|
3162
|
+
* This is a hardware limitation - use isConsoleResetSupported() to check first.
|
|
3163
|
+
*/
|
|
3164
|
+
async resetInConsoleMode() {
|
|
3165
|
+
if (this._parent) {
|
|
3166
|
+
return await this._parent.resetInConsoleMode();
|
|
3167
|
+
}
|
|
3168
|
+
if (!this.isConsoleResetSupported()) {
|
|
3169
|
+
this.logger.debug("Console reset not supported for ESP32-S2 USB-JTAG/CDC");
|
|
3170
|
+
return; // Do nothing
|
|
3171
|
+
}
|
|
3172
|
+
// For other devices: Use standard firmware reset
|
|
3173
|
+
const isWebUSB = this.port.isWebUSB === true;
|
|
3174
|
+
try {
|
|
3175
|
+
this.logger.debug("Resetting device in console mode");
|
|
3176
|
+
if (isWebUSB) {
|
|
3177
|
+
await this.hardResetToFirmwareWebUSB();
|
|
3178
|
+
}
|
|
3179
|
+
else {
|
|
3180
|
+
await this.hardResetToFirmware();
|
|
3181
|
+
}
|
|
3182
|
+
this.logger.debug("Device reset complete");
|
|
3183
|
+
}
|
|
3184
|
+
catch (err) {
|
|
3185
|
+
this.logger.error(`Reset failed: ${err}`);
|
|
3186
|
+
throw err;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3056
3189
|
/**
|
|
3057
3190
|
* @name drainInputBuffer
|
|
3058
3191
|
* Actively drain the input buffer by reading data for a specified time.
|
|
@@ -3067,7 +3200,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3067
3200
|
// Wait for the buffer to fill
|
|
3068
3201
|
await sleep(bufferingTime);
|
|
3069
3202
|
// Unsupported command response is sent 8 times and has
|
|
3070
|
-
// 14 bytes length including delimiter 0xC0 bytes.
|
|
3203
|
+
// 14 bytes length including delimiter SLIP_END (0xC0) bytes.
|
|
3071
3204
|
// At least part of it is read as a command response,
|
|
3072
3205
|
// but to be safe, read it all.
|
|
3073
3206
|
const bytesToDrain = 14 * 8;
|
|
@@ -3231,7 +3364,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3231
3364
|
// The stub expects 4 bytes (ACK), if we send less it will break out
|
|
3232
3365
|
try {
|
|
3233
3366
|
// Send SLIP frame with no data (just delimiters)
|
|
3234
|
-
const abortFrame = [
|
|
3367
|
+
const abortFrame = [this.SLIP_END, this.SLIP_END]; // Empty SLIP frame
|
|
3235
3368
|
await this.writeToStream(abortFrame);
|
|
3236
3369
|
this.logger.debug(`Sent abort frame to stub`);
|
|
3237
3370
|
// Give stub time to process abort
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare const connect: (logger: Logger) => Promise<ESPLoader>;
|
|
|
7
7
|
export declare const connectWithPort: (port: SerialPort, logger: Logger) => Promise<ESPLoader>;
|
|
8
8
|
export { parsePartitionTable, getPartitionTableOffset, formatSize, } from "./partition";
|
|
9
9
|
export type { Partition } from "./partition";
|
|
10
|
+
export { toHex, sleep, hexFormatter, formatMacAddr } from "./util";
|
|
10
11
|
export { FilesystemType, detectFilesystemType, detectFilesystemFromImage, getDefaultBlockSize, getBlockSizeCandidates, getESP8266FilesystemLayout, scanESP8266Filesystem, LITTLEFS_DEFAULT_BLOCK_SIZE, LITTLEFS_BLOCK_SIZE_CANDIDATES, FATFS_DEFAULT_BLOCK_SIZE, FATFS_BLOCK_SIZE_CANDIDATES, ESP8266_LITTLEFS_BLOCK_SIZE, ESP8266_LITTLEFS_BLOCK_SIZE_CANDIDATES, ESP8266_LITTLEFS_PAGE_SIZE, ESP8266_SPIFFS_PAGE_SIZE, ESP8266_SPIFFS_BLOCK_SIZE, } from "./wasm/filesystems";
|
|
11
12
|
export type { ESP8266FilesystemLayout } from "./wasm/filesystems";
|
|
12
13
|
export { SpiffsFS, SpiffsBuildConfig, SpiffsReader, DEFAULT_SPIFFS_CONFIG, type SpiffsFile, type SpiffsBuildConfigOptions, } from "./lib/spiffs/index";
|
package/dist/index.js
CHANGED
|
@@ -44,5 +44,7 @@ export const connectWithPort = async (port, logger) => {
|
|
|
44
44
|
return new ESPLoader(port, logger);
|
|
45
45
|
};
|
|
46
46
|
export { parsePartitionTable, getPartitionTableOffset, formatSize, } from "./partition";
|
|
47
|
+
// Export utility functions for use in UI code
|
|
48
|
+
export { toHex, sleep, hexFormatter, formatMacAddr } from "./util";
|
|
47
49
|
export { FilesystemType, detectFilesystemType, detectFilesystemFromImage, getDefaultBlockSize, getBlockSizeCandidates, getESP8266FilesystemLayout, scanESP8266Filesystem, LITTLEFS_DEFAULT_BLOCK_SIZE, LITTLEFS_BLOCK_SIZE_CANDIDATES, FATFS_DEFAULT_BLOCK_SIZE, FATFS_BLOCK_SIZE_CANDIDATES, ESP8266_LITTLEFS_BLOCK_SIZE, ESP8266_LITTLEFS_BLOCK_SIZE_CANDIDATES, ESP8266_LITTLEFS_PAGE_SIZE, ESP8266_SPIFFS_PAGE_SIZE, ESP8266_SPIFFS_BLOCK_SIZE, } from "./wasm/filesystems";
|
|
48
50
|
export { SpiffsFS, SpiffsBuildConfig, SpiffsReader, DEFAULT_SPIFFS_CONFIG, } from "./lib/spiffs/index";
|