ota-hub-reactjs 0.1.1 → 0.1.2
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.
|
@@ -178,6 +178,7 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
|
|
|
178
178
|
transport,
|
|
179
179
|
baudrate: use_baudrate,
|
|
180
180
|
enableTracing: false,
|
|
181
|
+
debugLogging: !!conn.debugLogging,
|
|
181
182
|
});
|
|
182
183
|
try {
|
|
183
184
|
await esploader.main();
|
|
@@ -229,12 +230,10 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
|
|
|
229
230
|
};
|
|
230
231
|
const disconnect = async (uuid, timeout = 2000) => {
|
|
231
232
|
const conn = base.getConnection(uuid);
|
|
232
|
-
// 1.
|
|
233
|
-
// If we have an active reader generator in state, call return() to
|
|
234
|
-
// gracefully resolve the pending reader.next() in the background loop
|
|
233
|
+
// 1. Politely kill our high-level generator
|
|
235
234
|
if (conn?.reader && typeof conn.reader.return === "function") {
|
|
236
235
|
try {
|
|
237
|
-
// @ts-ignore
|
|
236
|
+
// @ts-ignore
|
|
238
237
|
await conn.reader.return();
|
|
239
238
|
}
|
|
240
239
|
catch (e) {
|
|
@@ -243,7 +242,17 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
|
|
|
243
242
|
}
|
|
244
243
|
if (conn?.transport) {
|
|
245
244
|
try {
|
|
246
|
-
//
|
|
245
|
+
// 2. AGGRESSIVE LOCK REMOVAL (The Fix)
|
|
246
|
+
// Dig into esptool-js's internal state and force the native reader to release.
|
|
247
|
+
// If we don't do this, transport.disconnect() will hang and fail to close the port.
|
|
248
|
+
const internalReader = conn.transport.reader;
|
|
249
|
+
if (internalReader) {
|
|
250
|
+
await internalReader.cancel().catch(() => { });
|
|
251
|
+
if (typeof internalReader.releaseLock === "function") {
|
|
252
|
+
internalReader.releaseLock();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// 3. Now that locks are cleared, attempt standard disconnect
|
|
247
256
|
await Promise.race([
|
|
248
257
|
conn.transport.disconnect(),
|
|
249
258
|
new Promise((resolve) => setTimeout(resolve, timeout)),
|
|
@@ -253,7 +262,23 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
|
|
|
253
262
|
console.warn(`[${uuid}] Serial Disconnect error:`, e);
|
|
254
263
|
}
|
|
255
264
|
}
|
|
256
|
-
//
|
|
265
|
+
// 4. NATIVE SAFETY NET
|
|
266
|
+
// Guarantee the port is closed natively just in case the transport failed
|
|
267
|
+
if (conn?.port) {
|
|
268
|
+
try {
|
|
269
|
+
if (conn.port.readable?.locked) {
|
|
270
|
+
await conn.port.readable.cancel().catch(() => { });
|
|
271
|
+
}
|
|
272
|
+
if (conn.port.writable?.locked) {
|
|
273
|
+
await conn.port.writable.abort().catch(() => { });
|
|
274
|
+
}
|
|
275
|
+
await conn.port.close().catch(() => { });
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
// It will throw if already closed, which is perfectly fine.
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// 5. Always clear the transport and reset connection state
|
|
257
282
|
base.updateConnection(uuid, (c) => ({
|
|
258
283
|
...c,
|
|
259
284
|
port: releasePortByDefault ? undefined : c.port,
|
|
@@ -332,37 +357,35 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
|
|
|
332
357
|
};
|
|
333
358
|
// This function now handles an entire device flashing session
|
|
334
359
|
const handleFlashFirmware = async (uuid, assetsToFlash) => {
|
|
360
|
+
// Capture the original connection and port reference before disconnect
|
|
335
361
|
const conn = base.getConnection(uuid);
|
|
336
362
|
// 1. Bail if we don't have the required state
|
|
337
|
-
if (!conn ||
|
|
338
|
-
!conn.transport ||
|
|
339
|
-
!conn.port ||
|
|
340
|
-
!conn.esp ||
|
|
341
|
-
assetsToFlash.length === 0)
|
|
363
|
+
if (!conn || !conn.port || assetsToFlash.length === 0)
|
|
342
364
|
return;
|
|
343
365
|
// Gracefully cancel reader and completely drop the transport natively
|
|
344
366
|
await disconnect(uuid);
|
|
367
|
+
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
345
368
|
base.updateConnection(uuid, (c) => ({
|
|
346
369
|
...c,
|
|
370
|
+
port: conn.port, // Restore the port into state in case disconnect wiped it
|
|
347
371
|
isFlashing: true,
|
|
348
372
|
flashProgress: 0,
|
|
349
373
|
flashError: undefined,
|
|
350
374
|
}));
|
|
375
|
+
// Declare outside the try block (Fixes variable shadowing)
|
|
376
|
+
let transport;
|
|
377
|
+
let esploader;
|
|
351
378
|
try {
|
|
352
379
|
// --- Connect ONCE ---
|
|
353
|
-
|
|
354
|
-
|
|
380
|
+
transport = new Transport(conn.port, true);
|
|
381
|
+
esploader = new ESPLoader({
|
|
355
382
|
transport,
|
|
356
383
|
baudrate: 921600,
|
|
357
384
|
enableTracing: false,
|
|
385
|
+
// debugLogging: !!conn.debugLogging,
|
|
358
386
|
});
|
|
359
|
-
try
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
catch (e) {
|
|
363
|
-
console.log("failed to esploader.main()", e);
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
387
|
+
// This may fail, that'll bubble the failure up to the upper try/catch
|
|
388
|
+
await esploader.main();
|
|
366
389
|
// --- Prepare an ARRAY of files for the library ---
|
|
367
390
|
const fileArray = await Promise.all(assetsToFlash.map(async ({ blob, address }) => {
|
|
368
391
|
const arrayBuffer = await blob.arrayBuffer();
|
|
@@ -371,32 +394,34 @@ export function useESP32MultiDeviceWhisperer({ releasePortByDefault, ...props }
|
|
|
371
394
|
.join("");
|
|
372
395
|
return { data: binaryString, address };
|
|
373
396
|
}));
|
|
397
|
+
let lastRenderTime = 0;
|
|
374
398
|
const flashOptions = {
|
|
375
|
-
fileArray,
|
|
399
|
+
fileArray,
|
|
376
400
|
flashSize: "keep",
|
|
377
401
|
flashMode: "qio",
|
|
378
402
|
flashFreq: "80m",
|
|
379
403
|
eraseAll: fileArray.length > 1, // Writing more than 1 thing, so likely writing partitions.
|
|
380
404
|
compress: true,
|
|
381
405
|
reportProgress: (fileIndex, written, total) => {
|
|
382
|
-
// You can enhance progress reporting to show which file is being flashed
|
|
383
406
|
const progress = (written / total) * 100;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
407
|
+
const now = performance.now();
|
|
408
|
+
// Throttle UI updates to at most once every 100ms (~10 FPS),
|
|
409
|
+
// but ALWAYS ensure the final 100% tick renders.
|
|
410
|
+
if (now - lastRenderTime > 100 || written === total) {
|
|
411
|
+
lastRenderTime = now;
|
|
412
|
+
// Optional: Only log every 100ms as well to prevent console spam
|
|
413
|
+
console.log(`Flashing file ${fileIndex + 1}/${fileArray.length}: ${progress.toFixed(1)}%`);
|
|
414
|
+
base.updateConnection(uuid, (c) => ({
|
|
415
|
+
...c,
|
|
416
|
+
flashProgress: progress,
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
389
419
|
},
|
|
390
420
|
};
|
|
391
421
|
// --- Call writeFlash ONCE with all files ---
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
catch (e) {
|
|
397
|
-
console.log("failed to esploader.writeFlash", e);
|
|
398
|
-
}
|
|
399
|
-
// --- Disconnect ---
|
|
422
|
+
base.updateConnection(uuid, (c) => ({ ...c, flashProgress: -1 }));
|
|
423
|
+
await esploader.writeFlash(flashOptions);
|
|
424
|
+
// --- Disconnect cleanly ---
|
|
400
425
|
await esploader.after();
|
|
401
426
|
try {
|
|
402
427
|
await transport.disconnect();
|
package/package.json
CHANGED