From bebc22c4e3df0595b71eee6838e9bb841d68702f Mon Sep 17 00:00:00 2001 From: jedarden Date: Thu, 16 Apr 2026 12:24:56 -0400 Subject: [PATCH] fix: use state.port directly for provisioning, retry port open after flash getAuthorizedPort() is unreliable after esptool reboots the device. Use state.port (the port the user explicitly selected) and retry port.open up to 5 times with 1s gaps to handle the brief window while the device re-enumerates. Show specific UserError messages instead of the generic one. Co-Authored-By: Claude Sonnet 4.6 --- dashboard/js/onboard.js | 93 ++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/dashboard/js/onboard.js b/dashboard/js/onboard.js index df3b61d..c073519 100644 --- a/dashboard/js/onboard.js +++ b/dashboard/js/onboard.js @@ -698,46 +698,63 @@ }); } - function sendPayloadOverSerial(payload) { + async function sendPayloadOverSerial(payload) { // Firmware expects {"provision": {...}} format var wrappedPayload = { provision: payload }; - return getAuthorizedPort() - .then(function (port) { - if (!port) throw new UserError( - 'No device found. Please go back and connect your ESP32-S3 again.' - ); - return port; - }) - .then(function (port) { - return port.open({ baudRate: CONFIG.serialBaudRate }) - .then(function () { return port; }) - .catch(function () { - // Port might already be open from a previous step - return port; - }); - }) - .then(function (port) { - return sendSerialJSONAndWaitForResponse(port, wrappedPayload, 15000) - .then(function (response) { - if (!response) { - throw new UserError( - 'No response from device. Please ensure the ESP32-S3 is connected and try again.' - ); - } - if (response.ok === false) { - var errorMsg = response.error || 'Unknown error'; - if (errorMsg === 'missing_provision_key') { - throw new UserError('Firmware communication error. Please try again.'); - } - if (errorMsg === 'nvs_write_failed') { - throw new UserError('Failed to save configuration to device. Please try again.'); - } - throw new UserError('Provisioning failed: ' + errorMsg); - } - // Success - return the MAC address - return response.mac; - }); - }); + + // Prefer the port the user explicitly selected in the Connect step. Fall back to + // whatever the browser has previously authorized if state.port was somehow lost. + var port = state.port || await getAuthorizedPort(); + if (!port) { + throw new UserError('No device found. Please go back to Connect and select your ESP32-S3 again.'); + } + + // The port may be closed (esptool closes it after flashing). Open it with retries + // to handle the brief gap while the device reboots and re-enumerates. + var opened = false; + for (var attempt = 0; attempt < 5; attempt++) { + try { + await port.open({ baudRate: CONFIG.serialBaudRate }); + opened = true; + break; + } catch (e) { + // Already open → proceed + if (e && (e.message || '').toLowerCase().includes('already open')) { + opened = true; + break; + } + // Device not ready yet — wait and retry + if (attempt < 4) { + await new Promise(function (r) { setTimeout(r, 1000); }); + } + } + } + + if (!opened) { + throw new UserError( + 'Could not open serial port. Unplug and replug the USB cable, then try again.' + ); + } + + var response = await sendSerialJSONAndWaitForResponse(port, wrappedPayload, 15000); + try { await port.close(); } catch (_) {} + + if (!response) { + throw new UserError( + 'No response from device. Make sure the board finished booting and try again.' + ); + } + if (response.ok === false) { + var errorMsg = response.error || 'Unknown error'; + if (errorMsg === 'missing_provision_key') { + throw new UserError('Firmware communication error. Please try again.'); + } + if (errorMsg === 'nvs_write_failed') { + throw new UserError('Failed to save configuration to device. Please try again.'); + } + throw new UserError('Provisioning failed: ' + errorMsg); + } + return response.mac; } function showFormError(id, msg) {