# Project Architecture - Velxio Arduino Emulator ## Overview This project is a fully local Arduino emulator using official Wokwi repositories for maximum compatibility. It features real AVR8 CPU emulation, 48+ interactive electronic components, a comprehensive wire system, and a build-time component discovery pipeline. ``` ┌─────────────────────────────────────────────────────────────────────┐ │ USER (Browser) │ │ http://localhost:5173 │ └──────────────────────────┬──────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ FRONTEND (React 19 + Vite 7) │ │ │ │ ┌────────────────┐ ┌──────────────────┐ ┌───────────────────┐ │ │ │ Monaco Editor │ │ Zustand Stores │ │ SimulatorCanvas │ │ │ │ (Code Edit) │ │ (Editor+Sim) │ │ (Components+ │ │ │ │ C++ / Arduino │ │ │ │ Wires+Pins) │ │ │ └────────┬───────┘ └────────┬─────────┘ └────────┬──────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ AVRSimulator (avr8js) │ │ │ │ CPU 16MHz · Timer0/1/2 · USART · ADC · PORTB/C/D │ │ │ │ ~60fps · 267k cycles/frame · Speed 0.1x-10x │ │ │ └──────────────────────────────┬───────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ PinManager + PartSimulationRegistry │ │ │ │ Digital/PWM/Analog listeners · 16 registered parts │ │ │ │ LED · RGB · Button · Pot · LCD · Servo · Buzzer · etc. │ │ │ └──────────────────────────────┬───────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ 48+ wokwi-elements (Lit Web Components) │ │ │ │ DynamicComponent renderer · ComponentRegistry (metadata) │ │ │ │ ComponentPickerModal · Property dialog · Pin selector │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │ Wire System │ │ │ │ Orthogonal routing · Segment editing · 8 signal colors │ │ │ │ Overlap offset · Pin overlay · Grid snapping (20px) │ │ │ └──────────────────────────────────────────────────────────────┘ │ └──────────────────────────┬──────────────────────────────────────────┘ │ HTTP (Axios) ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ BACKEND (FastAPI + Python) │ │ http://localhost:8001 │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ POST /api/compile/ → Compile Arduino code to .hex │ │ │ │ GET /api/compile/boards → List available boards │ │ │ │ GET / → API info │ │ │ │ GET /health → Health check │ │ │ └──────────────────────────────┬──────────────────────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ ArduinoCLIService │ │ │ │ Auto-installs arduino:avr core │ │ │ │ Temp directory + subprocess.run via asyncio.to_thread │ │ │ └──────────────────────────────┬──────────────────────────────┘ │ └─────────────────────────────────┼──────────────────────────────────┘ │ ▼ ┌──────────────────────┐ │ arduino-cli │ │ (Local system) │ └──────────────────────┘ ``` ## Data Flow ### 1. Code Editing ``` User writes Arduino code ↓ Monaco Editor (C++, dark theme, autocomplete) ↓ Zustand useEditorStore ↓ State: { code, theme, fontSize } ``` ### 2. Compilation ``` Click "Compile" ↓ EditorToolbar.tsx → compileCode() ↓ Axios POST → http://localhost:8001/api/compile/ ↓ Backend: ArduinoCLIService.compile() ↓ arduino-cli compile --fqbn arduino:avr:uno --output-dir build/ ↓ Reads build/sketch.ino.hex → returns hex_content ↓ Frontend: useSimulatorStore.setCompiledHex(hex) ↓ Auto-calls loadHex() → CPU + peripherals created ``` ### 3. Simulation (Real AVR8 Emulation) ``` Click "Run" ↓ useSimulatorStore.startSimulation() ↓ AVRSimulator.start() ↓ requestAnimationFrame loop @ ~60fps ↓ Each frame: Math.floor(267000 × speed) cycles ↓ For each cycle: ├── avrInstruction(cpu) ← Execute AVR instruction └── cpu.tick() ← Update peripherals/timers ↓ Port writes → AVRIOPort listeners ↓ PinManager.updatePort(portName, newValue, oldValue) ↓ Per-pin callbacks fire for changed pins ↓ PartSimulationRegistry.onPinStateChange() ↓ wokwi web components update visually Additionally per frame: pollPwmRegisters() → reads OCR0A/B, OCR1AL/BL, OCR2A/B ↓ PinManager.updatePwm(pin, dutyCycle) ``` ### 4. Input Components Flow ``` User presses a pushbutton on canvas ↓ Web component fires 'button-press' event ↓ DynamicComponent catches event ↓ PartSimulationRegistry.attachEvents() handler ↓ AVRSimulator.setPinState(arduinoPin, LOW) ↓ AVRIOPort.setPin(bitIndex) injects external pin state ↓ CPU reads pin value in next instruction ``` ### 5. Wire Creation Flow ``` Click pin on component A → startWireCreation(endpoint) ↓ Mouse move → updateWireInProgress(x, y) ↓ WireInProgressRenderer shows dashed green L-shape preview ↓ Click pin on component B → finishWireCreation(endpoint) ↓ Wire created with midpoint control point ↓ WireLayer renders orthogonal SVG path ↓ Components subscribe to Arduino pins via wire lookup ``` ## Key Components ### Frontend #### 1. Stores (Zustand) **useEditorStore** — Code editor state | Property | Type | Default | |----------|------|---------| | `code` | `string` | Blink example sketch | | `theme` | `'vs-dark' \| 'light'` | `'vs-dark'` | | `fontSize` | `number` | `14` | Methods: `setCode()`, `setTheme()`, `setFontSize()` **useSimulatorStore** — Simulation + components + wires state | Property | Type | Description | |----------|------|-------------| | `simulator` | `AVRSimulator \| null` | CPU emulator instance | | `pinManager` | `PinManager` | Pin-to-component mapping | | `running` | `boolean` | Simulation active | | `compiledHex` | `string \| null` | Compiled hex content | | `components` | `Component[]` | All electronic components | | `wires` | `Wire[]` | All wire connections | | `selectedWireId` | `string \| null` | Currently selected wire | | `wireInProgress` | `WireInProgress \| null` | Wire being created | Methods (20+): - **Simulation**: `initSimulator()`, `loadHex()`, `startSimulation()`, `stopSimulation()`, `resetSimulation()`, `setCompiledHex()`, `setRunning()` - **Components**: `addComponent()`, `removeComponent()`, `updateComponent()`, `updateComponentState()`, `handleComponentEvent()`, `setComponents()` - **Wires**: `addWire()`, `removeWire()`, `updateWire()`, `setSelectedWire()`, `setWires()` - **Wire creation**: `startWireCreation()`, `updateWireInProgress()`, `finishWireCreation()`, `cancelWireCreation()` - **Wire positions**: `updateWirePositions(componentId)`, `recalculateAllWirePositions()` Notable behaviors: - `removeComponent()` cascades: removes all connected wires - `updateComponent()` auto-recalculates wire positions when x/y changes - `setCompiledHex()` auto-calls `loadHex()` #### 2. Simulation Engine **AVRSimulator** — Real ATmega328p emulation - **CPU**: 16MHz clock, 32KB program memory (16K words) - **Timers**: Timer0 (`timer0Config`), Timer1 (`timer1Config`), Timer2 (`timer2Config`) - **Serial**: USART (`usart0Config`) at 16MHz - **ADC**: Analog-to-digital converter (`adcConfig`) - **GPIO**: PORTB (pins 8-13), PORTC (A0-A5), PORTD (pins 0-7) - **Simulation loop**: ~60fps via `requestAnimationFrame`, `267000 × speed` cycles/frame - **Speed control**: 0.1x – 10x multiplier - **PWM polling**: Reads OCR0A/B, OCR1AL/BL, OCR2A/B each frame - **API**: `loadHex()`, `start()`, `stop()`, `reset()`, `step()`, `setSpeed()`, `setPinState()`, `getADC()` **PinManager** — Pin state tracking and listener dispatch - **Digital**: `onPinChange(pin, callback)`, `updatePort(portName, newValue, oldValue)`, `getPinState(pin)` - **PWM**: `onPwmChange(pin, callback)`, `updatePwm(pin, dutyCycle)`, `getPwmValue(pin)` - **Analog**: `onAnalogChange(pin, callback)`, `setAnalogVoltage(pin, voltage)` - **Utility**: `getListenersCount()`, `clearAllListeners()` **PartSimulationRegistry** — Plugin system for component behaviors - Interface: `onPinStateChange(pinName, state, element)` for outputs, `attachEvents(element, simulator, pinHelper) → cleanup` for inputs - **16 registered parts**: | Part | Type | Key Behavior | |------|------|--------------| | `led` | Output | Pin A state → `element.value` | | `rgb-led` | Output | Digital + PWM on R/G/B → `ledRed/Green/Blue` | | `led-bar-graph` | Output | 10 LEDs (A1-A10) → `.values` array | | `7segment` | Output | 8 segments (A-G + DP) → `.values` array | | `pushbutton` | Input | Press/release → `setPinState(pin, LOW/HIGH)` | | `pushbutton-6mm` | Input | Same as pushbutton | | `slide-switch` | Input | Change event → pin state | | `dip-switch-8` | Input | 8 independent switches | | `potentiometer` | Input | Value (0-1023) → ADC voltage injection | | `slide-potentiometer` | Input | Same via SIG/OUT pins | | `photoresistor-sensor` | Input/Output | Default 2.5V on AO, monitors DO for LED | | `analog-joystick` | Input | VRX/VRY (ADC) + SW (digital) | | `servo` | Output | Polls OCR1A/ICR1 → angle 0-180° | | `buzzer` | Output | Web Audio API, reads Timer2 registers | | `lcd1602` | Output | Full HD44780 4-bit protocol (16×2) | | `lcd2004` | Output | Full HD44780 4-bit protocol (20×4) | #### 3. Component System **ComponentRegistry** — Singleton from `/components-metadata.json` - **48 components** across 8 categories - Auto-generated at build time by `scripts/generate-component-metadata.ts` (TypeScript AST parser) - Methods: `getAllComponents()`, `getByCategory()`, `getById()`, `search()`, `getCategories()` | Category | Count | Components | |----------|-------|------------| | Boards | 4 | Arduino Uno, Mega, Nano, etc. | | Sensors | 6 | DHT22, HC-SR04, PIR, photoresistor, etc. | | Displays | 3 | LCD 1602, LCD 2004, 7-segment | | Input | 5 | Buttons, switches, potentiometers, joystick | | Output | 5 | LEDs, RGB LED, LED bar graph, buzzer | | Motors | 2 | Servo, stepper | | Passive | 4 | Resistor, capacitor, etc. | | Other | 19 | Various components | **DynamicComponent** — Generic web component renderer - Creates DOM elements with `document.createElement(metadata.tagName)` - Syncs React properties to web component properties - Extracts `pinInfo` from web component DOM (100ms polling, 2s timeout) - Integrates with PartSimulationRegistry for simulation events - Resolves Arduino pin from wire connections - Handles visual state: selection border, rotation, cursor, labels **ComponentPickerModal** — Component search and selection UI - Search bar with real-time filtering - Category tabs from registry - Live wokwi-element thumbnails (actual web components at reduced scale) - Component count, pin count, description badges #### 4. UI Components **SimulatorCanvas** (~472 lines) — Main simulation canvas - Arduino Uno board at fixed position - Dynamic component rendering via DynamicComponent - Component drag-and-drop with viewport→canvas coordinate conversion - Click vs. drag detection (time <300ms, distance <5px threshold) - Single-click: opens ComponentPropertyDialog - Double-click: opens PinSelector - Wire creation via pin clicks (crosshair cursor during creation) - Wire auto-recalculation on component move (retries at 100/300/500ms) - PinOverlay on all components (hidden during simulation) - Keyboard shortcuts: Delete/Backspace (remove), Escape (cancel wire) - PinManager subscriptions for output component state updates - Status indicator: Running/Stopped + component count - "+ Add Component" button → opens ComponentPickerModal **WireRenderer** (~400 lines) — Interactive wire display and editing - Orthogonal SVG path rendering - 10px invisible hitbox for easy clicking - Segment-based editing: hover highlights, drag perpendicular to orientation - `requestAnimationFrame` smooth drag with local preview state - Grid snapping (20px) applied on mouseUp - Invalid wire styling (red dashed) - Endpoint markers at start/end **WireLayer** — SVG overlay with automatic offset calculation for overlapping wires **WireInProgressRenderer** — Dashed green preview during wire creation (L-shaped routing) **PinOverlay** — 12px cyan circles at each pin position; green on hover with scale animation **ComponentPropertyDialog** — Shows pin roles, Arduino pin assignment, Rotate and Delete buttons **PinSelector** — Modal for assigning D0-D13 and A0-A5 to component pins **CodeEditor** — Monaco Editor wrapper (C++, dark theme, minimap, word wrap) **EditorToolbar** — Compile/Run/Stop/Reset buttons with status messages **ExamplesGallery** — Filterable card grid (category + difficulty filters) #### 5. Wire Utilities | Utility | Purpose | |---------|---------| | `wirePathGenerator.ts` | L-shape and multi-segment orthogonal SVG path generation | | `wireSegments.ts` | Segment computation, hit testing (8px tolerance), drag updates | | `wireColors.ts` | 8 signal-type colors + `determineSignalType()` | | `wireOffsetCalculator.ts` | Parallel overlap detection (5px tolerance), symmetric offset (6px spacing) | | `pinPositionCalculator.ts` | Pin coordinate conversion (element → canvas space), closest pin snap (20px) | | `hexParser.ts` | Intel HEX parser with checksum verification | | `captureCanvasPreview.ts` | SVG foreignObject preview image generation | ### Backend **FastAPI** app (port 8001) with CORS for ports 5173-5175 | Endpoint | Method | Description | |----------|--------|-------------| | `/` | GET | API info | | `/health` | GET | Health check | | `/api/compile/` | POST | Compile Arduino code → hex content | | `/api/compile/boards` | GET | List available boards | **ArduinoCLIService**: - Auto-installs `arduino:avr` core if missing - Creates temp sketch directory, runs `arduino-cli compile` via `asyncio.to_thread(subprocess.run)` - Reads `build/sketch.ino.hex` output - Board listing via `arduino-cli board listall` ### Pages & Routing | Route | Page | Layout | |-------|------|--------| | `/` | EditorPage | Header + split panels: Editor (left) + Simulator (right) | | `/examples` | ExamplesPage | Examples gallery with "Back to Editor" link | ### Example Projects (8) | ID | Title | Category | Difficulty | |----|-------|----------|------------| | `blink-led` | Blink LED | basics | beginner | | `traffic-light` | Traffic Light | basics | beginner | | `button-led` | Button Control | basics | beginner | | `fade-led` | Fade LED | basics | beginner | | `serial-hello` | Serial Hello World | communication | beginner | | `rgb-led` | RGB LED Colors | basics | intermediate | | `simon-says` | Simon Says Game | games | advanced | | `lcd-hello` | LCD 20x4 Display | displays | intermediate | Each example includes full Arduino sketch, component definitions, and wire connections. ### Wokwi Libraries (Local Clones) | Library | Location | Purpose | |---------|----------|---------| | wokwi-elements | `wokwi-libs/wokwi-elements/` | 48+ Lit Web Components | | avr8js | `wokwi-libs/avr8js/` | AVR8 ATmega328p emulator | | rp2040js | `wokwi-libs/rp2040js/` | RP2040 emulator (future) | | wokwi-features | `wokwi-libs/wokwi-features/` | Features documentation | ## Vite Integration ### Alias Configuration ```typescript // vite.config.ts resolve: { alias: { 'avr8js': path.resolve(__dirname, '../wokwi-libs/avr8js/dist/esm'), '@wokwi/elements': path.resolve(__dirname, '../wokwi-libs/wokwi-elements/dist/esm'), }, }, optimizeDeps: { include: ['avr8js', '@wokwi/elements'], } ``` This allows: - Import from local repos as if they were npm packages - Easy updates with `git pull` - Modify source code if needed for debugging ## Technology Stack ### Frontend | Technology | Version | Purpose | |------------|---------|---------| | React | 19.2 | UI framework | | Vite | 7.3 | Build tool & dev server | | TypeScript | 5.9 | Static typing | | Monaco Editor | 4.7 | Code editor (VS Code engine) | | Zustand | 5.0 | State management | | React Router | 7.13 | Client-side routing | | Axios | 1.13 | HTTP client | | wokwi-elements | local | 48+ electronic web components | | avr8js | local | AVR8 CPU emulator | ### Backend | Technology | Version | Purpose | |------------|---------|---------| | Python | 3.12+ | Runtime | | FastAPI | 0.115 | Web framework | | Uvicorn | 0.32 | ASGI server | ### External Tools | Tool | Purpose | |------|---------| | arduino-cli | Arduino compiler (subprocess) | | Git | Version control for Wokwi libs | ## Architecture Advantages ### Real Emulation - True AVR8 CPU execution, not simulation mockups - Same avr8js engine used by Wokwi.com - Accurate timing with configurable speed ### Plugin-Based Component Behaviors - PartSimulationRegistry decouples simulation logic from rendering - Easy to add new component behaviors - Supports both input (event-driven) and output (pin-state-driven) components ### Automatic Component Discovery - Build-time TypeScript AST parser extracts metadata from wokwi-elements source - No manual component registration needed - New wokwi-elements components appear automatically after rebuild ### Separation of Concerns - **Frontend**: UI, visualization, simulation engine - **Backend**: Compilation via arduino-cli - **Wokwi Libs**: Emulation and components (maintained by Wokwi community) ### Wokwi Compatibility - Official repositories = same functionality as Wokwi.com - Automatic updates with `git pull` - New components available immediately after rebuild ### Local Development - No internet required after initial setup - Local compilation with arduino-cli - All simulation runs in the browser ## Planned Improvements - **Serial Monitor** — UI for USART output display - **Project Persistence** — SQLite database for save/load - **Undo/Redo** — Edit history for code and circuit changes - **Multi-board Support** — Runtime board switching (Mega, Nano, ESP32) - **Wire Validation** — Electrical validation and error highlighting - **Export/Import** — Share projects as files ## References - [Wokwi Elements Repo](https://github.com/wokwi/wokwi-elements) - [AVR8js Repo](https://github.com/wokwi/avr8js) - [Wokwi Simulator](https://wokwi.com) - [Arduino CLI](https://arduino.github.io/arduino-cli/) - [FastAPI Docs](https://fastapi.tiangolo.com/) - [Vite Docs](https://vitejs.dev/)