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.
Files changed (49) hide show
  1. package/.nojekyll +0 -0
  2. package/README.md +100 -6
  3. package/apple-touch-icon.png +0 -0
  4. package/build-electron-cli.cjs +177 -0
  5. package/build-single-binary.cjs +295 -0
  6. package/css/light.css +11 -0
  7. package/css/style.css +225 -35
  8. package/dist/cli.d.ts +17 -0
  9. package/dist/cli.js +458 -0
  10. package/dist/esp_loader.d.ts +129 -21
  11. package/dist/esp_loader.js +1227 -222
  12. package/dist/index.d.ts +2 -1
  13. package/dist/index.js +37 -4
  14. package/dist/node-usb-adapter.d.ts +47 -0
  15. package/dist/node-usb-adapter.js +725 -0
  16. package/dist/stubs/index.d.ts +1 -2
  17. package/dist/stubs/index.js +4 -0
  18. package/dist/web/index.js +1 -1
  19. package/electron/cli-main.cjs +74 -0
  20. package/electron/main.cjs +338 -0
  21. package/electron/main.js +7 -2
  22. package/favicon.ico +0 -0
  23. package/fix-cli-imports.cjs +127 -0
  24. package/generate-icons.sh +89 -0
  25. package/icons/icon-128.png +0 -0
  26. package/icons/icon-144.png +0 -0
  27. package/icons/icon-152.png +0 -0
  28. package/icons/icon-192.png +0 -0
  29. package/icons/icon-384.png +0 -0
  30. package/icons/icon-512.png +0 -0
  31. package/icons/icon-72.png +0 -0
  32. package/icons/icon-96.png +0 -0
  33. package/index.html +94 -64
  34. package/install-android.html +411 -0
  35. package/js/modules/esptool.js +1 -1
  36. package/js/script.js +165 -160
  37. package/js/webusb-serial.js +1017 -0
  38. package/license.md +1 -1
  39. package/manifest.json +89 -0
  40. package/package.cli.json +29 -0
  41. package/package.json +31 -21
  42. package/screenshots/desktop.png +0 -0
  43. package/screenshots/mobile.png +0 -0
  44. package/src/cli.ts +618 -0
  45. package/src/esp_loader.ts +1438 -254
  46. package/src/index.ts +69 -3
  47. package/src/node-usb-adapter.ts +924 -0
  48. package/src/stubs/index.ts +4 -1
  49. package/sw.js +155 -0
package/index.html CHANGED
@@ -5,6 +5,14 @@
5
5
  <meta charset="utf-8" />
6
6
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1" />
8
+ <meta name="description" content="Flash & Read ESP devices using WebSerial and WebUSB" />
9
+ <meta name="theme-color" content="#1a1a1a" />
10
+ <link rel="manifest" href="manifest.json" />
11
+ <meta name="mobile-web-app-capable" content="yes" />
12
+ <meta name="apple-mobile-web-app-capable" content="yes" />
13
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
14
+ <meta name="apple-mobile-web-app-title" content="ESP32Tool" />
15
+ <link rel="apple-touch-icon" href="icons/icon-192.png" />
8
16
  <script>
9
17
  // Redirect to HTTPS if HTTP is requested.
10
18
  if (
@@ -33,92 +41,114 @@
33
41
  />
34
42
 
35
43
  <!-- import the webpage's javascript file -->
36
- <script module>
44
+ <script type="module">
37
45
  window.esptoolPackage = import(
38
- // In development we import locally.
39
- window.location.hostname === "localhost"
40
- ? "./js/modules/esptool.js"
41
- : "./js/modules/esptool.js"
46
+ "./js/modules/esptool.js"
42
47
  );
43
48
  </script>
44
- <script src="js/script.js" module defer></script>
49
+ <script src="js/script.js" type="module" defer></script>
50
+
51
+ <script>
52
+ // Service Worker for PWA functionality
53
+ // Derive base path from current document URL (more robust than repo-name regex)
54
+ const basePath = new URL(".", window.location.href).pathname;
55
+
56
+ if ('serviceWorker' in navigator) {
57
+ window.addEventListener('load', () => {
58
+ navigator.serviceWorker.register(basePath + 'sw.js', { scope: basePath })
59
+ .then((registration) => {
60
+ console.log('[PWA] Service Worker registered:', registration.scope);
61
+
62
+ // Check for updates
63
+ registration.addEventListener('updatefound', () => {
64
+ const newWorker = registration.installing;
65
+ newWorker.addEventListener('statechange', () => {
66
+ if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
67
+ console.log('[PWA] New version available! Reload to update.');
68
+ }
69
+ });
70
+ });
71
+ })
72
+ .catch((error) => {
73
+ console.log('[PWA] Service Worker registration failed:', error);
74
+ });
75
+ });
76
+ }
77
+ </script>
45
78
  </head>
