diff --git a/frontend/src/simulation/parts/PartSimulationRegistry.ts b/frontend/src/simulation/parts/PartSimulationRegistry.ts index 1da2cd5..06f25dd 100644 --- a/frontend/src/simulation/parts/PartSimulationRegistry.ts +++ b/frontend/src/simulation/parts/PartSimulationRegistry.ts @@ -1,7 +1,14 @@ import { AVRSimulator } from '../AVRSimulator'; import { RP2040Simulator } from '../RP2040Simulator'; -export type AnySimulator = AVRSimulator | RP2040Simulator; +/** Any simulator that components can interact with (AVR, RP2040, or ESP32 bridge shim). */ +export type AnySimulator = { + setPinState(pin: number, state: boolean): void; + isRunning(): boolean; + pinManager: import('../PinManager').PinManager; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + [key: string]: any; +} | AVRSimulator | RP2040Simulator; /** * Interface for simulation logic mapped to a specific wokwi-element diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index e7ae04e..39988d7 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -35,8 +35,41 @@ export const BOARD_LABELS: Record = { export const DEFAULT_BOARD_POSITION = { x: 50, y: 50 }; export const ARDUINO_POSITION = DEFAULT_BOARD_POSITION; +// ── Lightweight shim wrapping Esp32Bridge so component simulations (DHT22, etc.) +// can call setPinState / pinManager just like they would on a local simulator. ── +class Esp32BridgeShim { + pinManager: PinManager; + onSerialData: ((ch: string) => void) | null = null; + onPinChangeWithTime: ((pin: number, state: boolean, timeMs: number) => void) | null = null; + onBaudRateChange: ((baud: number) => void) | null = null; + private bridge: Esp32Bridge; + + constructor(bridge: Esp32Bridge, pm: PinManager) { + this.bridge = bridge; + this.pinManager = pm; + } + + setPinState(pin: number, state: boolean): void { this.bridge.sendPinEvent(pin, state); } + getCurrentCycles(): number { return -1; } + getClockHz(): number { return 240_000_000; } + isRunning(): boolean { return this.bridge.connected; } + serialWrite(text: string): void { + this.bridge.sendSerialBytes(Array.from(new TextEncoder().encode(text))); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getADC(): any { return null; } + getMCU(): null { return null; } + start(): void { /* managed by bridge */ } + stop(): void { /* managed by bridge */ } + reset(): void { /* managed by bridge */ } + setSpeed(_s: number): void { /* no-op */ } + getSpeed(): number { return 1; } + loadHex(_hex: string): void { /* no-op */ } + loadBinary(_b64: string): void { /* no-op */ } +} + // ── Runtime Maps (outside Zustand — not serialisable) ───────────────────── -const simulatorMap = new Map(); +const simulatorMap = new Map(); const pinManagerMap = new Map(); const bridgeMap = new Map(); const esp32BridgeMap = new Map(); @@ -97,7 +130,7 @@ interface SimulatorState { /** @deprecated use boards[x].x/y */ boardPosition: { x: number; y: number }; /** @deprecated use getBoardSimulator(activeBoardId) */ - simulator: AVRSimulator | RP2040Simulator | RiscVSimulator | Esp32C3Simulator | null; + simulator: AVRSimulator | RP2040Simulator | RiscVSimulator | Esp32C3Simulator | Esp32BridgeShim | null; /** @deprecated use getBoardPinManager(activeBoardId) */ pinManager: PinManager; running: boolean; @@ -337,6 +370,11 @@ export const useSimulatorStore = create((set, get) => { } }; esp32BridgeMap.set(id, bridge); + // Provide a shim so PartSimulationRegistry components (DHT22, etc.) + // can call setPinState / access pinManager on ESP32 boards. + const shim = new Esp32BridgeShim(bridge, pm); + shim.onSerialData = serialCallback; + simulatorMap.set(id, shim); } else { const sim = createSimulator( boardKind, @@ -645,10 +683,13 @@ export const useSimulatorStore = create((set, get) => { } }; esp32BridgeMap.set(boardId, bridge); + const shim = new Esp32BridgeShim(bridge, pm); + shim.onSerialData = serialCallback; + simulatorMap.set(boardId, shim); set((s) => ({ boardType: type, - simulator: null, + simulator: shim as any, compiledHex: null, serialOutput: '', serialBaudRate: 0, diff --git a/frontend/src/utils/boardPinMapping.ts b/frontend/src/utils/boardPinMapping.ts index 194846a..b273c8a 100644 --- a/frontend/src/utils/boardPinMapping.ts +++ b/frontend/src/utils/boardPinMapping.ts @@ -132,6 +132,13 @@ const ESP32_PIN_MAP: Record = { 'GPIO25': 25, 'GPIO26': 26, 'GPIO27': 27, 'GPIO32': 32, 'GPIO33': 33, 'GPIO34': 34, 'GPIO35': 35, 'GPIO36': 36, 'GPIO39': 39, + // Wokwi element "D" prefix aliases (esp32-devkit-v1-element pin names) + 'D2': 2, 'D4': 4, 'D5': 5, + 'D12': 12, 'D13': 13, 'D14': 14, 'D15': 15, + 'D16': 16, 'D17': 17, 'D18': 18, 'D19': 19, + 'D21': 21, 'D22': 22, 'D23': 23, + 'D25': 25, 'D26': 26, 'D27': 27, + 'D32': 32, 'D33': 33, 'D34': 34, 'D35': 35, // ADC aliases 'VP': 36, 'VN': 39, // Power / GND — not real GPIOs; mapped to -1 so WirePin skips silently