esp32tool 1.5.0 → 1.6.1
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/README.md +2 -0
- package/apple-touch-icon.png +0 -0
- package/css/style.css +388 -2
- package/dist/esp_loader.js +3 -13
- 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/index.html +7 -4
- package/js/modules/esptool.js +1 -1
- package/js/nvs-editor.js +732 -0
- package/js/script.js +142 -4
- package/package.json +8 -5
- package/screenshots/desktop.png +0 -0
- package/screenshots/mobile.png +0 -0
- package/src/esp_loader.ts +3 -16
- package/sw.js +2 -1
package/js/script.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { WebUSBSerial, requestSerialPort } from './webusb-serial.js';
|
|
3
3
|
import { ESP32ToolConsole } from './console.js';
|
|
4
4
|
import { HexEditor } from './hex-editor.js';
|
|
5
|
+
import { NVSEditor } from './nvs-editor.js';
|
|
5
6
|
|
|
6
7
|
// Make requestSerialPort available globally for esptool.js
|
|
7
8
|
// Use defensive assignment to avoid accidental overwrites
|
|
@@ -34,6 +35,7 @@ let currentMacAddr = null; // Store MAC address globally
|
|
|
34
35
|
let isConnected = false; // Track connection state
|
|
35
36
|
let consoleInstance = null; // ESP32ToolConsole instance
|
|
36
37
|
let hexEditorInstance = null; // HexEditor instance
|
|
38
|
+
let nvsEditorInstance = null; // NVSEditor instance
|
|
37
39
|
let baudRateBeforeConsole = null; // Store baudrate before opening console
|
|
38
40
|
let espLoaderBeforeConsole = null; // Store original ESPLoader before console
|
|
39
41
|
let chipFamilyBeforeConsole = null; // Store chipFamily before opening console
|
|
@@ -90,6 +92,14 @@ function clearAllCachedData() {
|
|
|
90
92
|
// Show the Read Partition Table button again
|
|
91
93
|
butReadPartitions.classList.remove('hidden');
|
|
92
94
|
|
|
95
|
+
// Close NVS editor if open
|
|
96
|
+
if (nvsEditorInstance) {
|
|
97
|
+
try { nvsEditorInstance.close(); } catch (_) {}
|
|
98
|
+
nvsEditorInstance = null;
|
|
99
|
+
}
|
|
100
|
+
nvseditorContainer.classList.add('hidden');
|
|
101
|
+
document.body.classList.remove('nvseditor-active');
|
|
102
|
+
|
|
93
103
|
// Hide Detect FS button
|
|
94
104
|
butDetectFS.classList.add('hidden');
|
|
95
105
|
|
|
@@ -232,6 +242,12 @@ const tabText = document.getElementById("tabText");
|
|
|
232
242
|
const tabHex = document.getElementById("tabHex");
|
|
233
243
|
const butHexEditor = document.getElementById("butHexEditor");
|
|
234
244
|
const hexeditorContainer = document.getElementById("hexeditor-container");
|
|
245
|
+
const butNVSEditor = document.getElementById("butNVSEditor");
|
|
246
|
+
const nvseditorContainer = document.getElementById("nvseditor-container");
|
|
247
|
+
|
|
248
|
+
// NVS and partition table layout constants
|
|
249
|
+
const PARTITION_TABLE_OFFSET = 0x8000;
|
|
250
|
+
const PARTITION_TABLE_SIZE = 0x1000;
|
|
235
251
|
|
|
236
252
|
let currentViewedFile = null;
|
|
237
253
|
let currentViewedFileData = null;
|
|
@@ -348,6 +364,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
348
364
|
butProgram.addEventListener("click", clickProgram);
|
|
349
365
|
butReadFlash.addEventListener("click", clickReadFlash);
|
|
350
366
|
butHexEditor.addEventListener("click", clickHexEditor);
|
|
367
|
+
butNVSEditor.addEventListener("click", clickNVSEditor);
|
|
351
368
|
butReadPartitions.addEventListener("click", clickReadPartitions);
|
|
352
369
|
butDetectFS.addEventListener("click", clickDetectFS);
|
|
353
370
|
butOpenFSManager.addEventListener("click", clickOpenFSManager);
|
|
@@ -800,12 +817,14 @@ async function clickConnect() {
|
|
|
800
817
|
if (isESP8266) {
|
|
801
818
|
// Hide partition table button for ESP8266
|
|
802
819
|
butReadPartitions.classList.add('hidden');
|
|
820
|
+
butNVSEditor.classList.add('hidden');
|
|
803
821
|
|
|
804
822
|
// Show ESP8266 filesystem detection button
|
|
805
823
|
butDetectFS.classList.remove('hidden');
|
|
806
824
|
} else {
|
|
807
825
|
// Show partition table button for ESP32
|
|
808
826
|
butReadPartitions.classList.remove('hidden');
|
|
827
|
+
butNVSEditor.classList.remove('hidden');
|
|
809
828
|
|
|
810
829
|
// Hide ESP8266 filesystem detection button
|
|
811
830
|
if (butDetectFS) {
|
|
@@ -1935,6 +1954,120 @@ async function clickHexEditor() {
|
|
|
1935
1954
|
}
|
|
1936
1955
|
}
|
|
1937
1956
|
|
|
1957
|
+
/**
|
|
1958
|
+
* @name clickNVSEditor
|
|
1959
|
+
* Click handler for the NVS Parser button.
|
|
1960
|
+
* Reads partition table, locates the NVS partition, reads it and opens the NVS editor.
|
|
1961
|
+
*/
|
|
1962
|
+
async function clickNVSEditor() {
|
|
1963
|
+
butNVSEditor.disabled = true;
|
|
1964
|
+
|
|
1965
|
+
// Guard against losing unsaved changes
|
|
1966
|
+
if (nvsEditorInstance && nvsEditorInstance.modified === true) {
|
|
1967
|
+
if (!confirm('You have unsaved changes in the NVS editor. Discard changes and reload?')) {
|
|
1968
|
+
butNVSEditor.disabled = false;
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
// User confirmed - close existing editor
|
|
1972
|
+
nvseditorContainer.classList.add('hidden');
|
|
1973
|
+
document.body.classList.remove('nvseditor-active');
|
|
1974
|
+
try { nvsEditorInstance.close(); } catch (_) {}
|
|
1975
|
+
nvsEditorInstance = null;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
try {
|
|
1979
|
+
// Step 1: Read partition table
|
|
1980
|
+
// Create and show NVS editor container with progress
|
|
1981
|
+
if (!nvsEditorInstance) {
|
|
1982
|
+
nvsEditorInstance = new NVSEditor(nvseditorContainer);
|
|
1983
|
+
}
|
|
1984
|
+
nvseditorContainer.classList.remove('hidden');
|
|
1985
|
+
document.body.classList.add('nvseditor-active');
|
|
1986
|
+
nvsEditorInstance.initProgressUI();
|
|
1987
|
+
nvsEditorInstance.showProgress('Reading partition table...', 0);
|
|
1988
|
+
|
|
1989
|
+
const ptData = await espStub.readFlash(PARTITION_TABLE_OFFSET, PARTITION_TABLE_SIZE);
|
|
1990
|
+
const partitions = parsePartitionTable(ptData);
|
|
1991
|
+
|
|
1992
|
+
// Step 2: Find NVS partition (type=0x01 data, subtype=0x02 nvs)
|
|
1993
|
+
const nvsPartition = partitions.find(p => p.type === 0x01 && p.subtype === 0x02);
|
|
1994
|
+
if (!nvsPartition) {
|
|
1995
|
+
throw new Error('No NVS partition found in partition table');
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
logMsg(`Found NVS partition "${nvsPartition.name}" at 0x${nvsPartition.offset.toString(16)}, size ${nvsPartition.size} bytes`);
|
|
1999
|
+
|
|
2000
|
+
// Step 3: Read the NVS partition
|
|
2001
|
+
nvsEditorInstance.showProgress('Reading NVS partition...', 10);
|
|
2002
|
+
const options = buildAdvancedOptions();
|
|
2003
|
+
const nvsData = await espStub.readFlash(
|
|
2004
|
+
nvsPartition.offset,
|
|
2005
|
+
nvsPartition.size,
|
|
2006
|
+
(packet, progress, totalSize) => {
|
|
2007
|
+
const pct = 10 + Math.floor((progress / totalSize) * 85);
|
|
2008
|
+
nvsEditorInstance.showProgress(
|
|
2009
|
+
`Reading NVS... ${Math.floor((progress / totalSize) * 100)}% (${(progress / 1024).toFixed(0)} / ${(totalSize / 1024).toFixed(0)} KB)`,
|
|
2010
|
+
pct
|
|
2011
|
+
);
|
|
2012
|
+
},
|
|
2013
|
+
options
|
|
2014
|
+
);
|
|
2015
|
+
|
|
2016
|
+
logMsg(`Successfully read ${nvsData.length} bytes from NVS partition`);
|
|
2017
|
+
|
|
2018
|
+
// Step 4: Open NVS editor
|
|
2019
|
+
nvsEditorInstance.showProgress('Parsing NVS data...', 95);
|
|
2020
|
+
nvsEditorInstance.open(nvsData, nvsPartition.offset, nvsPartition.name);
|
|
2021
|
+
|
|
2022
|
+
// Step 5: Set up write handler
|
|
2023
|
+
nvsEditorInstance.onWriteFlash = async (editedData) => {
|
|
2024
|
+
const editor = nvsEditorInstance;
|
|
2025
|
+
if (!editor) return;
|
|
2026
|
+
|
|
2027
|
+
editor.showProgress('Writing NVS partition...', 0);
|
|
2028
|
+
|
|
2029
|
+
try {
|
|
2030
|
+
const nvsBuffer = editedData.buffer.slice(
|
|
2031
|
+
editedData.byteOffset,
|
|
2032
|
+
editedData.byteOffset + editedData.byteLength
|
|
2033
|
+
);
|
|
2034
|
+
await espStub.flashData(
|
|
2035
|
+
nvsBuffer,
|
|
2036
|
+
(bytesWritten, totalBytes) => {
|
|
2037
|
+
const pct = Math.floor((bytesWritten / totalBytes) * 100);
|
|
2038
|
+
editor.showProgress(`Writing NVS... ${pct}%`, pct);
|
|
2039
|
+
},
|
|
2040
|
+
nvsPartition.offset
|
|
2041
|
+
);
|
|
2042
|
+
|
|
2043
|
+
editor.showProgress('Write complete!', 100);
|
|
2044
|
+
logMsg('NVS partition written successfully');
|
|
2045
|
+
await sleep(500);
|
|
2046
|
+
} finally {
|
|
2047
|
+
editor.hideProgress();
|
|
2048
|
+
}
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
// Step 6: Set up close handler
|
|
2052
|
+
nvsEditorInstance.onClose = () => {
|
|
2053
|
+
nvseditorContainer.classList.add('hidden');
|
|
2054
|
+
document.body.classList.remove('nvseditor-active');
|
|
2055
|
+
nvsEditorInstance = null;
|
|
2056
|
+
};
|
|
2057
|
+
|
|
2058
|
+
} catch (e) {
|
|
2059
|
+
errorMsg('Failed to open NVS editor: ' + e);
|
|
2060
|
+
nvseditorContainer.classList.add('hidden');
|
|
2061
|
+
document.body.classList.remove('nvseditor-active');
|
|
2062
|
+
if (nvsEditorInstance) {
|
|
2063
|
+
try { nvsEditorInstance.close(); } catch (_) {}
|
|
2064
|
+
nvsEditorInstance = null;
|
|
2065
|
+
}
|
|
2066
|
+
} finally {
|
|
2067
|
+
butNVSEditor.disabled = false;
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
|
|
1938
2071
|
/**
|
|
1939
2072
|
* @name clickOpenFSManager
|
|
1940
2073
|
* Click handler for the Open FS Manager button (ESP8266)
|
|
@@ -1967,8 +2100,6 @@ async function clickOpenFSManager() {
|
|
|
1967
2100
|
* Click handler for the read partitions button.
|
|
1968
2101
|
*/
|
|
1969
2102
|
async function clickReadPartitions() {
|
|
1970
|
-
const PARTITION_TABLE_OFFSET = 0x8000;
|
|
1971
|
-
const PARTITION_TABLE_SIZE = 0x1000; // Read 4KB to get all partitions
|
|
1972
2103
|
|
|
1973
2104
|
butReadPartitions.disabled = true;
|
|
1974
2105
|
butErase.disabled = true;
|
|
@@ -1976,8 +2107,7 @@ async function clickReadPartitions() {
|
|
|
1976
2107
|
butReadFlash.disabled = true;
|
|
1977
2108
|
|
|
1978
2109
|
try {
|
|
1979
|
-
logMsg(
|
|
1980
|
-
|
|
2110
|
+
logMsg(`Reading partition table from 0x${PARTITION_TABLE_OFFSET.toString(16)}...`);
|
|
1981
2111
|
const data = await espStub.readFlash(PARTITION_TABLE_OFFSET, PARTITION_TABLE_SIZE);
|
|
1982
2112
|
|
|
1983
2113
|
const partitions = parsePartitionTable(data);
|
|
@@ -2240,6 +2370,7 @@ function toggleUIToolbar(show) {
|
|
|
2240
2370
|
butErase.disabled = !show;
|
|
2241
2371
|
butReadFlash.disabled = !show;
|
|
2242
2372
|
butHexEditor.disabled = !show;
|
|
2373
|
+
butNVSEditor.disabled = !show;
|
|
2243
2374
|
butReadPartitions.disabled = !show;
|
|
2244
2375
|
}
|
|
2245
2376
|
|
|
@@ -2276,6 +2407,13 @@ function toggleUIConnected(connected) {
|
|
|
2276
2407
|
hexEditorInstance.close();
|
|
2277
2408
|
hexEditorInstance = null;
|
|
2278
2409
|
}
|
|
2410
|
+
// Close NVS editor if open (disconnect or unexpected port loss)
|
|
2411
|
+
if (nvsEditorInstance) {
|
|
2412
|
+
try { nvsEditorInstance.close(); } catch (_) {}
|
|
2413
|
+
nvsEditorInstance = null;
|
|
2414
|
+
}
|
|
2415
|
+
nvseditorContainer.classList.add('hidden');
|
|
2416
|
+
document.body.classList.remove('nvseditor-active');
|
|
2279
2417
|
}
|
|
2280
2418
|
butConnect.textContent = lbl;
|
|
2281
2419
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esp32tool",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Flash & Read ESP devices using WebSerial, Electron, and also Android mobile via WebUSB",
|
|
6
6
|
"main": "electron/main.cjs",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"test-pwa": "npx serve . -p 5004"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@electron-forge/cli": "^7.
|
|
44
|
+
"@electron-forge/cli": "^7.11.1",
|
|
45
45
|
"@electron-forge/maker-deb": "^7.11.1",
|
|
46
46
|
"@electron-forge/maker-dmg": "^7.11.1",
|
|
47
47
|
"@electron-forge/maker-rpm": "^7.11.1",
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"electron": "^40.4.1",
|
|
63
63
|
"electron-squirrel-startup": "^1.0.1",
|
|
64
64
|
"eslint": "^9.39.2",
|
|
65
|
-
"eslint-config-prettier": "^
|
|
66
|
-
"eslint-plugin-prettier": "^5.
|
|
65
|
+
"eslint-config-prettier": "^10.1.8",
|
|
66
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
67
67
|
"npm-run-all": "^4.1.5",
|
|
68
68
|
"prettier": "^3.8.1",
|
|
69
69
|
"rollup": "^4.57.0",
|
|
@@ -78,6 +78,9 @@
|
|
|
78
78
|
},
|
|
79
79
|
"overrides": {
|
|
80
80
|
"tmp": "^0.2.4",
|
|
81
|
-
"tar": "^7.5.6"
|
|
81
|
+
"tar": "^7.5.6",
|
|
82
|
+
"ajv": "^8.18.0",
|
|
83
|
+
"minimatch": "^10.2.1",
|
|
84
|
+
"@electron/asar": "^4.0.1"
|
|
82
85
|
}
|
|
83
86
|
}
|
package/screenshots/desktop.png
CHANGED
|
Binary file
|
package/screenshots/mobile.png
CHANGED
|
Binary file
|
package/src/esp_loader.ts
CHANGED
|
@@ -4468,7 +4468,7 @@ export class ESPLoader extends EventTarget {
|
|
|
4468
4468
|
} catch (err) {
|
|
4469
4469
|
if (err instanceof SlipReadError) {
|
|
4470
4470
|
this.logger.debug(
|
|
4471
|
-
|
|
4471
|
+
`${err.message} at byte 0x${resp.length.toString(16)}`,
|
|
4472
4472
|
);
|
|
4473
4473
|
|
|
4474
4474
|
// Send empty SLIP frame to abort the stub's read operation
|
|
@@ -4622,22 +4622,9 @@ export class ESPLoader extends EventTarget {
|
|
|
4622
4622
|
if (err instanceof SlipReadError) {
|
|
4623
4623
|
if (retryCount <= MAX_RETRIES) {
|
|
4624
4624
|
this.logger.debug(
|
|
4625
|
-
|
|
4625
|
+
`Cleared buffer and retrying (attempt ${retryCount}/${MAX_RETRIES})...`,
|
|
4626
4626
|
);
|
|
4627
|
-
|
|
4628
|
-
try {
|
|
4629
|
-
await this.drainInputBuffer(200);
|
|
4630
|
-
|
|
4631
|
-
// Clear application buffer
|
|
4632
|
-
await this.flushSerialBuffers();
|
|
4633
|
-
|
|
4634
|
-
// Wait before retry to let hardware settle
|
|
4635
|
-
await sleep(SYNC_TIMEOUT);
|
|
4636
|
-
|
|
4637
|
-
// Continue to retry the same chunk (will send NEW read command)
|
|
4638
|
-
} catch (drainErr) {
|
|
4639
|
-
this.logger.debug(`Buffer drain error: ${drainErr}`);
|
|
4640
|
-
}
|
|
4627
|
+
// Continue to retry the same chunk (will send NEW read command)
|
|
4641
4628
|
} else {
|
|
4642
4629
|
// All retries exhausted - attempt recovery by reloading stub
|
|
4643
4630
|
// IMPORTANT: Do NOT close port to keep ESP32 in bootloader mode
|
package/sw.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Service Worker for ESP32Tool PWA
|
|
2
|
-
const CACHE_NAME = 'esp32tool-v1.
|
|
2
|
+
const CACHE_NAME = 'esp32tool-v1.6.1';
|
|
3
3
|
const RUNTIME_CACHE = 'esp32tool-runtime';
|
|
4
4
|
|
|
5
5
|
// Core files to cache on install (relative paths work for any deployment path)
|
|
@@ -18,6 +18,7 @@ const CORE_ASSETS = [
|
|
|
18
18
|
// JavaScript
|
|
19
19
|
'./js/script.js',
|
|
20
20
|
'./js/hex-editor.js',
|
|
21
|
+
'./js/nvs-editor.js',
|
|
21
22
|
'./js/utilities.js',
|
|
22
23
|
'./js/webusb-serial.js',
|
|
23
24
|
'./js/modules/esptool.js',
|