Closes the remaining gaps in cross-board I2C so any topology of supported boards (Uno↔ESP32, two ESP32s, Uno↔Uno↔Uno, ESP32-C3 connected to anything, etc.) works end-to-end with all I2C components including write-only sinks (SSD1306, PCF8574, LCD-I2C). Implementation (6 phases): 1. **BFS routing in I2CBusManager**: connectToSlave + handleExternalConnect walk the bridge graph with a visited Set so multi-hop chains (A↔B↔C with the device on C) resolve transparently. A new forwarder-device shim is installed at intermediate hops so the existing handleExternalWrite/Read/Stop machinery routes through without per-method visited tracking. 2. **Per-peer proxy ownership in Esp32BridgeShim**: replaces the global _proxiedAddrs Set with _proxiedByPeer Map so concurrent bridges to the same ESP32 (e.g. wired to both Uno and Pico) don't wipe each other's proxies on teardown. Interconnect's per-wire teardown calls clearProxiesForPeer(peerBus) instead of clearAllProxies. 3. **BFS-aware proxy sync**: syncProxyFromPeer now walks the peer bus + its transitive bridges, so an ESP32 sees devices on boards two or more hops away. _peerDeviceLookup keeps a flat addr → device map for write-forwarding and resync. 4. **Periodic resync (250 ms)**: Esp32BridgeShim runs a setInterval while any proxy is live, re-dumping each device with dumpRegisters() and pushing updateProxyI2c only when an XOR- stride hash changes. This keeps RTC time advancing visible to ESP32 firmware without flooding the WS pipe with static calibration dumps. Hash is primed during initial sync so the first tick doesn't push a redundant identical buffer. 5. **Write-forwarding ProxySlave → peer**: backend ProxySlave buffers write bytes during the transaction and emits a `proxy_i2c_complete` event on STOP / repeated-START. Frontend Esp32Bridge dispatches the event to a new onProxyI2cComplete callback; the shim replays the byte sequence on the actual peer I2CDevice via writeByte() + stop(). Makes ESP32 firmware writes to peer SSD1306 actually repaint the OLED, peer PCF8574 latch updates, peer I2CMemoryDevice register mutations propagate. 6. **ESP32-C3 routed as bridge**: Interconnect.isBrowserSim no longer claims c3/xiao-c3/c3-supermini — they were already going through Esp32Bridge per the store's ESP32_RISCV_KINDS routing, but Interconnect was treating them as browser sims which broke proxy install. isEsp32Bridge now correctly includes c3 family + ESP32-S3 + Arduino Nano ESP32. Defensive: addBoard now disposes any existing shim's proxies before overwriting simulatorMap entry so test reruns don't leak timers. Tests: - 4 BFS multi-hop tests (i2c-multi-board-slave-gap.test.ts) - 11 cross-board scenarios + per-peer + write-forward + resync (i2c-esp32-multiboard-bridge.test.ts) - 1 real-firmware E2E for write-forward via QEMU (compile + load + observe proxy_i2c_complete arriving with the byte) - New sketch fixture: esp32_i2c_write_to_peer.ino Result: 90 test files / 1295 tests pass / 0 fail. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| public | ||
| scripts | ||
| src | ||
| .env.production | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc.json | ||
| Dockerfile | ||
| README.md | ||
| eslint.config.js | ||
| esp32 plan.md | ||
| index.html | ||
| nginx.conf | ||
| package.json | ||
| tsconfig.app.json | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| vite.config.ts | ||
README.md
Arduino Emulator - Frontend
React + TypeScript + Vite frontend for the Arduino emulator with visual simulator and code editor.
Features
- Monaco Code Editor - Full VSCode-like Arduino code editing experience
- Dynamic Component System - 48+ wokwi-elements components with search and categories
- Visual Simulator Canvas - Interactive drag-and-drop circuit builder
- Component Property Dialog - Single-click component interaction (rotate, delete, view pins)
- Segment-Based Wire Editing - Drag wire segments perpendicular to orientation (like Wokwi)
- Real AVR8 Emulation - Actual ATmega328p emulation using avr8js
- Pin Management - Automatic pin mapping and state synchronization
- Grid Snapping - 20px grid alignment for clean circuit layouts
Tech Stack
- React 18 - UI framework
- TypeScript - Static typing
- Vite 5 - Build tool and dev server
- Monaco Editor - Code editor (VSCode engine)
- Zustand - State management
- Axios - HTTP client for backend API
- avr8js - AVR8 CPU emulator (npm package)
- @wokwi/elements - Electronic web components (npm package)
Development
Prerequisites
- Node.js 18+
- Backend running at http://localhost:8001
Install Dependencies
npm install
Run Development Server
npm run dev
The app will be available at http://localhost:5173
Build for Production
npm run build
Output will be in the dist/ directory.
Lint
npm run lint
Project Structure
frontend/
├── src/
│ ├── components/
│ │ ├── velxio-components/ # React wrappers for wokwi-elements + Velxio-original parts
│ │ ├── editor/ # Monaco Editor components
│ │ │ ├── CodeEditor.tsx
│ │ │ └── EditorToolbar.tsx
│ │ └── simulator/ # Simulation canvas components
│ │ ├── SimulatorCanvas.tsx
│ │ ├── WireLayer.tsx
│ │ ├── WireRenderer.tsx
│ │ ├── PinOverlay.tsx
│ │ ├── ComponentPropertyDialog.tsx
│ │ ├── ComponentPickerModal.tsx
│ │ └── ComponentPalette.tsx
│ ├── simulation/
│ │ ├── AVRSimulator.ts # AVR8 CPU wrapper
│ │ └── PinManager.ts # Pin mapping and callbacks
│ ├── store/
│ │ ├── useEditorStore.ts # Code editor state
│ │ └── useSimulatorStore.ts # Simulation state
│ ├── services/
│ │ ├── api.ts # Backend API client
│ │ └── ComponentRegistry.ts # Component metadata
│ ├── types/ # TypeScript definitions
│ ├── utils/
│ │ ├── hexParser.ts # Intel HEX parser
│ │ ├── wirePathGenerator.ts # Wire SVG path generation
│ │ └── wireSegments.ts # Segment-based wire editing
│ ├── App.tsx # Main app component
│ └── main.tsx # Entry point
├── public/ # Static assets
├── vite.config.ts # Vite configuration
└── package.json
Key Architecture Patterns
State Management (Zustand)
Two main stores:
- useEditorStore - Code content, theme, compilation state
- useSimulatorStore - Simulation running state, components, wires, compiled hex
Wokwi Libraries
@wokwi/elements, avr8js and rp2040js are regular npm dependencies resolved
from node_modules like any other package — no local clones required.
AVR Simulation Loop
- Runs at ~60 FPS using
requestAnimationFrame - Executes ~267,000 CPU cycles per frame (16MHz / 60fps)
- Port listeners fire when GPIO registers change
- PinManager routes pin states to component callbacks
Component System
Components are Web Components from wokwi-elements:
- React wrappers in
velxio-components/ - Dynamic loading via ComponentRegistry
- Pin info extracted from component metadata
- State updates via refs and callbacks
Wire Editing System
Segment-based editing (like Wokwi):
- Wires consist of orthogonal segments (horizontal/vertical)
- Drag segments perpendicular to orientation:
- Horizontal segments: move up/down (ns-resize)
- Vertical segments: move left/right (ew-resize)
- Local preview state during drag (requestAnimationFrame)
- Store update only on mouse up with grid snapping (20px)
Performance Optimizations
requestAnimationFramefor smooth wire dragging- Local state for real-time previews
- Memoized path generation and segment computation
- Store updates batched at interaction completion
API Integration
Backend endpoints (http://localhost:8001):
POST /api/compile- Compile Arduino code to .hexGET /api/compile/status/{task_id}- Check compilation statusGET /api/compile/download/{filename}- Download compiled .hex
See backend documentation for API details.
Component Development
Adding a New Component Type
-
Check if wokwi-elements has the component:
ls ../third-party/wokwi-elements/src/ -
Create React wrapper in
src/components/velxio-components/:import React, { useRef, useEffect } from 'react'; export const WokwiMyComponent: React.FC<Props> = ({ ... }) => { const elementRef = useRef<any>(null); useEffect(() => { if (elementRef.current) { elementRef.current.setAttribute('prop', value); } }, [value]); return <wokwi-my-component ref={elementRef} />; }; -
Add to ComponentRegistry metadata
-
Use in SimulatorCanvas or make available in ComponentPalette
Troubleshooting
Monaco Editor Not Loading
- Check if
monaco-editoris installed - Verify Vite worker configuration in vite.config.ts
Components Not Rendering
- Ensure wokwi-elements is built:
cd ../third-party/wokwi-elements && npm run build - Check browser console for Web Component registration errors
- Verify Vite alias paths in vite.config.ts
Wire Editing Performance Issues
- Ensure
requestAnimationFrameis being used - Check that store updates only happen on mouse up, not during drag
- Verify no unnecessary re-renders with React DevTools
Pin Alignment Issues
- Pin coordinates from wokwi-elements are in CSS pixels
- Do NOT multiply by MM_TO_PX conversion factor
- Verify component position + pin offset calculation
Compilation Fails
- Check backend is running at http://localhost:8001
- Verify arduino-cli is installed and
arduino:avrcore is available - Check CORS configuration in backend