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/js/script.js
CHANGED
|
@@ -8,6 +8,16 @@ if (!globalThis.requestSerialPort) {
|
|
|
8
8
|
globalThis.requestSerialPort = requestSerialPort;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
// Utility functions imported from esptool module
|
|
12
|
+
let toHex, formatMacAddr, sleep;
|
|
13
|
+
|
|
14
|
+
// Load utilities from esptool package
|
|
15
|
+
window.esptoolPackage.then((esptoolMod) => {
|
|
16
|
+
toHex = esptoolMod.toHex;
|
|
17
|
+
formatMacAddr = esptoolMod.formatMacAddr;
|
|
18
|
+
sleep = esptoolMod.sleep;
|
|
19
|
+
});
|
|
20
|
+
|
|
11
21
|
let espStub;
|
|
12
22
|
let esp32s2ReconnectInProgress = false;
|
|
13
23
|
let currentLittleFS = null;
|
|
@@ -18,6 +28,7 @@ let currentFilesystemType = null; // 'littlefs', 'fatfs', or 'spiffs'
|
|
|
18
28
|
let littlefsModulePromise = null; // Cache for LittleFS WASM module
|
|
19
29
|
let lastReadFlashData = null; // Store last read flash data for ESP8266
|
|
20
30
|
let currentChipName = null; // Store chip name globally
|
|
31
|
+
let currentMacAddr = null; // Store MAC address globally
|
|
21
32
|
let isConnected = false; // Track connection state
|
|
22
33
|
let consoleInstance = null; // ESP32ToolConsole instance
|
|
23
34
|
let baudRateBeforeConsole = null; // Store baudrate before opening console
|
|
@@ -63,6 +74,7 @@ function clearAllCachedData() {
|
|
|
63
74
|
currentFilesystemType = null;
|
|
64
75
|
lastReadFlashData = null;
|
|
65
76
|
currentChipName = null;
|
|
77
|
+
currentMacAddr = null;
|
|
66
78
|
|
|
67
79
|
// Hide filesystem manager
|
|
68
80
|
littlefsManager.classList.add('hidden');
|
|
@@ -111,8 +123,8 @@ const chunkSizes = [
|
|
|
111
123
|
{ label: "8 KB", value: 0x2000 },
|
|
112
124
|
{ label: "16 KB (WebUSB)", value: 0x4000 },
|
|
113
125
|
{ label: "64 KB", value: 0x10000 },
|
|
114
|
-
{ label: "128 KB", value: 0x20000 },
|
|
115
|
-
{ label: "256 KB
|
|
126
|
+
{ label: "128 KB (Desktop)", value: 0x20000 },
|
|
127
|
+
{ label: "256 KB", value: 0x40000 }
|
|
116
128
|
];
|
|
117
129
|
|
|
118
130
|
// blockSize: Size of each data block sent by ESP (in bytes)
|
|
@@ -128,8 +140,7 @@ const blockSizes = [
|
|
|
128
140
|
{ label: "1024 B", value: 1024 },
|
|
129
141
|
{ label: "1984 B", value: 1984 },
|
|
130
142
|
{ label: "2024 B", value: 2024 },
|
|
131
|
-
{ label: "3968 B (Desktop)", value: 3968 }
|
|
132
|
-
{ label: "4096 B (Maximum)", value: 4096 }
|
|
143
|
+
{ label: "3968 B (Desktop)", value: 3968 }
|
|
133
144
|
];
|
|
134
145
|
|
|
135
146
|
// maxInFlight: Maximum unacknowledged bytes (in bytes)
|
|
@@ -147,15 +158,13 @@ const maxInFlights = [
|
|
|
147
158
|
{ label: "4096 B", value: 4096 },
|
|
148
159
|
{ label: "7936 B", value: 7936 },
|
|
149
160
|
{ label: "8192 B", value: 8192 },
|
|
150
|
-
{ label: "15872 B", value: 15872 },
|
|
161
|
+
{ label: "15872 B (Desktop)", value: 15872 },
|
|
151
162
|
{ label: "31744 B", value: 31744 },
|
|
152
|
-
{ label: "63488 B", value: 63488 }
|
|
163
|
+
{ label: "63488 B", value: 63488 },
|
|
164
|
+
{ label: "126976 B", value: 126976 },
|
|
165
|
+
{ label: "253952 B", value: 253952 }
|
|
153
166
|
];
|
|
154
167
|
|
|
155
|
-
const bufferSize = 512;
|
|
156
|
-
const colors = ["#00a7e9", "#f89521", "#be1e2d"];
|
|
157
|
-
const measurementPeriodId = "0001";
|
|
158
|
-
|
|
159
168
|
// Check if running in Electron
|
|
160
169
|
const isElectron = window.electronAPI && window.electronAPI.isElectron;
|
|
161
170
|
|
|
@@ -238,6 +247,46 @@ function isMobileDevice() {
|
|
|
238
247
|
return isMobileUA || (hasTouch && isSmallScreen);
|
|
239
248
|
}
|
|
240
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Detect if we're using WebUSB (mobile/Android) or Web Serial (desktop)
|
|
252
|
+
* WebUSB is typically used on Android devices
|
|
253
|
+
* Web Serial is used on desktop browsers
|
|
254
|
+
*/
|
|
255
|
+
function isUsingWebUSB() {
|
|
256
|
+
// If we have an active connection, check the port's isWebUSB property
|
|
257
|
+
if (espStub && espStub.port && typeof espStub.port.isWebUSB !== 'undefined') {
|
|
258
|
+
return espStub.port.isWebUSB === true;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Fallback: Check if we're on a mobile device (likely using WebUSB)
|
|
262
|
+
if (isMobileDevice()) {
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Check if Web Serial is NOT available but USB is (WebUSB only)
|
|
267
|
+
if (!("serial" in navigator) && "usb" in navigator) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Default to Web Serial (desktop)
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get default advanced parameters based on environment
|
|
277
|
+
* Desktop (Web Serial): Higher values for better performance
|
|
278
|
+
* Mobile/WebUSB: Lower values for compatibility
|
|
279
|
+
*/
|
|
280
|
+
function getDefaultAdvancedParams() {
|
|
281
|
+
const isWebUSB = isUsingWebUSB();
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
chunkSize: isWebUSB ? 0x4000 : 0x20000, // 16 KB for WebUSB, 128 KB for Desktop
|
|
285
|
+
blockSize: isWebUSB ? 248 : 3968, // 248 B for WebUSB, 3968 B for Desktop
|
|
286
|
+
maxInFlight: isWebUSB ? 248 : 15872 // 248 B for WebUSB, 15872 B for Desktop
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
241
290
|
// Update mobile classes and padding
|
|
242
291
|
function updateMobileClasses() {
|
|
243
292
|
const isMobile = isMobileDevice();
|
|
@@ -332,39 +381,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
332
381
|
window.addEventListener("error", function (event) {
|
|
333
382
|
console.log("Got an uncaught error: ", event.error);
|
|
334
383
|
});
|
|
335
|
-
|
|
336
|
-
// Header auto-hide functionality - DISABLED
|
|
337
|
-
const header = document.querySelector(".header");
|
|
338
|
-
const main = document.querySelector(".main");
|
|
339
|
-
|
|
340
|
-
/* DISABLED: Auto-hide header
|
|
341
|
-
// Show header on mouse enter at top of page
|
|
342
|
-
main.addEventListener("mousemove", (e) => {
|
|
343
|
-
if (e.clientY < 5 && header.classList.contains("header-hidden")) {
|
|
344
|
-
header.classList.remove("header-hidden");
|
|
345
|
-
main.classList.remove("no-header-padding");
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// Keep header visible when mouse is over it
|
|
350
|
-
header.addEventListener("mouseenter", () => {
|
|
351
|
-
header.classList.remove("header-hidden");
|
|
352
|
-
main.classList.remove("no-header-padding");
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
// Hide header when mouse leaves (only if connected)
|
|
356
|
-
header.addEventListener("mouseleave", () => {
|
|
357
|
-
if (espStub && header.classList.contains("header-hidden") === false) {
|
|
358
|
-
setTimeout(() => {
|
|
359
|
-
if (!header.matches(":hover")) {
|
|
360
|
-
header.classList.add("header-hidden");
|
|
361
|
-
main.classList.add("no-header-padding");
|
|
362
|
-
}
|
|
363
|
-
}, 1000);
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
*/
|
|
367
|
-
|
|
384
|
+
|
|
368
385
|
// Check for Web Serial or WebUSB support
|
|
369
386
|
if ("serial" in navigator || "usb" in navigator) {
|
|
370
387
|
const notSupported = document.getElementById("notSupported");
|
|
@@ -391,6 +408,9 @@ function initBaudRate() {
|
|
|
391
408
|
}
|
|
392
409
|
|
|
393
410
|
function initAdvancedParams() {
|
|
411
|
+
// Get default values based on environment (Desktop vs WebUSB)
|
|
412
|
+
const defaults = getDefaultAdvancedParams();
|
|
413
|
+
|
|
394
414
|
// Initialize chunkSize dropdown
|
|
395
415
|
for (let item of chunkSizes) {
|
|
396
416
|
const option = document.createElement("option");
|
|
@@ -398,8 +418,8 @@ function initAdvancedParams() {
|
|
|
398
418
|
option.value = item.value;
|
|
399
419
|
chunkSizeSelect.add(option);
|
|
400
420
|
}
|
|
401
|
-
// Set default: 16 KB for WebUSB,
|
|
402
|
-
chunkSizeSelect.value =
|
|
421
|
+
// Set default: 16 KB for WebUSB, 128 KB for Desktop
|
|
422
|
+
chunkSizeSelect.value = defaults.chunkSize;
|
|
403
423
|
|
|
404
424
|
// Initialize blockSize dropdown
|
|
405
425
|
for (let item of blockSizes) {
|
|
@@ -408,8 +428,8 @@ function initAdvancedParams() {
|
|
|
408
428
|
option.value = item.value;
|
|
409
429
|
blockSizeSelect.add(option);
|
|
410
430
|
}
|
|
411
|
-
// Set default:
|
|
412
|
-
blockSizeSelect.value =
|
|
431
|
+
// Set default: 248 B for WebUSB, 3968 B for Desktop
|
|
432
|
+
blockSizeSelect.value = defaults.blockSize;
|
|
413
433
|
|
|
414
434
|
// Initialize maxInFlight dropdown
|
|
415
435
|
for (let item of maxInFlights) {
|
|
@@ -418,8 +438,51 @@ function initAdvancedParams() {
|
|
|
418
438
|
option.value = item.value;
|
|
419
439
|
maxInFlightSelect.add(option);
|
|
420
440
|
}
|
|
421
|
-
// Set default:
|
|
422
|
-
maxInFlightSelect.value =
|
|
441
|
+
// Set default: 248 B for WebUSB, 15872 B for Desktop
|
|
442
|
+
maxInFlightSelect.value = defaults.maxInFlight;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Update advanced parameters after connection based on actual port type
|
|
447
|
+
* This ensures we use optimal values for WebUSB vs Web Serial
|
|
448
|
+
*/
|
|
449
|
+
function updateAdvancedParamsForConnection() {
|
|
450
|
+
// Get the correct defaults based on actual connection
|
|
451
|
+
const defaults = getDefaultAdvancedParams();
|
|
452
|
+
|
|
453
|
+
// Get current values
|
|
454
|
+
const currentChunkSize = parseInt(chunkSizeSelect.value);
|
|
455
|
+
const currentBlockSize = parseInt(blockSizeSelect.value);
|
|
456
|
+
const currentMaxInFlight = parseInt(maxInFlightSelect.value);
|
|
457
|
+
|
|
458
|
+
// Check if values are at old defaults (need updating)
|
|
459
|
+
const oldWebUSBDefaults = { chunkSize: 0x4000, blockSize: 248, maxInFlight: 248 };
|
|
460
|
+
const oldDesktopDefaults = { chunkSize: 0x40000, blockSize: 3968, maxInFlight: 15872 };
|
|
461
|
+
|
|
462
|
+
const isAtWebUSBDefaults =
|
|
463
|
+
currentChunkSize === oldWebUSBDefaults.chunkSize &&
|
|
464
|
+
currentBlockSize === oldWebUSBDefaults.blockSize &&
|
|
465
|
+
currentMaxInFlight === oldWebUSBDefaults.maxInFlight;
|
|
466
|
+
|
|
467
|
+
const isAtDesktopDefaults =
|
|
468
|
+
currentChunkSize === oldDesktopDefaults.chunkSize &&
|
|
469
|
+
currentBlockSize === oldDesktopDefaults.blockSize &&
|
|
470
|
+
currentMaxInFlight === oldDesktopDefaults.maxInFlight;
|
|
471
|
+
|
|
472
|
+
// Only update if at defaults (user hasn't customized)
|
|
473
|
+
if (isAtWebUSBDefaults || isAtDesktopDefaults) {
|
|
474
|
+
chunkSizeSelect.value = defaults.chunkSize;
|
|
475
|
+
blockSizeSelect.value = defaults.blockSize;
|
|
476
|
+
maxInFlightSelect.value = defaults.maxInFlight;
|
|
477
|
+
|
|
478
|
+
// Save the new values
|
|
479
|
+
saveSetting("chunkSize", defaults.chunkSize);
|
|
480
|
+
saveSetting("blockSize", defaults.blockSize);
|
|
481
|
+
saveSetting("maxInFlight", defaults.maxInFlight);
|
|
482
|
+
|
|
483
|
+
const connectionType = isUsingWebUSB() ? "WebUSB" : "Web Serial";
|
|
484
|
+
debugMsg(`Advanced parameters updated for ${connectionType} connection`);
|
|
485
|
+
}
|
|
423
486
|
}
|
|
424
487
|
|
|
425
488
|
function logMsg(text) {
|
|
@@ -500,16 +563,6 @@ function enableStyleSheet(node, enabled) {
|
|
|
500
563
|
node.disabled = !enabled;
|
|
501
564
|
}
|
|
502
565
|
|
|
503
|
-
function formatMacAddr(macAddr) {
|
|
504
|
-
return macAddr
|
|
505
|
-
.map((value) => value.toString(16).toUpperCase().padStart(2, "0"))
|
|
506
|
-
.join(":");
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function toHex(value) {
|
|
510
|
-
return "0x" + value.toString(16).padStart(2, "0");
|
|
511
|
-
}
|
|
512
|
-
|
|
513
566
|
/**
|
|
514
567
|
* Parse flash size string (e.g., "256KB", "4MB") to bytes
|
|
515
568
|
* @param {string} sizeStr - Flash size string with unit (KB or MB)
|
|
@@ -609,13 +662,9 @@ async function clickConnect() {
|
|
|
609
662
|
});
|
|
610
663
|
}
|
|
611
664
|
|
|
612
|
-
// Store port info for ESP32-S2 detection
|
|
613
|
-
let portInfo = esploader.port?.getInfo ? esploader.port.getInfo() : {};
|
|
614
|
-
let isESP32S2 = portInfo.usbVendorId === 0x303a && portInfo.usbProductId === 0x0002;
|
|
615
|
-
|
|
616
665
|
// Handle ESP32-S2 Native USB reconnection requirement for BROWSER
|
|
617
|
-
// Only add listener if not already in reconnect mode
|
|
618
|
-
if (!esp32s2ReconnectInProgress
|
|
666
|
+
// Only add listener if not already in reconnect mode
|
|
667
|
+
if (!esp32s2ReconnectInProgress) {
|
|
619
668
|
esploader.addEventListener("esp32s2-usb-reconnect", async () => {
|
|
620
669
|
// Prevent recursive calls
|
|
621
670
|
if (esp32s2ReconnectInProgress) {
|
|
@@ -625,57 +674,47 @@ async function clickConnect() {
|
|
|
625
674
|
esp32s2ReconnectInProgress = true;
|
|
626
675
|
logMsg("ESP32-S2 Native USB detected!");
|
|
627
676
|
toggleUIConnected(false);
|
|
677
|
+
const previousStubPort = espStub?.port;
|
|
628
678
|
espStub = undefined;
|
|
629
679
|
|
|
630
680
|
try {
|
|
631
681
|
// Close the port first
|
|
632
682
|
await esploader.port.close();
|
|
633
|
-
|
|
634
|
-
//
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
logMsg("ESP32-S2 has switched to CDC mode");
|
|
638
|
-
logMsg("Please press and HOLD the BOOT button on your ESP32-S2, then click Connect");
|
|
639
|
-
esp32s2ReconnectInProgress = false;
|
|
640
|
-
return;
|
|
641
|
-
}
|
|
642
|
-
// For Desktop Web Serial: Use the modal dialog approach
|
|
643
|
-
if (!isAndroid && esploader.port.forget) {
|
|
644
|
-
await esploader.port.forget();
|
|
683
|
+
|
|
684
|
+
// Use the modal dialog approach
|
|
685
|
+
if (previousStubPort && previousStubPort.readable) {
|
|
686
|
+
await previousStubPort.close();
|
|
645
687
|
}
|
|
646
|
-
} catch (
|
|
647
|
-
// Ignore
|
|
648
|
-
debugMsg(
|
|
688
|
+
} catch (closeErr) {
|
|
689
|
+
// Ignore port close errors
|
|
690
|
+
debugMsg(`Port close error (ignored): ${closeErr.message}`);
|
|
649
691
|
}
|
|
650
692
|
|
|
651
|
-
// Show modal dialog
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const reconnectBtn = document.getElementById("butReconnectS2");
|
|
693
|
+
// Show modal dialog
|
|
694
|
+
const modal = document.getElementById("esp32s2Modal");
|
|
695
|
+
const reconnectBtn = document.getElementById("butReconnectS2");
|
|
655
696
|
|
|
656
|
-
|
|
697
|
+
modal.classList.remove("hidden");
|
|
657
698
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
699
|
+
// Handle reconnect button click
|
|
700
|
+
const handleReconnect = async () => {
|
|
701
|
+
modal.classList.add("hidden");
|
|
702
|
+
reconnectBtn.removeEventListener("click", handleReconnect);
|
|
662
703
|
|
|
663
|
-
|
|
704
|
+
logMsg("Requesting new device selection...");
|
|
664
705
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
reconnectBtn.addEventListener("click", handleReconnect);
|
|
678
|
-
}
|
|
706
|
+
// Trigger port selection
|
|
707
|
+
try {
|
|
708
|
+
await clickConnect();
|
|
709
|
+
// Reset flag on successful connection
|
|
710
|
+
esp32s2ReconnectInProgress = false;
|
|
711
|
+
} catch (err) {
|
|
712
|
+
errorMsg("Failed to reconnect: " + err);
|
|
713
|
+
// Reset flag on error so user can try again
|
|
714
|
+
esp32s2ReconnectInProgress = false;
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
reconnectBtn.addEventListener("click", handleReconnect);
|
|
679
718
|
});
|
|
680
719
|
}
|
|
681
720
|
|
|
@@ -702,9 +741,14 @@ async function clickConnect() {
|
|
|
702
741
|
|
|
703
742
|
// Store chip info globally
|
|
704
743
|
currentChipName = esploader.chipName;
|
|
744
|
+
currentMacAddr = formatMacAddr(esploader.macAddr());
|
|
705
745
|
|
|
706
746
|
espStub = await esploader.runStub();
|
|
707
747
|
|
|
748
|
+
// Update advanced parameters based on actual connection type (WebUSB vs Web Serial)
|
|
749
|
+
// Only update if user hasn't manually changed them (still at defaults)
|
|
750
|
+
updateAdvancedParamsForConnection();
|
|
751
|
+
|
|
708
752
|
toggleUIConnected(true);
|
|
709
753
|
toggleUIToolbar(true);
|
|
710
754
|
|
|
@@ -760,11 +804,6 @@ async function clickConnect() {
|
|
|
760
804
|
async function changeBaudRate() {
|
|
761
805
|
saveSetting("baudrate", baudRateSelect.value);
|
|
762
806
|
if (espStub) {
|
|
763
|
-
// Skip for ESP8266 as it only supports 115200 baud in stub mode
|
|
764
|
-
if (espStub.chipName === "ESP8266") {
|
|
765
|
-
logMsg("ESP8266 stub only supports 115200 baud");
|
|
766
|
-
return;
|
|
767
|
-
}
|
|
768
807
|
let baud = parseInt(baudRateSelect.value);
|
|
769
808
|
if (baudRates.includes(baud)) {
|
|
770
809
|
await espStub.setBaudrate(baud);
|
|
@@ -807,6 +846,36 @@ async function clickShowLog() {
|
|
|
807
846
|
updateLogVisibility();
|
|
808
847
|
}
|
|
809
848
|
|
|
849
|
+
/**
|
|
850
|
+
* @name openConsolePortAndInit
|
|
851
|
+
* Helper to open port for console and initialize console UI
|
|
852
|
+
* Avoids code duplication across different console init flows
|
|
853
|
+
*/
|
|
854
|
+
async function openConsolePortAndInit(newPort) {
|
|
855
|
+
// Open the port at 115200 for console
|
|
856
|
+
await newPort.open({ baudRate: 115200 });
|
|
857
|
+
espStub.port = newPort;
|
|
858
|
+
espStub.connected = true;
|
|
859
|
+
|
|
860
|
+
// Keep parent/loader in sync (used by closeConsole)
|
|
861
|
+
if (espStub._parent) {
|
|
862
|
+
espStub._parent.port = newPort;
|
|
863
|
+
}
|
|
864
|
+
if (espLoaderBeforeConsole) {
|
|
865
|
+
espLoaderBeforeConsole.port = newPort;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
debugMsg("Port opened for console at 115200 baud");
|
|
869
|
+
|
|
870
|
+
// Device is already in firmware mode, port is open at 115200
|
|
871
|
+
// Initialize console directly
|
|
872
|
+
consoleSwitch.checked = true;
|
|
873
|
+
saveSetting("console", true);
|
|
874
|
+
|
|
875
|
+
// Initialize console UI and handlers
|
|
876
|
+
await initConsoleUI();
|
|
877
|
+
}
|
|
878
|
+
|
|
810
879
|
/**
|
|
811
880
|
* @name initConsoleUI
|
|
812
881
|
* Initialize console UI, event handlers, and start console instance
|
|
@@ -818,6 +887,9 @@ async function initConsoleUI() {
|
|
|
818
887
|
|
|
819
888
|
// Show console container and hide commands
|
|
820
889
|
consoleContainer.classList.remove("hidden");
|
|
890
|
+
|
|
891
|
+
// Add console-active class to body for mobile styling
|
|
892
|
+
document.body.classList.add("console-active");
|
|
821
893
|
const commands = document.getElementById("commands");
|
|
822
894
|
if (commands) commands.classList.add("hidden");
|
|
823
895
|
|
|
@@ -825,15 +897,29 @@ async function initConsoleUI() {
|
|
|
825
897
|
consoleInstance = new ESP32ToolConsole(espStub.port, consoleContainer, true);
|
|
826
898
|
await consoleInstance.init();
|
|
827
899
|
|
|
900
|
+
// Check if console reset is supported and hide button if not
|
|
901
|
+
if (espLoaderBeforeConsole && typeof espLoaderBeforeConsole.isConsoleResetSupported === 'function') {
|
|
902
|
+
const resetSupported = espLoaderBeforeConsole.isConsoleResetSupported();
|
|
903
|
+
const resetBtn = consoleContainer.querySelector("#console-reset-btn");
|
|
904
|
+
if (resetBtn) {
|
|
905
|
+
if (resetSupported) {
|
|
906
|
+
resetBtn.style.display = "";
|
|
907
|
+
} else {
|
|
908
|
+
resetBtn.style.display = "none";
|
|
909
|
+
debugMsg("Console reset disabled for ESP32-S2 USB-JTAG/CDC (hardware limitation)");
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
828
914
|
// Listen for console reset events
|
|
829
915
|
if (consoleResetHandler) {
|
|
830
916
|
consoleContainer.removeEventListener('console-reset', consoleResetHandler);
|
|
831
917
|
}
|
|
832
918
|
consoleResetHandler = async () => {
|
|
833
|
-
if (
|
|
919
|
+
if (espLoaderBeforeConsole && typeof espLoaderBeforeConsole.resetInConsoleMode === 'function') {
|
|
834
920
|
try {
|
|
835
921
|
debugMsg("Resetting device from console...");
|
|
836
|
-
await
|
|
922
|
+
await espLoaderBeforeConsole.resetInConsoleMode();
|
|
837
923
|
debugMsg("Device reset successful");
|
|
838
924
|
} catch (err) {
|
|
839
925
|
errorMsg("Failed to reset device: " + err.message);
|
|
@@ -867,34 +953,6 @@ async function clickConsole() {
|
|
|
867
953
|
|
|
868
954
|
if (shouldEnable) {
|
|
869
955
|
// After WDT reset, everything is gone - start fresh with port selection
|
|
870
|
-
if (isConnected && espStub && !espStub.connected) {
|
|
871
|
-
// Port was closed after WDT reset - select new port
|
|
872
|
-
try {
|
|
873
|
-
logMsg("Please select the serial port for console mode...");
|
|
874
|
-
const newPort = await navigator.serial.requestPort();
|
|
875
|
-
|
|
876
|
-
// Open the new port at 115200 for console
|
|
877
|
-
await newPort.open({ baudRate: 115200 });
|
|
878
|
-
|
|
879
|
-
// Update espStub to use the new port
|
|
880
|
-
espStub.port = newPort;
|
|
881
|
-
espStub.connected = true;
|
|
882
|
-
if (espStub._parent) {
|
|
883
|
-
espStub._parent.port = newPort;
|
|
884
|
-
}
|
|
885
|
-
if (espLoaderBeforeConsole) {
|
|
886
|
-
espLoaderBeforeConsole.port = newPort;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
logMsg("Port opened for console at 115200 baud");
|
|
890
|
-
} catch (openErr) {
|
|
891
|
-
errorMsg(`Failed to open port for console: ${openErr.message}`);
|
|
892
|
-
consoleSwitch.checked = false;
|
|
893
|
-
saveSetting("console", false);
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
956
|
// Initialize console if connected and not already created
|
|
899
957
|
if (isConnected && espStub && espStub.port && !consoleInstance) {
|
|
900
958
|
try {
|
|
@@ -904,7 +962,6 @@ async function clickConsole() {
|
|
|
904
962
|
const loaderToSave = espStub._parent || espStub;
|
|
905
963
|
const currentBaudrate = loaderToSave.currentBaudRate;
|
|
906
964
|
const currentChipFamily = espStub.chipFamily;
|
|
907
|
-
const currentIsStub = espStub.IS_STUB;
|
|
908
965
|
|
|
909
966
|
// CRITICAL: Save the PARENT loader (not the stub child!)
|
|
910
967
|
espLoaderBeforeConsole = loaderToSave;
|
|
@@ -928,21 +985,29 @@ async function clickConsole() {
|
|
|
928
985
|
// USB-JTAG/OTG device: Port was closed after WDT reset
|
|
929
986
|
debugMsg("Device reset to firmware mode (port closed)");
|
|
930
987
|
|
|
931
|
-
// Wait
|
|
932
|
-
|
|
988
|
+
// Wait for device to boot and USB port to become available
|
|
989
|
+
// Android/WebUSB needs more time than Desktop for USB enumeration
|
|
990
|
+
const isWebUSB = isUsingWebUSB();
|
|
991
|
+
const waitTime = isWebUSB ? 1000 : 500; // 1s for Android, 500ms for Desktop
|
|
992
|
+
debugMsg(`Waiting ${waitTime}ms for device to boot and USB to enumerate...`);
|
|
993
|
+
await sleep(waitTime);
|
|
933
994
|
|
|
934
|
-
// Check if this is ESP32-S2
|
|
995
|
+
// Check if this is ESP32-S2 or if we're on Android (WebUSB)
|
|
996
|
+
// Both need modal for user gesture
|
|
935
997
|
const isS2 = chipFamilyBeforeConsole === 0x3252; // CHIP_FAMILY_ESP32S2 = 0x3252
|
|
998
|
+
const needsModal = isS2 || isWebUSB;
|
|
936
999
|
|
|
937
|
-
if (
|
|
938
|
-
// ESP32-S2
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1000
|
+
if (needsModal) {
|
|
1001
|
+
// ESP32-S2 (all platforms) or Android (all chips): Use modal for user gesture
|
|
1002
|
+
|
|
1003
|
+
// Close old port if still open
|
|
1004
|
+
try {
|
|
1005
|
+
if (espStub.port && espStub.port.readable) {
|
|
1006
|
+
await espStub.port.close();
|
|
1007
|
+
debugMsg("Old port closed");
|
|
945
1008
|
}
|
|
1009
|
+
} catch (closeErr) {
|
|
1010
|
+
debugMsg(`Port close error (ignored): ${closeErr.message}`);
|
|
946
1011
|
}
|
|
947
1012
|
|
|
948
1013
|
// Wait a bit for browser to process
|
|
@@ -956,42 +1021,27 @@ async function clickConsole() {
|
|
|
956
1021
|
const modalTitle = modal.querySelector("h2");
|
|
957
1022
|
const modalText = modal.querySelector("p");
|
|
958
1023
|
if (modalTitle) modalTitle.textContent = "Device has been reset to firmware mode";
|
|
959
|
-
if (modalText)
|
|
1024
|
+
if (modalText) {
|
|
1025
|
+
modalText.textContent = isWebUSB
|
|
1026
|
+
? "Please click the button below to select the USB device for console."
|
|
1027
|
+
: "Please click the button below to select the serial port for console.";
|
|
1028
|
+
}
|
|
960
1029
|
|
|
961
1030
|
modal.classList.remove("hidden");
|
|
962
1031
|
|
|
963
|
-
// Handle reconnect button click
|
|
1032
|
+
// Handle reconnect button click (single-fire to prevent multiple prompts)
|
|
964
1033
|
const handleReconnect = async () => {
|
|
965
1034
|
modal.classList.add("hidden");
|
|
966
|
-
reconnectBtn.removeEventListener("click", handleReconnect);
|
|
967
1035
|
|
|
968
1036
|
try {
|
|
969
1037
|
// Request the NEW port (user gesture from button click)
|
|
970
|
-
debugMsg("Please select the
|
|
971
|
-
const newPort =
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
await newPort.open({ baudRate: 115200 });
|
|
975
|
-
espStub.port = newPort;
|
|
976
|
-
espStub.connected = true;
|
|
977
|
-
|
|
978
|
-
// Keep parent/loader in sync (used by closeConsole)
|
|
979
|
-
if (espStub._parent) {
|
|
980
|
-
espStub._parent.port = newPort;
|
|
981
|
-
}
|
|
982
|
-
if (espLoaderBeforeConsole) {
|
|
983
|
-
espLoaderBeforeConsole.port = newPort;
|
|
984
|
-
}
|
|
1038
|
+
debugMsg("Please select the port for console mode...");
|
|
1039
|
+
const newPort = isWebUSB
|
|
1040
|
+
? await WebUSBSerial.requestPort((...args) => logMsg(...args))
|
|
1041
|
+
: await navigator.serial.requestPort();
|
|
985
1042
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
// Device is already in firmware mode, port is open at 115200
|
|
989
|
-
// Initialize console directly
|
|
990
|
-
consoleSwitch.checked = true;
|
|
991
|
-
saveSetting("console", true);
|
|
992
|
-
|
|
993
|
-
// Initialize console UI and handlers
|
|
994
|
-
await initConsoleUI();
|
|
1043
|
+
// Use helper to open port and initialize console
|
|
1044
|
+
await openConsolePortAndInit(newPort);
|
|
995
1045
|
} catch (err) {
|
|
996
1046
|
errorMsg(`Failed to open port for console: ${err.message}`);
|
|
997
1047
|
consoleSwitch.checked = false;
|
|
@@ -999,36 +1049,17 @@ async function clickConsole() {
|
|
|
999
1049
|
}
|
|
1000
1050
|
};
|
|
1001
1051
|
|
|
1002
|
-
|
|
1052
|
+
// Use { once: true } to ensure single-fire and automatic cleanup
|
|
1053
|
+
reconnectBtn.addEventListener("click", handleReconnect, { once: true });
|
|
1003
1054
|
} else {
|
|
1004
|
-
// ESP32-S3/C3/C5/C6/H2/P4: Direct requestPort
|
|
1055
|
+
// Desktop (Web Serial) with ESP32-S3/C3/C5/C6/H2/P4: Direct requestPort
|
|
1005
1056
|
try {
|
|
1006
|
-
// Request port selection from user (direct
|
|
1057
|
+
// Request port selection from user (direct)
|
|
1007
1058
|
debugMsg("Please select the serial port again for console mode...");
|
|
1008
1059
|
const newPort = await navigator.serial.requestPort();
|
|
1009
1060
|
|
|
1010
|
-
//
|
|
1011
|
-
await newPort
|
|
1012
|
-
espStub.port = newPort;
|
|
1013
|
-
espStub.connected = true;
|
|
1014
|
-
|
|
1015
|
-
// Keep parent/loader in sync (used by closeConsole)
|
|
1016
|
-
if (espStub._parent) {
|
|
1017
|
-
espStub._parent.port = newPort;
|
|
1018
|
-
}
|
|
1019
|
-
if (espLoaderBeforeConsole) {
|
|
1020
|
-
espLoaderBeforeConsole.port = newPort;
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
debugMsg("Port opened for console at 115200 baud");
|
|
1024
|
-
|
|
1025
|
-
// Device is already in firmware mode, port is open at 115200
|
|
1026
|
-
// Initialize console directly
|
|
1027
|
-
consoleSwitch.checked = true;
|
|
1028
|
-
saveSetting("console", true);
|
|
1029
|
-
|
|
1030
|
-
// Initialize console UI and handlers
|
|
1031
|
-
await initConsoleUI();
|
|
1061
|
+
// Use helper to open port and initialize console
|
|
1062
|
+
await openConsolePortAndInit(newPort);
|
|
1032
1063
|
} catch (err) {
|
|
1033
1064
|
errorMsg(`Failed to open port for console: ${err.message}`);
|
|
1034
1065
|
consoleSwitch.checked = false;
|
|
@@ -1080,82 +1111,69 @@ async function clickConsole() {
|
|
|
1080
1111
|
* Close console and restore device to bootloader state
|
|
1081
1112
|
*/
|
|
1082
1113
|
async function closeConsole() {
|
|
1114
|
+
// Remove console-active class from body FIRST to restore visibility
|
|
1115
|
+
document.body.classList.remove("console-active");
|
|
1116
|
+
|
|
1083
1117
|
// Hide console and show commands again
|
|
1084
1118
|
consoleContainer.classList.add("hidden");
|
|
1085
1119
|
const commands = document.getElementById("commands");
|
|
1086
|
-
if (commands)
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
await consoleInstance.disconnect();
|
|
1091
|
-
} catch (err) {
|
|
1092
|
-
debugMsg("Error disconnecting console: " + err);
|
|
1093
|
-
}
|
|
1094
|
-
consoleInstance = null;
|
|
1120
|
+
if (commands) {
|
|
1121
|
+
commands.classList.remove("hidden");
|
|
1122
|
+
// Force display to ensure it's visible
|
|
1123
|
+
commands.style.display = "";
|
|
1095
1124
|
}
|
|
1096
1125
|
|
|
1097
1126
|
// Restore original state (bootloader + stub + baudrate)
|
|
1098
1127
|
if (espLoaderBeforeConsole && Number.isFinite(baudRateBeforeConsole)) {
|
|
1099
|
-
//
|
|
1100
|
-
|
|
1128
|
+
// Disconnect console first to release locks
|
|
1129
|
+
if (consoleInstance) {
|
|
1130
|
+
try {
|
|
1131
|
+
await consoleInstance.disconnect();
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
debugMsg("Error disconnecting console: " + err);
|
|
1134
|
+
}
|
|
1135
|
+
consoleInstance = null;
|
|
1136
|
+
}
|
|
1101
1137
|
|
|
1138
|
+
// Use esp_loader's exitConsoleMode function
|
|
1102
1139
|
try {
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1140
|
+
const needsManualReconnect = await espLoaderBeforeConsole.exitConsoleMode();
|
|
1141
|
+
|
|
1142
|
+
if (needsManualReconnect) {
|
|
1143
|
+
// ESP32-S2: Port has changed, need to select new port
|
|
1144
|
+
logMsg("ESP32-S2: Port changed - please select the new port");
|
|
1145
|
+
toggleUIConnected(false);
|
|
1146
|
+
espStub = undefined;
|
|
1147
|
+
|
|
1148
|
+
// Wait a moment for port to stabilize
|
|
1149
|
+
await sleep(1000);
|
|
1106
1150
|
|
|
1151
|
+
// Trigger port selection
|
|
1107
1152
|
try {
|
|
1108
|
-
|
|
1109
|
-
const newPort = await navigator.serial.requestPort();
|
|
1110
|
-
|
|
1111
|
-
// Update the loader to use the new port
|
|
1112
|
-
espLoaderBeforeConsole.port = newPort;
|
|
1113
|
-
|
|
1114
|
-
debugMsg("Port selected, reconnecting to bootloader...");
|
|
1115
|
-
} catch (portErr) {
|
|
1116
|
-
errorMsg(`Failed to select port: ${portErr.message}`);
|
|
1117
|
-
// Reset connection state to allow fresh connect
|
|
1118
|
-
espStub = undefined;
|
|
1119
|
-
toggleUIConnected(false);
|
|
1153
|
+
await clickConnect();
|
|
1120
1154
|
espLoaderBeforeConsole = null;
|
|
1121
1155
|
baudRateBeforeConsole = null;
|
|
1122
1156
|
chipFamilyBeforeConsole = null;
|
|
1123
|
-
|
|
1157
|
+
} catch (err) {
|
|
1158
|
+
errorMsg("Failed to reconnect: " + err);
|
|
1124
1159
|
}
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
// Now espLoaderBeforeConsole is in bootloader state (IS_STUB = false)
|
|
1136
|
-
// Reload stub using the reconnected bootloader
|
|
1137
|
-
const newStub = await espLoaderBeforeConsole.runStub();
|
|
1138
|
-
espStub = newStub;
|
|
1139
|
-
|
|
1140
|
-
// Restore original baudrate
|
|
1141
|
-
if (baudRateBeforeConsole !== 115200) {
|
|
1142
|
-
await espStub.setBaudrate(baudRateBeforeConsole);
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
espLoaderBeforeConsole = null;
|
|
1146
|
-
baudRateBeforeConsole = null;
|
|
1147
|
-
chipFamilyBeforeConsole = null;
|
|
1148
|
-
} catch (err) {
|
|
1149
|
-
errorMsg("Failed to restore state after console: " + err.message);
|
|
1150
|
-
// Attempt to disconnect cleanly to allow reconnection
|
|
1151
|
-
try {
|
|
1152
|
-
if (espLoaderBeforeConsole?.port) {
|
|
1153
|
-
await espLoaderBeforeConsole.port.close();
|
|
1160
|
+
} else {
|
|
1161
|
+
// Other devices: reconnectToBootloader was called successfully
|
|
1162
|
+
// Reload stub
|
|
1163
|
+
const newStub = await espLoaderBeforeConsole.runStub();
|
|
1164
|
+
espStub = newStub;
|
|
1165
|
+
|
|
1166
|
+
// Restore baudrate
|
|
1167
|
+
if (baudRateBeforeConsole !== 115200) {
|
|
1168
|
+
await espStub.setBaudrate(baudRateBeforeConsole);
|
|
1154
1169
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1170
|
+
|
|
1171
|
+
espLoaderBeforeConsole = null;
|
|
1172
|
+
baudRateBeforeConsole = null;
|
|
1173
|
+
chipFamilyBeforeConsole = null;
|
|
1157
1174
|
}
|
|
1158
|
-
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
errorMsg("Failed to exit console mode: " + err.message);
|
|
1159
1177
|
espStub = undefined;
|
|
1160
1178
|
toggleUIConnected(false);
|
|
1161
1179
|
espLoaderBeforeConsole = null;
|
|
@@ -1634,7 +1652,10 @@ async function clickReadFlash() {
|
|
|
1634
1652
|
return;
|
|
1635
1653
|
}
|
|
1636
1654
|
|
|
1637
|
-
|
|
1655
|
+
// Create filename with chip type and MAC address
|
|
1656
|
+
const chipInfo = currentChipName ? currentChipName.replace(/\s+/g, '_') : 'ESP';
|
|
1657
|
+
const macInfo = currentMacAddr ? currentMacAddr.replace(/:/g, '') : '';
|
|
1658
|
+
const defaultFilename = `${chipInfo}${macInfo ? '_' + macInfo : ''}_flash_0x${offset.toString(16)}_0x${size.toString(16)}.bin`;
|
|
1638
1659
|
|
|
1639
1660
|
baudRateSelect.disabled = true;
|
|
1640
1661
|
butErase.disabled = true;
|
|
@@ -1952,7 +1973,10 @@ function displayPartitions(partitions) {
|
|
|
1952
1973
|
* Download a partition
|
|
1953
1974
|
*/
|
|
1954
1975
|
async function downloadPartition(partition) {
|
|
1955
|
-
|
|
1976
|
+
// Create filename with chip type and MAC address
|
|
1977
|
+
const chipInfo = currentChipName ? currentChipName.replace(/\s+/g, '_') : 'ESP';
|
|
1978
|
+
const macInfo = currentMacAddr ? currentMacAddr.replace(/:/g, '') : '';
|
|
1979
|
+
const defaultFilename = `${chipInfo}${macInfo ? '_' + macInfo : ''}_${partition.name}_0x${partition.offset.toString(16)}.bin`;
|
|
1956
1980
|
|
|
1957
1981
|
const partitionProgress = document.getElementById("partitionProgress");
|
|
1958
1982
|
const progressBar = partitionProgress.querySelector("div");
|
|
@@ -2041,13 +2065,7 @@ function toggleUIConnected(connected) {
|
|
|
2041
2065
|
if (connected) {
|
|
2042
2066
|
lbl = "Disconnect";
|
|
2043
2067
|
isConnected = true;
|
|
2044
|
-
|
|
2045
|
-
/* DISABLED: Auto-hide header after connection
|
|
2046
|
-
setTimeout(() => {
|
|
2047
|
-
header.classList.add("header-hidden");
|
|
2048
|
-
main.classList.add("no-header-padding");
|
|
2049
|
-
}, 2000); // Hide after 2 seconds
|
|
2050
|
-
*/
|
|
2068
|
+
|
|
2051
2069
|
} else {
|
|
2052
2070
|
isConnected = false;
|
|
2053
2071
|
toggleUIToolbar(false);
|
|
@@ -2066,16 +2084,14 @@ function toggleUIConnected(connected) {
|
|
|
2066
2084
|
if (commands) commands.classList.remove("hidden");
|
|
2067
2085
|
consoleSwitch.checked = false;
|
|
2068
2086
|
saveSetting("console", false);
|
|
2069
|
-
|
|
2070
|
-
/* DISABLED: Show header when disconnected
|
|
2071
|
-
header.classList.remove("header-hidden");
|
|
2072
|
-
main.classList.remove("no-header-padding");
|
|
2073
|
-
*/
|
|
2074
2087
|
}
|
|
2075
2088
|
butConnect.textContent = lbl;
|
|
2076
2089
|
}
|
|
2077
2090
|
|
|
2078
2091
|
function loadAllSettings() {
|
|
2092
|
+
// Get default values based on environment (Desktop vs WebUSB)
|
|
2093
|
+
const defaults = getDefaultAdvancedParams();
|
|
2094
|
+
|
|
2079
2095
|
// Load all saved settings or defaults
|
|
2080
2096
|
autoscroll.checked = loadSetting("autoscroll", true);
|
|
2081
2097
|
baudRateSelect.value = loadSetting("baudrate", 2000000);
|
|
@@ -2085,10 +2101,10 @@ function loadAllSettings() {
|
|
|
2085
2101
|
consoleSwitch.checked = loadSetting("console", false);
|
|
2086
2102
|
advancedMode.checked = loadSetting("advanced", false);
|
|
2087
2103
|
|
|
2088
|
-
// Load advanced parameters
|
|
2089
|
-
chunkSizeSelect.value = loadSetting("chunkSize",
|
|
2090
|
-
blockSizeSelect.value = loadSetting("blockSize",
|
|
2091
|
-
maxInFlightSelect.value = loadSetting("maxInFlight",
|
|
2104
|
+
// Load advanced parameters with environment-specific defaults
|
|
2105
|
+
chunkSizeSelect.value = loadSetting("chunkSize", defaults.chunkSize);
|
|
2106
|
+
blockSizeSelect.value = loadSetting("blockSize", defaults.blockSize);
|
|
2107
|
+
maxInFlightSelect.value = loadSetting("maxInFlight", defaults.maxInFlight);
|
|
2092
2108
|
|
|
2093
2109
|
// Apply show log setting
|
|
2094
2110
|
updateLogVisibility();
|
|
@@ -2120,10 +2136,6 @@ function ucWords(text) {
|
|
|
2120
2136
|
.replace(/(?<= )[^\s]|^./g, (a) => a.toUpperCase());
|
|
2121
2137
|
}
|
|
2122
2138
|
|
|
2123
|
-
function sleep(ms) {
|
|
2124
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
2139
|
/**
|
|
2128
2140
|
* Save data to file - uses Electron API in desktop app, browser download otherwise
|
|
2129
2141
|
*/
|
|
@@ -3071,8 +3083,11 @@ async function clickLittlefsBackup() {
|
|
|
3071
3083
|
logMsg(`Creating ${getFilesystemDisplayName()} backup image...`);
|
|
3072
3084
|
const image = currentLittleFS.toImage();
|
|
3073
3085
|
|
|
3086
|
+
// Create filename with chip type and MAC address
|
|
3087
|
+
const chipInfo = currentChipName ? currentChipName.replace(/\s+/g, '_') : 'ESP';
|
|
3088
|
+
const macInfo = currentMacAddr ? currentMacAddr.replace(/:/g, '') : '';
|
|
3074
3089
|
const fsType = currentFilesystemType || 'filesystem';
|
|
3075
|
-
const filename = `${currentLittleFSPartition.name}_${fsType}_backup.bin`;
|
|
3090
|
+
const filename = `${chipInfo}${macInfo ? '_' + macInfo : ''}_${currentLittleFSPartition.name}_${fsType}_backup.bin`;
|
|
3076
3091
|
await saveDataToFile(image, filename);
|
|
3077
3092
|
|
|
3078
3093
|
logMsg(`${getFilesystemDisplayName()} backup saved as "${filename}"`);
|