esp32tool 1.4.1 → 1.6.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/js/script.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // Import WebUSB serial support for Android compatibility
2
2
  import { WebUSBSerial, requestSerialPort } from './webusb-serial.js';
3
3
  import { ESP32ToolConsole } from './console.js';
4
+ import { HexEditor } from './hex-editor.js';
5
+ import { NVSEditor } from './nvs-editor.js';
4
6
 
5
7
  // Make requestSerialPort available globally for esptool.js
6
8
  // Use defensive assignment to avoid accidental overwrites
@@ -32,6 +34,8 @@ let currentChipName = null; // Store chip name globally
32
34
  let currentMacAddr = null; // Store MAC address globally
33
35
  let isConnected = false; // Track connection state
34
36
  let consoleInstance = null; // ESP32ToolConsole instance
37
+ let hexEditorInstance = null; // HexEditor instance
38
+ let nvsEditorInstance = null; // NVSEditor instance
35
39
  let baudRateBeforeConsole = null; // Store baudrate before opening console
36
40
  let espLoaderBeforeConsole = null; // Store original ESPLoader before console
37
41
  let chipFamilyBeforeConsole = null; // Store chipFamily before opening console
@@ -88,6 +92,14 @@ function clearAllCachedData() {
88
92
  // Show the Read Partition Table button again
89
93
  butReadPartitions.classList.remove('hidden');
90
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
+
91
103
  // Hide Detect FS button
92
104
  butDetectFS.classList.add('hidden');
93
105
 
@@ -228,6 +240,14 @@ const butCloseFileViewer = document.getElementById("butCloseFileViewer");
228
240
  const butDownloadFromViewer = document.getElementById("butDownloadFromViewer");
229
241
  const tabText = document.getElementById("tabText");
230
242
  const tabHex = document.getElementById("tabHex");
243
+ const butHexEditor = document.getElementById("butHexEditor");
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;
231
251
 
232
252
  let currentViewedFile = null;
233
253
  let currentViewedFileData = null;
@@ -343,6 +363,8 @@ document.addEventListener("DOMContentLoaded", () => {
343
363
  butErase.addEventListener("click", clickErase);
344
364
  butProgram.addEventListener("click", clickProgram);
345
365
  butReadFlash.addEventListener("click", clickReadFlash);
366
+ butHexEditor.addEventListener("click", clickHexEditor);
367
+ butNVSEditor.addEventListener("click", clickNVSEditor);
346
368
  butReadPartitions.addEventListener("click", clickReadPartitions);
347
369
  butDetectFS.addEventListener("click", clickDetectFS);
348
370
  butOpenFSManager.addEventListener("click", clickOpenFSManager);
@@ -565,6 +587,36 @@ function enableStyleSheet(node, enabled) {
565
587
  node.disabled = !enabled;
566
588
  }
567
589
 
590
+ /**
591
+ * Build advanced flash read/write options from the UI controls.
592
+ * Returns the options object or undefined if advanced mode is off.
593
+ * @returns {{ chunkSize?: number, blockSize?: number, maxInFlight?: number } | undefined}
594
+ */
595
+ function buildAdvancedOptions() {
596
+ if (!advancedMode.checked) return undefined;
597
+
598
+ const validate = (name, value) => {
599
+ if (value === undefined) return undefined;
600
+ if (!Number.isFinite(value) || value <= 0) {
601
+ throw new Error(`Invalid ${name}: ${value}`);
602
+ }
603
+ return value;
604
+ };
605
+
606
+ const chunkSize = validate("chunkSize", parseInt(chunkSizeSelect.value));
607
+ const blockSize = validate("blockSize", parseInt(blockSizeSelect.value));
608
+ const maxInFlight = validate("maxInFlight", parseInt(maxInFlightSelect.value));
609
+
610
+ // blockSize and maxInFlight must both be finite or both non-finite
611
+ const hasBlockSize = Number.isFinite(blockSize);
612
+ const hasMaxInFlight = Number.isFinite(maxInFlight);
613
+ if (hasBlockSize !== hasMaxInFlight) {
614
+ throw new Error("blockSize and maxInFlight must be provided together");
615
+ }
616
+
617
+ return { chunkSize, blockSize, maxInFlight };
618
+ }
619
+
568
620
  /**
569
621
  * Parse flash size string (e.g., "256KB", "4MB") to bytes
570
622
  * @param {string} sizeStr - Flash size string with unit (KB or MB)
@@ -765,12 +817,14 @@ async function clickConnect() {
765
817
  if (isESP8266) {
766
818
  // Hide partition table button for ESP8266
767
819
  butReadPartitions.classList.add('hidden');
820
+ butNVSEditor.classList.add('hidden');
768
821
 
769
822
  // Show ESP8266 filesystem detection button
770
823
  butDetectFS.classList.remove('hidden');
771
824
  } else {
772
825
  // Show partition table button for ESP32
773
826
  butReadPartitions.classList.remove('hidden');
827
+ butNVSEditor.classList.remove('hidden');
774
828
 
775
829
  // Hide ESP8266 filesystem detection button
776
830
  if (butDetectFS) {
@@ -1721,30 +1775,8 @@ async function clickReadFlash() {
1721
1775
  const progressBar = readProgress.querySelector("div");
1722
1776
 
1723
1777
  // Prepare options object if advanced mode is enabled
1724
- // Option validation helpers
1725
- const validateOption = (name, value) => {
1726
- if (value === undefined) return undefined;
1727
- if (!Number.isFinite(value) || value <= 0) {
1728
- throw new Error(`Invalid ${name}: ${value}`);
1729
- }
1730
- return value;
1731
- };
1732
-
1733
- let options = undefined;
1734
- let chunkSizeOpt, blockSizeOpt, maxInFlightOpt;
1735
- if (advancedMode.checked) {
1736
- chunkSizeOpt = validateOption("chunkSize", parseInt(chunkSizeSelect.value));
1737
- blockSizeOpt = validateOption("blockSize", parseInt(blockSizeSelect.value));
1738
- maxInFlightOpt = validateOption("maxInFlight", parseInt(maxInFlightSelect.value));
1739
- if ((blockSizeOpt ?? maxInFlightOpt) &&
1740
- (blockSizeOpt === undefined || maxInFlightOpt === undefined)) {
1741
- throw new Error("blockSize and maxInFlight must be provided together");
1742
- }
1743
- options = {
1744
- chunkSize: chunkSizeOpt,
1745
- blockSize: blockSizeOpt,
1746
- maxInFlight: maxInFlightOpt
1747
- };
1778
+ const options = buildAdvancedOptions();
1779
+ if (options) {
1748
1780
  logMsg(`Advanced mode: chunkSize=0x${options.chunkSize?.toString(16)}, blockSize=${options.blockSize}, maxInFlight=${options.maxInFlight}`);
1749
1781
  }
1750
1782
 
@@ -1802,6 +1834,240 @@ async function clickReadFlash() {
1802
1834
  }
1803
1835
  }
1804
1836
 
1837
+ /**
1838
+ * @name clickHexEditor
1839
+ * Click handler for the Hex Editor button.
1840
+ * Reads the entire flash into a hex editor window.
1841
+ */
1842
+ async function clickHexEditor() {
1843
+ const offset = parseInt(readOffset.value, 16);
1844
+ const size = parseInt(readSize.value, 16);
1845
+
1846
+ if (isNaN(offset) || isNaN(size) || size <= 0) {
1847
+ errorMsg("Invalid offset or size value");
1848
+ return;
1849
+ }
1850
+
1851
+ // Disable button to prevent concurrent reads
1852
+ butHexEditor.disabled = true;
1853
+
1854
+ try {
1855
+ // Create and show hex editor
1856
+ if (!hexEditorInstance) {
1857
+ hexEditorInstance = new HexEditor(hexeditorContainer);
1858
+ }
1859
+
1860
+ // Show the container and progress overlay immediately
1861
+ hexeditorContainer.classList.remove('hidden');
1862
+ document.body.classList.add('hexeditor-active');
1863
+
1864
+ // Build a temporary UI for progress display
1865
+ hexEditorInstance.initProgressUI();
1866
+ hexEditorInstance.showProgress('Reading flash...', 0);
1867
+
1868
+ // Prepare options
1869
+ const options = buildAdvancedOptions();
1870
+
1871
+ const data = await espStub.readFlash(
1872
+ offset,
1873
+ size,
1874
+ (packet, progress, totalSize) => {
1875
+ const pct = Math.floor((progress / totalSize) * 100);
1876
+ hexEditorInstance.showProgress(
1877
+ `Reading flash... ${pct}% (${(progress / 1024).toFixed(0)} / ${(totalSize / 1024).toFixed(0)} KB)`,
1878
+ pct
1879
+ );
1880
+ },
1881
+ options
1882
+ );
1883
+
1884
+ logMsg(`Successfully read ${data.length} bytes from flash for hex editor`);
1885
+
1886
+ // Open hex editor with the data
1887
+ hexEditorInstance.open(data, offset);
1888
+
1889
+ // Set up write handler
1890
+ hexEditorInstance.onWriteFlash = async (editedData, modifiedOffsets) => {
1891
+ // Snapshot the editor instance to avoid null dereference if disconnected mid-write
1892
+ const editor = hexEditorInstance;
1893
+ if (!editor) return;
1894
+
1895
+ // Group modified bytes into contiguous sectors (4KB aligned)
1896
+ const SECTOR_SIZE = 0x1000;
1897
+ const sectors = new Set();
1898
+ for (const off of modifiedOffsets) {
1899
+ sectors.add(Math.floor(off / SECTOR_SIZE) * SECTOR_SIZE);
1900
+ }
1901
+
1902
+ const sortedSectors = [...sectors].sort((a, b) => a - b);
1903
+ let written = 0;
1904
+ const total = sortedSectors.length;
1905
+
1906
+ for (const sectorOff of sortedSectors) {
1907
+ const sectorEnd = Math.min(sectorOff + SECTOR_SIZE, editedData.length);
1908
+ const sectorData = editedData.slice(sectorOff, sectorEnd);
1909
+ const flashAddr = offset + sectorOff;
1910
+
1911
+ editor.showProgress(
1912
+ `Writing sector at 0x${flashAddr.toString(16).toUpperCase()}... (${written + 1}/${total})`,
1913
+ Math.floor((written / total) * 100)
1914
+ );
1915
+
1916
+ await espStub.flashData(
1917
+ sectorData.buffer,
1918
+ (bytesWritten, totalBytes) => {
1919
+ const sectorPct = Math.floor((bytesWritten / totalBytes) * 100);
1920
+ editor.showProgress(
1921
+ `Writing sector at 0x${flashAddr.toString(16).toUpperCase()}... ${sectorPct}% (${written + 1}/${total})`,
1922
+ Math.floor(((written + bytesWritten / totalBytes) / total) * 100)
1923
+ );
1924
+ },
1925
+ flashAddr
1926
+ );
1927
+ written++;
1928
+ }
1929
+
1930
+ editor.showProgress('Write complete!', 100);
1931
+ logMsg(`Successfully wrote ${total} sector(s) to flash`);
1932
+ await sleep(500);
1933
+ editor.hideProgress();
1934
+ };
1935
+
1936
+ // Set up close handler
1937
+ hexEditorInstance.onClose = () => {
1938
+ hexeditorContainer.classList.add('hidden');
1939
+ document.body.classList.remove('hexeditor-active');
1940
+ hexEditorInstance = null;
1941
+ };
1942
+
1943
+ } catch (e) {
1944
+ errorMsg("Failed to read flash for hex editor: " + e);
1945
+ hexeditorContainer.classList.add('hidden');
1946
+ document.body.classList.remove('hexeditor-active');
1947
+ if (hexEditorInstance) {
1948
+ // close() handles ResizeObserver/keydown cleanup; onClose is not yet wired here so null manually
1949
+ try { hexEditorInstance.close(); } catch (_) {}
1950
+ hexEditorInstance = null;
1951
+ }
1952
+ } finally {
1953
+ butHexEditor.disabled = false;
1954
+ }
1955
+ }
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
+
1805
2071
  /**
1806
2072
  * @name clickOpenFSManager
1807
2073
  * Click handler for the Open FS Manager button (ESP8266)
@@ -1834,8 +2100,6 @@ async function clickOpenFSManager() {
1834
2100
  * Click handler for the read partitions button.
1835
2101
  */
1836
2102
  async function clickReadPartitions() {
1837
- const PARTITION_TABLE_OFFSET = 0x8000;
1838
- const PARTITION_TABLE_SIZE = 0x1000; // Read 4KB to get all partitions
1839
2103
 
1840
2104
  butReadPartitions.disabled = true;
1841
2105
  butErase.disabled = true;
@@ -1843,8 +2107,7 @@ async function clickReadPartitions() {
1843
2107
  butReadFlash.disabled = true;
1844
2108
 
1845
2109
  try {
1846
- logMsg("Reading partition table from 0x8000...");
1847
-
2110
+ logMsg(`Reading partition table from 0x${PARTITION_TABLE_OFFSET.toString(16)}...`);
1848
2111
  const data = await espStub.readFlash(PARTITION_TABLE_OFFSET, PARTITION_TABLE_SIZE);
1849
2112
 
1850
2113
  const partitions = parsePartitionTable(data);
@@ -2106,6 +2369,8 @@ function toggleUIToolbar(show) {
2106
2369
  }
2107
2370
  butErase.disabled = !show;
2108
2371
  butReadFlash.disabled = !show;
2372
+ butHexEditor.disabled = !show;
2373
+ butNVSEditor.disabled = !show;
2109
2374
  butReadPartitions.disabled = !show;
2110
2375
  }
2111
2376
 
@@ -2136,6 +2401,19 @@ function toggleUIConnected(connected) {
2136
2401
  if (commands) commands.classList.remove("hidden");
2137
2402
  consoleSwitch.checked = false;
2138
2403
  saveSetting("console", false);
2404
+
2405
+ // Close hex editor if open
2406
+ if (hexEditorInstance) {
2407
+ hexEditorInstance.close();
2408
+ hexEditorInstance = null;
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');
2139
2417
  }
2140
2418
  butConnect.textContent = lbl;
2141
2419
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "esp32tool",
3
- "version": "1.4.1",
3
+ "version": "1.6.0",
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",
@@ -59,7 +59,7 @@
59
59
  "@types/serialport": "^10.2.0",
60
60
  "@types/w3c-web-serial": "^1.0.7",
61
61
  "archiver": "^7.0.1",
62
- "electron": "^40.2.1",
62
+ "electron": "^40.4.1",
63
63
  "electron-squirrel-startup": "^1.0.1",
64
64
  "eslint": "^9.39.2",
65
65
  "eslint-config-prettier": "^10.1.8",
@@ -67,9 +67,9 @@
67
67
  "npm-run-all": "^4.1.5",
68
68
  "prettier": "^3.8.1",
69
69
  "rollup": "^4.57.0",
70
- "serve": "^14.2.4",
70
+ "serve": "^14.2.5",
71
71
  "typescript": "^5.7.3",
72
- "typescript-eslint": "^8.55.0"
72
+ "typescript-eslint": "^8.56.0"
73
73
  },
74
74
  "dependencies": {
75
75
  "pako": "^2.1.0",
Binary file
Binary file
package/src/cli.ts CHANGED
@@ -280,7 +280,7 @@ async function connectViaUSB(
280
280
  if (webPort) {
281
281
  try {
282
282
  await webPort.close();
283
- } catch (closeErr) {
283
+ } catch (_closeErr) {
284
284
  // Ignore close errors
285
285
  }
286
286
  }
@@ -582,7 +582,7 @@ async function main() {
582
582
  if (esploader) {
583
583
  try {
584
584
  await esploader.disconnect();
585
- } catch (disconnectErr) {
585
+ } catch (_disconnectErr) {
586
586
  // Ignore disconnect errors during error handling
587
587
  }
588
588
  }
package/src/const.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  import { toByteArray } from "./util";
2
2
 
3
3
  export interface Logger {
4
- /* eslint-disable @typescript-eslint/no-explicit-any */
5
4
  log(msg: string, ...args: any[]): void;
6
5
  error(msg: string, ...args: any[]): void;
7
6
  debug(msg: string, ...args: any[]): void;
8
- /* eslint-enable @typescript-eslint/no-explicit-any */
9
7
  }
10
8
 
11
9
  export const baudRates = [
package/src/esp_loader.ts CHANGED
@@ -1026,14 +1026,18 @@ export class ESPLoader extends EventTarget {
1026
1026
  }
1027
1027
  } else {
1028
1028
  if (step.dtr !== undefined) {
1029
- webusb
1030
- ? await this.setDTRWebUSB(step.dtr)
1031
- : await this.setDTR(step.dtr);
1029
+ if (webusb) {
1030
+ await this.setDTRWebUSB(step.dtr);
1031
+ } else {
1032
+ await this.setDTR(step.dtr);
1033
+ }
1032
1034
  }
1033
1035
  if (step.rts !== undefined) {
1034
- webusb
1035
- ? await this.setRTSWebUSB(step.rts)
1036
- : await this.setRTS(step.rts);
1036
+ if (webusb) {
1037
+ await this.setRTSWebUSB(step.rts);
1038
+ } else {
1039
+ await this.setRTS(step.rts);
1040
+ }
1037
1041
  }
1038
1042
  }
1039
1043
  if (step.delayMs) await sleep(step.delayMs);
@@ -1558,7 +1562,7 @@ export class ESPLoader extends EventTarget {
1558
1562
  `Connected CDC/JTAG successfully with ${strategy.name} reset.`,
1559
1563
  );
1560
1564
  return;
1561
- } catch (error) {
1565
+ } catch (_error) {
1562
1566
  throw new Error("Sync timeout or abandoned");
1563
1567
  }
1564
1568
  }
@@ -2492,7 +2496,7 @@ export class ESPLoader extends EventTarget {
2492
2496
 
2493
2497
  // Restart Readloop
2494
2498
  this.readLoop();
2495
- } catch (e) {
2499
+ } catch (_e) {
2496
2500
  // this.logger.error(`Reconfigure port error: ${e}`);
2497
2501
  // throw new Error(`Unable to change the baud rate to ${baud}: ${e}`);
2498
2502
  } finally {
@@ -3285,7 +3289,7 @@ export class ESPLoader extends EventTarget {
3285
3289
  // Wait for pending writes to complete
3286
3290
  try {
3287
3291
  await this._writeChain;
3288
- } catch (err) {
3292
+ } catch (_err) {
3289
3293
  // this.logger.debug(`Pending write error during disconnect: ${err}`);
3290
3294
  }
3291
3295
 
@@ -3294,7 +3298,7 @@ export class ESPLoader extends EventTarget {
3294
3298
  try {
3295
3299
  await this._writer.close();
3296
3300
  this._writer.releaseLock();
3297
- } catch (err) {
3301
+ } catch (_err) {
3298
3302
  // this.logger.debug(`Writer close/release error: ${err}`);
3299
3303
  }
3300
3304
  this._writer = undefined;
@@ -3305,7 +3309,7 @@ export class ESPLoader extends EventTarget {
3305
3309
  const writer = this.port.writable.getWriter();
3306
3310
  await writer.close();
3307
3311
  writer.releaseLock();
3308
- } catch (err) {
3312
+ } catch (_err) {
3309
3313
  // this.logger.debug(`Direct writer close error: ${err}`);
3310
3314
  }
3311
3315
  }
@@ -3334,7 +3338,7 @@ export class ESPLoader extends EventTarget {
3334
3338
  // Only cancel if reader is still active
3335
3339
  try {
3336
3340
  this._reader.cancel();
3337
- } catch (err) {
3341
+ } catch (_err) {
3338
3342
  // Reader already released, resolve immediately
3339
3343
  clearTimeout(timeout);
3340
3344
  resolve(undefined);
@@ -3365,7 +3369,7 @@ export class ESPLoader extends EventTarget {
3365
3369
  // Wait for pending writes to complete
3366
3370
  try {
3367
3371
  await this._writeChain;
3368
- } catch (err) {
3372
+ } catch (_err) {
3369
3373
  // this.logger.debug(`Pending write error during release: ${err}`);
3370
3374
  }
3371
3375
 
@@ -87,7 +87,7 @@ export function createNodeUSBAdapter(
87
87
  if (device.configDescriptor?.bConfigurationValue !== 1) {
88
88
  device.setConfiguration(1);
89
89
  }
90
- } catch (err) {
90
+ } catch (_err) {
91
91
  // Already configured
92
92
  }
93
93
 
@@ -145,7 +145,7 @@ export function createNodeUSBAdapter(
145
145
  if (usbInterface.isKernelDriverActive()) {
146
146
  usbInterface.detachKernelDriver();
147
147
  }
148
- } catch (err) {
148
+ } catch (_err) {
149
149
  // Ignore - may not be supported on all platforms
150
150
  }
151
151
 
@@ -189,7 +189,7 @@ export function createNodeUSBAdapter(
189
189
  if (vendorId === 0x10c4) {
190
190
  try {
191
191
  // Clear halt on endpoints
192
- await new Promise<void>((resolve, reject) => {
192
+ await new Promise<void>((resolve, _reject) => {
193
193
  device.controlTransfer(
194
194
  0x02, // Clear Feature, Endpoint
195
195
  0x01, // ENDPOINT_HALT
@@ -203,7 +203,7 @@ export function createNodeUSBAdapter(
203
203
  );
204
204
  });
205
205
 
206
- await new Promise<void>((resolve, reject) => {
206
+ await new Promise<void>((resolve, _reject) => {
207
207
  device.controlTransfer(
208
208
  0x02, // Clear Feature, Endpoint
209
209
  0x01, // ENDPOINT_HALT
@@ -216,7 +216,7 @@ export function createNodeUSBAdapter(
216
216
  },
217
217
  );
218
218
  });
219
- } catch (err) {
219
+ } catch (_err) {
220
220
  // Ignore
221
221
  }
222
222
  }
@@ -234,7 +234,7 @@ export function createNodeUSBAdapter(
234
234
  try {
235
235
  endpointIn.stopPoll();
236
236
  endpointIn.removeAllListeners();
237
- } catch (err) {
237
+ } catch (_err) {
238
238
  // Ignore
239
239
  }
240
240
  }
@@ -242,7 +242,7 @@ export function createNodeUSBAdapter(
242
242
  if (readableStream) {
243
243
  try {
244
244
  await readableStream.cancel();
245
- } catch (err) {
245
+ } catch (_err) {
246
246
  // Ignore
247
247
  }
248
248
  readableStream = null;
@@ -251,7 +251,7 @@ export function createNodeUSBAdapter(
251
251
  if (writableStream) {
252
252
  try {
253
253
  await writableStream.close();
254
- } catch (err) {
254
+ } catch (_err) {
255
255
  // Ignore
256
256
  }
257
257
  writableStream = null;
@@ -264,14 +264,14 @@ export function createNodeUSBAdapter(
264
264
  try {
265
265
  const usbInterface = device.interface(interfaceNumber);
266
266
  usbInterface.release(true, () => {});
267
- } catch (err) {
267
+ } catch (_err) {
268
268
  // Ignore
269
269
  }
270
270
  }
271
271
 
272
272
  try {
273
273
  device.close();
274
- } catch (err) {
274
+ } catch (_err) {
275
275
  // Ignore
276
276
  }
277
277
  },
@@ -469,7 +469,7 @@ export function createNodeUSBAdapter(
469
469
  try {
470
470
  logger.error(`USB read error: ${err.message}`);
471
471
  // Don't close on error, just log it
472
- } catch (e) {
472
+ } catch (_e) {
473
473
  // Ignore errors in error handler
474
474
  }
475
475
  });
@@ -477,7 +477,7 @@ export function createNodeUSBAdapter(
477
477
  endpointIn!.on("end", () => {
478
478
  try {
479
479
  controller.close();
480
- } catch (err) {
480
+ } catch (_err) {
481
481
  // Ignore errors when closing controller
482
482
  }
483
483
  });
@@ -488,7 +488,7 @@ export function createNodeUSBAdapter(
488
488
  try {
489
489
  endpointIn.stopPoll();
490
490
  endpointIn.removeAllListeners();
491
- } catch (err) {
491
+ } catch (_err) {
492
492
  // Ignore
493
493
  }
494
494
  }