esp32tool 1.6.6 → 1.7.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 (119) hide show
  1. package/apple-touch-icon.png +0 -0
  2. package/css/style.css +325 -25
  3. package/dist/const.d.ts +6 -1
  4. package/dist/const.js +9 -3
  5. package/dist/esp_loader.js +35 -22
  6. package/dist/flash_jedec.js +5 -0
  7. package/dist/stubs/esp32.json +5 -5
  8. package/dist/stubs/esp32c2.json +4 -4
  9. package/dist/stubs/esp32c3.json +4 -4
  10. package/dist/stubs/esp32c5.json +4 -4
  11. package/dist/stubs/esp32c6.json +4 -4
  12. package/dist/stubs/esp32c61.json +4 -4
  13. package/dist/stubs/esp32h2.json +4 -4
  14. package/dist/stubs/esp32p4.json +4 -4
  15. package/dist/stubs/esp32p4r3.json +4 -4
  16. package/dist/stubs/esp32s2.json +4 -4
  17. package/dist/stubs/esp32s3.json +27 -5
  18. package/dist/stubs/esp32s31.json +8 -0
  19. package/dist/stubs/esp8266.json +5 -5
  20. package/dist/stubs/index.js +4 -2
  21. package/dist/util/console-color.js +3 -3
  22. package/dist/util/timestamp-transformer.js +24 -1
  23. package/dist/web/esp32-n1hWo9nn.js +1 -0
  24. package/dist/web/esp32c2-CPnHdueO.js +1 -0
  25. package/dist/web/esp32c3-ea1mvbdS.js +1 -0
  26. package/dist/web/esp32c5-Dzn4lVbd.js +1 -0
  27. package/dist/web/esp32c6-DL8Ekn6h.js +1 -0
  28. package/dist/web/esp32c61-DDIvCrFA.js +1 -0
  29. package/dist/web/esp32h2-Dnu87P5q.js +1 -0
  30. package/dist/web/esp32p4-HGPqzQsU.js +1 -0
  31. package/dist/web/esp32p4r3-DounZFQB.js +1 -0
  32. package/dist/web/esp32s2-gzKRBlZl.js +1 -0
  33. package/dist/web/esp32s3-BzPgJvNE.js +1 -0
  34. package/dist/web/esp32s31-Ck4fXdho.js +1 -0
  35. package/dist/web/esp8266-KToZdvHe.js +1 -0
  36. package/dist/web/index.js +1 -1
  37. package/electron/cli-main.cjs +19 -19
  38. package/electron/main.cjs +167 -148
  39. package/electron/preload.js +16 -18
  40. package/icons/icon-128.png +0 -0
  41. package/icons/icon-144.png +0 -0
  42. package/icons/icon-152.png +0 -0
  43. package/icons/icon-192.png +0 -0
  44. package/icons/icon-384.png +0 -0
  45. package/icons/icon-512.png +0 -0
  46. package/icons/icon-72.png +0 -0
  47. package/icons/icon-96.png +0 -0
  48. package/js/console.js +21 -12
  49. package/js/hex-editor.js +216 -163
  50. package/js/improv.js +59 -21
  51. package/js/modules/esp32-n1hWo9nn.js +1 -0
  52. package/js/modules/esp32c2-CPnHdueO.js +1 -0
  53. package/js/modules/esp32c3-ea1mvbdS.js +1 -0
  54. package/js/modules/esp32c5-Dzn4lVbd.js +1 -0
  55. package/js/modules/esp32c6-DL8Ekn6h.js +1 -0
  56. package/js/modules/esp32c61-DDIvCrFA.js +1 -0
  57. package/js/modules/esp32h2-Dnu87P5q.js +1 -0
  58. package/js/modules/esp32p4-HGPqzQsU.js +1 -0
  59. package/js/modules/esp32p4r3-DounZFQB.js +1 -0
  60. package/js/modules/esp32s2-gzKRBlZl.js +1 -0
  61. package/js/modules/esp32s3-BzPgJvNE.js +1 -0
  62. package/js/modules/esp32s31-Ck4fXdho.js +1 -0
  63. package/js/modules/esp8266-KToZdvHe.js +1 -0
  64. package/js/modules/esptool.js +1 -1
  65. package/js/nvs-editor.js +1366 -182
  66. package/js/script.js +1048 -845
  67. package/js/util/console-color.js +3 -3
  68. package/js/util/timestamp-transformer.js +24 -1
  69. package/js/webusb-serial.js +1075 -950
  70. package/package.cli.json +2 -2
  71. package/package.json +22 -13
  72. package/screenshots/desktop.png +0 -0
  73. package/screenshots/mobile.png +0 -0
  74. package/src/const.ts +12 -3
  75. package/src/esp_loader.ts +41 -24
  76. package/src/flash_jedec.ts +5 -0
  77. package/src/stubs/README.md +3 -1
  78. package/src/stubs/esp32.json +5 -5
  79. package/src/stubs/esp32c2.json +4 -4
  80. package/src/stubs/esp32c3.json +4 -4
  81. package/src/stubs/esp32c5.json +4 -4
  82. package/src/stubs/esp32c6.json +4 -4
  83. package/src/stubs/esp32c61.json +4 -4
  84. package/src/stubs/esp32h2.json +4 -4
  85. package/src/stubs/esp32h4.json +8 -0
  86. package/src/stubs/esp32p4.json +4 -4
  87. package/src/stubs/esp32p4r3.json +4 -4
  88. package/src/stubs/esp32s2.json +4 -4
  89. package/src/stubs/esp32s3.json +27 -5
  90. package/src/stubs/esp32s31.json +8 -0
  91. package/src/stubs/esp8266.json +5 -5
  92. package/src/stubs/index.ts +3 -2
  93. package/src/util/console-color.ts +3 -3
  94. package/src/util/timestamp-transformer.ts +27 -1
  95. package/sw.js +1 -1
  96. package/dist/web/esp32-BRKoi17y.js +0 -1
  97. package/dist/web/esp32c2-Btgr_lwh.js +0 -1
  98. package/dist/web/esp32c3-CHKfoI8W.js +0 -1
  99. package/dist/web/esp32c5-BDW4KtLo.js +0 -1
  100. package/dist/web/esp32c6-il8tTxAG.js +0 -1
  101. package/dist/web/esp32c61-thKzxBGf.js +0 -1
  102. package/dist/web/esp32h2-CxoUHv_P.js +0 -1
  103. package/dist/web/esp32p4-D3jLP-jY.js +0 -1
  104. package/dist/web/esp32p4r3-CqI71ojR.js +0 -1
  105. package/dist/web/esp32s2-iX3WoDbg.js +0 -1
  106. package/dist/web/esp32s3-DGwDVIgz.js +0 -1
  107. package/dist/web/esp8266-CUwxJpGa.js +0 -1
  108. package/js/modules/esp32-BRKoi17y.js +0 -1
  109. package/js/modules/esp32c2-Btgr_lwh.js +0 -1
  110. package/js/modules/esp32c3-CHKfoI8W.js +0 -1
  111. package/js/modules/esp32c5-BDW4KtLo.js +0 -1
  112. package/js/modules/esp32c6-il8tTxAG.js +0 -1
  113. package/js/modules/esp32c61-thKzxBGf.js +0 -1
  114. package/js/modules/esp32h2-CxoUHv_P.js +0 -1
  115. package/js/modules/esp32p4-D3jLP-jY.js +0 -1
  116. package/js/modules/esp32p4r3-CqI71ojR.js +0 -1
  117. package/js/modules/esp32s2-iX3WoDbg.js +0 -1
  118. package/js/modules/esp32s3-DGwDVIgz.js +0 -1
  119. package/js/modules/esp8266-CUwxJpGa.js +0 -1
