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.
| Board | CPU | Emulator |
|---|---|---|
| Arduino Uno | ATmega328p @ 16 MHz | avr8js |
| Arduino Nano | ATmega328p @ 16 MHz | avr8js |
| Arduino Mega | ATmega2560 @ 16 MHz | avr8js |
| Raspberry Pi Pico | RP2040 @ 133 MHz | rp2040js |
| ESP32-C3 / XIAO C3 / C3 SuperMini | RV32IMC @ 160 MHz | Esp32C3Simulator (browser) |
| ESP32 / ESP32-S3 | Xtensa LX6/LX7 @ 240 MHz | QEMU (lcgamboa) |
Follow these steps to simulate your first Arduino sketch.
No installation needed — go to{' '} https://velxio.dev{' '} and start coding immediately.
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.
Prerequisites: Node.js 18+, Python 3.12+, arduino-cli
{`git clone https://github.com/davidmonterocrespo24/velxio.git
cd velxio`}
{`cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8001`}
{`cd frontend
npm install
npm run dev`}
Open http://localhost:5173.
{`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`}
{`void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(500);
digitalWrite(13, LOW);
delay(500);
}`}
arduino-cli and returns a .hex file.| Problem | Solution |
|---|---|
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. |
Velxio uses real CPU emulation rather than a simplified model. This document describes how each layer of the simulation works.
{`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`}
The AVR backend uses avr8js, which implements a complete ATmega328p / ATmega2560 processor.
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`}
| Peripheral | Details |
|---|---|
| GPIO | PORTB (pins 8–13), PORTC (A0–A5), PORTD (pins 0–7) |
| Timer0 / Timer1 / Timer2 | millis(), delay(), PWM via analogWrite() |
| USART | Full transmit and receive — powers the Serial Monitor |
| ADC | 10-bit, 5 V reference on pins A0–A5 |
| SPI | Hardware SPI (enables ILI9341, SD card, etc.) |
| I2C (TWI) | Hardware I2C with virtual device bus |
| Arduino Pin | AVR Port | Bit |
|---|---|---|
| 0–7 | PORTD | 0–7 |
| 8–13 | PORTB | 0–5 |
| A0–A5 | PORTC | 0–5 |
The RP2040 backend uses rp2040js.
Arduino compilation produces Intel HEX format. The parser in hexParser.ts:
:Uint8Array of program bytesAVRSimulator converts this to a Uint16Array (16-bit words, little-endian)| File | Purpose |
|---|---|
frontend/src/simulation/AVRSimulator.ts | AVR8 CPU emulator wrapper |
frontend/src/simulation/PinManager.ts | Maps Arduino pins to UI components |
frontend/src/utils/hexParser.ts | Intel HEX parser |
frontend/src/components/simulator/SimulatorCanvas.tsx | Canvas rendering |
backend/app/services/arduino_cli.py | arduino-cli wrapper |
backend/app/api/routes/compile.py | Compilation API endpoint |
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.
| Color | Signal Type |
|---|---|
| Red | VCC (power) |
| Black | GND (ground) |
| Blue | Analog |
| Green | Digital |
| Purple | PWM |
| Gold | I2C (SDA/SCL) |
| Orange | SPI (MOSI/MISO/SCK) |
| Cyan | USART (TX/RX) |
| Component | Description |
|---|---|
| LED | Single LED with configurable color |
| RGB LED | Three-color LED (red, green, blue channels) |
| 7-Segment Display | Single digit numeric display |
| LCD 16×2 | 2-line character LCD (I2C or parallel) |
| LCD 20×4 | 4-line character LCD |
| ILI9341 TFT | 240×320 color TFT display (SPI) |
| Buzzer | Passive piezo buzzer |
| NeoPixel | Individually addressable RGB LED strip |
| Component | Description |
|---|---|
| Push Button | Momentary push button |
| Slide Switch | SPDT slide switch |
| Potentiometer | Analog voltage divider (ADC input) |
| Rotary Encoder | Incremental rotary encoder |
| Keypad 4×4 | 16-button matrix keypad |
| Joystick | Dual-axis analog joystick |
| Component | Description |
|---|---|
| HC-SR04 | Ultrasonic distance sensor |
| DHT22 | Temperature and humidity sensor |
| PIR Motion | Passive infrared motion sensor |
| Photoresistor | Light-dependent resistor (LDR) |
| IR Receiver | 38 kHz infrared receiver |
| Component | Description |
|---|---|
| Resistor | Standard resistor (configurable value) |
| Capacitor | Electrolytic capacitor |
| Inductor | Coil inductor |
Each component has a Property Dialog accessible by clicking it on the canvas:
| Property | Description |
|---|---|
| Arduino Pin | The digital or analog pin this component is connected to |
| Color | Visual color (LEDs, wires) |
| Value | Component value (e.g., resistance in Ω) |
| Rotation | Rotate in 90° increments |
| Delete | Remove the component from the canvas |
Features that are implemented, in progress, and planned for future releases of Velxio.
arduino-cli (multi-file sketch support).zip filesmainVelxio 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.
{`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`}
{`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()`}
{`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`}
{`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`}
| Store | Key State | Purpose |
|---|---|---|
useEditorStore | files[], activeFileId | Multi-file Monaco workspace |
useSimulatorStore | simulator, components, wires, running | Simulation + canvas state |
useAuthStore | user, token | Auth (persisted localStorage) |
useProjectStore | projectId, slug | Currently open project |
| Route | Description |
|---|---|
POST /api/compile/ | Compile sketch files → Intel HEX / UF2 |
GET /api/compile/boards | List 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 /health | Health check endpoint |
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'
}`}
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.
| Library | Location | Purpose |
|---|---|---|
| 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 |
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',
},
}`}
{`# Windows
update-wokwi-libs.bat`}
{`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`}
Regenerate component metadata so new components appear in the picker:
{`cd frontend
npx tsx ../scripts/generate-component-metadata.ts`}
| Category | Components |
|---|---|
| Boards | Arduino Uno, Mega, Nano, ESP32 DevKit |
| Sensors | DHT22, HC-SR04, PIR, Photoresistor, NTC, Joystick |
| Displays | LCD 16×2, LCD 20×4, 7-Segment |
| Input | Push button, 6mm button, Slide switch, DIP switch 8, Potentiometer |
| Output | LED, RGB LED, LED bar graph, Buzzer, NeoPixel |
| Motors | Servo, Stepper motor |
| Passive | Resistor, Slide potentiometer, LED ring, Matrix keypad |
| Other | IR receiver, DS1307 RTC, breadboards, etc. |
{`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);
}`}
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.
| Tool | Description |
|---|---|
compile_project | Compile Arduino sketch files → Intel HEX / binary |
run_project | Compile and mark artifact as simulation-ready |
import_wokwi_json | Parse a Wokwi diagram.json → Velxio circuit |
export_wokwi_json | Serialise a Velxio circuit → Wokwi diagram.json |
create_circuit | Create a new circuit definition |
update_circuit | Merge changes into an existing circuit |
generate_code_files | Generate starter .ino code from a circuit |
{`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"]
}
}
}`}
{`cd backend
python mcp_sse_server.py --port 8002`}
MCP client config:
{`{
"mcpServers": {
"velxio": { "url": "http://localhost:8002/sse" }
}
}`}
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" }
]
}`}
| Board | FQBN |
|---|---|
| Arduino Uno | arduino:avr:uno |
| Arduino Mega | arduino:avr:mega |
| Arduino Nano | arduino:avr:nano |
| Raspberry Pi Pico | rp2040:rp2040:rpipico |
{`// 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"
}
}`}
{`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`}
A comprehensive overview of all features currently implemented in Velxio.
| Feature | Status |
|---|---|
| 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 |
| Feature | Status |
|---|---|
| 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 |
| Part | Type | Status |
|---|---|---|
| LED | Output | ✅ |
| RGB LED | Output (digital + PWM) | ✅ |
| LED Bar Graph (10 LEDs) | Output | ✅ |
| 7-Segment Display | Output | ✅ |
| Pushbutton | Input | ✅ |
| Pushbutton 6mm | Input | ✅ |
| Slide Switch | Input | ✅ |
| DIP Switch 8 | Input | ✅ |
| Potentiometer | Input (ADC) | ✅ |
| Slide Potentiometer | Input (ADC) | ✅ |
| Photoresistor | Input / Output | ✅ |
| Analog Joystick | Input (ADC + digital) | ✅ |
| Servo | Output | ✅ |
| Buzzer | Output (Web Audio) | ✅ |
| LCD 1602 | Output (full HD44780) | ✅ |
| LCD 2004 | Output (full HD44780) | ✅ |
| Feature | Status |
|---|---|
| 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 | Category | Difficulty |
|---|---|---|
| Blink LED | Basics | Beginner |
| Traffic Light | Basics | Beginner |
| Button Control | Basics | Beginner |
| Fade LED (PWM) | Basics | Beginner |
| Serial Hello World | Communication | Beginner |
| RGB LED Colors | Basics | Intermediate |
| Simon Says Game | Games | Advanced |
| LCD 20×4 Display | Displays | Intermediate |
| Problem | Solution |
|---|---|
| Components not displayed | |
Cannot find module 'avr8js' |
|
| LED doesn't blink | Compile first, then click Run. Check pin assignment in the component property dialog. |
| New component not in picker | |
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.
| Board | CPU | Flash | RAM |
|---|---|---|---|
| ESP32-C3 | RV32IMC @ 160 MHz | 4 MB | 384 KB DRAM |
| XIAO ESP32-C3 | RV32IMC @ 160 MHz | 4 MB | 384 KB DRAM |
| C3 SuperMini | RV32IMC @ 160 MHz | 4 MB | 384 KB DRAM |
| Region | Base Address | Size | Description |
|---|---|---|---|
| IROM (Flash) | 0x42000000 | 4 MB | Code stored in flash |
| DROM (Flash R/O) | 0x3C000000 | 4 MB | Read-only data in flash |
| DRAM | 0x3FC80000 | 384 KB | Data RAM (stack + heap) |
| IRAM | 0x4037C000 | 384 KB | Instruction RAM (copied from flash) |
| UART0 | 0x60000000 | 1 KB | Serial port 0 (GPIO 20/21) |
| GPIO | 0x60004000 | 512 B | GPIO output / input / enable |
When you click Compile + Run for an ESP32-C3 board:
arduino-cli using the esp32:esp32 core.0x1000,
partition table at 0x8000, application at 0x10000.esp32ImageParser.ts finds the app at offset 0x10000,
reads the 24-byte ESP32 image header (magic 0xE9), and extracts all segments
(load address + data).Esp32C3Simulator.| Register | Offset | Description |
|---|---|---|
GPIO_OUT_REG | +0x04 | Current output value |
GPIO_OUT_W1TS | +0x08 | Set bits (write 1 to set) |
GPIO_OUT_W1TC | +0x0C | Clear bits (write 1 to clear) |
GPIO_ENABLE_REG | +0x20 | Output enable |
GPIO_IN_REG | +0x3C | Input pin states |
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).
| File | Role |
|---|---|
simulation/RiscVCore.ts | RV32IMC interpreter (step, MMIO hooks) |
simulation/Esp32C3Simulator.ts | ESP32-C3 peripherals, memory map, lifecycle |
utils/esp32ImageParser.ts | Parses merged flash image, extracts segments |
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.
arduino-cli to an ESP32 .bin flash image.Esp32Bridge).| Board | CPU | Emulation |
|---|---|---|
| ESP32 | Xtensa LX6 dual-core @ 240 MHz | QEMU (lcgamboa) |
| ESP32-S3 | Xtensa LX7 dual-core @ 240 MHz | QEMU (lcgamboa) |
Serial.print()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.
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.
| Board | FQBN | Built-in LED |
|---|---|---|
| Raspberry Pi Pico | rp2040:rp2040:rpipico | GPIO 25 |
| Raspberry Pi Pico W | rp2040:rp2040:rpipicow | GPIO 25 (via CYW43) |
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).
| Peripheral | Support | Notes |
|---|---|---|
| GPIO (30 pins) | Full | Digital read/write, pull-up/down |
| UART0 / UART1 | Full | Serial Monitor via UART0 |
| ADC (ch 0–3) | Full | GPIO 26–29; ch 4 = temperature sensor |
| I2C0 / I2C1 | Partial | DS1307 RTC, TempSensor, EEPROM virtual devices |
| SPI0 / SPI1 | Loopback | TX looped back to RX |
| PWM | Frequency only | No waveform output to components |
| Timer / Alarm | Full | Used by delay() and millis() |
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.
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.
multicore_launch_core1() has no effectSee the complete technical reference:{' '} RP2040_EMULATION.md
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.
| Board | QEMU Machine | CPU |
|---|---|---|
| Raspberry Pi 3B | raspi3b | BCM2837, 4× Cortex-A53 @ 1.2 GHz |
QEMU exposes two UART channels to the backend:
print() output appears here.
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).
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`}
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.
See the complete technical reference:{' '} RASPBERRYPI3_EMULATION.md