aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-04 20:32:43 -0700
committerAdam Soltys <[email protected]>2026-09-14 08:24:48 -0700
commit57191e941016f15a9c52de7e95e7bb2a04c9de13 (patch)
tree0dbd8a723efca8e64f2f7ed5f4e9bfe845dc05c3 /src
parent4245a2ecc029f2908970ce3074113603acf1eae7 (diff)
downloadcoinos-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
Diffstat (limited to 'src')
-rw-r--r--src/routes/(app)/pos/+page.svelte40
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"}