diff options
| author | Adam Soltys <[email protected]> | 2026-09-04 20:32:43 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-14 08:24:48 -0700 |
| commit | 57191e941016f15a9c52de7e95e7bb2a04c9de13 (patch) | |
| tree | 0dbd8a723efca8e64f2f7ed5f4e9bfe845dc05c3 | |
| parent | 4245a2ecc029f2908970ce3074113603acf1eae7 (diff) | |
| download | coinos-ui-57191e941016f15a9c52de7e95e7bb2a04c9de13.tar.gz coinos-ui-57191e941016f15a9c52de7e95e7bb2a04c9de13.zip | |
pos: reset the device and release the serial port when leaving the flasher
Connecting loads the esptool stub and parks the chip in the ROM download
mode; only a successful flash reset it and closed the port. Navigating
away, a failed connect, or a failed flash left the device looking frozen
(OLED keeps its last frame, LED pin pulled up, keypad dead) with the
browser still holding the port. Reset + release on component destroy and
pagehide, on connect failure, and from a new Disconnect button.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01VMi2Y1oKPZ3TeeFmLej47y
| -rw-r--r-- | src/routes/(app)/pos/+page.svelte | 40 |
1 files changed, 34 insertions, 6 deletions
diff --git a/src/routes/(app)/pos/+page.svelte b/src/routes/(app)/pos/+page.svelte index eb074b1a..5a11d148 100644 --- a/src/routes/(app)/pos/+page.svelte +++ b/src/routes/(app)/pos/+page.svelte @@ -1,5 +1,6 @@ <script> import { enhance } from "$app/forms"; + import { onDestroy } from "svelte"; import { ESPLoader, Transport } from "esptool-js"; import { hex } from "@scure/base"; @@ -77,15 +78,30 @@ portInfo = (await device.getInfo?.()) ? JSON.stringify(await device.getInfo()) : "Connected"; } catch (e) { console.log(e); - connected = false; + // A failed connect must not leave the port claimed: the browser keeps + // it open until someone calls close(), and the chip may already be + // sitting in the ROM download mode. + await resetDevice(); } }; + // Close the serial port and forget it. The browser holds a port open until + // it is explicitly closed — navigating away does not release it. + let releasePort = async () => { + try { + await transport?.disconnect(); + } catch (e) {} + transport = undefined; + esploader = undefined; + connected = false; + }; + // Pulse RTS to hard-reset the chip out of the ROM bootloader so it boots // the app with the new flash contents. Same sequence esptool.py uses // ("Hard resetting via RTS pin"); on the C3's USB-JTAG-serial port RTS // drives EN, so the device re-enumerates and must be reconnected. let resetDevice = async () => { + if (!transport) return; try { await transport.setDTR(false); await transport.setRTS(true); @@ -94,14 +110,25 @@ } catch (e) { console.warn("reset failed", e); } - try { - await transport.disconnect(); - } catch (e) {} - esploader = undefined; - connected = false; + await releasePort(); portInfo = "Device reset. Reconnect to flash again."; }; + // Connecting parks the chip in the ROM download mode (esptool stub loaded, + // app not running). Until it is hard-reset the OLED keeps whatever the app + // last drew and the keypad is dead, so it looks frozen rather than + // "in flashing mode". Leaving the page — SPA navigation, reload, or tab + // close — must therefore reset the device and release the port; neither + // happens on its own. + onDestroy(() => { + resetDevice(); + }); + $effect(() => { + const onHide = () => resetDevice(); + window.addEventListener("pagehide", onHide); + return () => window.removeEventListener("pagehide", onHide); + }); + let flashConfig = async () => { if (!esploader || !bytes) return; if (bytes.length !== 0x20000) { @@ -253,6 +280,7 @@ <div class="flex gap-2 justify-center"> <button class="btn" class:btn-neutral={tab === "config"} onclick={() => (tab = "config")}>Flash config</button> <button class="btn" class:btn-neutral={tab === "firmware"} onclick={() => (tab = "firmware")}>Flash firmware</button> + <button class="btn" onclick={resetDevice} title="Reset the device into its app and release the serial port">Disconnect</button> </div> {#if tab === "config"} |