Binary file
package/css/style.css CHANGED
@@ -69,7 +69,8 @@
69
69
  }
70
70
 
71
71
  @keyframes pulse {
72
- 0%, 100% {
72
+ 0%,
73
+ 100% {
73
74
  transform: scale(1);
74
75
  box-shadow: 0 0 0 0 rgba(255, 107, 0, 0.7);
75
76
  }
@@ -90,7 +91,9 @@ button,
90
91
  }
91
92
 
92
93
  body {
93
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Arial, sans-serif;
94
+ font-family:
95
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu,
96
+ "Helvetica Neue", Arial, sans-serif;
94
97
  font-style: normal;
95
98
  font-weight: 400;
96
99
  margin: 0;
@@ -261,7 +264,9 @@ div.clear {
261
264
 
262
265
  #log {
263
266
  max-width: 100%;
264
- font-family: "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace;
267
+ font-family:
268
+ "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New",
269
+ monospace;
265
270
  font-style: normal;
266
271
  font-weight: 400;
267
272
  font-size: 16px;
@@ -343,7 +348,9 @@ div.clear {
343
348
  line-height: 25px;
344
349
  font-size: 14px;
345
350
  color: white;
346
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Arial, sans-serif;
351
+ font-family:
352
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu,
353
+ "Helvetica Neue", Arial, sans-serif;
347
354
  font-style: normal;
348
355
  font-weight: 600;
349
356
  box-sizing: border-box;
@@ -391,8 +398,6 @@ div.clear {
391
398
  border-color: #67ac38;
392
399
  }
393
400
 
394
-
395
-
396
401
  #commands {
397
402
  min-width: 600px;
398
403
  justify-content: center;
@@ -410,7 +415,7 @@ div.clear {
410
415
  }
411
416
 
412
417
  /* Hide upload rows 2-8 by default, show dynamically via JavaScript */
413
- #commands .upload:nth-child(n+2):nth-child(-n+8) {
418
+ #commands .upload:nth-child(n + 2):nth-child(-n + 8) {
414
419
  display: none;
415
420
  }
416
421
 
@@ -486,8 +491,12 @@ div.clear {
486
491
  }
487
492
 
488
493
  @keyframes indeterminate-stripes {
489
- 0% { background-position: 0 0; }
490
- 100% { background-position: 28px 0; }
494
+ 0% {
495
+ background-position: 0 0;
496
+ }
497
+ 100% {
498
+ background-position: 28px 0;
499
+ }
491
500
  }
492
501
 
493
502
  #eraseProgress {
@@ -625,7 +634,9 @@ div.clear {
625
634
  border-style: solid;
626
635
  cursor: pointer;
627
636
  background-color: transparent;
628
- transition: background-color 0.2s, color 0.2s;
637
+ transition:
638
+ background-color 0.2s,
639
+ color 0.2s;
629
640
  }
630
641
 
631
642
  .partition-download-btn:hover {
@@ -976,7 +987,10 @@ div.clear {
976
987
  border-radius: 25px;
977
988
  cursor: pointer;
978
989
  margin-top: 20px;
979
- transition: background-color 0.2s, color 0.2s, border-color 0.2s;
990
+ transition:
991
+ background-color 0.2s,
992
+ color 0.2s,
993
+ border-color 0.2s;
980
994
  display: inline-flex;
981
995
  align-items: center;
982
996
  justify-content: center;
@@ -1089,7 +1103,9 @@ div.clear {
1089
1103
 
1090
1104
  .file-viewer-body pre {
1091
1105
  margin: 0;
1092
- font-family: "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace;
1106
+ font-family:
1107
+ "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New",
1108
+ monospace;
1093
1109
  font-size: 13px;
1094
1110
  line-height: 1.5;
1095
1111
  white-space: pre;
@@ -1100,7 +1116,9 @@ div.clear {
1100
1116
  }
1101
1117
 
1102
1118
  .file-viewer-body .hex-view {
1103
- font-family: "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace;
1119
+ font-family:
1120
+ "SF Mono", "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New",
1121
+ monospace;
1104
1122
  font-size: 12px;
1105
1123
  line-height: 1.6;
1106
1124
  }
@@ -1134,6 +1152,262 @@ div.clear {
1134
1152
  gap: 10px;
1135
1153
  }
1136
1154
 
1155
+ /* NVS Editor Dialog Styles */
1156
+ .nvs-dialog-overlay {
1157
+ position: fixed;
1158
+ top: 0;
1159
+ left: 0;
1160
+ width: 100%;
1161
+ height: 100%;
1162
+ background-color: rgba(0, 0, 0, 0.7);
1163
+ display: flex;
1164
+ align-items: center;
1165
+ justify-content: center;
1166
+ z-index: 10000;
1167
+ }
1168
+
1169
+ .nvs-dialog {
1170
+ background-color: #fff;
1171
+ border-radius: 10px;
1172
+ box-shadow: 0 5px 30px rgba(0, 0, 0, 0.3);
1173
+ max-width: 600px;
1174
+ max-height: 80vh;
1175
+ width: 90%;
1176
+ overflow: hidden;
1177
+ display: flex;
1178
+ flex-direction: column;
1179
+ }
1180
+
1181
+ .nvs-dialog-header {
1182
+ display: flex;
1183
+ justify-content: space-between;
1184
+ align-items: center;
1185
+ padding: 20px 25px;
1186
+ border-bottom: 2px solid #e0e0e0;
1187
+ background-color: #f8f8f8;
1188
+ }
1189
+
1190
+ .nvs-dialog-header h3 {
1191
+ margin: 0;
1192
+ font-size: 18px;
1193
+ color: #333;
1194
+ }
1195
+
1196
+ .nvs-dialog-close {
1197
+ background: none;
1198
+ border: none;
1199
+ font-size: 24px;
1200
+ color: #999;
1201
+ cursor: pointer;
1202
+ padding: 0;
1203
+ width: 30px;
1204
+ height: 30px;
1205
+ display: flex;
1206
+ align-items: center;
1207
+ justify-content: center;
1208
+ border-radius: 50%;
1209
+ transition: all 0.2s;
1210
+ }
1211
+
1212
+ .nvs-dialog-close:hover {
1213
+ background-color: #f0f0f0;
1214
+ color: #333;
1215
+ }
1216
+
1217
+ .nvs-dialog-close:focus,
1218
+ .nvs-dialog-close:focus-visible {
1219
+ outline: 2px solid #0078d4;
1220
+ outline-offset: 2px;
1221
+ background-color: #f0f0f0;
1222
+ color: #333;
1223
+ }
1224
+
1225
+ .nvs-dialog-body {
1226
+ padding: 25px;
1227
+ overflow-y: auto;
1228
+ flex: 1;
1229
+ color: #333;
1230
+ }
1231
+
1232
+ .nvs-dialog-body h4 {
1233
+ margin: 0 0 12px 0;
1234
+ font-size: 15px;
1235
+ color: #333;
1236
+ text-transform: uppercase;
1237
+ font-weight: 700;
1238
+ letter-spacing: 0.5px;
1239
+ }
1240
+
1241
+ .nvs-dialog-body pre {
1242
+ background-color: #f5f5f5;
1243
+ padding: 12px 15px;
1244
+ border-radius: 6px;
1245
+ margin: 0 0 18px 0;
1246
+ font-family:
1247
+ "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
1248
+ monospace;
1249
+ font-size: 14px;
1250
+ line-height: 1.6;
1251
+ white-space: pre-wrap;
1252
+ overflow-wrap: break-word;
1253
+ border: 1px solid #e0e0e0;
1254
+ }
1255
+
1256
+ .nvs-ok {
1257
+ color: #71ae1e;
1258
+ font-weight: 600;
1259
+ }
1260
+
1261
+ .nvs-warn {
1262
+ color: #ff6b00;
1263
+ font-weight: 600;
1264
+ }
1265
+
1266
+ .nvs-error {
1267
+ color: #c64141;
1268
+ font-weight: 600;
1269
+ }
1270
+
1271
+ .nvs-blob-item {
1272
+ background-color: #f8f8f8;
1273
+ border: 1px solid #e0e0e0;
1274
+ border-radius: 5px;
1275
+ padding: 15px;
1276
+ margin-bottom: 10px;
1277
+ }
1278
+
1279
+ .nvs-blob-item div {
1280
+ margin: 5px 0;
1281
+ font-size: 13px;
1282
+ }
1283
+
1284
+ .nvs-blob-item button {
1285
+ margin-right: 10px;
1286
+ margin-top: 10px;
1287
+ padding: 5px 12px;
1288
+ font-size: 12px;
1289
+ border-style: solid;
1290
+ cursor: pointer;
1291
+ border-radius: 12px;
1292
+ }
1293
+
1294
+ .nvs-blob-item button:focus,
1295
+ .nvs-blob-item button:focus-visible {
1296
+ outline: 2px solid #0078d4;
1297
+ outline-offset: 2px;
1298
+ }
1299
+
1300
+ .nvs-issue-group {
1301
+ margin: 6px 0 14px 0;
1302
+ padding: 8px 10px;
1303
+ background-color: #fff8f3;
1304
+ border: 1px solid #ffd9b8;
1305
+ border-radius: 6px;
1306
+ }
1307
+
1308
+ .nvs-issue-title {
1309
+ font-weight: 700;
1310
+ font-size: 13px;
1311
+ margin-bottom: 6px;
1312
+ }
1313
+
1314
+ pre.nvs-issue {
1315
+ background-color: #fff;
1316
+ border: 1px solid #f0d3b8;
1317
+ border-radius: 4px;
1318
+ padding: 6px 8px;
1319
+ margin: 4px 0;
1320
+ font-family:
1321
+ "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
1322
+ monospace;
1323
+ font-size: 12px;
1324
+ line-height: 1.5;
1325
+ white-space: pre-wrap;
1326
+ overflow-wrap: break-word;
1327
+ color: #333;
1328
+ flex: 1;
1329
+ }
1330
+
1331
+ .nvs-issue-row {
1332
+ display: flex;
1333
+ align-items: center;
1334
+ gap: 8px;
1335
+ flex-wrap: wrap;
1336
+ margin: 4px 0;
1337
+ }
1338
+
1339
+ .nvs-issue-actions {
1340
+ display: inline-flex;
1341
+ gap: 4px;
1342
+ flex-shrink: 0;
1343
+ }
1344
+
1345
+ .nvs-issue-btn {
1346
+ padding: 4px 8px;
1347
+ font-size: 12px;
1348
+ border: 1px solid #c0c0c0;
1349
+ border-radius: 4px;
1350
+ background: #fafafa;
1351
+ color: #333;
1352
+ cursor: pointer;
1353
+ white-space: nowrap;
1354
+ }
1355
+
1356
+ .nvs-issue-btn:hover {
1357
+ background: #f0f0f0;
1358
+ border-color: #888;
1359
+ }
1360
+
1361
+ .nvs-issue-btn:focus,
1362
+ .nvs-issue-btn:focus-visible {
1363
+ outline: 2px solid #0078d4;
1364
+ outline-offset: 2px;
1365
+ background: #f0f0f0;
1366
+ border-color: #888;
1367
+ }
1368
+
1369
+ .nvs-issue-delete:hover {
1370
+ background: #ffe0e0;
1371
+ border-color: #c64141;
1372
+ color: #c64141;
1373
+ }
1374
+
1375
+ .nvs-issue-delete:focus,
1376
+ .nvs-issue-delete:focus-visible {
1377
+ outline: 2px solid #c64141;
1378
+ outline-offset: 2px;
1379
+ background: #ffe0e0;
1380
+ border-color: #c64141;
1381
+ color: #c64141;
1382
+ }
1383
+
1384
+ /* Highlight effect when jumping to entry/page */
1385
+ .nvs-row-highlight {
1386
+ animation: nvs-flash-row 2.2s ease-out;
1387
+ }
1388
+
1389
+ .nvs-page-highlight {
1390
+ animation: nvs-flash-page 2.2s ease-out;
1391
+ }
1392
+
1393
+ @keyframes nvs-flash-row {
1394
+ 0% {
1395
+ background-color: #fff3a8;
1396
+ }
1397
+ 100% {
1398
+ background-color: transparent;
1399
+ }
1400
+ }
1401
+
1402
+ @keyframes nvs-flash-page {
1403
+ 0% {
1404
+ box-shadow: 0 0 0 3px #ffb74d;
1405
+ }
1406
+ 100% {
1407
+ box-shadow: 0 0 0 0 transparent;
1408
+ }
1409
+ }
1410
+
1137
1411
  /* Mobile Optimizations */
1138
1412
  @media (max-width: 768px) {
1139
1413
  /* Larger touch targets and better readability */
@@ -2313,7 +2587,8 @@ body.console-active .main {
2313
2587
  background-color: #1c1c1c;
2314
2588
  display: flex;
2315
2589
  flex-direction: column;
2316
- font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;
2590
+ font-family:
2591
+ SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;
2317
2592
  color: #ddd;
2318
2593
  }
2319
2594
 
@@ -2638,8 +2913,11 @@ body.hexeditor-active .main {
2638
2913
  /* Progress overlay for loading */
2639
2914
  .hexeditor-progress-overlay {
2640
2915
  position: absolute;
2641
- top: 0; left: 0; right: 0; bottom: 0;
2642
- background-color: rgba(0,0,0,0.85);
2916
+ top: 0;
2917
+ left: 0;
2918
+ right: 0;
2919
+ bottom: 0;
2920
+ background-color: rgba(0, 0, 0, 0.85);
2643
2921
  display: flex;
2644
2922
  flex-direction: column;
2645
2923
  align-items: center;
@@ -2776,7 +3054,9 @@ body.hexeditor-active .main {
2776
3054
  background-color: #1c1c1c;
2777
3055
  display: flex;
2778
3056
  flex-direction: column;
2779
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
3057
+ font-family:
3058
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
3059
+ sans-serif;
2780
3060
  color: #ddd;
2781
3061
  }
2782
3062
 
@@ -2952,10 +3232,18 @@ body.nvseditor-active .main {
2952
3232
  border-bottom: 1px solid #3a3a3a;
2953
3233
  }
2954
3234
 
2955
- .nvs-page-header.state-active { border-left: 4px solid #43a047; }
2956
- .nvs-page-header.state-full { border-left: 4px solid #ffa726; }
2957
- .nvs-page-header.state-freeing { border-left: 4px solid #ef5350; }
2958
- .nvs-page-header.state-other { border-left: 4px solid #666; }
3235
+ .nvs-page-header.state-active {
3236
+ border-left: 4px solid #43a047;
3237
+ }
3238
+ .nvs-page-header.state-full {
3239
+ border-left: 4px solid #ffa726;
3240
+ }
3241
+ .nvs-page-header.state-freeing {
3242
+ border-left: 4px solid #ef5350;
3243
+ }
3244
+ .nvs-page-header.state-other {
3245
+ border-left: 4px solid #666;
3246
+ }
2959
3247
 
2960
3248
  .nvs-page-state {
2961
3249
  font-weight: bold;
@@ -2965,10 +3253,22 @@ body.nvseditor-active .main {
2965
3253
  text-transform: uppercase;
2966
3254
  }
2967
3255
 
2968
- .state-active .nvs-page-state { background: #1b5e20; color: #a5d6a7; }
2969
- .state-full .nvs-page-state { background: #e65100; color: #ffcc80; }
2970
- .state-freeing .nvs-page-state { background: #b71c1c; color: #ef9a9a; }
2971
- .state-other .nvs-page-state { background: #424242; color: #999; }
3256
+ .state-active .nvs-page-state {
3257
+ background: #1b5e20;
3258
+ color: #a5d6a7;
3259
+ }
3260
+ .state-full .nvs-page-state {
3261
+ background: #e65100;
3262
+ color: #ffcc80;
3263
+ }
3264
+ .state-freeing .nvs-page-state {
3265
+ background: #b71c1c;
3266
+ color: #ef9a9a;
3267
+ }
3268
+ .state-other .nvs-page-state {
3269
+ background: #424242;
3270
+ color: #999;
3271
+ }
2972
3272
 
2973
3273
  /* NVS Namespace */
2974
3274
  .nvs-namespace {
package/dist/const.d.ts CHANGED
@@ -307,7 +307,7 @@ export declare const ESP32P4_PMU_EXT_LDO_P0_0P1A_REG: number;
307
307
  export declare const ESP32P4_PMU_0P1A_TARGET0_0: number;
308
308
  export declare const ESP32P4_PMU_0P1A_FORCE_TIEH_SEL_0: number;
309
309
  export declare const ESP32P4_PMU_DATE_REG: number;
310
- export declare const ESP32S31_SPI_REG_BASE = 542113792;
310
+ export declare const ESP32S31_SPI_REG_BASE = 542117888;
311
311
  export declare const ESP32S31_BASEFUSEADDR = 544296960;
312
312
  export declare const ESP32S31_EFUSE_BLOCK1_ADDR: number;
313
313
  export declare const ESP32S31_MACFUSEADDR: number;
@@ -319,6 +319,11 @@ export declare const ESP32S31_SPI_MISO_DLEN_OFFS = 40;
319
319
  export declare const ESP32S31_SPI_W0_OFFS = 88;
320
320
  export declare const ESP32S31_UART_DATE_REG_ADDR: number;
321
321
  export declare const ESP32S31_BOOTLOADER_FLASH_OFFSET = 8192;
322
+ export declare const ESP32S31_DR_REG_LP_WDT_BASE = 545263616;
323
+ export declare const ESP32S31_RTC_CNTL_WDTCONFIG0_REG: number;
324
+ export declare const ESP32S31_RTC_CNTL_WDTCONFIG1_REG: number;
325
+ export declare const ESP32S31_RTC_CNTL_WDTWPROTECT_REG: number;
326
+ export declare const ESP32S31_RTC_CNTL_WDT_WKEY = 1356348065;
322
327
  export interface SpiFlashAddresses {
323
328
  regBase: number;
324
329
  baseFuse: number;
package/dist/const.js CHANGED
@@ -352,10 +352,10 @@ export const ESP32P4_PMU_EXT_LDO_P0_0P1A_REG = ESP32P4_DR_REG_PMU_BASE + 0x1b8;
352
352
  export const ESP32P4_PMU_0P1A_TARGET0_0 = 0xff << 23;
353
353
  export const ESP32P4_PMU_0P1A_FORCE_TIEH_SEL_0 = 1 << 7;
354
354
  export const ESP32P4_PMU_DATE_REG = ESP32P4_DR_REG_PMU_BASE + 0x3fc;
355
- export const ESP32S31_SPI_REG_BASE = 0x20500000;
355
+ export const ESP32S31_SPI_REG_BASE = 0x20501000;
356
356
  export const ESP32S31_BASEFUSEADDR = 0x20715000;
357
- export const ESP32S31_EFUSE_BLOCK1_ADDR = ESP32S31_BASEFUSEADDR + 0x044;
358
- export const ESP32S31_MACFUSEADDR = 0x20715000 + 0x044;
357
+ export const ESP32S31_EFUSE_BLOCK1_ADDR = ESP32S31_BASEFUSEADDR + 0x050;
358
+ export const ESP32S31_MACFUSEADDR = ESP32S31_BASEFUSEADDR + 0x050;
359
359
  export const ESP32S31_SPI_USR_OFFS = 0x18;
360
360
  export const ESP32S31_SPI_USR1_OFFS = 0x1c;
361
361
  export const ESP32S31_SPI_USR2_OFFS = 0x20;
@@ -364,6 +364,12 @@ export const ESP32S31_SPI_MISO_DLEN_OFFS = 0x28;
364
364
  export const ESP32S31_SPI_W0_OFFS = 0x58;
365
365
  export const ESP32S31_UART_DATE_REG_ADDR = 0x2038a000 + 0x8c;
366
366
  export const ESP32S31_BOOTLOADER_FLASH_OFFSET = 0x2000;
367
+ // ESP32-S31 RTC Watchdog Timer registers (LP_WDT)
368
+ export const ESP32S31_DR_REG_LP_WDT_BASE = 0x20801000;
369
+ export const ESP32S31_RTC_CNTL_WDTCONFIG0_REG = ESP32S31_DR_REG_LP_WDT_BASE + 0x0000; // LP_WDT_RWDT_CONFIG0_REG
370
+ export const ESP32S31_RTC_CNTL_WDTCONFIG1_REG = ESP32S31_DR_REG_LP_WDT_BASE + 0x0004; // LP_WDT_RWDT_CONFIG1_REG
371
+ export const ESP32S31_RTC_CNTL_WDTWPROTECT_REG = ESP32S31_DR_REG_LP_WDT_BASE + 0x0018; // LP_WDT_RWDT_WPROTECT_REG
372
+ export const ESP32S31_RTC_CNTL_WDT_WKEY = 0x50d83aa1;
367
373
  export const SYNC_PACKET = toByteArray("\x07\x07\x12 UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU");
368
374
  export const CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000;
369
375
  // Image Chip IDs (used by ESP32-C3 and later for chip detection)
@@ -1,5 +1,5 @@
1
1
  /// <reference types="@types/w3c-web-serial" />
2
- import { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP32C2, CHIP_FAMILY_ESP32C3, CHIP_FAMILY_ESP32C5, CHIP_FAMILY_ESP32C6, CHIP_FAMILY_ESP32C61, CHIP_FAMILY_ESP32H2, CHIP_FAMILY_ESP32H4, CHIP_FAMILY_ESP32H21, CHIP_FAMILY_ESP32P4, CHIP_FAMILY_ESP32S31, CHIP_FAMILY_ESP8266, MAX_TIMEOUT, DEFAULT_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, ESP_CHANGE_BAUDRATE, ESP_CHECKSUM_MAGIC, ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_DATA, ESP_MEM_END, ESP_READ_REG, ESP_WRITE_REG, ESP_SPI_ATTACH, ESP_SYNC, ESP_GET_SECURITY_INFO, FLASH_SECTOR_SIZE, FLASH_WRITE_SIZE, STUB_FLASH_WRITE_SIZE, MEM_END_ROM_TIMEOUT, ROM_INVALID_RECV_MSG, SYNC_PACKET, SYNC_TIMEOUT, USB_RAM_BLOCK, ESP_ERASE_FLASH, ESP_ERASE_REGION, ESP_READ_FLASH, CHIP_ERASE_TIMEOUT, FLASH_READ_TIMEOUT, timeoutPerMb, ESP_ROM_BAUD, USB_JTAG_SERIAL_PID, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, getSpiFlashAddresses, DETECTED_FLASH_SIZES, CHIP_DETECT_MAGIC_REG_ADDR, CHIP_DETECT_MAGIC_VALUES, CHIP_ID_TO_INFO, ESP32_BASEFUSEADDR, ESP32_APB_CTL_DATE_ADDR, ESP32S2_EFUSE_BLOCK1_ADDR, ESP32S3_EFUSE_BLOCK1_ADDR, ESP32C2_EFUSE_BLOCK2_ADDR, ESP32C5_EFUSE_BLOCK1_ADDR, ESP32C6_EFUSE_BLOCK1_ADDR, ESP32C61_EFUSE_BLOCK1_ADDR, ESP32H2_EFUSE_BLOCK1_ADDR, ESP32P4_EFUSE_BLOCK1_ADDR, ESP32S31_EFUSE_BLOCK1_ADDR, SlipReadError, ESP32S2_RTC_CNTL_WDTWPROTECT_REG, ESP32S2_RTC_CNTL_WDTCONFIG0_REG, ESP32S2_RTC_CNTL_WDTCONFIG1_REG, ESP32S2_RTC_CNTL_WDT_WKEY, ESP32S2_RTC_CNTL_OPTION1_REG, ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32S3_RTC_CNTL_WDTWPROTECT_REG, ESP32S3_RTC_CNTL_WDTCONFIG0_REG, ESP32S3_RTC_CNTL_WDTCONFIG1_REG, ESP32S3_RTC_CNTL_WDT_WKEY, ESP32S3_RTC_CNTL_OPTION1_REG, ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32C3_EFUSE_RD_MAC_SPI_SYS_3_REG, ESP32C3_EFUSE_RD_MAC_SPI_SYS_5_REG, ESP32C3_RTC_CNTL_WDTWPROTECT_REG, ESP32C3_RTC_CNTL_WDTCONFIG0_REG, ESP32C3_RTC_CNTL_WDTCONFIG1_REG, ESP32C3_RTC_CNTL_WDT_WKEY, ESP32C5_C6_RTC_CNTL_WDTWPROTECT_REG, ESP32C5_C6_RTC_CNTL_WDTCONFIG0_REG, ESP32C5_C6_RTC_CNTL_WDTCONFIG1_REG, ESP32C5_C6_RTC_CNTL_WDT_WKEY, ESP32C5_UART_CLKDIV_REG, ESP32C5_PCR_SYSCLK_CONF_REG, ESP32C5_PCR_SYSCLK_XTAL_FREQ_V, ESP32C5_PCR_SYSCLK_XTAL_FREQ_S, ESP32P4_RTC_CNTL_WDTWPROTECT_REG, ESP32P4_RTC_CNTL_WDTCONFIG0_REG, ESP32P4_RTC_CNTL_WDTCONFIG1_REG, ESP32P4_RTC_CNTL_WDT_WKEY, ESP32P4_RTC_CNTL_OPTION1_REG, ESP32P4_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32P4_LP_SYSTEM_REG_ANA_XPD_PAD_GROUP_REG, ESP32P4_PMU_EXT_LDO_P0_0P1A_ANA_REG, ESP32P4_PMU_ANA_0P1A_EN_CUR_LIM_0, ESP32P4_PMU_EXT_LDO_P0_0P1A_REG, ESP32P4_PMU_0P1A_TARGET0_0, ESP32P4_PMU_0P1A_FORCE_TIEH_SEL_0, ESP32P4_PMU_DATE_REG, ESP32S2_UARTDEV_BUF_NO, ESP32S2_UARTDEV_BUF_NO_USB_OTG, ESP32S3_UARTDEV_BUF_NO, ESP32S3_UARTDEV_BUF_NO_USB_OTG, ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C3_BUF_UART_NO_OFFSET, ESP32C5_UARTDEV_BUF_NO, ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C6_UARTDEV_BUF_NO, ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C61_UARTDEV_BUF_NO_REV_LE2, ESP32C61_UARTDEV_BUF_NO_REV_GT2, ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_LE2, ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_GT2, ESP32H2_UARTDEV_BUF_NO, ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32H4_UARTDEV_BUF_NO, ESP32H4_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32P4_UARTDEV_BUF_NO_REV0, ESP32P4_UARTDEV_BUF_NO_REV300, ESP32P4_UARTDEV_BUF_NO_USB_OTG, ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL, } from "./const";
2
+ import { CHIP_FAMILY_ESP32, CHIP_FAMILY_ESP32S2, CHIP_FAMILY_ESP32S3, CHIP_FAMILY_ESP32C2, CHIP_FAMILY_ESP32C3, CHIP_FAMILY_ESP32C5, CHIP_FAMILY_ESP32C6, CHIP_FAMILY_ESP32C61, CHIP_FAMILY_ESP32H2, CHIP_FAMILY_ESP32H4, CHIP_FAMILY_ESP32H21, CHIP_FAMILY_ESP32P4, CHIP_FAMILY_ESP32S31, CHIP_FAMILY_ESP8266, MAX_TIMEOUT, DEFAULT_TIMEOUT, ERASE_REGION_TIMEOUT_PER_MB, ESP_CHANGE_BAUDRATE, ESP_CHECKSUM_MAGIC, ESP_FLASH_BEGIN, ESP_FLASH_DATA, ESP_FLASH_END, ESP_MEM_BEGIN, ESP_MEM_DATA, ESP_MEM_END, ESP_READ_REG, ESP_WRITE_REG, ESP_SPI_ATTACH, ESP_SYNC, ESP_GET_SECURITY_INFO, FLASH_SECTOR_SIZE, FLASH_WRITE_SIZE, STUB_FLASH_WRITE_SIZE, MEM_END_ROM_TIMEOUT, ROM_INVALID_RECV_MSG, SYNC_PACKET, SYNC_TIMEOUT, USB_RAM_BLOCK, ESP_ERASE_FLASH, ESP_ERASE_REGION, ESP_READ_FLASH, CHIP_ERASE_TIMEOUT, FLASH_READ_TIMEOUT, timeoutPerMb, ESP_ROM_BAUD, USB_JTAG_SERIAL_PID, ESP_FLASH_DEFL_BEGIN, ESP_FLASH_DEFL_DATA, ESP_FLASH_DEFL_END, getSpiFlashAddresses, DETECTED_FLASH_SIZES, CHIP_DETECT_MAGIC_REG_ADDR, CHIP_DETECT_MAGIC_VALUES, CHIP_ID_TO_INFO, ESP32_BASEFUSEADDR, ESP32_APB_CTL_DATE_ADDR, ESP32S2_EFUSE_BLOCK1_ADDR, ESP32S3_EFUSE_BLOCK1_ADDR, ESP32C2_EFUSE_BLOCK2_ADDR, ESP32C5_EFUSE_BLOCK1_ADDR, ESP32C6_EFUSE_BLOCK1_ADDR, ESP32C61_EFUSE_BLOCK1_ADDR, ESP32H2_EFUSE_BLOCK1_ADDR, ESP32P4_EFUSE_BLOCK1_ADDR, ESP32S31_EFUSE_BLOCK1_ADDR, ESP32S31_RTC_CNTL_WDTWPROTECT_REG, ESP32S31_RTC_CNTL_WDTCONFIG0_REG, ESP32S31_RTC_CNTL_WDTCONFIG1_REG, ESP32S31_RTC_CNTL_WDT_WKEY, SlipReadError, ESP32S2_RTC_CNTL_WDTWPROTECT_REG, ESP32S2_RTC_CNTL_WDTCONFIG0_REG, ESP32S2_RTC_CNTL_WDTCONFIG1_REG, ESP32S2_RTC_CNTL_WDT_WKEY, ESP32S2_RTC_CNTL_OPTION1_REG, ESP32S2_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32S3_RTC_CNTL_WDTWPROTECT_REG, ESP32S3_RTC_CNTL_WDTCONFIG0_REG, ESP32S3_RTC_CNTL_WDTCONFIG1_REG, ESP32S3_RTC_CNTL_WDT_WKEY, ESP32S3_RTC_CNTL_OPTION1_REG, ESP32S3_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32C3_EFUSE_RD_MAC_SPI_SYS_3_REG, ESP32C3_EFUSE_RD_MAC_SPI_SYS_5_REG, ESP32C3_RTC_CNTL_WDTWPROTECT_REG, ESP32C3_RTC_CNTL_WDTCONFIG0_REG, ESP32C3_RTC_CNTL_WDTCONFIG1_REG, ESP32C3_RTC_CNTL_WDT_WKEY, ESP32C5_C6_RTC_CNTL_WDTWPROTECT_REG, ESP32C5_C6_RTC_CNTL_WDTCONFIG0_REG, ESP32C5_C6_RTC_CNTL_WDTCONFIG1_REG, ESP32C5_C6_RTC_CNTL_WDT_WKEY, ESP32C5_UART_CLKDIV_REG, ESP32C5_PCR_SYSCLK_CONF_REG, ESP32C5_PCR_SYSCLK_XTAL_FREQ_V, ESP32C5_PCR_SYSCLK_XTAL_FREQ_S, ESP32P4_RTC_CNTL_WDTWPROTECT_REG, ESP32P4_RTC_CNTL_WDTCONFIG0_REG, ESP32P4_RTC_CNTL_WDTCONFIG1_REG, ESP32P4_RTC_CNTL_WDT_WKEY, ESP32P4_RTC_CNTL_OPTION1_REG, ESP32P4_RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK, ESP32P4_LP_SYSTEM_REG_ANA_XPD_PAD_GROUP_REG, ESP32P4_PMU_EXT_LDO_P0_0P1A_ANA_REG, ESP32P4_PMU_ANA_0P1A_EN_CUR_LIM_0, ESP32P4_PMU_EXT_LDO_P0_0P1A_REG, ESP32P4_PMU_0P1A_TARGET0_0, ESP32P4_PMU_0P1A_FORCE_TIEH_SEL_0, ESP32P4_PMU_DATE_REG, ESP32S2_UARTDEV_BUF_NO, ESP32S2_UARTDEV_BUF_NO_USB_OTG, ESP32S3_UARTDEV_BUF_NO, ESP32S3_UARTDEV_BUF_NO_USB_OTG, ESP32S3_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C3_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C3_BUF_UART_NO_OFFSET, ESP32C5_UARTDEV_BUF_NO, ESP32C5_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C6_UARTDEV_BUF_NO, ESP32C6_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32C61_UARTDEV_BUF_NO_REV_LE2, ESP32C61_UARTDEV_BUF_NO_REV_GT2, ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_LE2, ESP32C61_UARTDEV_BUF_NO_USB_JTAG_SERIAL_REV_GT2, ESP32H2_UARTDEV_BUF_NO, ESP32H2_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32H4_UARTDEV_BUF_NO, ESP32H4_UARTDEV_BUF_NO_USB_JTAG_SERIAL, ESP32P4_UARTDEV_BUF_NO_REV0, ESP32P4_UARTDEV_BUF_NO_REV300, ESP32P4_UARTDEV_BUF_NO_USB_OTG, ESP32P4_UARTDEV_BUF_NO_USB_JTAG_SERIAL, } from "./const";
3
3
  import { getStubCode } from "./stubs";
4
4
  import { hexFormatter, padTo, sleep, slipEncode, toHex } from "./util";
5
5
  import { FLASH_MANUFACTURERS, FLASH_DEVICES } from "./flash_jedec";
@@ -1269,6 +1269,13 @@ export class ESPLoader extends EventTarget {
1269
1269
  WDTCONFIG1_REG = ESP32P4_RTC_CNTL_WDTCONFIG1_REG;
1270
1270
  WDT_WKEY = ESP32P4_RTC_CNTL_WDT_WKEY;
1271
1271
  }
1272
+ else if (this.chipFamily === CHIP_FAMILY_ESP32S31) {
1273
+ // S31 uses LP_WDT (Low Power Watchdog Timer)
1274
+ WDTWPROTECT_REG = ESP32S31_RTC_CNTL_WDTWPROTECT_REG;
1275
+ WDTCONFIG0_REG = ESP32S31_RTC_CNTL_WDTCONFIG0_REG;
1276
+ WDTCONFIG1_REG = ESP32S31_RTC_CNTL_WDTCONFIG1_REG;
1277
+ WDT_WKEY = ESP32S31_RTC_CNTL_WDT_WKEY;
1278
+ }
1272
1279
  else {
1273
1280
  throw new Error(`rtcWdtResetChipSpecific() is not supported for ${this.chipFamily}`);
1274
1281
  }
@@ -1311,11 +1318,12 @@ export class ESPLoader extends EventTarget {
1311
1318
  }
1312
1319
  // Check if chip supports WDT reset
1313
1320
  // WDT reset is not needed for ESP32-C3
1314
- // WDT reset is supported by: ESP32-S2, ESP32-S3, ESP32-P4
1321
+ // WDT reset is supported by: ESP32-S2, ESP32-S3, ESP32-P4, ESP32-S31
1315
1322
  // WDT reset is NOT supported by: ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2
1316
1323
  const supportsWdtReset = this.chipFamily === CHIP_FAMILY_ESP32S2 ||
1317
1324
  this.chipFamily === CHIP_FAMILY_ESP32S3 ||
1318
- this.chipFamily === CHIP_FAMILY_ESP32P4;
1325
+ this.chipFamily === CHIP_FAMILY_ESP32P4 ||
1326
+ this.chipFamily === CHIP_FAMILY_ESP32S31;
1319
1327
  if (!supportsWdtReset) {
1320
1328
  this.logger.debug(`${this.chipName} does not support WDT reset - using classic reset instead`);
1321
1329
  // Use classic reset for chips without WDT support
@@ -3539,7 +3547,6 @@ export class ESPLoader extends EventTarget {
3539
3547
  // Retry loop for this chunk
3540
3548
  while (!chunkSuccess && retryCount <= MAX_RETRIES) {
3541
3549
  let resp = new Uint8Array(0);
3542
- let lastAckedLength = 0; // Track last acknowledged length
3543
3550
  try {
3544
3551
  // Only log on first attempt or retries
3545
3552
  if (retryCount === 0) {
@@ -3571,7 +3578,10 @@ export class ESPLoader extends EventTarget {
3571
3578
  blockSize = base * 65; // 63 * 65 = 4095 (close to 0x1000)
3572
3579
  maxInFlight = base * 130; // 63 * 130 = 8190 (close to blockSize * 2)
3573
3580
  }
3574
- const pkt = pack("<IIII", currentAddr, chunkSize, blockSize, maxInFlight);
3581
+ // The stub (legacy approach) interprets the 4th READ_FLASH parameter as
3582
+ // max_unacked_packets (a packet count), not bytes. Convert accordingly.
3583
+ const maxUnackedPackets = Math.max(1, Math.ceil(maxInFlight / blockSize));
3584
+ const pkt = pack("<IIII", currentAddr, chunkSize, blockSize, maxUnackedPackets);
3575
3585
  const chunkStartTime = Date.now();
3576
3586
  const [res] = await this.checkCommand(ESP_READ_FLASH, pkt);
3577
3587
  if (res != 0) {
@@ -3586,11 +3596,12 @@ export class ESPLoader extends EventTarget {
3586
3596
  catch (err) {
3587
3597
  if (err instanceof SlipReadError) {
3588
3598
  this.logger.debug(`${err.message} at byte 0x${resp.length.toString(16)}`);
3589
- // Send empty SLIP frame to abort the stub's read operation
3590
- // The stub expects 4 bytes (ACK), if we send less it will break out
3599
+ // Send a 1-byte SLIP frame to abort the stub's read operation.
3600
+ // The stub checks ack_size == 4; a differently-sized frame
3601
+ // causes it to break out of its send loop.
3602
+ // (Empty frames 0xC0 0xC0 are silently ignored by the stub.)
3591
3603
  try {
3592
- // Send SLIP frame with no data (just delimiters)
3593
- const abortFrame = [this.SLIP_END, this.SLIP_END]; // Empty SLIP frame
3604
+ const abortFrame = [this.SLIP_END, 0x00, this.SLIP_END];
3594
3605
  await this.writeToStream(abortFrame);
3595
3606
  this.logger.debug(`Sent abort frame to stub`);
3596
3607
  // Give stub time to process abort
@@ -3615,21 +3626,23 @@ export class ESPLoader extends EventTarget {
3615
3626
  newResp.set(resp);
3616
3627
  newResp.set(packetData, resp.length);
3617
3628
  resp = newResp;
3618
- // Send acknowledgment when we've received maxInFlight bytes
3619
- // The stub sends packets until (num_sent - num_acked) >= max_in_flight
3620
- // We MUST wait for all packets before sending ACK
3621
- const shouldAck = resp.length >= chunkSize || // End of chunk
3622
- resp.length >= lastAckedLength + maxInFlight; // Received all packets
3623
- if (shouldAck) {
3624
- const ackData = pack("<I", resp.length);
3625
- const slipEncodedAck = slipEncode(ackData);
3626
- await this.writeToStream(slipEncodedAck);
3627
- // Update lastAckedLength to current response length
3628
- // This ensures next ACK is sent at the right time
3629
- lastAckedLength = resp.length;
3630
- }
3629
+ // Send ACK after every packet (mirrors Python esptool behaviour).
3630
+ // The stub (legacy approach) uses ACK count for flow-control; sending
3631
+ // after each packet keeps the pipeline flowing without overwhelming
3632
+ // the USB buffer.
3633
+ const ackData = pack("<I", resp.length);
3634
+ const slipEncodedAck = slipEncode(ackData);
3635
+ await this.writeToStream(slipEncodedAck);
3631
3636
  }
3632
3637
  }
3638
+ // The stub sends a 16-byte MD5 hash after every chunk.
3639
+ // Read and discard it so it doesn't corrupt the next command response.
3640
+ try {
3641
+ await this.readPacket(FLASH_READ_TIMEOUT);
3642
+ }
3643
+ catch {
3644
+ // Ignore timeout — stub may not have sent MD5 yet if something failed
3645
+ }
3633
3646
  // Chunk read successfully - append to all data
3634
3647
  const newAllData = new Uint8Array(allData.length + resp.length);
3635
3648
  newAllData.set(allData);
@@ -12,6 +12,7 @@ export const FLASH_MANUFACTURERS = {
12
12
  0x5e: "Zbit",
13
13
  0x37: "AMIC",
14
14
  0xe0: "Berg Micro",
15
+ 0x46: "XTX Technology",
15
16
  };
16
17
  export const FLASH_DEVICES = {
17
18
  0x010219: "S25FL256S (256Mbit)",
@@ -110,4 +111,8 @@ export const FLASH_DEVICES = {
110
111
  0xef6011: "W25Q10EW (1Mbit)",
111
112
  0xef6012: "W25Q20EW (2Mbit)",
112
113
  0xef6013: "W25Q40EW (4Mbit)",
114
+ 0x464015: "XT25F16B (16Mbit)",
115
+ 0x464016: "XT25F32B (32Mbit)",
116
+ 0x464017: "XT25F64B (64Mbit)",
117
+ 0x464018: "XT25F128B (128Mbit)",
113
118
  };