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/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("Reading partition table from 0x8000...");
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.5.0",
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.6.1",
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": "^9.1.0",
66
- "eslint-plugin-prettier": "^5.2.1",
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
  }
Binary file
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
- `SLIP read error at ${resp.length} bytes: ${err.message}`,
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
- `${err.message} at 0x${currentAddr.toString(16)}. Draining buffer and retrying (attempt ${retryCount}/${MAX_RETRIES})...`,
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.5.0';
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',