diff --git a/frontend/src/simulation/Interconnect.ts b/frontend/src/simulation/Interconnect.ts index 28ba9a5a..51709cd9 100644 --- a/frontend/src/simulation/Interconnect.ts +++ b/frontend/src/simulation/Interconnect.ts @@ -747,6 +747,18 @@ export function notifyBoardReady(_boardId: string, wires: readonly Wire[]): void updateI2CBridges(wires); } +/** + * Re-attempt the serial hook for a board whose sim/bridge did not exist + * when the routes were built. Routes are installed at page load; a Pi's + * bridge is created at Run — so the TX hook silently no-opped and the + * guest's UART bytes never reached the wire. The store calls this when + * the bridge connects; ensureSerialHook is idempotent via its flag. + */ +export function reensureSerialHooks(boardId: string): void { + const entry = boards.get(boardId); + if (entry && entry.serialFanout.size > 0) ensureSerialHook(entry); +} + /** For tests: reset all internal state. */ export function resetInterconnect(): void { for (const r of routes.values()) r.teardown(); diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index 1b080194..a2cab443 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -55,6 +55,7 @@ import { isBreadboard } from '../utils/breadboardNets'; import { computeSeating } from '../utils/breadboardSnap'; import { createSerialBatcher } from './serialBatcher'; import { + reensureSerialHooks as icReensureSerialHooks, bindBoard as icBindBoard, unbindBoard as icUnbindBoard, updateWires as icUpdateWires, @@ -1344,6 +1345,10 @@ export const useSimulatorStore = create((set, get) => { }); }; bridgeMap.set(id, bridge); + // The UART routes were built at page load, when this bridge did + // not exist — re-attempt the TX hook now that it does, or the + // guest's header-UART bytes never reach the canvas wire. + icReensureSerialHooks(id); } else if (isEsp32Kind(boardKind)) { wireEsp32Board(id, boardKind, pm); } else if (isStm32BoardKind(boardKind)) {