esp32tool 1.1.8 → 1.2.0
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/.nojekyll +0 -0
- package/README.md +100 -6
- package/apple-touch-icon.png +0 -0
- package/build-electron-cli.cjs +177 -0
- package/build-single-binary.cjs +295 -0
- package/css/light.css +11 -0
- package/css/style.css +225 -35
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +458 -0
- package/dist/esp_loader.d.ts +129 -21
- package/dist/esp_loader.js +1227 -222
- package/dist/index.d.ts +2 -1
- package/dist/index.js +37 -4
- package/dist/node-usb-adapter.d.ts +47 -0
- package/dist/node-usb-adapter.js +725 -0
- package/dist/stubs/index.d.ts +1 -2
- package/dist/stubs/index.js +4 -0
- package/dist/web/index.js +1 -1
- package/electron/cli-main.cjs +74 -0
- package/electron/main.cjs +338 -0
- package/electron/main.js +7 -2
- package/favicon.ico +0 -0
- package/fix-cli-imports.cjs +127 -0
- package/generate-icons.sh +89 -0
- 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/index.html +94 -64
- package/install-android.html +411 -0
- package/js/modules/esptool.js +1 -1
- package/js/script.js +165 -160
- package/js/webusb-serial.js +1017 -0
- package/license.md +1 -1
- package/manifest.json +89 -0
- package/package.cli.json +29 -0
- package/package.json +31 -21
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/cli.ts +618 -0
- package/src/esp_loader.ts +1438 -254
- package/src/index.ts +69 -3
- package/src/node-usb-adapter.ts +924 -0
- package/src/stubs/index.ts +4 -1
- package/sw.js +155 -0
package/js/script.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// Import WebUSB serial support for Android compatibility
|
|
2
|
+
import { WebUSBSerial, requestSerialPort } from './webusb-serial.js';
|
|
3
|
+
|
|
4
|
+
// Make requestSerialPort available globally for esptool.js
|
|
5
|
+
// Use defensive assignment to avoid accidental overwrites
|
|
6
|
+
if (!globalThis.requestSerialPort) {
|
|
7
|
+
globalThis.requestSerialPort = requestSerialPort;
|
|
8
|
+
}
|
|
9
|
+
|
|
1
10
|
let espStub;
|
|
2
11
|
let esp32s2ReconnectInProgress = false;
|
|
3
12
|
let currentLittleFS = null;
|
|
@@ -8,6 +17,7 @@ let currentFilesystemType = null; // 'littlefs', 'fatfs', or 'spiffs'
|
|
|
8
17
|
let littlefsModulePromise = null; // Cache for LittleFS WASM module
|
|
9
18
|
let lastReadFlashData = null; // Store last read flash data for ESP8266
|
|
10
19
|
let currentChipName = null; // Store chip name globally
|
|
20
|
+
let isConnected = false; // Track connection state
|
|
11
21
|
|
|
12
22
|
/**
|
|
13
23
|
* Get display name for current filesystem type
|
|
@@ -96,7 +106,7 @@ const isElectron = window.electronAPI && window.electronAPI.isElectron;
|
|
|
96
106
|
const maxLogLength = 100;
|
|
97
107
|
const log = document.getElementById("log");
|
|
98
108
|
const butConnect = document.getElementById("butConnect");
|
|
99
|
-
const
|
|
109
|
+
const baudRateSelect = document.getElementById("baudRate");
|
|
100
110
|
const butClear = document.getElementById("butClear");
|
|
101
111
|
const butErase = document.getElementById("butErase");
|
|
102
112
|
const butProgram = document.getElementById("butProgram");
|
|
@@ -190,7 +200,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
190
200
|
updateUploadRowsVisibility();
|
|
191
201
|
|
|
192
202
|
autoscroll.addEventListener("click", clickAutoscroll);
|
|
193
|
-
|
|
203
|
+
baudRateSelect.addEventListener("change", changeBaudRate);
|
|
194
204
|
darkMode.addEventListener("click", clickDarkMode);
|
|
195
205
|
debugMode.addEventListener("click", clickDebugMode);
|
|
196
206
|
showLog.addEventListener("click", clickShowLog);
|
|
@@ -228,7 +238,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
228
238
|
}
|
|
229
239
|
});
|
|
230
240
|
|
|
231
|
-
|
|
241
|
+
// Check for Web Serial or WebUSB support
|
|
242
|
+
if ("serial" in navigator || "usb" in navigator) {
|
|
232
243
|
const notSupported = document.getElementById("notSupported");
|
|
233
244
|
notSupported.classList.add("hidden");
|
|
234
245
|
}
|
|
@@ -244,7 +255,7 @@ function initBaudRate() {
|
|
|
244
255
|
var option = document.createElement("option");
|
|
245
256
|
option.text = rate + " Baud";
|
|
246
257
|
option.value = rate;
|
|
247
|
-
|
|
258
|
+
baudRateSelect.add(option);
|
|
248
259
|
}
|
|
249
260
|
}
|
|
250
261
|
|
|
@@ -266,31 +277,8 @@ function debugMsg(...args) {
|
|
|
266
277
|
if (!debugMode.checked) {
|
|
267
278
|
return;
|
|
268
279
|
}
|
|
269
|
-
|
|
270
|
-
function getStackTrace() {
|
|
271
|
-
let stack = new Error().stack;
|
|
272
|
-
//console.log(stack);
|
|
273
|
-
stack = stack.split("\n").map((v) => v.trim());
|
|
274
|
-
stack.shift();
|
|
275
|
-
stack.shift();
|
|
276
|
-
|
|
277
|
-
let trace = [];
|
|
278
|
-
for (let line of stack) {
|
|
279
|
-
line = line.replace("at ", "");
|
|
280
|
-
trace.push({
|
|
281
|
-
func: line.substr(0, line.indexOf("(") - 1),
|
|
282
|
-
pos: line.substring(line.indexOf(".js:") + 4, line.lastIndexOf(":")),
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
280
|
|
|
286
|
-
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
let stack = getStackTrace();
|
|
290
|
-
stack.shift();
|
|
291
|
-
let top = stack.shift();
|
|
292
|
-
let prefix =
|
|
293
|
-
'<span class="debug-function">[' + top.func + ":" + top.pos + "]</span> ";
|
|
281
|
+
let prefix = "";
|
|
294
282
|
for (let arg of args) {
|
|
295
283
|
if (arg === undefined) {
|
|
296
284
|
logMsg(prefix + "undefined");
|
|
@@ -355,19 +343,60 @@ function formatMacAddr(macAddr) {
|
|
|
355
343
|
.join(":");
|
|
356
344
|
}
|
|
357
345
|
|
|
346
|
+
function toHex(value) {
|
|
347
|
+
return "0x" + value.toString(16).padStart(2, "0");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Parse flash size string (e.g., "256KB", "4MB") to bytes
|
|
352
|
+
* @param {string} sizeStr - Flash size string with unit (KB or MB)
|
|
353
|
+
* @returns {number} Size in bytes
|
|
354
|
+
*/
|
|
355
|
+
function parseFlashSize(sizeStr) {
|
|
356
|
+
if (!sizeStr || typeof sizeStr !== 'string') {
|
|
357
|
+
return 0;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Extract number and unit
|
|
361
|
+
const match = sizeStr.match(/^(\d+)(KB|MB)$/i);
|
|
362
|
+
if (!match) {
|
|
363
|
+
// If no unit, assume it's already in MB (legacy behavior)
|
|
364
|
+
const num = parseInt(sizeStr);
|
|
365
|
+
return isNaN(num) ? 0 : num * 1024 * 1024;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const value = parseInt(match[1]);
|
|
369
|
+
const unit = match[2].toUpperCase();
|
|
370
|
+
|
|
371
|
+
if (unit === 'KB') {
|
|
372
|
+
return value * 1024; // KB to bytes
|
|
373
|
+
} else if (unit === 'MB') {
|
|
374
|
+
return value * 1024 * 1024; // MB to bytes
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return 0;
|
|
378
|
+
}
|
|
379
|
+
|
|
358
380
|
/**
|
|
359
381
|
* @name clickConnect
|
|
360
382
|
* Click handler for the connect/disconnect button.
|
|
361
383
|
*/
|
|
362
384
|
async function clickConnect() {
|
|
385
|
+
console.log('[clickConnect] Function called');
|
|
386
|
+
|
|
363
387
|
if (espStub) {
|
|
388
|
+
console.log('[clickConnect] Already connected, disconnecting...');
|
|
364
389
|
// Remove disconnect event listener to prevent it from firing during manual disconnect
|
|
365
390
|
if (espStub.handleDisconnect) {
|
|
366
391
|
espStub.removeEventListener("disconnect", espStub.handleDisconnect);
|
|
367
392
|
}
|
|
368
393
|
|
|
369
394
|
await espStub.disconnect();
|
|
370
|
-
|
|
395
|
+
try {
|
|
396
|
+
await espStub.port?.close?.();
|
|
397
|
+
} catch (e) {
|
|
398
|
+
// ignore double-close
|
|
399
|
+
}
|
|
371
400
|
toggleUIConnected(false);
|
|
372
401
|
espStub = undefined;
|
|
373
402
|
|
|
@@ -377,13 +406,45 @@ async function clickConnect() {
|
|
|
377
406
|
return;
|
|
378
407
|
}
|
|
379
408
|
|
|
409
|
+
console.log('[clickConnect] Getting esploaderMod...');
|
|
380
410
|
const esploaderMod = await window.esptoolPackage;
|
|
381
411
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
412
|
+
// Platform detection: Android always uses WebUSB, Desktop uses Web Serial
|
|
413
|
+
const userAgent = navigator.userAgent || '';
|
|
414
|
+
const isAndroid = /Android/i.test(userAgent);
|
|
415
|
+
|
|
416
|
+
// Only log platform details to UI in debug mode (avoid fingerprinting surface)
|
|
417
|
+
if (debugMode.checked) {
|
|
418
|
+
const platformMsg = `Platform: ${isAndroid ? 'Android' : 'Desktop'} (UA: ${userAgent.substring(0, 50)}...)`;
|
|
419
|
+
logMsg(platformMsg);
|
|
420
|
+
}
|
|
421
|
+
logMsg(`Using: ${isAndroid ? 'WebUSB' : 'Web Serial'}`);
|
|
422
|
+
|
|
423
|
+
let esploader;
|
|
424
|
+
|
|
425
|
+
if (isAndroid) {
|
|
426
|
+
// Android: Use WebUSB directly
|
|
427
|
+
console.log('[Connect] Using WebUSB for Android');
|
|
428
|
+
try {
|
|
429
|
+
const port = await WebUSBSerial.requestPort((...args) => logMsg(...args));
|
|
430
|
+
esploader = await esploaderMod.connectWithPort(port, {
|
|
431
|
+
log: (...args) => logMsg(...args),
|
|
432
|
+
debug: (...args) => debugMsg(...args),
|
|
433
|
+
error: (...args) => errorMsg(...args),
|
|
434
|
+
});
|
|
435
|
+
} catch (err) {
|
|
436
|
+
logMsg(`WebUSB connection failed: ${err.message || err}`);
|
|
437
|
+
throw err;
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
// Desktop: Use Web Serial (standard esptool connect)
|
|
441
|
+
console.log('[Connect] Using Web Serial for Desktop');
|
|
442
|
+
esploader = await esploaderMod.connect({
|
|
443
|
+
log: (...args) => logMsg(...args),
|
|
444
|
+
debug: (...args) => debugMsg(...args),
|
|
445
|
+
error: (...args) => errorMsg(...args),
|
|
446
|
+
});
|
|
447
|
+
}
|
|
387
448
|
|
|
388
449
|
// Store port info for ESP32-S2 detection
|
|
389
450
|
let portInfo = esploader.port?.getInfo ? esploader.port.getInfo() : {};
|
|
@@ -404,119 +465,73 @@ async function clickConnect() {
|
|
|
404
465
|
espStub = undefined;
|
|
405
466
|
|
|
406
467
|
try {
|
|
468
|
+
// Close the port first
|
|
407
469
|
await esploader.port.close();
|
|
408
470
|
|
|
409
|
-
|
|
471
|
+
// For Android WebUSB: ESP32-S2 automatic reconnection doesn't work
|
|
472
|
+
// Show message and let user reconnect manually with BOOT button
|
|
473
|
+
if (isAndroid) {
|
|
474
|
+
logMsg("ESP32-S2 has switched to CDC mode");
|
|
475
|
+
logMsg("Please press and HOLD the BOOT button on your ESP32-S2, then click Connect");
|
|
476
|
+
esp32s2ReconnectInProgress = false;
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
// For Desktop Web Serial: Use the modal dialog approach
|
|
480
|
+
if (!isAndroid && esploader.port.forget) {
|
|
410
481
|
await esploader.port.forget();
|
|
411
482
|
}
|
|
412
483
|
} catch (disconnectErr) {
|
|
413
484
|
// Ignore disconnect errors
|
|
485
|
+
console.warn("Error during disconnect:", disconnectErr);
|
|
414
486
|
}
|
|
415
487
|
|
|
416
|
-
// Show modal dialog
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
modal.classList.remove("hidden");
|
|
421
|
-
|
|
422
|
-
// Handle reconnect button click
|
|
423
|
-
const handleReconnect = async () => {
|
|
424
|
-
modal.classList.add("hidden");
|
|
425
|
-
reconnectBtn.removeEventListener("click", handleReconnect);
|
|
488
|
+
// Show modal dialog ONLY for Desktop
|
|
489
|
+
if (!isAndroid) {
|
|
490
|
+
const modal = document.getElementById("esp32s2Modal");
|
|
491
|
+
const reconnectBtn = document.getElementById("butReconnectS2");
|
|
426
492
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
493
|
+
modal.classList.remove("hidden");
|
|
494
|
+
|
|
495
|
+
// Handle reconnect button click
|
|
496
|
+
const handleReconnect = async () => {
|
|
497
|
+
modal.classList.add("hidden");
|
|
498
|
+
reconnectBtn.removeEventListener("click", handleReconnect);
|
|
499
|
+
|
|
500
|
+
logMsg("Requesting new device selection...");
|
|
501
|
+
|
|
502
|
+
// Trigger port selection
|
|
503
|
+
try {
|
|
504
|
+
await clickConnect();
|
|
505
|
+
// Reset flag on successful connection
|
|
506
|
+
esp32s2ReconnectInProgress = false;
|
|
507
|
+
} catch (err) {
|
|
508
|
+
errorMsg("Failed to reconnect: " + err);
|
|
509
|
+
// Reset flag on error so user can try again
|
|
510
|
+
esp32s2ReconnectInProgress = false;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
reconnectBtn.addEventListener("click", handleReconnect);
|
|
515
|
+
}
|
|
440
516
|
});
|
|
441
517
|
}
|
|
442
518
|
|
|
443
519
|
try {
|
|
444
520
|
await esploader.initialize();
|
|
445
521
|
} catch (err) {
|
|
446
|
-
//
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// Wait for new port to appear
|
|
459
|
-
logMsg("Waiting for ESP32-S2 CDC port...");
|
|
460
|
-
|
|
461
|
-
const waitForNewPort = new Promise((resolve) => {
|
|
462
|
-
const checkInterval = setInterval(() => {
|
|
463
|
-
if (navigator.serial && navigator.serial.getPorts) {
|
|
464
|
-
navigator.serial.getPorts().then(ports => {
|
|
465
|
-
if (ports.length > 0) {
|
|
466
|
-
clearInterval(checkInterval);
|
|
467
|
-
resolve(ports[0]);
|
|
468
|
-
}
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
}, 50);
|
|
472
|
-
|
|
473
|
-
// Timeout after 500 ms
|
|
474
|
-
setTimeout(() => {
|
|
475
|
-
clearInterval(checkInterval);
|
|
476
|
-
resolve(null);
|
|
477
|
-
}, 500);
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
const newPort = await waitForNewPort;
|
|
481
|
-
|
|
482
|
-
if (!newPort) {
|
|
483
|
-
esp32s2ReconnectInProgress = false;
|
|
484
|
-
throw new Error("ESP32-S2 CDC port did not appear in time");
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
// Additional small delay to ensure port is ready
|
|
488
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
489
|
-
|
|
490
|
-
// Open the new port and create ESPLoader directly
|
|
491
|
-
await newPort.open({ baudRate: 115200 });
|
|
492
|
-
logMsg("Connected successfully.");
|
|
493
|
-
|
|
494
|
-
esploader = new esploaderMod.ESPLoader(newPort, {
|
|
495
|
-
log: (...args) => logMsg(...args),
|
|
496
|
-
debug: (...args) => debugMsg(...args),
|
|
497
|
-
error: (...args) => errorMsg(...args),
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
// Initialize the new connection
|
|
501
|
-
await esploader.initialize();
|
|
502
|
-
|
|
503
|
-
esp32s2ReconnectInProgress = false;
|
|
504
|
-
logMsg("ESP32-S2 reconnection successful!");
|
|
505
|
-
} else {
|
|
506
|
-
// If ESP32-S2 reconnect is in progress (browser modal), suppress the error
|
|
507
|
-
if (esp32s2ReconnectInProgress) {
|
|
508
|
-
logMsg("Initialization interrupted for ESP32-S2 reconnection.");
|
|
509
|
-
return;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Not ESP32-S2 or reconnect already attempted
|
|
513
|
-
try {
|
|
514
|
-
await esploader.disconnect();
|
|
515
|
-
} catch (disconnectErr) {
|
|
516
|
-
// Ignore disconnect errors
|
|
517
|
-
}
|
|
518
|
-
throw err;
|
|
522
|
+
// If ESP32-S2 reconnect is in progress (handled by event listener), suppress the error
|
|
523
|
+
if (esp32s2ReconnectInProgress) {
|
|
524
|
+
logMsg("Initialization interrupted for ESP32-S2 reconnection.");
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Not ESP32-S2 or other error
|
|
529
|
+
try {
|
|
530
|
+
await esploader.disconnect();
|
|
531
|
+
} catch (disconnectErr) {
|
|
532
|
+
// Ignore disconnect errors
|
|
519
533
|
}
|
|
534
|
+
throw err;
|
|
520
535
|
}
|
|
521
536
|
|
|
522
537
|
logMsg("Connected to " + esploader.chipName);
|
|
@@ -526,6 +541,7 @@ async function clickConnect() {
|
|
|
526
541
|
currentChipName = esploader.chipName;
|
|
527
542
|
|
|
528
543
|
espStub = await esploader.runStub();
|
|
544
|
+
|
|
529
545
|
toggleUIConnected(true);
|
|
530
546
|
toggleUIToolbar(true);
|
|
531
547
|
|
|
@@ -549,12 +565,12 @@ async function clickConnect() {
|
|
|
549
565
|
|
|
550
566
|
// Set detected flash size in the read size field
|
|
551
567
|
if (espStub.flashSize) {
|
|
552
|
-
const flashSizeBytes =
|
|
568
|
+
const flashSizeBytes = parseFlashSize(espStub.flashSize);
|
|
553
569
|
readSize.value = "0x" + flashSizeBytes.toString(16);
|
|
554
570
|
}
|
|
555
571
|
|
|
556
572
|
// Set the selected baud rate
|
|
557
|
-
let baud = parseInt(
|
|
573
|
+
let baud = parseInt(baudRateSelect.value);
|
|
558
574
|
if (baudRates.includes(baud)) {
|
|
559
575
|
await espStub.setBaudrate(baud);
|
|
560
576
|
}
|
|
@@ -573,9 +589,9 @@ async function clickConnect() {
|
|
|
573
589
|
* Change handler for the Baud Rate selector.
|
|
574
590
|
*/
|
|
575
591
|
async function changeBaudRate() {
|
|
576
|
-
saveSetting("baudrate",
|
|
592
|
+
saveSetting("baudrate", baudRateSelect.value);
|
|
577
593
|
if (espStub) {
|
|
578
|
-
let baud = parseInt(
|
|
594
|
+
let baud = parseInt(baudRateSelect.value);
|
|
579
595
|
if (baudRates.includes(baud)) {
|
|
580
596
|
await espStub.setBaudrate(baud);
|
|
581
597
|
}
|
|
@@ -651,8 +667,8 @@ async function clickDetectFS() {
|
|
|
651
667
|
butDetectFS.disabled = true;
|
|
652
668
|
logMsg('Detecting ESP8266 filesystem...');
|
|
653
669
|
|
|
654
|
-
const
|
|
655
|
-
const
|
|
670
|
+
const flashSizeBytes = parseFlashSize(espStub.flashSize);
|
|
671
|
+
const flashSizeMB = flashSizeBytes / (1024 * 1024);
|
|
656
672
|
const esptoolMod = await window.esptoolPackage;
|
|
657
673
|
|
|
658
674
|
// Scan flash for filesystem signatures - optimized based on flash size
|
|
@@ -870,7 +886,7 @@ async function clickErase() {
|
|
|
870
886
|
}
|
|
871
887
|
|
|
872
888
|
if (confirmed) {
|
|
873
|
-
|
|
889
|
+
baudRateSelect.disabled = true;
|
|
874
890
|
butErase.disabled = true;
|
|
875
891
|
butProgram.disabled = true;
|
|
876
892
|
try {
|
|
@@ -882,7 +898,7 @@ async function clickErase() {
|
|
|
882
898
|
errorMsg(e);
|
|
883
899
|
} finally {
|
|
884
900
|
butErase.disabled = false;
|
|
885
|
-
|
|
901
|
+
baudRateSelect.disabled = false;
|
|
886
902
|
butProgram.disabled = getValidFiles().length == 0;
|
|
887
903
|
}
|
|
888
904
|
}
|
|
@@ -909,7 +925,7 @@ async function clickProgram() {
|
|
|
909
925
|
});
|
|
910
926
|
};
|
|
911
927
|
|
|
912
|
-
|
|
928
|
+
baudRateSelect.disabled = true;
|
|
913
929
|
butErase.disabled = true;
|
|
914
930
|
butProgram.disabled = true;
|
|
915
931
|
for (let i = 0; i < firmware.length; i++) {
|
|
@@ -943,7 +959,7 @@ async function clickProgram() {
|
|
|
943
959
|
progress[i].querySelector("div").style.width = "0";
|
|
944
960
|
}
|
|
945
961
|
butErase.disabled = false;
|
|
946
|
-
|
|
962
|
+
baudRateSelect.disabled = false;
|
|
947
963
|
butProgram.disabled = getValidFiles().length == 0;
|
|
948
964
|
logMsg("To run the new firmware, please reset your device.");
|
|
949
965
|
}
|
|
@@ -1032,7 +1048,7 @@ async function clickReadFlash() {
|
|
|
1032
1048
|
|
|
1033
1049
|
const defaultFilename = `flash_0x${offset.toString(16)}_0x${size.toString(16)}.bin`;
|
|
1034
1050
|
|
|
1035
|
-
|
|
1051
|
+
baudRateSelect.disabled = true;
|
|
1036
1052
|
butErase.disabled = true;
|
|
1037
1053
|
butProgram.disabled = true;
|
|
1038
1054
|
butReadFlash.disabled = true;
|
|
@@ -1062,8 +1078,6 @@ async function clickReadFlash() {
|
|
|
1062
1078
|
const esptoolMod = await window.esptoolPackage;
|
|
1063
1079
|
const fsType = esptoolMod.detectFilesystemFromImage(data, chipName);
|
|
1064
1080
|
|
|
1065
|
-
logMsg(`Filesystem detection: ${fsType} (chipName: ${chipName})`);
|
|
1066
|
-
|
|
1067
1081
|
if (fsType !== 'unknown') {
|
|
1068
1082
|
logMsg(`Detected ${fsType} filesystem in read data`);
|
|
1069
1083
|
|
|
@@ -1090,7 +1104,7 @@ async function clickReadFlash() {
|
|
|
1090
1104
|
readProgress.classList.add("hidden");
|
|
1091
1105
|
readProgress.querySelector("div").style.width = "0";
|
|
1092
1106
|
butErase.disabled = false;
|
|
1093
|
-
|
|
1107
|
+
baudRateSelect.disabled = false;
|
|
1094
1108
|
butProgram.disabled = getValidFiles().length == 0;
|
|
1095
1109
|
butReadFlash.disabled = false;
|
|
1096
1110
|
readOffset.disabled = false;
|
|
@@ -1420,7 +1434,7 @@ function toggleUIConnected(connected) {
|
|
|
1420
1434
|
function loadAllSettings() {
|
|
1421
1435
|
// Load all saved settings or defaults
|
|
1422
1436
|
autoscroll.checked = loadSetting("autoscroll", true);
|
|
1423
|
-
|
|
1437
|
+
baudRateSelect.value = loadSetting("baudrate", 2000000);
|
|
1424
1438
|
darkMode.checked = loadSetting("darkmode", false);
|
|
1425
1439
|
debugMode.checked = loadSetting("debugmode", true);
|
|
1426
1440
|
showLog.checked = loadSetting("showlog", false);
|
|
@@ -1586,10 +1600,8 @@ async function detectFilesystemType(offset, size) {
|
|
|
1586
1600
|
*/
|
|
1587
1601
|
async function loadLittlefsModule() {
|
|
1588
1602
|
if (!littlefsModulePromise) {
|
|
1589
|
-
//
|
|
1590
|
-
const basePath = window.location.pathname
|
|
1591
|
-
? window.location.pathname
|
|
1592
|
-
: window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/') + 1);
|
|
1603
|
+
// Derive base path from current document URL (works for all hosting layouts)
|
|
1604
|
+
const basePath = new URL(".", window.location.href).pathname;
|
|
1593
1605
|
const modulePath = `${basePath}src/wasm/littlefs/index.js`;
|
|
1594
1606
|
|
|
1595
1607
|
littlefsModulePromise = import(modulePath)
|
|
@@ -1671,9 +1683,7 @@ async function openLittleFS(partition) {
|
|
|
1671
1683
|
logMsg('Mounting LittleFS filesystem...');
|
|
1672
1684
|
|
|
1673
1685
|
// Import constants from esptool module
|
|
1674
|
-
const basePath = window.location.pathname
|
|
1675
|
-
? window.location.pathname
|
|
1676
|
-
: window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/') + 1);
|
|
1686
|
+
const basePath = new URL(".", window.location.href).pathname;
|
|
1677
1687
|
const esptoolModulePath = `${basePath}js/modules/esptool.js`;
|
|
1678
1688
|
const {
|
|
1679
1689
|
LITTLEFS_BLOCK_SIZE_CANDIDATES,
|
|
@@ -1820,9 +1830,7 @@ async function openFatFS(partition) {
|
|
|
1820
1830
|
}
|
|
1821
1831
|
|
|
1822
1832
|
// Load FatFS module
|
|
1823
|
-
const basePath = window.location.pathname
|
|
1824
|
-
? window.location.pathname
|
|
1825
|
-
: window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/') + 1);
|
|
1833
|
+
const basePath = new URL(".", window.location.href).pathname;
|
|
1826
1834
|
const modulePath = `${basePath}src/wasm/fatfs/index.js`;
|
|
1827
1835
|
const module = await import(modulePath);
|
|
1828
1836
|
const { createFatFSFromImage, createFatFS } = module;
|
|
@@ -1944,9 +1952,7 @@ async function openSPIFFS(partition) {
|
|
|
1944
1952
|
logMsg(`Partition size: ${formatSize(partition.size)} (${partition.size} bytes)`);
|
|
1945
1953
|
|
|
1946
1954
|
// Import SPIFFS module
|
|
1947
|
-
const basePath = window.location.pathname
|
|
1948
|
-
? window.location.pathname
|
|
1949
|
-
: window.location.pathname.substring(0, window.location.pathname.lastIndexOf('/') + 1);
|
|
1955
|
+
const basePath = new URL(".", window.location.href).pathname;
|
|
1950
1956
|
const modulePath = `${basePath}js/modules/esptool.js`;
|
|
1951
1957
|
|
|
1952
1958
|
const {
|
|
@@ -2163,7 +2169,6 @@ async function openSPIFFS(partition) {
|
|
|
2163
2169
|
refreshLittleFS();
|
|
2164
2170
|
|
|
2165
2171
|
logMsg('SPIFFS filesystem opened successfully');
|
|
2166
|
-
logMsg('Note: SPIFFS is a flat filesystem - directories are not supported.');
|
|
2167
2172
|
} catch (e) {
|
|
2168
2173
|
errorMsg(`Failed to open SPIFFS: ${e.message || e}`);
|
|
2169
2174
|
console.error('SPIFFS open error:', e);
|