tasmota-webserial-esptool 9.2.9 → 9.2.10
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.js +1 -1
- package/dist/esp_loader.d.ts +28 -3
- package/dist/esp_loader.js +157 -61
- 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/js/modules/esptool.js +1 -1
- package/js/script.js +207 -180
- package/js/webusb-serial.js +21 -1
- package/package.json +3 -3
- package/src/const.ts +1 -1
- package/src/esp_loader.ts +175 -65
- package/src/index.ts +3 -0
- package/src/util.ts +9 -0
package/src/esp_loader.ts
CHANGED
|
@@ -141,9 +141,13 @@ export class ESPLoader extends EventTarget {
|
|
|
141
141
|
__inputBuffer?: number[];
|
|
142
142
|
__inputBufferReadIndex?: number;
|
|
143
143
|
__totalBytesRead?: number;
|
|
144
|
-
|
|
144
|
+
public currentBaudRate: number = ESP_ROM_BAUD;
|
|
145
145
|
private _maxUSBSerialBaudrate?: number;
|
|
146
146
|
public __reader?: ReadableStreamDefaultReader<Uint8Array>;
|
|
147
|
+
private SLIP_END = 0xc0;
|
|
148
|
+
private SLIP_ESC = 0xdb;
|
|
149
|
+
private SLIP_ESC_END = 0xdc;
|
|
150
|
+
private SLIP_ESC_ESC = 0xdd;
|
|
147
151
|
private _isESP32S2NativeUSB: boolean = false;
|
|
148
152
|
private _initializationSucceeded: boolean = false;
|
|
149
153
|
private __commandLock: Promise<[number, number[]]> = Promise.resolve([0, []]);
|
|
@@ -757,17 +761,14 @@ export class ESPLoader extends EventTarget {
|
|
|
757
761
|
|
|
758
762
|
// Always read from browser's serial buffer immediately
|
|
759
763
|
// to prevent browser buffer overflow. Don't apply back-pressure here.
|
|
760
|
-
const chunk = Array.from(value);
|
|
764
|
+
const chunk = Array.from(value as Uint8Array);
|
|
761
765
|
Array.prototype.push.apply(this._inputBuffer, chunk);
|
|
762
766
|
|
|
763
767
|
// Track total bytes read from serial port
|
|
764
768
|
this._totalBytesRead += value.length;
|
|
765
769
|
}
|
|
766
770
|
} catch {
|
|
767
|
-
//
|
|
768
|
-
if (!this._consoleMode) {
|
|
769
|
-
this.logger.error("Read loop got disconnected");
|
|
770
|
-
}
|
|
771
|
+
// this.logger.error("Read loop got disconnected");
|
|
771
772
|
} finally {
|
|
772
773
|
// Always reset reconfiguring flag when read loop ends
|
|
773
774
|
// This prevents "Cannot write during port reconfiguration" errors
|
|
@@ -1462,9 +1463,9 @@ export class ESPLoader extends EventTarget {
|
|
|
1462
1463
|
}
|
|
1463
1464
|
} catch (error) {
|
|
1464
1465
|
lastError = error as Error;
|
|
1465
|
-
this.logger.debug(
|
|
1466
|
-
`${strategy.name} reset failed: ${(error as Error).message}`,
|
|
1467
|
-
);
|
|
1466
|
+
// this.logger.debug(
|
|
1467
|
+
// `${strategy.name} reset failed: ${(error as Error).message}`,
|
|
1468
|
+
// );
|
|
1468
1469
|
|
|
1469
1470
|
// Set abandon flag to stop any in-flight operations
|
|
1470
1471
|
this._abandonCurrentOperation = true;
|
|
@@ -2129,47 +2130,47 @@ export class ESPLoader extends EventTarget {
|
|
|
2129
2130
|
"Timed out waiting for packet " + waitingFor,
|
|
2130
2131
|
);
|
|
2131
2132
|
}
|
|
2132
|
-
const
|
|
2133
|
+
const byte = this._readByte()!;
|
|
2133
2134
|
|
|
2134
2135
|
if (partialPacket === null) {
|
|
2135
2136
|
// waiting for packet header
|
|
2136
|
-
if (
|
|
2137
|
+
if (byte == this.SLIP_END) {
|
|
2137
2138
|
partialPacket = [];
|
|
2138
2139
|
} else {
|
|
2139
2140
|
if (this.debug) {
|
|
2140
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
2141
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
2141
2142
|
this.logger.debug(
|
|
2142
2143
|
"Remaining data in serial buffer: " +
|
|
2143
2144
|
hexFormatter(this._inputBuffer),
|
|
2144
2145
|
);
|
|
2145
2146
|
}
|
|
2146
2147
|
throw new SlipReadError(
|
|
2147
|
-
"Invalid head of packet (" + toHex(
|
|
2148
|
+
"Invalid head of packet (" + toHex(byte) + ")",
|
|
2148
2149
|
);
|
|
2149
2150
|
}
|
|
2150
2151
|
} else if (inEscape) {
|
|
2151
2152
|
// part-way through escape sequence
|
|
2152
2153
|
inEscape = false;
|
|
2153
|
-
if (
|
|
2154
|
-
partialPacket.push(
|
|
2155
|
-
} else if (
|
|
2156
|
-
partialPacket.push(
|
|
2154
|
+
if (byte == this.SLIP_ESC_END) {
|
|
2155
|
+
partialPacket.push(this.SLIP_END);
|
|
2156
|
+
} else if (byte == this.SLIP_ESC_ESC) {
|
|
2157
|
+
partialPacket.push(this.SLIP_ESC);
|
|
2157
2158
|
} else {
|
|
2158
2159
|
if (this.debug) {
|
|
2159
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
2160
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
2160
2161
|
this.logger.debug(
|
|
2161
2162
|
"Remaining data in serial buffer: " +
|
|
2162
2163
|
hexFormatter(this._inputBuffer),
|
|
2163
2164
|
);
|
|
2164
2165
|
}
|
|
2165
2166
|
throw new SlipReadError(
|
|
2166
|
-
"Invalid SLIP escape (0xdb, " + toHex(
|
|
2167
|
+
"Invalid SLIP escape (0xdb, " + toHex(byte) + ")",
|
|
2167
2168
|
);
|
|
2168
2169
|
}
|
|
2169
|
-
} else if (
|
|
2170
|
+
} else if (byte == this.SLIP_ESC) {
|
|
2170
2171
|
// start of escape sequence
|
|
2171
2172
|
inEscape = true;
|
|
2172
|
-
} else if (
|
|
2173
|
+
} else if (byte == this.SLIP_END) {
|
|
2173
2174
|
// end of packet
|
|
2174
2175
|
if (this.debug)
|
|
2175
2176
|
this.logger.debug(
|
|
@@ -2180,7 +2181,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2180
2181
|
return partialPacket;
|
|
2181
2182
|
} else {
|
|
2182
2183
|
// normal byte in packet
|
|
2183
|
-
partialPacket.push(
|
|
2184
|
+
partialPacket.push(byte);
|
|
2184
2185
|
}
|
|
2185
2186
|
}
|
|
2186
2187
|
}
|
|
@@ -2214,46 +2215,46 @@ export class ESPLoader extends EventTarget {
|
|
|
2214
2215
|
this.logger.debug(
|
|
2215
2216
|
"Read " + readBytes.length + " bytes: " + hexFormatter(readBytes),
|
|
2216
2217
|
);
|
|
2217
|
-
for (const
|
|
2218
|
+
for (const byte of readBytes) {
|
|
2218
2219
|
if (partialPacket === null) {
|
|
2219
2220
|
// waiting for packet header
|
|
2220
|
-
if (
|
|
2221
|
+
if (byte == this.SLIP_END) {
|
|
2221
2222
|
partialPacket = [];
|
|
2222
2223
|
} else {
|
|
2223
2224
|
if (this.debug) {
|
|
2224
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
2225
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
2225
2226
|
this.logger.debug(
|
|
2226
2227
|
"Remaining data in serial buffer: " +
|
|
2227
2228
|
hexFormatter(this._inputBuffer),
|
|
2228
2229
|
);
|
|
2229
2230
|
}
|
|
2230
2231
|
throw new SlipReadError(
|
|
2231
|
-
"Invalid head of packet (" + toHex(
|
|
2232
|
+
"Invalid head of packet (" + toHex(byte) + ")",
|
|
2232
2233
|
);
|
|
2233
2234
|
}
|
|
2234
2235
|
} else if (inEscape) {
|
|
2235
2236
|
// part-way through escape sequence
|
|
2236
2237
|
inEscape = false;
|
|
2237
|
-
if (
|
|
2238
|
-
partialPacket.push(
|
|
2239
|
-
} else if (
|
|
2240
|
-
partialPacket.push(
|
|
2238
|
+
if (byte == this.SLIP_ESC_END) {
|
|
2239
|
+
partialPacket.push(this.SLIP_END);
|
|
2240
|
+
} else if (byte == this.SLIP_ESC_ESC) {
|
|
2241
|
+
partialPacket.push(this.SLIP_ESC);
|
|
2241
2242
|
} else {
|
|
2242
2243
|
if (this.debug) {
|
|
2243
|
-
this.logger.debug("Read invalid data: " + toHex(
|
|
2244
|
+
this.logger.debug("Read invalid data: " + toHex(byte));
|
|
2244
2245
|
this.logger.debug(
|
|
2245
2246
|
"Remaining data in serial buffer: " +
|
|
2246
2247
|
hexFormatter(this._inputBuffer),
|
|
2247
2248
|
);
|
|
2248
2249
|
}
|
|
2249
2250
|
throw new SlipReadError(
|
|
2250
|
-
"Invalid SLIP escape (0xdb, " + toHex(
|
|
2251
|
+
"Invalid SLIP escape (0xdb, " + toHex(byte) + ")",
|
|
2251
2252
|
);
|
|
2252
2253
|
}
|
|
2253
|
-
} else if (
|
|
2254
|
+
} else if (byte == this.SLIP_ESC) {
|
|
2254
2255
|
// start of escape sequence
|
|
2255
2256
|
inEscape = true;
|
|
2256
|
-
} else if (
|
|
2257
|
+
} else if (byte == this.SLIP_END) {
|
|
2257
2258
|
// end of packet
|
|
2258
2259
|
if (this.debug)
|
|
2259
2260
|
this.logger.debug(
|
|
@@ -2264,7 +2265,7 @@ export class ESPLoader extends EventTarget {
|
|
|
2264
2265
|
return partialPacket;
|
|
2265
2266
|
} else {
|
|
2266
2267
|
// normal byte in packet
|
|
2267
|
-
partialPacket.push(
|
|
2268
|
+
partialPacket.push(byte);
|
|
2268
2269
|
}
|
|
2269
2270
|
}
|
|
2270
2271
|
}
|
|
@@ -2341,9 +2342,9 @@ export class ESPLoader extends EventTarget {
|
|
|
2341
2342
|
|
|
2342
2343
|
// Track current baudrate for reconnect
|
|
2343
2344
|
if (this._parent) {
|
|
2344
|
-
this._parent.
|
|
2345
|
+
this._parent.currentBaudRate = baud;
|
|
2345
2346
|
} else {
|
|
2346
|
-
this.
|
|
2347
|
+
this.currentBaudRate = baud;
|
|
2347
2348
|
}
|
|
2348
2349
|
|
|
2349
2350
|
// Warn if baudrate exceeds USB-Serial chip capability
|
|
@@ -2429,8 +2430,8 @@ export class ESPLoader extends EventTarget {
|
|
|
2429
2430
|
// Restart Readloop
|
|
2430
2431
|
this.readLoop();
|
|
2431
2432
|
} catch (e) {
|
|
2432
|
-
this.logger.error(`Reconfigure port error: ${e}`);
|
|
2433
|
-
throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
|
|
2433
|
+
// this.logger.error(`Reconfigure port error: ${e}`);
|
|
2434
|
+
// throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
|
|
2434
2435
|
} finally {
|
|
2435
2436
|
// Always reset flag, even on error or early return
|
|
2436
2437
|
this._isReconfiguring = false;
|
|
@@ -3138,20 +3139,6 @@ export class ESPLoader extends EventTarget {
|
|
|
3138
3139
|
}
|
|
3139
3140
|
}
|
|
3140
3141
|
|
|
3141
|
-
private get _currentBaudRate(): number {
|
|
3142
|
-
return this._parent
|
|
3143
|
-
? this._parent._currentBaudRate
|
|
3144
|
-
: this.__currentBaudRate;
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
private set _currentBaudRate(value: number) {
|
|
3148
|
-
if (this._parent) {
|
|
3149
|
-
this._parent._currentBaudRate = value;
|
|
3150
|
-
} else {
|
|
3151
|
-
this.__currentBaudRate = value;
|
|
3152
|
-
}
|
|
3153
|
-
}
|
|
3154
|
-
|
|
3155
3142
|
async writeToStream(data: number[]) {
|
|
3156
3143
|
if (!this.port.writable) {
|
|
3157
3144
|
this.logger.debug("Port writable stream not available, skipping write");
|
|
@@ -3231,7 +3218,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3231
3218
|
return;
|
|
3232
3219
|
}
|
|
3233
3220
|
if (!this.port.writable) {
|
|
3234
|
-
this.logger.debug("Port already closed, skipping disconnect");
|
|
3221
|
+
// this.logger.debug("Port already closed, skipping disconnect");
|
|
3235
3222
|
return;
|
|
3236
3223
|
}
|
|
3237
3224
|
|
|
@@ -3239,7 +3226,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3239
3226
|
try {
|
|
3240
3227
|
await this._writeChain;
|
|
3241
3228
|
} catch (err) {
|
|
3242
|
-
this.logger.debug(`Pending write error during disconnect: ${err}`);
|
|
3229
|
+
// this.logger.debug(`Pending write error during disconnect: ${err}`);
|
|
3243
3230
|
}
|
|
3244
3231
|
|
|
3245
3232
|
// Release persistent writer before closing
|
|
@@ -3248,7 +3235,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3248
3235
|
await this._writer.close();
|
|
3249
3236
|
this._writer.releaseLock();
|
|
3250
3237
|
} catch (err) {
|
|
3251
|
-
this.logger.debug(`Writer close/release error: ${err}`);
|
|
3238
|
+
// this.logger.debug(`Writer close/release error: ${err}`);
|
|
3252
3239
|
}
|
|
3253
3240
|
this._writer = undefined;
|
|
3254
3241
|
} else {
|
|
@@ -3259,7 +3246,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3259
3246
|
await writer.close();
|
|
3260
3247
|
writer.releaseLock();
|
|
3261
3248
|
} catch (err) {
|
|
3262
|
-
this.logger.debug(`Direct writer close error: ${err}`);
|
|
3249
|
+
// this.logger.debug(`Direct writer close error: ${err}`);
|
|
3263
3250
|
}
|
|
3264
3251
|
}
|
|
3265
3252
|
|
|
@@ -3288,7 +3275,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3288
3275
|
try {
|
|
3289
3276
|
this._reader.cancel();
|
|
3290
3277
|
} catch (err) {
|
|
3291
|
-
this.logger.debug(`Reader cancel error: ${err}`);
|
|
3278
|
+
// this.logger.debug(`Reader cancel error: ${err}`);
|
|
3292
3279
|
// Reader already released, resolve immediately
|
|
3293
3280
|
clearTimeout(timeout);
|
|
3294
3281
|
resolve(undefined);
|
|
@@ -3328,7 +3315,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3328
3315
|
try {
|
|
3329
3316
|
await this._writeChain;
|
|
3330
3317
|
} catch (err) {
|
|
3331
|
-
this.logger.debug(`Pending write error during release: ${err}`);
|
|
3318
|
+
// this.logger.debug(`Pending write error during release: ${err}`);
|
|
3332
3319
|
}
|
|
3333
3320
|
|
|
3334
3321
|
// Release writer
|
|
@@ -3442,6 +3429,8 @@ export class ESPLoader extends EventTarget {
|
|
|
3442
3429
|
isUsbJtag = this.isUsbJtagOrOtg;
|
|
3443
3430
|
}
|
|
3444
3431
|
|
|
3432
|
+
// Release reader/writer so console can create new ones
|
|
3433
|
+
// This is needed for Desktop (Web Serial) to unlock streams
|
|
3445
3434
|
if (isUsbJtag) {
|
|
3446
3435
|
// USB-JTAG/OTG devices: Use watchdog reset which closes port
|
|
3447
3436
|
const wasReset = await this._resetToFirmwareIfNeeded();
|
|
@@ -3463,6 +3452,18 @@ export class ESPLoader extends EventTarget {
|
|
|
3463
3452
|
this.logger.debug(`Could not reset device: ${err}`);
|
|
3464
3453
|
}
|
|
3465
3454
|
|
|
3455
|
+
// For WebUSB (Android), recreate streams after hardware reset
|
|
3456
|
+
if (this.isWebUSB()) {
|
|
3457
|
+
try {
|
|
3458
|
+
// Use the public recreateStreams() method to safely recreate streams
|
|
3459
|
+
// without closing the port (important after hardware reset)
|
|
3460
|
+
await (this.port as any).recreateStreams();
|
|
3461
|
+
this.logger.debug("WebUSB streams recreated for console mode");
|
|
3462
|
+
} catch (err) {
|
|
3463
|
+
this.logger.debug(`Failed to recreate WebUSB streams: ${err}`);
|
|
3464
|
+
}
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3466
3467
|
return false; // Port stays open
|
|
3467
3468
|
}
|
|
3468
3469
|
}
|
|
@@ -3488,7 +3489,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3488
3489
|
: "USB-JTAG/Serial";
|
|
3489
3490
|
|
|
3490
3491
|
this.logger.log(
|
|
3491
|
-
`Resetting ${this.
|
|
3492
|
+
`Resetting ${this.chipName || "device"} (${resetMethod}) to boot into firmware...`,
|
|
3492
3493
|
);
|
|
3493
3494
|
|
|
3494
3495
|
// Set console mode flag before reset to prevent subsequent hardReset calls
|
|
@@ -3567,7 +3568,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3567
3568
|
|
|
3568
3569
|
try {
|
|
3569
3570
|
this.logger.log("Reconnecting serial port...");
|
|
3570
|
-
const savedBaudRate = this.
|
|
3571
|
+
const savedBaudRate = this.currentBaudRate;
|
|
3571
3572
|
|
|
3572
3573
|
this.connected = false;
|
|
3573
3574
|
this.__inputBuffer = [];
|
|
@@ -3616,7 +3617,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3616
3617
|
try {
|
|
3617
3618
|
await this.port.open({ baudRate: ESP_ROM_BAUD });
|
|
3618
3619
|
this.connected = true;
|
|
3619
|
-
this.
|
|
3620
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
3620
3621
|
} catch (err) {
|
|
3621
3622
|
throw new Error(`Failed to open port: ${err}`);
|
|
3622
3623
|
}
|
|
@@ -3758,7 +3759,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3758
3759
|
try {
|
|
3759
3760
|
await this.port.open({ baudRate: ESP_ROM_BAUD });
|
|
3760
3761
|
this.connected = true;
|
|
3761
|
-
this.
|
|
3762
|
+
this.currentBaudRate = ESP_ROM_BAUD;
|
|
3762
3763
|
} catch (err) {
|
|
3763
3764
|
throw new Error(`Failed to open port: ${err}`);
|
|
3764
3765
|
}
|
|
@@ -3805,6 +3806,115 @@ export class ESPLoader extends EventTarget {
|
|
|
3805
3806
|
}
|
|
3806
3807
|
}
|
|
3807
3808
|
|
|
3809
|
+
/**
|
|
3810
|
+
* @name exitConsoleMode
|
|
3811
|
+
* Exit console mode and return to bootloader
|
|
3812
|
+
* For ESP32-S2, uses reconnectToBootloader which will trigger port change
|
|
3813
|
+
* @returns true if manual reconnection is needed (ESP32-S2), false otherwise
|
|
3814
|
+
*/
|
|
3815
|
+
async exitConsoleMode(): Promise<boolean> {
|
|
3816
|
+
if (this._parent) {
|
|
3817
|
+
return await this._parent.exitConsoleMode();
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
// Clear console mode flag
|
|
3821
|
+
this._consoleMode = false;
|
|
3822
|
+
|
|
3823
|
+
// Check if this is ESP32-S2 with USB-JTAG/OTG
|
|
3824
|
+
const isESP32S2 = this.chipFamily === CHIP_FAMILY_ESP32S2;
|
|
3825
|
+
|
|
3826
|
+
// For ESP32-S2: if _isUsbJtagOrOtg is undefined, try to detect it
|
|
3827
|
+
// If detection fails or is undefined, assume USB-JTAG/OTG (conservative/safe path)
|
|
3828
|
+
let isUsbJtagOrOtg = this._isUsbJtagOrOtg;
|
|
3829
|
+
if (isESP32S2 && isUsbJtagOrOtg === undefined) {
|
|
3830
|
+
try {
|
|
3831
|
+
isUsbJtagOrOtg = await this.detectUsbConnectionType();
|
|
3832
|
+
} catch (err) {
|
|
3833
|
+
this.logger.debug(
|
|
3834
|
+
`USB detection failed, assuming USB-JTAG/OTG for ESP32-S2: ${err}`,
|
|
3835
|
+
);
|
|
3836
|
+
isUsbJtagOrOtg = true; // Conservative fallback for ESP32-S2
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
if (isESP32S2 && isUsbJtagOrOtg) {
|
|
3841
|
+
// ESP32-S2 USB: Use reconnectToBootloader which handles the mode switch
|
|
3842
|
+
// This will close the port and the device will reboot to bootloader
|
|
3843
|
+
this.logger.log("ESP32-S2 USB detected - reconnecting to bootloader");
|
|
3844
|
+
|
|
3845
|
+
try {
|
|
3846
|
+
await this.reconnectToBootloader();
|
|
3847
|
+
} catch (err) {
|
|
3848
|
+
this.logger.debug(`Reconnect error (expected for ESP32-S2): ${err}`);
|
|
3849
|
+
}
|
|
3850
|
+
|
|
3851
|
+
// For ESP32-S2, port will change, so return true to indicate manual reconnection needed
|
|
3852
|
+
return true;
|
|
3853
|
+
}
|
|
3854
|
+
|
|
3855
|
+
// For other devices, use standard reconnectToBootloader
|
|
3856
|
+
await this.reconnectToBootloader();
|
|
3857
|
+
return false; // No manual reconnection needed
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3860
|
+
/**
|
|
3861
|
+
* @name isConsoleResetSupported
|
|
3862
|
+
* Check if console reset is supported for this device
|
|
3863
|
+
* ESP32-S2 USB-JTAG/CDC does not support reset in console mode
|
|
3864
|
+
* because any reset causes USB port to be lost (hardware limitation)
|
|
3865
|
+
*/
|
|
3866
|
+
isConsoleResetSupported(): boolean {
|
|
3867
|
+
if (this._parent) {
|
|
3868
|
+
return this._parent.isConsoleResetSupported();
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3871
|
+
// For ESP32-S2: if _isUsbJtagOrOtg is undefined, assume USB-JTAG/OTG (conservative)
|
|
3872
|
+
// This means console reset is NOT supported (safer default)
|
|
3873
|
+
const isS2UsbJtag =
|
|
3874
|
+
this.chipFamily === CHIP_FAMILY_ESP32S2 &&
|
|
3875
|
+
(this._isUsbJtagOrOtg === true || this._isUsbJtagOrOtg === undefined);
|
|
3876
|
+
return !isS2UsbJtag; // Not supported for ESP32-S2 USB-JTAG/CDC
|
|
3877
|
+
}
|
|
3878
|
+
|
|
3879
|
+
/**
|
|
3880
|
+
* @name resetInConsoleMode
|
|
3881
|
+
* Reset device while in console mode (firmware mode)
|
|
3882
|
+
*
|
|
3883
|
+
* NOTE: For ESP32-S2 USB-JTAG/CDC, ANY reset (hardware or software) causes
|
|
3884
|
+
* the USB port to be lost because the device switches USB modes during reset.
|
|
3885
|
+
* This is a hardware limitation - use isConsoleResetSupported() to check first.
|
|
3886
|
+
*/
|
|
3887
|
+
async resetInConsoleMode(): Promise<void> {
|
|
3888
|
+
if (this._parent) {
|
|
3889
|
+
return await this._parent.resetInConsoleMode();
|
|
3890
|
+
}
|
|
3891
|
+
|
|
3892
|
+
if (!this.isConsoleResetSupported()) {
|
|
3893
|
+
this.logger.debug(
|
|
3894
|
+
"Console reset not supported for ESP32-S2 USB-JTAG/CDC",
|
|
3895
|
+
);
|
|
3896
|
+
return; // Do nothing
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
// For other devices: Use standard firmware reset
|
|
3900
|
+
const isWebUSB = (this.port as any).isWebUSB === true;
|
|
3901
|
+
|
|
3902
|
+
try {
|
|
3903
|
+
this.logger.debug("Resetting device in console mode");
|
|
3904
|
+
|
|
3905
|
+
if (isWebUSB) {
|
|
3906
|
+
await this.hardResetToFirmwareWebUSB();
|
|
3907
|
+
} else {
|
|
3908
|
+
await this.hardResetToFirmware();
|
|
3909
|
+
}
|
|
3910
|
+
|
|
3911
|
+
this.logger.debug("Device reset complete");
|
|
3912
|
+
} catch (err) {
|
|
3913
|
+
this.logger.error(`Reset failed: ${err}`);
|
|
3914
|
+
throw err;
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3808
3918
|
/**
|
|
3809
3919
|
* @name drainInputBuffer
|
|
3810
3920
|
* Actively drain the input buffer by reading data for a specified time.
|
|
@@ -3820,7 +3930,7 @@ export class ESPLoader extends EventTarget {
|
|
|
3820
3930
|
await sleep(bufferingTime);
|
|
3821
3931
|
|
|
3822
3932
|
// Unsupported command response is sent 8 times and has
|
|
3823
|
-
// 14 bytes length including delimiter 0xC0 bytes.
|
|
3933
|
+
// 14 bytes length including delimiter SLIP_END (0xC0) bytes.
|
|
3824
3934
|
// At least part of it is read as a command response,
|
|
3825
3935
|
// but to be safe, read it all.
|
|
3826
3936
|
const bytesToDrain = 14 * 8;
|
|
@@ -4040,7 +4150,7 @@ export class ESPLoader extends EventTarget {
|
|
|
4040
4150
|
// The stub expects 4 bytes (ACK), if we send less it will break out
|
|
4041
4151
|
try {
|
|
4042
4152
|
// Send SLIP frame with no data (just delimiters)
|
|
4043
|
-
const abortFrame = [
|
|
4153
|
+
const abortFrame = [this.SLIP_END, this.SLIP_END]; // Empty SLIP frame
|
|
4044
4154
|
await this.writeToStream(abortFrame);
|
|
4045
4155
|
this.logger.debug(`Sent abort frame to stub`);
|
|
4046
4156
|
|
package/src/index.ts
CHANGED
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));
|