46
79
  <body>
47
80
  <header class="header">
48
- <div class="left">
49
- <div class="title">ESP32Tool</div>
50
- </div>
51
- <div class="right">
52
- <div class="controls-row">
53
- <label for="showlog">Show Log</label>
54
- <div class="onoffswitch">
55
- <input
56
- type="checkbox"
57
- name="showlog"
58
- class="onoffswitch-checkbox"
59
- id="showlog"
60
- />
61
- <label class="onoffswitch-label" for="showlog">
62
- <span class="onoffswitch-inner"></span>
63
- <span class="onoffswitch-switch"></span>
64
- </label>
65
- </div>
66
-
67
- <span class="log-controls">
68
- <label for="autoscroll">Autoscroll</label>
69
- <div class="onoffswitch">
70
- <input
71
- type="checkbox"
72
- name="autoscroll"
73
- class="onoffswitch-checkbox"
74
- id="autoscroll"
75
- />
76
- <label class="onoffswitch-label" for="autoscroll">
77
- <span class="onoffswitch-inner"></span>
78
- <span class="onoffswitch-switch"></span>
79
- </label>
80
- </div>
81
- <button id="butClear" type="button" class="small-btn">
82
- Clear
83
- </button>
84
- </span>
81
+ <div class="header-content">
82
+ <button id="butConnect" type="button">Connect</button>
83
+ <label for="baudRate">Baud</label>
84
+ <select id="baudRate"></select>
85
+
86
+ <label for="showlog">Show Log</label>
87
+ <div class="onoffswitch">
88
+ <input
89
+ type="checkbox"
90
+ name="showlog"
91
+ class="onoffswitch-checkbox"
92
+ id="showlog"
93
+ />
94
+ <label class="onoffswitch-label" for="showlog">
95
+ <span class="onoffswitch-inner"></span>
96
+ <span class="onoffswitch-switch"></span>
97
+ </label>
98
+ </div>
85
99
 
86
- <label for="debugmode">Debug</label>
100
+ <span class="log-controls">
101
+ <label for="autoscroll">Autoscroll</label>
87
102
  <div class="onoffswitch">
88
103
  <input
89
104
  type="checkbox"
90
- name="debugmode"
105
+ name="autoscroll"
91
106
  class="onoffswitch-checkbox"
92
- id="debugmode"
107
+ id="autoscroll"
93
108
  />
94
- <label class="onoffswitch-label" for="debugmode">
109
+ <label class="onoffswitch-label" for="autoscroll">
95
110
  <span class="onoffswitch-inner"></span>
96
111
  <span class="onoffswitch-switch"></span>
97
112
  </label>
98
113
  </div>
114
+ <button id="butClear" type="button" class="small-btn">
115
+ Clear
116
+ </button>
117
+ </span>
99
118
 
100
- <label for="darkmode">Dark</label>
101
- <div class="onoffswitch">
102
- <input
103
- type="checkbox"
104
- name="darkmode"
105
- class="onoffswitch-checkbox"
106
- id="darkmode"
107
- />
108
- <label class="onoffswitch-label" for="darkmode">
109
- <span class="onoffswitch-inner"></span>
110
- <span class="onoffswitch-switch"></span>
111
- </label>
112
- </div>
119
+ <label for="debugmode">Debug</label>
120
+ <div class="onoffswitch">
121
+ <input
122
+ type="checkbox"
123
+ name="debugmode"
124
+ class="onoffswitch-checkbox"
125
+ id="debugmode"
126
+ />
127
+ <label class="onoffswitch-label" for="debugmode">
128
+ <span class="onoffswitch-inner"></span>
129
+ <span class="onoffswitch-switch"></span>
130
+ </label>
131
+ </div>
132
+
133
+ <label for="darkmode">Dark</label>
134
+ <div class="onoffswitch">
135
+ <input
136
+ type="checkbox"
137
+ name="darkmode"
138
+ class="onoffswitch-checkbox"
139
+ id="darkmode"
140
+ />
141
+ <label class="onoffswitch-label" for="darkmode">
142
+ <span class="onoffswitch-inner"></span>
143
+ <span class="onoffswitch-switch"></span>
144
+ </label>
113
145
  </div>
