import React, { useState, useEffect } from 'react'; import { Link, useParams, useNavigate } from 'react-router-dom'; import { AppHeader } from '../components/layout/AppHeader'; import './DocsPage.css'; const GITHUB_URL = 'https://github.com/davidmonterocrespo24/velxio'; const BASE_URL = 'https://velxio.dev'; const AUTHOR = { '@type': 'Person', name: 'David Montero Crespo', url: 'https://github.com/davidmonterocrespo24' } as const; /* ── Icons ─────────────────────────────────────────────── */ const IcoGitHub = () => ( ); /* ── Doc sections ──────────────────────────────────────── */ type SectionId = | 'intro' | 'getting-started' | 'emulator' | 'riscv-emulation' | 'esp32-emulation' | 'rp2040-emulation' | 'raspberry-pi3-emulation' | 'components' | 'roadmap' | 'architecture' | 'wokwi-libs' | 'mcp' | 'setup'; const VALID_SECTIONS: SectionId[] = [ 'intro', 'getting-started', 'emulator', 'riscv-emulation', 'esp32-emulation', 'rp2040-emulation', 'raspberry-pi3-emulation', 'components', 'roadmap', 'architecture', 'wokwi-libs', 'mcp', 'setup', ]; interface NavItem { id: SectionId; label: string; } const NAV_ITEMS: NavItem[] = [ { id: 'intro', label: 'Introduction' }, { id: 'getting-started', label: 'Getting Started' }, { id: 'emulator', label: 'Emulator Architecture' }, { id: 'riscv-emulation', label: 'RISC-V Emulation (ESP32-C3)' }, { id: 'esp32-emulation', label: 'ESP32 Emulation (Xtensa)' }, { id: 'rp2040-emulation', label: 'RP2040 Emulation (Raspberry Pi Pico)' }, { id: 'raspberry-pi3-emulation', label: 'Raspberry Pi 3 Emulation (QEMU)' }, { id: 'components', label: 'Components Reference' }, { id: 'architecture', label: 'Project Architecture' }, { id: 'wokwi-libs', label: 'Wokwi Libraries' }, { id: 'mcp', label: 'MCP Server' }, { id: 'setup', label: 'Project Status' }, { id: 'roadmap', label: 'Roadmap' }, ]; /* ── Per-section SEO metadata ──────────────────────────── */ interface SectionMeta { title: string; description: string; } const SECTION_META: Record = { 'intro': { title: 'Introduction — Velxio Documentation', description: 'Learn about Velxio, the free open-source Arduino emulator with real AVR8 and RP2040 CPU emulation and 48+ interactive electronic components.', }, 'getting-started': { title: 'Getting Started — Velxio Documentation', description: 'Get started with Velxio: use the hosted editor, self-host with Docker, or set up a local development environment. Simulate your first Arduino sketch in minutes.', }, 'emulator': { title: 'Emulator Architecture — Velxio Documentation', description: 'How Velxio emulates AVR8 (ATmega328p), RP2040, and RISC-V (ESP32-C3) CPUs. Covers execution loops, peripherals, and pin mapping for all supported boards.', }, 'riscv-emulation': { title: 'RISC-V Emulation (ESP32-C3) — Velxio Documentation', description: 'Browser-side RV32IMC emulator for ESP32-C3, XIAO ESP32-C3, and C3 SuperMini. Covers memory map, GPIO, UART0, the ESP32 image parser, RV32IMC ISA, and test suite.', }, 'esp32-emulation': { title: 'ESP32 Emulation (Xtensa) — Velxio Documentation', description: 'QEMU-based emulation for ESP32 and ESP32-S3 (Xtensa LX6/LX7). Covers the lcgamboa fork, libqemu-xtensa, GPIO, WiFi, I2C, SPI, RMT/NeoPixel, and LEDC/PWM.', }, 'components': { title: 'Components Reference — Velxio Documentation', description: 'Full reference for all 48+ interactive electronic components in Velxio: LEDs, displays, sensors, buttons, potentiometers, and more. Includes wiring and property details.', }, 'roadmap': { title: 'Roadmap — Velxio Documentation', description: "Velxio's feature roadmap: what's implemented, what's in progress, and what's planned for future releases.", }, 'architecture': { title: 'Project Architecture — Velxio Documentation', description: 'Detailed overview of the Velxio system architecture: frontend, backend, AVR8 emulation pipeline, data flows, Zustand stores, and wire system.', }, 'wokwi-libs': { title: 'Wokwi Libraries — Velxio Documentation', description: 'How Velxio integrates the official Wokwi open-source libraries: avr8js, wokwi-elements, and rp2040js. Covers configuration, updates, and the 48 available components.', }, 'mcp': { title: 'MCP Server — Velxio Documentation', description: 'Velxio MCP Server reference: integrate AI agents (Claude, Cursor) with Velxio via Model Context Protocol. Covers tools, transports, circuit format, and example walkthroughs.', }, 'setup': { title: 'Project Status — Velxio Documentation', description: 'Complete status of all implemented Velxio features: AVR emulation, component system, wire system, code editor, example projects, and next steps.', }, 'rp2040-emulation': { title: 'RP2040 Emulation (Raspberry Pi Pico) — Velxio Documentation', description: 'How Velxio emulates the Raspberry Pi Pico and Pico W using rp2040js: ARM Cortex-M0+ at 133 MHz, GPIO, UART, ADC, I2C, SPI, PWM and WFI optimization.', }, 'raspberry-pi3-emulation': { title: 'Raspberry Pi 3 Emulation (QEMU) — Velxio Documentation', description: 'How Velxio emulates a full Raspberry Pi 3B using QEMU raspi3b: real Raspberry Pi OS, Python + RPi.GPIO shim, dual-channel UART, VFS, and multi-board serial bridge.', }, }; /* ── Section content ───────────────────────────────────── */ const IntroSection: React.FC = () => (
// overview

Introduction

Velxio is a fully local, open-source Arduino emulator that runs entirely in your browser. Write Arduino C++ code, compile it with a real arduino-cli backend, and simulate it using true AVR8 / RP2040 CPU emulation — with 48+ interactive electronic components, all without installing any software on your machine.

Why Velxio?

Supported Boards

BoardCPUEmulator
Arduino UnoATmega328p @ 16 MHzavr8js
Arduino NanoATmega328p @ 16 MHzavr8js
Arduino MegaATmega2560 @ 16 MHzavr8js
Raspberry Pi PicoRP2040 @ 133 MHzrp2040js
ESP32-C3 / XIAO C3 / C3 SuperMiniRV32IMC @ 160 MHzEsp32C3Simulator (browser)
ESP32 / ESP32-S3Xtensa LX6/LX7 @ 240 MHzQEMU (lcgamboa)
Live Demo:{' '} velxio.dev {' '}— no installation needed, open the editor and start simulating immediately.
); const GettingStartedSection: React.FC = () => (
// setup

Getting Started

Follow these steps to simulate your first Arduino sketch.

Option 1: Use the Hosted Version

No installation needed — go to{' '} https://velxio.dev{' '} and start coding immediately.

Option 2: Self-Host with Docker

Run a single Docker command to start a fully local instance:

{`docker run -d \\
  --name velxio \\
  -p 3080:80 \\
  -v $(pwd)/data:/app/data \\
  ghcr.io/davidmonterocrespo24/velxio:master`}

Then open http://localhost:3080 in your browser.

Option 3: Manual Setup (Development)

Prerequisites: Node.js 18+, Python 3.12+, arduino-cli

1. Clone the repository

{`git clone https://github.com/davidmonterocrespo24/velxio.git
cd velxio`}

2. Start the backend

{`cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8001`}

3. Start the frontend

{`cd frontend
npm install
npm run dev`}

Open http://localhost:5173.

4. Set up arduino-cli (first time)

{`arduino-cli core update-index
arduino-cli core install arduino:avr

# For Raspberry Pi Pico support:
arduino-cli config add board_manager.additional_urls \\
  https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json
arduino-cli core install rp2040:rp2040`}

Your First Simulation

  1. Open the editor at velxio.dev/editor.
  2. Select a board from the toolbar (e.g., Arduino Uno).
  3. Write Arduino code in the Monaco editor, for example:
{`void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(500);
}`}
  1. Click Compile — the backend calls arduino-cli and returns a .hex file.
  2. Click Run — the AVR8 emulator executes the compiled program.
  3. Add components using the component picker (click the + button on the canvas).
  4. Connect wires by clicking a component pin and then another pin.

Troubleshooting

ProblemSolution
arduino-cli: command not found Install arduino-cli and add it to your PATH.
LED doesn't blink Check the browser console for port listener errors; verify pin assignment in the component property dialog.
Serial Monitor is empty Ensure Serial.begin() is called inside setup() before any Serial.print().
Compilation errors Check the compilation console at the bottom of the editor for full arduino-cli output.
); const EmulatorSection: React.FC = () => (
// internals

Emulator Architecture

Velxio uses real CPU emulation rather than a simplified model. This document describes how each layer of the simulation works.

High-Level Data Flow

{`User Code (Monaco Editor)
        │
        ▼
   Zustand Store (useEditorStore)
        │
        ▼
  FastAPI Backend ──► arduino-cli ──► .hex / .uf2 file
        │
        ▼
  AVRSimulator / RP2040Simulator
        │ loadHex()
        ▼
  CPU execution loop (~60 FPS via requestAnimationFrame)
        │
        ▼
  Port listeners (PORTB / PORTC / PORTD)
        │
        ▼
  PinManager ──► Component state ──► React re-renders`}

AVR8 Emulation (Arduino Uno / Nano / Mega)

The AVR backend uses avr8js, which implements a complete ATmega328p / ATmega2560 processor.

Execution Loop

Each animation frame executes approximately 267,000 CPU cycles (16 MHz ÷ 60 FPS):

{`avrInstruction(cpu);  // decode and execute one AVR instruction
cpu.tick();           // advance peripheral timers and counters`}

Supported Peripherals

PeripheralDetails
GPIOPORTB (pins 8–13), PORTC (A0–A5), PORTD (pins 0–7)
Timer0 / Timer1 / Timer2millis(), delay(), PWM via analogWrite()
USARTFull transmit and receive — powers the Serial Monitor
ADC10-bit, 5 V reference on pins A0–A5
SPIHardware SPI (enables ILI9341, SD card, etc.)
I2C (TWI)Hardware I2C with virtual device bus

Pin Mapping

Arduino PinAVR PortBit
0–7PORTD0–7
8–13PORTB0–5
A0–A5PORTC0–5

RP2040 Emulation (Raspberry Pi Pico)

The RP2040 backend uses rp2040js.

HEX File Format

Arduino compilation produces Intel HEX format. The parser in hexParser.ts:

  1. Reads lines starting with :
  2. Extracts the address, record type, and data bytes
  3. Returns a Uint8Array of program bytes
  4. AVRSimulator converts this to a Uint16Array (16-bit words, little-endian)

Key Source Files

FilePurpose
frontend/src/simulation/AVRSimulator.tsAVR8 CPU emulator wrapper
frontend/src/simulation/PinManager.tsMaps Arduino pins to UI components
frontend/src/utils/hexParser.tsIntel HEX parser
frontend/src/components/simulator/SimulatorCanvas.tsxCanvas rendering
backend/app/services/arduino_cli.pyarduino-cli wrapper
backend/app/api/routes/compile.pyCompilation API endpoint
); const ComponentsSection: React.FC = () => (
// reference

Components Reference

Velxio ships with 48+ interactive electronic components powered by{' '} wokwi-elements. All components can be placed on the simulation canvas, connected with wires, and interact with your Arduino sketch in real time.

Adding Components

  1. Click the + button on the simulation canvas.
  2. Use search or browse by category in the component picker.
  3. Click a component to place it on the canvas.
  4. Drag to reposition; click to open the Property Dialog.

Connecting Components

  1. Click a pin on any component — a wire starts from that pin.
  2. Click a destination pin to complete the connection.
  3. Wires are color-coded by signal type:
ColorSignal Type
RedVCC (power)
BlackGND (ground)
BlueAnalog
GreenDigital
PurplePWM
GoldI2C (SDA/SCL)
OrangeSPI (MOSI/MISO/SCK)
CyanUSART (TX/RX)

Component Categories

Output

ComponentDescription
LEDSingle LED with configurable color
RGB LEDThree-color LED (red, green, blue channels)
7-Segment DisplaySingle digit numeric display
LCD 16×22-line character LCD (I2C or parallel)
LCD 20×44-line character LCD
ILI9341 TFT240×320 color TFT display (SPI)
BuzzerPassive piezo buzzer
NeoPixelIndividually addressable RGB LED strip

Input

ComponentDescription
Push ButtonMomentary push button
Slide SwitchSPDT slide switch
PotentiometerAnalog voltage divider (ADC input)
Rotary EncoderIncremental rotary encoder
Keypad 4×416-button matrix keypad
JoystickDual-axis analog joystick

Sensors

ComponentDescription
HC-SR04Ultrasonic distance sensor
DHT22Temperature and humidity sensor
PIR MotionPassive infrared motion sensor
PhotoresistorLight-dependent resistor (LDR)
IR Receiver38 kHz infrared receiver

Passive Components

ComponentDescription
ResistorStandard resistor (configurable value)
CapacitorElectrolytic capacitor
InductorCoil inductor

Component Properties

Each component has a Property Dialog accessible by clicking it on the canvas:

PropertyDescription
Arduino PinThe digital or analog pin this component is connected to
ColorVisual color (LEDs, wires)
ValueComponent value (e.g., resistance in Ω)
RotationRotate in 90° increments
DeleteRemove the component from the canvas
); const RoadmapSection: React.FC = () => (
// future

Roadmap

Features that are implemented, in progress, and planned for future releases of Velxio.

✅ Implemented

🔄 In Progress

🗓 Planned — Near-Term

🗓 Planned — Mid-Term

🗓 Planned — Long-Term

Want to contribute?{' '} Feature requests, bug reports, and pull requests are welcome at{' '} github.com/davidmonterocrespo24/velxio.
); /* ── Architecture Section ─────────────────────────────── */ const ArchitectureSection: React.FC = () => (
// system design

Project Architecture

Velxio 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.

High-Level Overview

{`Browser (React + Vite)
  ├── Monaco Editor ──► useEditorStore (Zustand)
  ├── SimulatorCanvas ──► useSimulatorStore (Zustand)
  │     ├── AVRSimulator (avr8js)   16 MHz AVR8 CPU
  │     ├── RP2040Simulator (rp2040js)
  │     ├── PinManager              pin → component mapping
  │     ├── PartSimulationRegistry  16 interactive parts
  │     └── 48+ wokwi-elements      Lit Web Components
  └── HTTP (Axios) ──► FastAPI Backend (port 8001)
        └── ArduinoCLIService ──► arduino-cli subprocess`}

Data Flows

1. Compilation

{`Click "Compile"
  → EditorToolbar reads all workspace files
  → POST /api/compile/  { files, board_fqbn }
  → Backend: ArduinoCLIService writes temp dir
  → arduino-cli compile --fqbn  --output-dir build/
  → Returns hex_content (Intel HEX string)
  → useSimulatorStore.setCompiledHex() → loadHex()`}

2. Simulation Loop

{`Click "Run"
  → AVRSimulator.start()
  → requestAnimationFrame loop @ ~60 FPS
  → Each frame: Math.floor(267 000 × speed) cycles
    ├── avrInstruction(cpu)   — decode + execute one AVR instruction
    └── cpu.tick()            — advance Timer0/1/2, USART, ADC
  → PORTB/C/D write listeners fire
  → PinManager.updatePort() → per-pin callbacks
  → PartSimulationRegistry.onPinStateChange()
  → wokwi-elements update visually`}

3. Input Components

{`User presses button on canvas
  → wokwi web component fires 'button-press' event
  → DynamicComponent catches event
  → PartSimulationRegistry.attachEvents() handler
  → AVRSimulator.setPinState(arduinoPin, LOW)
  → AVRIOPort.setPin() injects external pin state
  → CPU reads pin in next instruction`}

Key Frontend Stores (Zustand)

StoreKey StatePurpose
useEditorStorefiles[], activeFileIdMulti-file Monaco workspace
useSimulatorStoresimulator, components, wires, runningSimulation + canvas state
useAuthStoreuser, tokenAuth (persisted localStorage)
useProjectStoreprojectId, slugCurrently open project

Backend Routes

RouteDescription
POST /api/compile/Compile sketch files → Intel HEX / UF2
GET /api/compile/boardsList available boards
GET/POST /api/auth/*Email/password + Google OAuth
GET/POST /api/projects/*CRUD project persistence (SQLite)
GET /api/libraries/*Arduino Library Manager integration
GET /healthHealth check endpoint

Wire System

Wires are stored as objects with start/end endpoints tied to component pin positions:

{`{
  id: string
  start: { componentId, pinName, x, y }
  end:   { componentId, pinName, x, y }
  color: string
  signalType: 'digital' | 'analog' | 'power-vcc' | 'power-gnd'
}`}
Full details:{' '} See{' '} docs/ARCHITECTURE.md {' '} in the repository.
); /* ── Wokwi Libraries Section ──────────────────────────── */ const WokwiLibsSection: React.FC = () => (
// open-source libs

Wokwi Libraries

Velxio uses official Wokwi open-source repositories cloned locally in wokwi-libs/. This gives you up-to-date, compatible emulation engines and visual components without npm registry dependencies.

Cloned Repositories

LibraryLocationPurpose
wokwi-elements wokwi-libs/wokwi-elements/ 48+ Lit Web Components (LEDs, LCDs, sensors, buttons…)
avr8js wokwi-libs/avr8js/ ATmega328p / ATmega2560 CPU emulator at 16 MHz
rp2040js wokwi-libs/rp2040js/ Raspberry Pi Pico (RP2040) emulator

Vite Configuration

frontend/vite.config.ts uses path aliases so imports resolve to the local builds:

{`resolve: {
  alias: {
    'avr8js':          '../wokwi-libs/avr8js/dist/esm',
    '@wokwi/elements': '../wokwi-libs/wokwi-elements/dist/esm',
  },
}`}

Updating the Libraries

All at once (recommended)

{`# Windows
update-wokwi-libs.bat`}

Manually

{`cd wokwi-libs/wokwi-elements
git pull origin main
npm install && npm run build

cd ../avr8js
git pull origin main
npm install && npm run build

cd ../rp2040js
git pull origin main
npm install && npm run build`}

After updating wokwi-elements

Regenerate component metadata so new components appear in the picker:

{`cd frontend
npx tsx ../scripts/generate-component-metadata.ts`}

Available Wokwi Components (48)

CategoryComponents
BoardsArduino Uno, Mega, Nano, ESP32 DevKit
SensorsDHT22, HC-SR04, PIR, Photoresistor, NTC, Joystick
DisplaysLCD 16×2, LCD 20×4, 7-Segment
InputPush button, 6mm button, Slide switch, DIP switch 8, Potentiometer
OutputLED, RGB LED, LED bar graph, Buzzer, NeoPixel
MotorsServo, Stepper motor
PassiveResistor, Slide potentiometer, LED ring, Matrix keypad
OtherIR receiver, DS1307 RTC, breadboards, etc.

How avr8js Powers the Simulation

{`import { CPU, avrInstruction, AVRTimer, AVRUSART, AVRADC, AVRIOPort } from 'avr8js';

const cpu   = new CPU(programMemory);          // ATmega328p at 16 MHz
const portB = new AVRIOPort(cpu, portBConfig); // digital pins 8-13
const portC = new AVRIOPort(cpu, portCConfig); // analog pins A0-A5
const portD = new AVRIOPort(cpu, portDConfig); // digital pins 0-7

function runFrame() {
  const cycles = Math.floor(267_000 * speed);
  for (let i = 0; i < cycles; i++) {
    avrInstruction(cpu); // execute one AVR instruction
    cpu.tick();          // advance timers + peripherals
  }
  requestAnimationFrame(runFrame);
}`}
Full details:{' '} See{' '} docs/WOKWI_LIBS.md {' '} in the repository.
); /* ── MCP Server Section ───────────────────────────────── */ const McpSection: React.FC = () => (
// AI integration

MCP Server

Velxio exposes a{' '} Model Context Protocol {' '} (MCP) server that lets AI agents (Claude, Cursor, and others) create circuits, generate code, and compile Arduino sketches directly.

Available Tools

ToolDescription
compile_projectCompile Arduino sketch files → Intel HEX / binary
run_projectCompile and mark artifact as simulation-ready
import_wokwi_jsonParse a Wokwi diagram.json → Velxio circuit
export_wokwi_jsonSerialise a Velxio circuit → Wokwi diagram.json
create_circuitCreate a new circuit definition
update_circuitMerge changes into an existing circuit
generate_code_filesGenerate starter .ino code from a circuit

Transport Options

1. stdio — Claude Desktop / CLI agents

{`cd backend
python mcp_server.py`}

Claude Desktop config (~/.claude/claude_desktop_config.json):

{`{
  "mcpServers": {
    "velxio": {
      "command": "python",
      "args": ["/absolute/path/to/velxio/backend/mcp_server.py"]
    }
  }
}`}

2. SSE / HTTP — Cursor IDE / web agents

{`cd backend
python mcp_sse_server.py --port 8002`}

MCP client config:

{`{
  "mcpServers": {
    "velxio": { "url": "http://localhost:8002/sse" }
  }
}`}

Circuit Data Format

Velxio circuits are plain JSON objects:

{`{
  "board_fqbn": "arduino:avr:uno",
  "version": 1,
  "components": [
    { "id": "led1", "type": "wokwi-led", "left": 200, "top": 100,
      "rotate": 0, "attrs": { "color": "red" } }
  ],
  "connections": [
    { "from_part": "uno", "from_pin": "13",
      "to_part": "led1", "to_pin": "A", "color": "green" }
  ]
}`}

Supported Board FQBNs

BoardFQBN
Arduino Unoarduino:avr:uno
Arduino Megaarduino:avr:mega
Arduino Nanoarduino:avr:nano
Raspberry Pi Picorp2040:rp2040:rpipico

Example — Blink LED from Scratch

{`// Step 1 — Create a circuit
{
  "tool": "create_circuit",
  "arguments": {
    "board_fqbn": "arduino:avr:uno",
    "components": [
      { "id": "led1", "type": "wokwi-led",
        "left": 150, "top": 100, "attrs": { "color": "red" } },
      { "id": "r1", "type": "wokwi-resistor",
        "left": 150, "top": 180, "attrs": { "value": "220" } }
    ],
    "connections": [
      { "from_part": "uno", "from_pin": "13",
        "to_part": "led1", "to_pin": "A", "color": "green" },
      { "from_part": "led1", "from_pin": "C",
        "to_part": "r1",   "to_pin": "1", "color": "black" },
      { "from_part": "r1",   "from_pin": "2",
        "to_part": "uno",  "to_pin": "GND.1", "color": "black" }
    ]
  }
}

// Step 2 — Generate code
{
  "tool": "generate_code_files",
  "arguments": {
    "circuit": "",
    "sketch_name": "blink",
    "extra_instructions": "Blink the red LED every 500ms"
  }
}

// Step 3 — Compile
{
  "tool": "compile_project",
  "arguments": {
    "files": [
      {
        "name": "blink.ino",
        "content": "void setup(){pinMode(13,OUTPUT);}\\nvoid loop(){digitalWrite(13,HIGH);delay(500);digitalWrite(13,LOW);delay(500);}"
      }
    ],
    "board": "arduino:avr:uno"
  }
}`}

Setup

{`cd backend
pip install -r requirements.txt

# Ensure arduino-cli is installed
arduino-cli version
arduino-cli core update-index
arduino-cli core install arduino:avr

# Run tests
python -m pytest tests/test_mcp_tools.py -v`}
Full reference:{' '} See{' '} docs/MCP.md {' '} in the repository.
); /* ── Setup / Project Status Section ──────────────────── */ const SetupSection: React.FC = () => (
// project status

Project Status

A comprehensive overview of all features currently implemented in Velxio.

AVR Emulation (avr8js)

FeatureStatus
ATmega328p CPU at 16 MHz✅ Working
Timer0, Timer1, Timer2✅ Working
USART (Serial)✅ Working
ADC (analogRead)✅ Working
Full GPIO (PORTB / PORTC / PORTD)✅ Working
~60 FPS loop (267k cycles/frame)✅ Working
Speed control (0.1× – 10×)✅ Working
PWM monitoring (6 channels)✅ Working
External pin injection (inputs)✅ Working

Component System (48+)

FeatureStatus
Automatic discovery via AST✅ 48 components detected
ComponentPickerModal with search✅ Working
9 categories with filters✅ Working
Generic DynamicComponent renderer✅ Working
Drag-and-drop on canvas✅ Working
Rotation (90° increments)✅ Working
Properties dialog (single-click)✅ Working
Pin overlay (clickable cyan dots)✅ Working

Interactive Parts (16 simulated)

PartTypeStatus
LEDOutput
RGB LEDOutput (digital + PWM)
LED Bar Graph (10 LEDs)Output
7-Segment DisplayOutput
PushbuttonInput
Pushbutton 6mmInput
Slide SwitchInput
DIP Switch 8Input
PotentiometerInput (ADC)
Slide PotentiometerInput (ADC)
PhotoresistorInput / Output
Analog JoystickInput (ADC + digital)
ServoOutput
BuzzerOutput (Web Audio)
LCD 1602Output (full HD44780)
LCD 2004Output (full HD44780)

Wire System

FeatureStatus
Pin-to-pin creation with click✅ Working
Real-time preview (green dashed)✅ Working
Orthogonal routing (no diagonals)✅ Working
Segment editing (perpendicular drag)✅ Working
8 colours by signal type✅ Working
Auto-update when moving components✅ Working
Grid snapping (20 px)✅ Working
Wire selection and deletion✅ Working

Example Projects (8)

ExampleCategoryDifficulty
Blink LEDBasicsBeginner
Traffic LightBasicsBeginner
Button ControlBasicsBeginner
Fade LED (PWM)BasicsBeginner
Serial Hello WorldCommunicationBeginner
RGB LED ColorsBasicsIntermediate
Simon Says GameGamesAdvanced
LCD 20×4 DisplayDisplaysIntermediate

Troubleshooting

ProblemSolution
Components not displayed
cd wokwi-libs/wokwi-elements{'\n'}npm run build
Cannot find module 'avr8js'
cd wokwi-libs/avr8js{'\n'}npm install && npm run build
LED doesn't blink Compile first, then click Run. Check pin assignment in the component property dialog.
New component not in picker
cd frontend{'\n'}npx tsx ../scripts/generate-component-metadata.ts
Full status:{' '} See{' '} docs/SETUP_COMPLETE.md {' '} in the repository.
); const RiscVEmulationSection: React.FC = () => (
// risc-v

RISC-V Emulation (ESP32-C3)

ESP32-C3, XIAO ESP32-C3, and C3 SuperMini boards use a RISC-V RV32IMC core running at 160 MHz. Velxio emulates them entirely in the browser — no backend, no QEMU, no WebAssembly pipeline. The emulator is written in pure TypeScript and runs at real-time speeds.

Supported Boards

BoardCPUFlashRAM
ESP32-C3RV32IMC @ 160 MHz4 MB384 KB DRAM
XIAO ESP32-C3RV32IMC @ 160 MHz4 MB384 KB DRAM
C3 SuperMiniRV32IMC @ 160 MHz4 MB384 KB DRAM

Memory Map

RegionBase AddressSizeDescription
IROM (Flash)0x420000004 MBCode stored in flash
DROM (Flash R/O)0x3C0000004 MBRead-only data in flash
DRAM0x3FC80000384 KBData RAM (stack + heap)
IRAM0x4037C000384 KBInstruction RAM (copied from flash)
UART00x600000001 KBSerial port 0 (GPIO 20/21)
GPIO0x60004000512 BGPIO output / input / enable

ISA Support

Compilation Flow

When you click Compile + Run for an ESP32-C3 board:

  1. The backend compiles your sketch with arduino-cli using the esp32:esp32 core.
  2. The resulting binary is a merged 4 MB flash image: bootloader at 0x1000, partition table at 0x8000, application at 0x10000.
  3. The frontend's esp32ImageParser.ts finds the app at offset 0x10000, reads the 24-byte ESP32 image header (magic 0xE9), and extracts all segments (load address + data).
  4. Each segment is written into the correct memory region (IROM, DROM, DRAM, or IRAM) of the Esp32C3Simulator.
  5. The CPU starts executing from the entry point specified in the image header.

GPIO Registers

RegisterOffsetDescription
GPIO_OUT_REG+0x04Current output value
GPIO_OUT_W1TS+0x08Set bits (write 1 to set)
GPIO_OUT_W1TC+0x0CClear bits (write 1 to clear)
GPIO_ENABLE_REG+0x20Output enable
GPIO_IN_REG+0x3CInput pin states

UART0

Writing a byte to 0x60000000 (UART0 FIFO) triggers the onSerialData callback, which streams characters to the Serial Monitor. Reading from the same address pops from the receive FIFO (used by Serial.read()). The UART status register always returns 0 (TX ready).

Key Source Files

FileRole
simulation/RiscVCore.tsRV32IMC interpreter (step, MMIO hooks)
simulation/Esp32C3Simulator.tsESP32-C3 peripherals, memory map, lifecycle
utils/esp32ImageParser.tsParses merged flash image, extracts segments
Full details:{' '} docs/RISCV_EMULATION.md {' '} in the repository.
); const Esp32EmulationSection: React.FC = () => (
// xtensa

ESP32 Emulation (Xtensa)

ESP32 and ESP32-S3 boards use an Xtensa LX6 / LX7 architecture. Because no production-quality Xtensa emulator is available as pure JavaScript, Velxio uses a QEMU-based backend for these boards — the lcgamboa fork with libqemu-xtensa, compiled to a native binary and served by the FastAPI backend.

Note: This section applies only to ESP32 and ESP32-S3 (Xtensa). For ESP32-C3, XIAO ESP32-C3, and C3 SuperMini (RISC-V), see{' '} RISC-V Emulation (ESP32-C3) in the sidebar — those boards run entirely in the browser.

How It Works

  1. Arduino sketch is compiled by arduino-cli to an ESP32 .bin flash image.
  2. Frontend sends the binary to the backend via WebSocket (Esp32Bridge).
  3. Backend spawns a QEMU process with the lcgamboa Xtensa plugin, loads the image.
  4. GPIO and UART events are forwarded over the WebSocket back to the browser.
  5. Frontend updates component states (LEDs, display, etc.) in real time.

Supported Boards

BoardCPUEmulation
ESP32Xtensa LX6 dual-core @ 240 MHzQEMU (lcgamboa)
ESP32-S3Xtensa LX7 dual-core @ 240 MHzQEMU (lcgamboa)

Peripheral Support

Requirements

QEMU-based emulation requires the Velxio backend to be running. This means it works with the hosted version at{' '} velxio.dev{' '} and with Docker self-hosting, but not in a pure static frontend deployment.

Full details:{' '} docs/ESP32_EMULATION.md {' '} in the repository.
); /* ── RP2040 Emulation Section ─────────────────────────── */ const Rp2040EmulationSection: React.FC = () => (
// arm cortex-m0+

RP2040 Emulation (Raspberry Pi Pico)

The Raspberry Pi Pico and Pico W are emulated entirely in the browser using{' '} rp2040js, an open-source ARM Cortex-M0+ emulator. No QEMU or backend process is required — the binary runs at full speed inside a Web Worker.

Supported Boards

Raspberry Pi Pico Raspberry Pi Pico
Raspberry Pi Pico W Raspberry Pi Pico W
BoardFQBNBuilt-in LED
Raspberry Pi Picorp2040:rp2040:rpipicoGPIO 25
Raspberry Pi Pico Wrp2040:rp2040:rpipicowGPIO 25 (via CYW43)

Binary Format

The backend compiles the sketch with arduino-cli targeting rp2040:rp2040:rpipico and returns a raw ARM binary (.bin) encoded in base64. Unlike AVR, there is no Intel HEX — the binary is loaded directly into the RP2040 flash at offset 0.

The backend also prepends #define Serial Serial1 to the sketch so that Arduino Serial calls are redirected to UART0 (the virtual serial port streamed to the Serial Monitor).

Peripherals

PeripheralSupportNotes
GPIO (30 pins)FullDigital read/write, pull-up/down
UART0 / UART1FullSerial Monitor via UART0
ADC (ch 0–3)FullGPIO 26–29; ch 4 = temperature sensor
I2C0 / I2C1PartialDS1307 RTC, TempSensor, EEPROM virtual devices
SPI0 / SPI1LoopbackTX looped back to RX
PWMFrequency onlyNo waveform output to components
Timer / AlarmFullUsed by delay() and millis()

WFI Optimisation

When the CPU executes a WFI (Wait For Interrupt) instruction, the emulator fast-forwards the system clock to the next scheduled alarm instead of spinning through idle cycles. This dramatically reduces CPU usage during delay() calls.

Simulation Loop

The simulation runs inside requestAnimationFrame at ~60 FPS. Each frame executes approximately 2,200,000 CPU cycles (133 MHz / 60 fps). GPIO listeners fire whenever a pin state changes and update the visual components on the canvas.

Known Limitations

Full Documentation

See the complete technical reference:{' '} RP2040_EMULATION.md

); /* ── Raspberry Pi 3 Emulation Section ─────────────────── */ const RaspberryPi3EmulationSection: React.FC = () => (
// qemu raspi3b

Raspberry Pi 3 Emulation (QEMU)

The Raspberry Pi 3B is emulated using QEMU 8.1.3 with -M raspi3b. This is the only board in Velxio that runs a full operating system — a real{' '} Raspberry Pi OS (Trixie) image booted inside the emulator. Users write Python scripts (not C++), which are executed by the real Python 3 interpreter inside the VM.

Supported Boards

BoardQEMU MachineCPU
Raspberry Pi 3Braspi3bBCM2837, 4× Cortex-A53 @ 1.2 GHz

Dual-Channel Serial Architecture

QEMU exposes two UART channels to the backend:

RPi.GPIO Shim

A custom RPi.GPIO shim is injected at /usr/local/lib/python3.11/dist-packages/RPi/GPIO.py inside the VM. When user code calls GPIO.output(pin, value), the shim writes a line like GPIO 17 1 to ttyAMA1. The backend reads this, fires a gpio_change WebSocket event, and the frontend updates the visual component on the canvas.

The reverse also works: the frontend can send SET 17 1 via WebSocket → backend → ttyAMA1 → shim → user code reads it via GPIO.input(17).

Virtual File System (VFS)

Each Raspberry Pi board instance has its own VFS that maps to files inside the running VM. The default tree is:

{`/home/pi/
├── script.py     ← user's main Python script
└── lib/
    └── helper.py ← optional helper library`}

Overlay Images

The base Raspberry Pi OS SD image is never modified. Each session creates a fresh qcow2 overlay on top of the base image, so all runtime changes are isolated and discarded when the session ends.

Known Limitations

Full Documentation

See the complete technical reference:{' '} RASPBERRYPI3_EMULATION.md

); const SECTION_MAP: Record = { intro: IntroSection, 'getting-started': GettingStartedSection, emulator: EmulatorSection, 'riscv-emulation': RiscVEmulationSection, 'esp32-emulation': Esp32EmulationSection, 'rp2040-emulation': Rp2040EmulationSection, 'raspberry-pi3-emulation': RaspberryPi3EmulationSection, components: ComponentsSection, roadmap: RoadmapSection, architecture: ArchitectureSection, 'wokwi-libs': WokwiLibsSection, mcp: McpSection, setup: SetupSection, }; /* ── Page ─────────────────────────────────────────────── */ export const DocsPage: React.FC = () => { const { section } = useParams<{ section?: string }>(); const navigate = useNavigate(); const [sidebarOpen, setSidebarOpen] = useState(false); // Derive active section from URL; fall back to 'intro' const activeSection: SectionId = section && VALID_SECTIONS.includes(section as SectionId) ? (section as SectionId) : 'intro'; // Redirect bare /docs → /docs/intro so every section has a canonical URL useEffect(() => { if (!section) { navigate('/docs/intro', { replace: true }); } }, [section, navigate]); // Capture the original values once on mount and restore them on unmount useEffect(() => { const origTitle = document.title; // Helper to capture an element and its original attribute value const captureAttr = (selector: string, attr: string): [E | null, string] => { const el = document.querySelector(selector); return [el, el?.getAttribute(attr) ?? '']; }; const [descEl, origDesc] = captureAttr('meta[name="description"]', 'content'); const [canonicalEl, origCanonical] = captureAttr('link[rel="canonical"]', 'href'); const [ogTitleEl, origOgTitle] = captureAttr('meta[property="og:title"]', 'content'); const [ogDescEl, origOgDesc] = captureAttr('meta[property="og:description"]', 'content'); const [ogUrlEl, origOgUrl] = captureAttr('meta[property="og:url"]', 'content'); const [twTitleEl, origTwTitle] = captureAttr('meta[name="twitter:title"]', 'content'); const [twDescEl, origTwDesc] = captureAttr('meta[name="twitter:description"]', 'content'); return () => { document.title = origTitle; descEl?.setAttribute('content', origDesc); canonicalEl?.setAttribute('href', origCanonical); ogTitleEl?.setAttribute('content', origOgTitle); ogDescEl?.setAttribute('content', origOgDesc); ogUrlEl?.setAttribute('content', origOgUrl); twTitleEl?.setAttribute('content', origTwTitle); twDescEl?.setAttribute('content', origTwDesc); document.getElementById('docs-jsonld')?.remove(); }; }, []); // runs once on mount; cleanup runs once on unmount // Update all head metadata + JSON-LD per section. // No cleanup here — the mount effect above restores defaults on unmount, // and on a section change the next run of this effect immediately overwrites. useEffect(() => { const meta = SECTION_META[activeSection]; const pageUrl = `${BASE_URL}/docs/${activeSection}`; document.title = meta.title; const set = (selector: string, value: string) => document.querySelector(selector)?.setAttribute('content', value); set('meta[name="description"]', meta.description); set('meta[property="og:title"]', meta.title); set('meta[property="og:description"]', meta.description); set('meta[property="og:url"]', pageUrl); set('meta[name="twitter:title"]', meta.title); set('meta[name="twitter:description"]', meta.description); const canonicalEl = document.querySelector('link[rel="canonical"]'); if (canonicalEl) canonicalEl.setAttribute('href', pageUrl); // Build the breadcrumb section label for JSON-LD const activeNav = NAV_ITEMS.find((i) => i.id === activeSection); const sectionLabel = activeNav?.label ?? activeSection; // Inject / update JSON-LD structured data for this doc page const ldId = 'docs-jsonld'; let ldScript = document.getElementById(ldId) as HTMLScriptElement | null; if (!ldScript) { ldScript = document.createElement('script'); ldScript.id = ldId; ldScript.type = 'application/ld+json'; document.head.appendChild(ldScript); } ldScript.textContent = JSON.stringify({ '@context': 'https://schema.org', '@graph': [ { '@type': 'TechArticle', headline: meta.title, description: meta.description, url: pageUrl, isPartOf: { '@type': 'WebSite', url: `${BASE_URL}/`, name: 'Velxio' }, inLanguage: 'en-US', author: AUTHOR, }, { '@type': 'BreadcrumbList', itemListElement: [ { '@type': 'ListItem', position: 1, name: 'Home', item: `${BASE_URL}/` }, { '@type': 'ListItem', position: 2, name: 'Documentation', item: `${BASE_URL}/docs/intro` }, { '@type': 'ListItem', position: 3, name: sectionLabel, item: pageUrl }, ], }, ], }); }, [activeSection]); const ActiveContent = SECTION_MAP[activeSection]; const activeIdx = NAV_ITEMS.findIndex((i) => i.id === activeSection); return (
Documentation
{/* Sidebar */} {/* Main content */}
{/* Prev / Next navigation */}
{activeIdx > 0 && ( window.scrollTo(0, 0)} > ← {NAV_ITEMS[activeIdx - 1].label} )} {activeIdx < NAV_ITEMS.length - 1 && ( window.scrollTo(0, 0)} > {NAV_ITEMS[activeIdx + 1].label} → )}
); };