114
- <select id="baudRate"></select>
115
- <button id="butConnect" type="button">Connect</button>
116
146
  </div>
117
147
  </header>
118
148
  <main class="main">
119
149
  <div id="notSupported" class="notSupported">
120
- Sorry, <b>Web Serial</b> is not supported on this device, make sure
121
- you're running Chrome 89 or later.
150
+ Sorry, <b>Web Serial</b> or <b>WebUSB</b> is not supported on this device.
151
+ Make sure you're running Chrome 89 or later (Desktop) or Chrome 61+ (Android with USB OTG).
122
152
  </div>
123
153
  <div id="app">
124
154
  <div id="commands">
@@ -0,0 +1,411 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Install ESP32Tool on Android</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
16
+ line-height: 1.6;
17
+ color: #333;
18
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
19
+ min-height: 100vh;
20
+ padding: 20px;
21
+ }
22
+
23
+ .container {
24
+ max-width: 800px;
25
+ margin: 0 auto;
26
+ background: white;
27
+ border-radius: 20px;
28
+ padding: 40px;
29
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
30
+ }
31
+
32
+ h1 {
33
+ color: #667eea;
34
+ margin-bottom: 10px;
35
+ font-size: 2.5em;
36
+ }
37
+
38
+ .subtitle {
39
+ color: #666;
40
+ margin-bottom: 30px;
41
+ font-size: 1.2em;
42
+ }
43
+
44
+ .step {
45
+ background: #f8f9fa;
46
+ border-left: 4px solid #667eea;
47
+ padding: 20px;
48
+ margin: 20px 0;
49
+ border-radius: 8px;
50
+ }
51
+
52
+ .step-number {
53
+ display: inline-block;
54
+ background: #667eea;
55
+ color: white;
56
+ width: 30px;
57
+ height: 30px;
58
+ border-radius: 50%;
59
+ text-align: center;
60
+ line-height: 30px;
61
+ font-weight: bold;
62
+ margin-right: 10px;
63
+ }
64
+
65
+ .step h3 {
66
+ display: inline-block;
67
+ color: #333;
68
+ margin-bottom: 10px;
69
+ }
70
+
71
+ .step p {
72
+ margin-left: 40px;
73
+ color: #666;
74
+ }
75
+
76
+ .requirements {
77
+ background: #fff3cd;
78
+ border: 2px solid #ffc107;
79
+ border-radius: 8px;
80
+ padding: 20px;
81
+ margin: 30px 0;
82
+ }
83
+
84
+ .requirements h3 {
85
+ color: #856404;
86
+ margin-bottom: 10px;
87
+ }
88
+
89
+ .requirements ul {
90
+ margin-left: 20px;
91
+ color: #856404;
92
+ }
93
+
94
+ .requirements li {
95
+ margin: 5px 0;
96
+ }
97
+
98
+ .cta-button {
99
+ display: inline-block;
100
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
101
+ color: white;
102
+ padding: 15px 40px;
103
+ border-radius: 50px;
104
+ text-decoration: none;
105
+ font-weight: bold;
106
+ font-size: 1.2em;
107
+ margin: 20px 0;
108
+ transition: transform 0.2s;
109
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
110
+ }
111
+
112
+ .cta-button:hover {
113
+ transform: translateY(-2px);
114
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
115
+ }
116
+
117
+ .features {
118
+ display: grid;
119
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
120
+ gap: 20px;
121
+ margin: 30px 0;
122
+ }
123
+
124
+ .feature {
125
+ text-align: center;
126
+ padding: 20px;
127
+ }
128
+
129
+ .feature-icon {
130
+ font-size: 3em;
131
+ margin-bottom: 10px;
132
+ }
133
+
134
+ .feature h4 {
135
+ color: #667eea;
136
+ margin-bottom: 5px;
137
+ }
138
+
139
+ .note {
140
+ background: #d1ecf1;
141
+ border-left: 4px solid #0c5460;
142
+ padding: 15px;
143
+ margin: 20px 0;
144
+ border-radius: 4px;
145
+ color: #0c5460;
146
+ }
147
+
148
+ @media (max-width: 600px) {
149
+ .container {
150
+ padding: 20px;
151
+ }
152
+
153
+ h1 {
154
+ font-size: 2em;
155
+ }
156
+
157
+ .features {
158
+ grid-template-columns: 1fr;
159
+ }
160
+ }
161
+ </style>
162
+ <script>
163
+ // Detect browser language and show appropriate content
164
+ document.addEventListener('DOMContentLoaded', function() {
165
+ const userLang = navigator.language || navigator.userLanguage;
166
+ const isGerman = userLang.toLowerCase().startsWith('de');
167
+
168
+ if (isGerman) {
169
+ document.getElementById('content-en').style.display = 'none';
170
+ document.getElementById('content-de').style.display = 'block';
171
+ document.documentElement.lang = 'de';
172
+ document.title = 'ESP32Tool auf Android installieren';
173
+ } else {
174
+ document.getElementById('content-en').style.display = 'block';
175
+ document.getElementById('content-de').style.display = 'none';
176
+ document.documentElement.lang = 'en';
177
+ document.title = 'Install ESP32Tool on Android';
178
+ }
179
+ });
180
+ </script>
181
+ </head>
182
+ <body>
183
+ <!-- English Version -->
184
+ <div id="content-en" class="container">
185
+ <h1>📱 ESP32Tool for Android</h1>
186
+ <p class="subtitle">Flash and manage ESP devices directly on your Android smartphone!</p>
187
+
188
+ <div class="features">
189
+ <div class="feature">
190
+ <div class="feature-icon">⚡</div>
191
+ <h4>Fast</h4>
192
+ <p>Up to 10x faster than esptool.py</p>
193
+ </div>
194
+ <div class="feature">
195
+ <div class="feature-icon">📂</div>
196
+ <h4>Filesystem</h4>
197
+ <p>LittleFS, SPIFFS & FATFS support</p>
198
+ </div>
199
+ <div class="feature">
200
+ <div class="feature-icon">🔌</div>
201
+ <h4>USB OTG</h4>
202
+ <p>Direct USB connection</p>
203
+ </div>
204
+ <div class="feature">
205
+ <div class="feature-icon">🌐</div>
206
+ <h4>Offline</h4>
207
+ <p>Works without internet</p>
208
+ </div>
209
+ </div>
210
+
211
+ <div class="requirements">
212
+ <h3>⚙️ Requirements</h3>
213
+ <ul>
214
+ <li>Android 5.0+ (Lollipop or higher)</li>
215
+ <li>Chrome for Android (Version 61+, recommended: latest version)</li>
216
+ <li>USB OTG adapter or USB-C to USB-A cable</li>
217
+ <li>ESP32 or ESP8266 device</li>
218
+ </ul>
219
+ </div>
220
+
221
+ <h2 style="margin-top: 40px; color: #667eea;">📋 Installation Guide</h2>
222
+
223
+ <div class="step">
224
+ <span class="step-number">1</span>
225
+ <h3>Open Chrome</h3>
226
+ <p>Open this page in <strong>Chrome for Android</strong> (not Firefox or Samsung Internet)</p>
227
+ </div>
228
+
229
+ <div class="step">
230
+ <span class="step-number">2</span>
231
+ <h3>Navigate to main page</h3>
232
+ <p>Click the button below to go to the ESP32Tool app</p>
233
+ </div>
234
+
235
+ <div style="text-align: center;">
236
+ <a href="index.html" class="cta-button">🚀 Open ESP32Tool</a>
237
+ </div>
238
+
239
+ <div class="step">
240
+ <span class="step-number">3</span>
241
+ <h3>Install app</h3>
242
+ <p>Tap the <strong>menu (⋮)</strong> in the top right and select <strong>"Add to Home screen"</strong> or <strong>"Install app"</strong></p>
243
+ </div>
244
+
245
+ <div class="step">
246
+ <span class="step-number">4</span>
247
+ <h3>Confirm installation</h3>
248
+ <p>Confirm the installation. The app will appear on your home screen</p>
249
+ </div>
250
+
251
+ <div class="step">
252
+ <span class="step-number">5</span>
253
+ <h3>Connect ESP device</h3>
254
+ <p>Connect your ESP32/ESP8266 via USB OTG adapter to your Android device</p>
255
+ </div>
256
+
257
+ <div class="step">
258
+ <span class="step-number">6</span>
259
+ <h3>Grant USB permission</h3>
260
+ <p>Click "Connect" in the app and select your ESP device. Allow USB access when prompted</p>
261
+ </div>
262
+
263
+ <div class="note">
264
+ <strong>💡 Tip:</strong> The app works without installation directly in the browser. However, installation provides a better app experience and offline functionality.
265
+ </div>
266
+
267
+ <div class="note">
268
+ <strong>⚠️ Important:</strong> WebUSB only works with USB OTG (On-The-Go). Make sure your Android device supports USB Host Mode.
269
+ </div>
270
+
271
+ <h2 style="margin-top: 40px; color: #667eea;">🎯 Getting Started</h2>
272
+
273
+ <div class="step">
274
+ <h3>Flash firmware</h3>
275
+ <p>Select a .bin file, enter the offset (e.g. 0x0 for bootloader) and click "Program"</p>
276
+ </div>
277
+
278
+ <div class="step">
279
+ <h3>Manage filesystem</h3>
280
+ <p>Click "Read Partition Table" and then "Detect FS and Open Manager" to manage files</p>
281
+ </div>
282
+
283
+ <div class="step">
284
+ <h3>Read flash</h3>
285
+ <p>Enter address and size and click "Read Flash" to read the memory</p>
286
+ </div>
287
+
288
+ <div style="text-align: center; margin-top: 40px; padding-top: 40px; border-top: 2px solid #eee;">
289
+ <p style="color: #666;">
290
+ <strong>ESP32Tool</strong> – Made with ❤️ by Adafruit, Nabu Casa & Johann Obermeier<br>
291
+ <a href="https://github.com/Jason2866/esp32tool" style="color: #667eea;">GitHub</a> |
292
+ <a href="https://jason2866.github.io/esp32tool" style="color: #667eea;">Web Version</a>
293
+ </p>
294
+ </div>
295
+ </div>
296
+
297
+ <!-- German Version -->
298
+ <div id="content-de" class="container" style="display: none;">
299
+ <h1>📱 ESP32Tool für Android</h1>
300
+ <p class="subtitle">Flashe und verwalte ESP-Geräte direkt auf deinem Android-Smartphone!</p>
301
+
302
+ <div class="features">
303
+ <div class="feature">
304
+ <div class="feature-icon">⚡</div>
305
+ <h4>Schnell</h4>
306
+ <p>Bis zu 10x schneller als esptool.py</p>
307
+ </div>
308
+ <div class="feature">
309
+ <div class="feature-icon">📂</div>
310
+ <h4>Filesystem</h4>
311
+ <p>LittleFS, SPIFFS & FATFS Support</p>
312
+ </div>
313
+ <div class="feature">
314
+ <div class="feature-icon">🔌</div>
315
+ <h4>USB OTG</h4>
316
+ <p>Direkte USB-Verbindung</p>
317
+ </div>
318
+ <div class="feature">
319
+ <div class="feature-icon">🌐</div>
320
+ <h4>Offline</h4>
321
+ <p>Funktioniert ohne Internet</p>
322
+ </div>
323
+ </div>
324
+
325
+ <div class="requirements">
326
+ <h3>⚙️ Voraussetzungen</h3>
327
+ <ul>
328
+ <li>Android 5.0+ (Lollipop oder höher)</li>
329
+ <li>Chrome für Android (Version 61+, empfohlen: neueste Version)</li>
330
+ <li>USB OTG-Adapter oder USB-C zu USB-A Kabel</li>
331
+ <li>ESP32 oder ESP8266 Gerät</li>
332
+ </ul>
333
+ </div>
334
+
335
+ <h2 style="margin-top: 40px; color: #667eea;">📋 Installations-Anleitung</h2>
336
+
337
+ <div class="step">
338
+ <span class="step-number">1</span>
339
+ <h3>Chrome öffnen</h3>
340
+ <p>Öffne diese Seite in <strong>Chrome für Android</strong> (nicht Firefox oder Samsung Internet)</p>
341
+ </div>
342
+
343
+ <div class="step">
344
+ <span class="step-number">2</span>
345
+ <h3>Zur Hauptseite navigieren</h3>
346
+ <p>Klicke auf den Button unten, um zur ESP32Tool-App zu gelangen</p>
347
+ </div>
348
+
349
+ <div style="text-align: center;">
350
+ <a href="index.html" class="cta-button">🚀 ESP32Tool öffnen</a>
351
+ </div>
352
+
353
+ <div class="step">
354
+ <span class="step-number">3</span>
355
+ <h3>App installieren</h3>
356
+ <p>Tippe auf das <strong>Menü (⋮)</strong> oben rechts und wähle <strong>"Zum Startbildschirm hinzufügen"</strong> oder <strong>"App installieren"</strong></p>
357
+ </div>
358
+
359
+ <div class="step">
360
+ <span class="step-number">4</span>
361
+ <h3>Installation bestätigen</h3>
362
+ <p>Bestätige die Installation. Die App erscheint auf deinem Homescreen</p>
363
+ </div>
364
+
365
+ <div class="step">
366
+ <span class="step-number">5</span>
367
+ <h3>ESP-Gerät verbinden</h3>
368
+ <p>Verbinde dein ESP32/ESP8266 über den USB OTG-Adapter mit deinem Android-Gerät</p>
369
+ </div>
370
+
371
+ <div class="step">
372
+ <span class="step-number">6</span>
373
+ <h3>USB-Berechtigung erteilen</h3>
374
+ <p>Klicke auf "Connect" in der App und wähle dein ESP-Gerät aus. Erlaube den USB-Zugriff wenn gefragt</p>
375
+ </div>
376
+
377
+ <div class="note">
378
+ <strong>💡 Tipp:</strong> Die App funktioniert auch ohne Installation direkt im Browser. Die Installation bietet aber ein besseres App-Erlebnis und Offline-Funktionalität.
379
+ </div>
380
+
381
+ <div class="note">
382
+ <strong>⚠️ Wichtig:</strong> WebUSB funktioniert nur mit USB OTG (On-The-Go). Stelle sicher, dass dein Android-Gerät USB Host Mode unterstützt.
383
+ </div>
384
+
385
+ <h2 style="margin-top: 40px; color: #667eea;">🎯 Erste Schritte</h2>
386
+
387
+ <div class="step">
388
+ <h3>Firmware flashen</h3>
389
+ <p>Wähle eine .bin-Datei, gib den Offset ein (z.B. 0x0 für Bootloader) und klicke auf "Program"</p>
390
+ </div>
391
+
392
+ <div class="step">
393
+ <h3>Filesystem verwalten</h3>
394
+ <p>Klicke auf "Read Partition Table" und dann auf "Detect FS and Open Manager" um Dateien zu verwalten</p>
395
+ </div>
396
+
397
+ <div class="step">
398
+ <h3>Flash auslesen</h3>
399
+ <p>Gib Adresse und Größe ein und klicke auf "Read Flash" um den Speicher auszulesen</p>
400
+ </div>
401
+
402
+ <div style="text-align: center; margin-top: 40px; padding-top: 40px; border-top: 2px solid #eee;">
403
+ <p style="color: #666;">
404
+ <strong>ESP32Tool</strong> – Made with ❤️ by Adafruit, Nabu Casa & Johann Obermeier<br>
405
+ <a href="https://github.com/Jason2866/esp32tool" style="color: #667eea;">GitHub</a> |
406
+ <a href="https://jason2866.github.io/esp32tool" style="color: #667eea;">Web Version</a>
407
+ </p>
408
+ </div>
409
+ </div>
410
+ </body>
411
+ </html>