Refactor code structure for improved readability and maintainability

pull/47/head
David Montero Crespo 2026-03-16 14:22:23 -03:00
parent a40471fa38
commit 2b19181c2b
8 changed files with 10611 additions and 76 deletions

241
README.md
View File

@ -1,8 +1,8 @@
# Velxio: Arduino Emulator
# Velxio: Arduino & Embedded Board Emulator
**Live at [velxio.dev](https://velxio.dev)**
A fully local, open-source Arduino emulator. Write Arduino code, compile it, and simulate it with real AVR8 CPU emulation and 48+ interactive electronic components — all running in your browser.
A fully local, open-source multi-board emulator. Write Arduino C++ or Python, compile it, and simulate it with real CPU emulation and 48+ interactive electronic components — all running in your browser.
[![Live Demo](https://img.shields.io/badge/Live%20Demo-velxio.dev-007acc?style=for-the-badge)](https://velxio.dev)
[![Docker Image](https://img.shields.io/badge/Docker-ghcr.io%2Fdavidmonterocrespo24%2Fvelxio-2496ED?style=for-the-badge&logo=docker&logoColor=white)](https://github.com/davidmonterocrespo24/velxio/pkgs/container/velxio)
@ -12,22 +12,21 @@ A fully local, open-source Arduino emulator. Write Arduino code, compile it, and
[![Commercial License](https://img.shields.io/badge/Commercial%20License-Available-green?style=for-the-badge)](COMMERCIAL_LICENSE.md)
---
<a href="https://www.producthunt.com/products/velxio?embed=true&amp;utm_source=badge-featured&amp;utm_medium=badge&amp;utm_campaign=badge-velxio" target="_blank" rel="noopener noreferrer"><img alt="Velxio - Arduino emulator running entirely in the browser | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1092514&amp;theme=dark&amp;t=1772998619179"></a>
[![Product Hunt](https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1092514&theme=dark&t=1772998619179)](https://www.producthunt.com/products/velxio)
---
## Support the Project
Velxio is free and open-source. Building and maintaining a full Arduino emulator takes a lot of time — if it saves you time or you enjoy the project, sponsoring me directly helps keep development going.
Velxio is free and open-source. Building and maintaining a full multi-board emulator takes a lot of time — if it saves you time or you enjoy the project, sponsoring me directly helps keep development going.
| Platform | Link |
|---|---|
| --- | --- |
| **GitHub Sponsors** (preferred) | [github.com/sponsors/davidmonterocrespo24](https://github.com/sponsors/davidmonterocrespo24) |
| **PayPal** | [paypal.me/odoonext](https://paypal.me/odoonext) |
Your support helps cover server costs, library maintenance, and frees up time to add new boards, components, and features. Thank you!
Your support helps cover server costs, library maintenance, and frees up time to add new boards, components, and features. Thank you!
---
@ -41,7 +40,7 @@ To self-host with Docker (single command):
docker run -d -p 3080:80 ghcr.io/davidmonterocrespo24/velxio:master
```
Then open **http://localhost:3080**.
Then open <http://localhost:3080>.
---
@ -65,60 +64,133 @@ Component Picker showing 48 available components with visual previews, search, a
---
## Supported Boards
<table>
<tr>
<td align="center"><img src="frontend/public/boards/pi-pico.svg" width="130" alt="Raspberry Pi Pico"/><br/><b>Raspberry Pi Pico</b></td>
<td align="center"><img src="frontend/public/boards/pi-pico-w.svg" width="130" alt="Raspberry Pi Pico W"/><br/><b>Raspberry Pi Pico W</b></td>
<td align="center"><img src="frontend/public/boards/esp32-devkit-c-v4.svg" width="130" alt="ESP32 DevKit C"/><br/><b>ESP32 DevKit C</b></td>
<td align="center"><img src="frontend/public/boards/esp32-s3.svg" width="130" alt="ESP32-S3"/><br/><b>ESP32-S3</b></td>
</tr>
<tr>
<td align="center"><img src="frontend/public/boards/esp32-c3.svg" width="130" alt="ESP32-C3"/><br/><b>ESP32-C3</b></td>
<td align="center"><img src="frontend/public/boards/xiao-esp32-c3.svg" width="130" alt="Seeed XIAO ESP32-C3"/><br/><b>Seeed XIAO ESP32-C3</b></td>
<td align="center"><img src="frontend/public/boards/esp32c3-supermini.svg" width="130" alt="ESP32-C3 SuperMini"/><br/><b>ESP32-C3 SuperMini</b></td>
<td align="center"><img src="frontend/public/boards/esp32-cam.svg" width="130" alt="ESP32-CAM"/><br/><b>ESP32-CAM</b></td>
</tr>
<tr>
<td align="center"><img src="frontend/public/boards/xiao-esp32-s3.svg" width="130" alt="Seeed XIAO ESP32-S3"/><br/><b>Seeed XIAO ESP32-S3</b></td>
<td align="center"><img src="frontend/public/boards/arduino-nano-esp32.svg" width="130" alt="Arduino Nano ESP32"/><br/><b>Arduino Nano ESP32</b></td>
<td align="center"><img src="frontend/public/boards/Raspberry_Pi_3.svg" width="130" alt="Raspberry Pi 3B"/><br/><b>Raspberry Pi 3B</b></td>
<td align="center">Arduino Uno / Nano / Mega<br/>(ATmega328p / 2560)</td>
</tr>
</table>
| Board | CPU | Engine | Language |
| ----- | --- | ------ | -------- |
| Arduino Uno | ATmega328p @ 16 MHz | avr8js (browser) | C++ (Arduino) |
| Arduino Nano | ATmega328p @ 16 MHz | avr8js (browser) | C++ (Arduino) |
| Arduino Mega | ATmega2560 @ 16 MHz | avr8js (browser) | C++ (Arduino) |
| Raspberry Pi Pico | RP2040 @ 125 MHz | rp2040js (browser) | C++ (Arduino) |
| Raspberry Pi Pico W | RP2040 @ 125 MHz | rp2040js (browser) | C++ (Arduino) |
| ESP32 DevKit C | Xtensa LX6 @ 240 MHz | QEMU lcgamboa (backend) | C++ (Arduino) |
| ESP32-S3 | Xtensa LX7 @ 240 MHz | QEMU lcgamboa (backend) | C++ (Arduino) |
| ESP32-CAM | Xtensa LX6 @ 240 MHz | QEMU lcgamboa (backend) | C++ (Arduino) |
| ESP32-C3 | RISC-V RV32IMC @ 160 MHz | RiscVCore.ts (browser) | C++ (Arduino) |
| Seeed XIAO ESP32-C3 | RISC-V RV32IMC @ 160 MHz | RiscVCore.ts (browser) | C++ (Arduino) |
| ESP32-C3 SuperMini | RISC-V RV32IMC @ 160 MHz | RiscVCore.ts (browser) | C++ (Arduino) |
| Seeed XIAO ESP32-S3 | Xtensa LX7 @ 240 MHz | QEMU lcgamboa (backend) | C++ (Arduino) |
| Arduino Nano ESP32 | Xtensa LX6 @ 240 MHz | QEMU lcgamboa (backend) | C++ (Arduino) |
| Raspberry Pi 3B | ARM Cortex-A53 @ 1.2 GHz | QEMU raspi3b (backend) | Python |
---
## Features
### Code Editing
- **Monaco Editor** — Full C++ editor with syntax highlighting, autocomplete, minimap, and dark theme
- **Multi-file workspace** — create, rename, delete, and switch between multiple `.ino` / `.h` / `.cpp` files
- **Arduino compilation** via `arduino-cli` backend — compile sketches to `.hex` / `.uf2` files
- **Monaco Editor** — Full C++ / Python editor with syntax highlighting, autocomplete, minimap, and dark theme
- **Multi-file workspace** — create, rename, delete, and switch between multiple `.ino` / `.h` / `.cpp` / `.py` files
- **Arduino compilation** via `arduino-cli` backend — compile sketches to `.hex` / `.bin` files
- **Compile / Run / Stop / Reset** toolbar buttons with status messages
- **Compilation console** — resizable output panel showing full compiler output, warnings, and errors
### Multi-Board Support
- **Arduino Uno** (ATmega328p) — full AVR8 emulation via avr8js
- **Arduino Nano** (ATmega328p) — full AVR8 emulation
- **Arduino Mega** (ATmega2560) — full AVR8 emulation
- **Raspberry Pi Pico** (RP2040) — full RP2040 emulation via rp2040js, compiled with arduino-pico core
- Board selector in the toolbar — switch boards without restarting
### Multi-Board Simulation
### AVR8 Simulation (Arduino Uno / Nano / Mega)
- **Real ATmega328p emulation** at 16 MHz using avr8js
- **Full GPIO support** — PORTB (pins 8-13), PORTC (A0-A5), PORTD (pins 0-7)
- **Timer0/Timer1/Timer2** peripheral support (`millis()`, `delay()`, PWM via `analogWrite()`)
- **USART (Serial)** — full transmit and receive support
- **ADC**`analogRead()` on pins A0-A5, voltage injection from UI components
- **SPI** — hardware SPI peripheral (enables ILI9341, SD card, etc.)
#### AVR8 (Arduino Uno / Nano / Mega)
- **Real ATmega328p / ATmega2560 emulation** at 16 MHz via avr8js
- **Full GPIO** — PORTB (pins 813), PORTC (A0A5), PORTD (pins 07)
- **Timer0/Timer1/Timer2**`millis()`, `delay()`, PWM via `analogWrite()`
- **USART** — full transmit and receive, auto baud-rate detection
- **ADC**`analogRead()` on A0A5, voltage injection from potentiometers on canvas
- **SPI** — hardware SPI peripheral (ILI9341, SD card, etc.)
- **I2C (TWI)** — hardware I2C with virtual device bus
- **~60 FPS simulation loop** with `requestAnimationFrame`
- ~60 FPS simulation loop via `requestAnimationFrame`
### RP2040 Simulation (Raspberry Pi Pico)
- **Real RP2040 emulation** via rp2040js at 133 MHz
- **UART0** serial output displayed in Serial Monitor
- **ADC** — 12-bit, 3.3V reference on GPIO 26-29 (A0-A3)
#### RP2040 (Raspberry Pi Pico / Pico W)
### ESP32 Simulation (via lcgamboa QEMU)
- **Real Xtensa LX6 dual-core emulation** via [lcgamboa/qemu](https://github.com/lcgamboa/qemu) fork
- **Full GPIO** — all 40 GPIO pins with direction tracking and state callbacks
- **UART0/1/2** — multi-UART serial with baud-rate detection
- **ADC** — 12-bit, 3.3V reference on all ADC-capable pins (03300 mV injection)
- **I2C** — synchronous bus with virtual device response support
- **Real RP2040 emulation** at 125 MHz via rp2040js — ARM Cortex-M0+
- **All 30 GPIO pins** — input/output, event listeners, pin state injection
- **UART0 + UART1** — serial output in Serial Monitor; Serial input from UI
- **ADC** — 12-bit on GPIO 2629 (A0A3) + internal temperature sensor (ch4)
- **I2C0 + I2C1** — master mode with virtual device bus (DS1307, TMP102, EEPROM)
- **SPI0 + SPI1** — loopback default; custom handler supported
- **PWM** — available on any GPIO pin
- **WFI optimization**`delay()` skips ahead in simulation time instead of busy-waiting
- **Oscilloscope** — GPIO transition timestamps at ~8 ns resolution
- Compiled with the [earlephilhower arduino-pico](https://github.com/earlephilhower/arduino-pico) core
See [docs/RP2040_EMULATION.md](docs/RP2040_EMULATION.md) for full technical details.
#### ESP32 / ESP32-S3 (Xtensa QEMU)
- **Real Xtensa LX6/LX7 dual-core emulation** via [lcgamboa/qemu](https://github.com/lcgamboa/qemu)
- **Full GPIO** — all 40 GPIO pins, direction tracking, state callbacks, GPIO3239 fix
- **UART0/1/2** — multi-UART serial, baud-rate detection
- **ADC** — 12-bit on all ADC-capable pins (03300 mV injection from potentiometers)
- **I2C** — synchronous bus with virtual device response
- **SPI** — full-duplex with configurable MISO byte injection
- **RMT / NeoPixel** — hardware RMT decoder with WS2812 24-bit GRB frame decoding
- **LEDC/PWM** — 16-channel duty cycle readout, mapped to LED brightness
- **RMT / NeoPixel** — hardware RMT decoder, WS2812 24-bit GRB frame decoding
- **LEDC/PWM** — 16-channel duty cycle readout, LEDC→GPIO mapping, LED brightness
- **WiFi** — SLIRP NAT emulation (`WiFi.begin("PICSimLabWifi", "")`)
- **arduino-esp32 2.0.17 (IDF 4.4.x)** — only compatible version with lcgamboa WiFi emulation
- **Crash detection** — banner notification in UI with dismiss button
- **Fully included in the Docker image** — zero extra setup required
- Requires arduino-esp32 **2.0.17** (IDF 4.4.x) — only version compatible with lcgamboa WiFi
See [docs/ESP32_EMULATION.md](docs/ESP32_EMULATION.md) for the complete installation guide.
See [docs/ESP32_EMULATION.md](docs/ESP32_EMULATION.md) for setup and full technical details.
#### ESP32-C3 / XIAO-C3 (RISC-V, in-browser)
- **RV32IMC emulation** in TypeScript — no backend, no QEMU, no WebSocket
- **GPIO 021** via W1TS/W1TC MMIO registers
- **UART0** serial output in Serial Monitor
- **Instant startup** — zero latency, works offline
- **CI-testable** — same TypeScript runs in Vitest
See [docs/RISCV_EMULATION.md](docs/RISCV_EMULATION.md) for full technical details.
#### Raspberry Pi 3B (QEMU raspi3b)
- **Full BCM2837 emulation** via `qemu-system-aarch64 -M raspi3b`
- **Boots real Raspberry Pi OS** (Trixie) — runs Python scripts directly
- **RPi.GPIO shim** — drop-in replacement for the GPIO library; sends pin events to the frontend over a text protocol
- **GPIO 027** — output and input, event detection, PWM (binary state)
- **Dual serial** — ttyAMA0 for user Serial Monitor, ttyAMA1 for GPIO protocol
- **Virtual File System** — edit Python scripts in the UI, upload to Pi at boot
- **Multi-board serial bridge** — Pi ↔ Arduino serial communication on the same canvas
- **qcow2 overlay** — base SD image never modified; session changes are isolated
See [docs/RASPBERRYPI3_EMULATION.md](docs/RASPBERRYPI3_EMULATION.md) for full technical details.
### Serial Monitor
- **Live serial output** — characters as the sketch sends them via `Serial.print()`
- **Live serial output** — characters as the sketch/script sends them
- **Auto baud-rate detection** — reads hardware registers, no manual configuration needed
- **Send data** to the Arduino RX pin from the UI
- **Send data** to the RX pin from the UI
- **Autoscroll** with toggle
### Component System (48+ Components)
- **48 electronic components** from wokwi-elements
- **Component picker** with search, category filters, and live previews
- **Drag-and-drop** repositioning on the simulation canvas
@ -126,16 +198,19 @@ See [docs/ESP32_EMULATION.md](docs/ESP32_EMULATION.md) for the complete installa
- **Property dialog** — pin roles, Arduino pin assignment, rotate & delete
### Wire System
- **Wire creation** — click a pin to start, click another pin to connect
- **Orthogonal routing** — no diagonal paths
- **8 signal-type wire colors**: Red (VCC), Black (GND), Blue (Analog), Green (Digital), Purple (PWM), Gold (I2C), Orange (SPI), Cyan (USART)
- **Segment-based wire editing** — drag segments perpendicular to their orientation
### Library Manager
- Browse and install the full Arduino library index directly from the UI
- Live search, installed tab, version display
### Auth & Project Persistence
- **Email/password** and **Google OAuth** sign-in
- **Project save** with name, description, and public/private visibility
- **Project URL** — each project gets a permanent URL at `/project/:id`
@ -143,7 +218,8 @@ See [docs/ESP32_EMULATION.md](docs/ESP32_EMULATION.md) for the complete installa
- **User profile** at `/:username` showing public projects
### Example Projects
- 8 built-in examples (Blink, Traffic Light, Button Control, Fade LED, Serial Hello World, RGB LED, Simon Says, LCD 20×4)
- Built-in examples including Blink, Traffic Light, Button Control, Fade LED, Serial Hello World, RGB LED, Simon Says, LCD 20×4, and Pi + Arduino serial control
- One-click loading into the editor
---
@ -161,9 +237,10 @@ docker run -d \
ghcr.io/davidmonterocrespo24/velxio:master
```
Open **http://localhost:3080**.
Open <http://localhost:3080>.
The `/app/data` volume contains:
- `velxio.db` — SQLite database (users, projects metadata)
- `projects/{id}/` — sketch files per project
@ -179,7 +256,7 @@ docker compose -f docker-compose.prod.yml up -d
#### Environment variables (`backend/.env`)
| Variable | Default | Description |
|---|---|---|
| --- | --- | --- |
| `SECRET_KEY` | *(required)* | JWT signing secret |
| `DATABASE_URL` | `sqlite+aiosqlite:////app/data/velxio.db` | SQLite path |
| `DATA_DIR` | `/app/data` | Directory for project files |
@ -209,42 +286,53 @@ npm install
npm run dev
```
Open **http://localhost:5173**.
Open <http://localhost:5173>.
**arduino-cli setup (first time):**
```bash
arduino-cli core update-index
arduino-cli core install arduino:avr
# For Raspberry Pi Pico:
# For Raspberry Pi Pico / Pico W:
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
# For ESP32 / ESP32-S3 / ESP32-C3:
arduino-cli config add board_manager.additional_urls \
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
arduino-cli core install esp32:esp32@2.0.17
```
---
## Project Structure
```
```text
velxio/
├── frontend/ # React + Vite + TypeScript
│ └── src/
│ ├── pages/ # LandingPage, EditorPage, ProjectByIdPage, ...
│ ├── pages/ # LandingPage, EditorPage, UserProfilePage, ...
│ ├── components/ # Editor, simulator canvas, modals, layout
│ ├── simulation/ # AVRSimulator, RP2040Simulator, PinManager
│ ├── store/ # Zustand stores (auth, editor, simulator, project)
│ ├── simulation/ # AVRSimulator, RP2040Simulator, RiscVCore,
│ │ # RaspberryPi3Bridge, Esp32Bridge, PinManager
│ ├── store/ # Zustand stores (auth, editor, simulator, project, vfs)
│ └── services/ # API clients
├── backend/ # FastAPI + Python
│ └── app/
│ ├── api/routes/ # compile, auth, projects, libraries
│ ├── api/routes/ # compile, auth, projects, libraries, simulation (ws)
│ ├── models/ # User, Project (SQLAlchemy)
│ ├── services/ # arduino_cli, project_files
│ ├── services/ # arduino_cli, esp32_worker, qemu_manager, gpio_shim
│ └── core/ # config, security, dependencies
├── wokwi-libs/ # Local clones of Wokwi repos
│ ├── wokwi-elements/
│ ├── avr8js/
│ └── rp2040js/
│ ├── wokwi-elements/ # Web Components for electronic parts
│ ├── avr8js/ # AVR8 CPU emulator
│ ├── rp2040js/ # RP2040 emulator
│ └── qemu-lcgamboa/ # QEMU fork for ESP32 Xtensa emulation
├── img/ # Raspberry Pi 3 boot images (kernel8.img, dtb, OS image)
├── deploy/ # nginx.conf, entrypoint.sh
├── docs/ # Technical documentation
├── Dockerfile.standalone # Single-container production image
├── docker-compose.yml # Development compose
└── docker-compose.prod.yml # Production compose
@ -255,10 +343,15 @@ velxio/
## Technologies
| Layer | Stack |
|---|---|
| --- | --- |
| Frontend | React 19, Vite 7, TypeScript 5.9, Monaco Editor, Zustand, React Router 7 |
| Backend | FastAPI, SQLAlchemy 2.0 async, aiosqlite, uvicorn |
| Simulation | avr8js (AVR8), rp2040js (RP2040), wokwi-elements (Web Components) |
| AVR Simulation | avr8js (ATmega328p / ATmega2560) |
| RP2040 Simulation | rp2040js (ARM Cortex-M0+) |
| RISC-V Simulation | RiscVCore.ts (RV32IMC, custom TypeScript) |
| ESP32 Simulation | QEMU 8.1.3 lcgamboa fork (Xtensa LX6/LX7) |
| Raspberry Pi 3 Simulation | QEMU 8.1.3 (`qemu-system-aarch64 -M raspi3b`) + Raspberry Pi OS Trixie |
| UI Components | wokwi-elements (Web Components) |
| Compiler | arduino-cli (subprocess) |
| Auth | JWT (httpOnly cookie), Google OAuth 2.0 |
| Persistence | SQLite + disk volume (`/app/data/projects/{id}/`) |
@ -266,6 +359,24 @@ velxio/
---
## Documentation
| Topic | Document |
| --- | --- |
| Getting Started | [docs/getting-started.md](docs/getting-started.md) |
| Architecture Overview | [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) |
| Emulator Architecture | [docs/emulator.md](docs/emulator.md) |
| Wokwi Libraries Integration | [docs/WOKWI_LIBS.md](docs/WOKWI_LIBS.md) |
| RP2040 Emulation (Pico) | [docs/RP2040_EMULATION.md](docs/RP2040_EMULATION.md) |
| Raspberry Pi 3 Emulation | [docs/RASPBERRYPI3_EMULATION.md](docs/RASPBERRYPI3_EMULATION.md) |
| ESP32 Emulation (Xtensa) | [docs/ESP32_EMULATION.md](docs/ESP32_EMULATION.md) |
| RISC-V Emulation (ESP32-C3) | [docs/RISCV_EMULATION.md](docs/RISCV_EMULATION.md) |
| Components Reference | [docs/components.md](docs/components.md) |
| MCP Server | [docs/MCP.md](docs/MCP.md) |
| Roadmap | [docs/roadmap.md](docs/roadmap.md) |
---
## Troubleshooting
**`arduino-cli: command not found`** — install arduino-cli and add to PATH.
@ -274,7 +385,11 @@ velxio/
**Serial Monitor shows nothing** — ensure `Serial.begin()` is called before `Serial.print()`.
**Compilation errors** — check the compilation console; verify the correct core is installed.
**ESP32 not starting** — verify `libqemu-xtensa.dll` (Windows) or `libqemu-xtensa.so` (Linux) is present in `backend/app/services/`.
**Pi 3 takes too long to boot** — QEMU needs 25 seconds to initialize; the "booting" status in the UI is expected.
**Compilation errors** — check the compilation console; verify the correct core is installed for the selected board.
---
@ -288,7 +403,7 @@ Join the Discord server to ask questions, share projects, and follow updates:
Suggestions, bug reports, and pull requests are welcome at [github.com/davidmonterocrespo24/velxio](https://github.com/davidmonterocrespo24/velxio).
If you'd like to support the project financially, see the [Support the Project](#-support-the-project) section above or sponsor directly at [github.com/sponsors/davidmonterocrespo24](https://github.com/sponsors/davidmonterocrespo24).
If you'd like to support the project financially, see the [Support the Project](#support-the-project) section above or sponsor directly at [github.com/sponsors/davidmonterocrespo24](https://github.com/sponsors/davidmonterocrespo24).
> **Note:** All contributors must sign a Contributor License Agreement (CLA) so that the dual-licensing model remains valid. A CLA check runs automatically on pull requests.
@ -297,7 +412,7 @@ If you'd like to support the project financially, see the [Support the Project](
Velxio uses a **dual-licensing** model:
| Use case | License | Cost |
|----------|---------|------|
| --- | --- | --- |
| Personal, educational, open-source (AGPLv3 compliant) | [AGPLv3](LICENSE) | Free |
| Proprietary / closed-source product or SaaS | [Commercial License](COMMERCIAL_LICENSE.md) | Paid |
@ -313,5 +428,7 @@ See [LICENSE](LICENSE) and [COMMERCIAL_LICENSE.md](COMMERCIAL_LICENSE.md) for fu
- [avr8js](https://github.com/wokwi/avr8js) — AVR8 emulator
- [wokwi-elements](https://github.com/wokwi/wokwi-elements) — Electronic web components
- [rp2040js](https://github.com/wokwi/rp2040js) — RP2040 emulator
- [lcgamboa/qemu](https://github.com/lcgamboa/qemu) — QEMU fork for ESP32 Xtensa emulation
- [arduino-cli](https://github.com/arduino/arduino-cli) — Arduino compiler
- [Monaco Editor](https://microsoft.github.io/monaco-editor/) — Code editor
- [QEMU](https://www.qemu.org) — Machine emulator (Raspberry Pi 3)

View File

@ -0,0 +1,593 @@
# Raspberry Pi 3 Emulation (BCM2837 / ARM Cortex-A53)
> Status: **Functional** · Backend QEMU process · WebSocket communication
> Engine: **QEMU 8.1.3** (`qemu-system-aarch64 -M raspi3b`)
> Platform: **BCM2837 ARM Cortex-A53 @ 1.2 GHz** — 64-bit ARMv8, quad-core
> Runs: **Python scripts** (Raspberry Pi OS Trixie) — no Arduino compilation needed
> Available on: all platforms (Windows, macOS, Linux, Docker)
---
## Table of Contents
1. [Overview](#1-overview)
2. [Supported Boards](#2-supported-boards)
3. [Emulator Architecture](#3-emulator-architecture)
4. [System Components](#4-system-components)
5. [Boot Sequence — Step by Step](#5-boot-sequence--step-by-step)
6. [GPIO Shim — How Python Controls GPIO](#6-gpio-shim--how-python-controls-gpio)
7. [WebSocket Protocol](#7-websocket-protocol)
8. [Serial Communication (UART)](#8-serial-communication-uart)
9. [Pin Mapping — Physical to BCM GPIO](#9-pin-mapping--physical-to-bcm-gpio)
10. [Virtual File System (VFS)](#10-virtual-file-system-vfs)
11. [Multi-Board Integration — Pi + Arduino](#11-multi-board-integration--pi--arduino)
12. [Boot Images](#12-boot-images)
13. [QEMU Launch Command](#13-qemu-launch-command)
14. [Known Limitations](#14-known-limitations)
15. [Differences vs Other Emulators](#15-differences-vs-other-emulators)
16. [Key Files](#16-key-files)
---
## 1. Overview
The **Raspberry Pi 3B** is a full Linux single-board computer based on the **Broadcom BCM2837** SoC (4× ARM Cortex-A53, ARMv8 64-bit). Unlike the other boards in Velxio — which compile and run Arduino C++ code — the Raspberry Pi 3 emulation **boots a real Raspberry Pi OS** (Trixie) inside QEMU and lets you run Python scripts that interact with GPIO.
There is **no compilation step** for the Raspberry Pi: you write a Python script in the editor, the backend uploads it to the emulated filesystem, and the Pi OS executes it directly.
### Emulation Engine Comparison
| Board | Engine | Location | Language |
| ----- | ------ | -------- | -------- |
| Arduino Uno / Nano / Mega | avr8js | Browser | C++ (Arduino) |
| Raspberry Pi Pico | rp2040js | Browser | C++ (Arduino) |
| ESP32-C3 / XIAO-C3 | RiscVCore.ts | Browser | C++ (Arduino) |
| ESP32 / ESP32-S3 | QEMU lcgamboa (Xtensa) | Backend WebSocket | C++ (Arduino) |
| **Raspberry Pi 3B** | **QEMU 8.1.3 (raspi3b)** | **Backend WebSocket** | **Python** |
### Key Differences from Arduino-based Boards
- **No FQBN** — no arduino-cli compilation; the board kind has `FQBN = null`
- **Boots a real OS** — Raspberry Pi OS Trixie runs inside QEMU (~25 seconds boot time)
- **Python runtime** — scripts use `RPi.GPIO` (or a compatible shim) to interact with GPIO
- **Persistent storage** — the OS image is a real disk image; a qcow2 overlay is used per session so the base image is never modified
- **Multi-board serial** — the Pi can communicate with co-simulated Arduino boards via virtual serial lines
---
## 2. Supported Boards
| Board | QEMU Machine | CPU | Notes |
| ----- | ------------ | --- | ----- |
| Raspberry Pi 3B | `raspi3b` | BCM2837, 4× Cortex-A53 | Full Raspberry Pi OS support |
> **Raspberry Pi 3B+** and **Pi 4** are not currently supported. The `raspi3b` machine type in QEMU closely matches the standard 3B hardware.
---
## 3. Emulator Architecture
```text
Python Script (user writes in editor)
▼ (uploaded via WebSocket / VFS)
/home/pi/script.py (inside Raspberry Pi OS)
▼ python3 /home/pi/script.py
RPi.GPIO (shim) ← intercepted by gpio_shim.py
├── GPIO.output(17, HIGH) → "GPIO 17 1\n" → ttyAMA1 → Backend
│ │
│ ▼
│ gpio_change event
│ WebSocket → Frontend
│ PinManager → LED visual
└── Serial.print() → ttyAMA0 → Backend → serial_output → Serial Monitor
```
### Communication Channels
The Raspberry Pi uses **two independent TCP serial ports** exposed through QEMU:
| Channel | QEMU Serial | TCP Port | Purpose |
| ------- | ----------- | -------- | ------- |
| User Serial | `-serial tcp:...:N` | dynamic | User `print()` output and `input()` — visible in Serial Monitor |
| GPIO Protocol | `-serial tcp:...:M` | dynamic | GPIO shim protocol (`GPIO <pin> <val>\n`) |
Both ports are allocated dynamically at startup to avoid conflicts on the host machine.
---
## 4. System Components
### Backend
| Component | File | Responsibility |
| --------- | ---- | -------------- |
| `QemuManager` | `backend/app/services/qemu_manager.py` | Singleton that manages all Pi instances (one per WebSocket client) |
| `PiInstance` | `backend/app/services/qemu_manager.py` | Runtime state for one running Pi: QEMU process, TCP ports, overlay path |
| `gpio_shim` | `backend/app/services/gpio_shim.py` | `RPi.GPIO` drop-in replacement; speaks the GPIO text protocol over ttyAMA1 |
| WebSocket route | `backend/app/api/routes/simulation.py` | `GET /api/simulation/ws/{client_id}` — bidirectional JSON message bus |
### Frontend
| Component | File | Responsibility |
| --------- | ---- | -------------- |
| `RaspberryPi3Bridge` | `frontend/src/simulation/RaspberryPi3Bridge.ts` | WebSocket connection manager; sends/receives JSON messages |
| `useSimulatorStore` | `frontend/src/store/useSimulatorStore.ts` | Zustand store; wires bridge events to board state and pin manager |
| `useVfsStore` | `frontend/src/store/useVfsStore.ts` | Virtual filesystem tree per board; Python script editing |
| `RaspberryPi3.tsx` | `frontend/src/components/components-wokwi/RaspberryPi3.tsx` | React board component (SVG image, 40-pin header) |
| `boardPinMapping.ts` | `frontend/src/utils/boardPinMapping.ts` | Physical pin → BCM GPIO number translation |
---
## 5. Boot Sequence — Step by Step
```text
1. User clicks "Start" (or "Run")
2. SimulatorCanvas detects board kind 'raspberry-pi-3'
→ calls startBoard(boardId)
3. useSimulatorStore calls RaspberryPi3Bridge.connect()
4. Bridge opens WebSocket:
ws://localhost:8001/api/simulation/ws/<boardId>
→ sends { type: 'start_pi', data: { board: 'raspberry-pi-3' } }
5. Backend (simulation.py) routes to:
QemuManager.start_instance(client_id, 'raspberry-pi-3', callback)
6. QemuManager._boot(inst):
a. Allocate two free TCP ports (serial_port, gpio_port)
b. Create qcow2 overlay over base SD image:
qemu-img create -f qcow2 -b raspios.img overlay_<id>.qcow2
c. Launch qemu-system-aarch64 (see Section 13 for full command)
d. Emit { type: 'system', event: 'booting' }
7. Wait ~2 seconds for QEMU to initialize TCP servers
8. QemuManager._connect_serial(inst):
→ Connect to ttyAMA0 TCP socket
→ Start async reader loop (forwards bytes as serial_output events)
→ Emit { type: 'system', event: 'booted' }
9. QemuManager._connect_gpio(inst):
→ Connect to ttyAMA1 TCP socket
→ Start async reader loop (parses "GPIO <pin> <val>\n" lines)
10. Frontend receives 'booted' event
→ Board UI updates to "running" state
→ Serial Monitor shows first Linux kernel output
11. Python script runs:
→ python3 /home/pi/script.py (auto-launched via -append init=/bin/sh)
```
---
## 6. GPIO Shim — How Python Controls GPIO
The `gpio_shim.py` module is injected into the Raspberry Pi OS at the standard `RPi.GPIO` installation path:
```
/usr/local/lib/python3.11/dist-packages/RPi/GPIO.py
```
When a Python script does `import RPi.GPIO as GPIO`, it gets this shim instead of the real hardware driver. The shim communicates over `/dev/ttyAMA1` (the second QEMU serial port) using a simple text protocol.
### GPIO Text Protocol
```text
Pi → Backend (output state change):
"GPIO <bcm_pin> <0|1>\n"
Example: "GPIO 17 1\n" ← GPIO 17 driven HIGH
Backend → Pi (external input, e.g. button press from canvas):
"SET <bcm_pin> <0|1>\n"
Example: "SET 22 1\n" ← button wired to GPIO 22 pressed
```
### Supported RPi.GPIO API
```python
import RPi.GPIO as GPIO
# Numbering mode
GPIO.setmode(GPIO.BCM) # use BCM numbers (GPIO17, GPIO22, ...)
GPIO.setmode(GPIO.BOARD) # use physical pin numbers (11, 15, ...)
# Pin direction
GPIO.setup(17, GPIO.OUT)
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# Digital output
GPIO.output(17, GPIO.HIGH) # → sends "GPIO 17 1\n" to backend
GPIO.output(17, GPIO.LOW) # → sends "GPIO 17 0\n" to backend
GPIO.output(17, True) # equivalent to GPIO.HIGH
# Digital input
state = GPIO.input(22) # reads last known state (updated by "SET" messages)
# Event detection
GPIO.add_event_detect(22, GPIO.RISING, callback=my_callback)
GPIO.add_event_detect(22, GPIO.FALLING, callback=my_callback)
GPIO.add_event_detect(22, GPIO.BOTH, callback=my_callback)
# PWM (simplified — simulated as digital output)
pwm = GPIO.PWM(18, 1000) # pin 18, 1000 Hz
pwm.start(75) # 75% duty cycle → HIGH (duty > 50% → HIGH, else LOW)
pwm.ChangeDutyCycle(25) # 25% duty cycle → LOW
pwm.stop()
# Cleanup
GPIO.cleanup()
GPIO.cleanup(17) # clean specific pin
```
> **PWM limitation:** The shim does not implement real PWM waveforms. It converts duty cycle to a binary state: `duty > 50` → HIGH, `duty ≤ 50` → LOW. Visual LED dimming is not supported for Pi GPIO PWM.
### Example Python Script (Blink LED)
```python
#!/usr/bin/env python3
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
try:
while True:
GPIO.output(17, GPIO.HIGH)
print("LED ON")
time.sleep(1)
GPIO.output(17, GPIO.LOW)
print("LED OFF")
time.sleep(1)
finally:
GPIO.cleanup()
```
---
## 7. WebSocket Protocol
All communication between the frontend and backend passes through a single WebSocket connection per board instance.
**Endpoint:** `GET /api/simulation/ws/{client_id}`
### Frontend → Backend Messages
| Message Type | Payload | Description |
| ------------ | ------- | ----------- |
| `start_pi` | `{ board: "raspberry-pi-3" }` | Launch QEMU, start the Pi |
| `stop_pi` | _(empty)_ | Stop QEMU, clean up overlay |
| `serial_input` | `{ bytes: number[] }` | Send bytes to ttyAMA0 (Serial Monitor → Pi) |
| `gpio_in` | `{ pin: number, state: 0\|1 }` | Inject external GPIO state (button press from canvas) |
### Backend → Frontend Messages
| Message Type | Payload | Description |
| ------------ | ------- | ----------- |
| `serial_output` | `{ data: string }` | String data from ttyAMA0 (Pi print output) |
| `gpio_change` | `{ pin: number, state: 0\|1 }` | A GPIO pin changed state (driven by Python script) |
| `system` | `{ event: "booting"\|"booted"\|"exited" }` | Boot lifecycle events |
| `error` | `{ message: string }` | Error from QEMU or backend |
---
## 8. Serial Communication (UART)
The Raspberry Pi 3 exposes two UART ports through QEMU:
| Port | Device | Physical Pins | Role |
| ---- | ------ | ------------- | ---- |
| UART0 (ttyAMA0) | `/dev/ttyAMA0` | GPIO14 (TX), GPIO15 (RX) | User serial — `print()` output, `input()`, `serial.Serial()` |
| UART1 (ttyAMA1) | `/dev/ttyAMA1` | — (internal) | GPIO shim protocol — reserved, not accessible to user scripts |
### Serial Monitor Integration
Anything the Python script writes to stdout or to `/dev/ttyAMA0` appears in the Serial Monitor panel:
```python
# stdout (print) — captured automatically
print("Hello from Pi!")
# Direct ttyAMA0 (explicit serial)
import serial
port = serial.Serial('/dev/ttyAMA0', baudrate=9600, timeout=1)
port.write(b"Hello Arduino!\n")
```
### Sending Text to the Pi
Text typed in the Serial Monitor input box is sent to ttyAMA0 as a `serial_input` message, which the Pi receives via `input()` or by reading `/dev/ttyAMA0`.
---
## 9. Pin Mapping — Physical to BCM GPIO
The Raspberry Pi 3B has a standard **40-pin GPIO header** (2 rows × 20 columns). The table below shows the mapping from physical pin number to BCM GPIO number:
| Physical | BCM | Function | Physical | BCM | Function |
| -------- | --- | -------- | -------- | --- | -------- |
| 1 | — | 3.3 V | 2 | — | 5 V |
| 3 | **2** | I2C1 SDA | 4 | — | 5 V |
| 5 | **3** | I2C1 SCL | 6 | — | GND |
| 7 | **4** | GPIO | 8 | **14** | UART TX |
| 9 | — | GND | 10 | **15** | UART RX |
| 11 | **17** | GPIO | 12 | **18** | PWM0 |
| 13 | **27** | GPIO | 14 | — | GND |
| 15 | **22** | GPIO | 16 | **23** | GPIO |
| 17 | — | 3.3 V | 18 | **24** | GPIO |
| 19 | **10** | SPI MOSI | 20 | — | GND |
| 21 | **9** | SPI MISO | 22 | **25** | GPIO |
| 23 | **11** | SPI SCLK | 24 | **8** | SPI CE0 |
| 25 | — | GND | 26 | **7** | SPI CE1 |
| 27 | — | ID_SD | 28 | — | ID_SC |
| 29 | **5** | GPIO | 30 | — | GND |
| 31 | **6** | GPIO | 32 | **12** | PWM0 |
| 33 | **13** | PWM1 | 34 | — | GND |
| 35 | **19** | SPI1 MISO | 36 | **16** | SPI1 CE2 |
| 37 | **26** | GPIO | 38 | **20** | SPI1 MOSI |
| 39 | — | GND | 40 | **21** | SPI1 SCLK |
> Pins 27 and 28 are reserved for ID EEPROM. Power and GND pins have BCM = —.
### Pin Resolution in Frontend
```typescript
// Wire connects physical pin "8" on the Pi board
boardPinToNumber('raspberry-pi-3', '8') // → 14 (BCM GPIO14, UART TX)
boardPinToNumber('raspberry-pi-3', 'GPIO17') // → 17
boardPinToNumber('raspberry-pi-3', 'GND') // → null (not a GPIO)
```
---
## 10. Virtual File System (VFS)
Each Raspberry Pi 3 board instance has its own **virtual filesystem tree** stored in the `useVfsStore` Zustand store. This lets you create and edit Python scripts directly in the Velxio editor before they are uploaded to the Pi.
### Default VFS Tree
```text
/
└── home/
└── pi/
├── script.py ← main Python script (editable)
└── hello.sh ← example shell script
```
### Default `script.py`
```python
#!/usr/bin/env python3
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
while True:
GPIO.output(17, GPIO.HIGH)
print("LED on")
time.sleep(1)
GPIO.output(17, GPIO.LOW)
print("LED off")
time.sleep(1)
```
### VFS API
```typescript
const vfs = useVfsStore.getState();
vfs.initBoardVfs(boardId) // create default tree
vfs.createNode(boardId, parentId, 'app.py', 'file') // add new file
vfs.setContent(boardId, nodeId, pythonCode) // update file content
vfs.serializeForUpload(boardId) // returns [{ path, content }, ...]
```
Files in the VFS are uploaded to the Pi OS at boot via the WebSocket connection before the script is executed.
---
## 11. Multi-Board Integration — Pi + Arduino
The Raspberry Pi 3 can be placed on the same canvas as Arduino or other boards. When wires connect a Pi GPIO pin to an Arduino pin, the stores route data between them automatically.
### Pi → Arduino (Serial TX)
```text
Pi Python script:
port.write(b"LED_ON\n")
▼ ttyAMA0 byte output
serial_output WebSocket message
▼ useSimulatorStore (serial callback)
AVRSimulator.serialWrite("L") ← feeds byte into Arduino RX FIFO
▼ Arduino sketch:
String cmd = Serial.readStringUntil('\n');
if (cmd == "LED_ON") digitalWrite(8, HIGH);
```
### Arduino → Pi (Serial RX)
```text
Arduino sketch:
Serial.println("SENSOR:1023");
▼ USART byte emitted
useSimulatorStore serial callback
▼ bridge.sendSerialBytes([charCode, ...])
serial_input WebSocket message → Backend
▼ qemu_manager.send_serial_bytes(client_id, bytes)
ttyAMA0 receives bytes → Pi reads with:
line = port.readline() # "SENSOR:1023\n"
```
### Example Project: Pi + Arduino LED Control
This example (included in the gallery as `pi-to-arduino-led-control`) demonstrates bidirectional serial communication:
**Pi Script:**
```python
import serial, time
port = serial.Serial('/dev/ttyAMA0', baudrate=9600, timeout=1)
for _ in range(3):
port.write(b"LED1_ON\n")
time.sleep(0.5)
port.write(b"LED1_OFF\n")
time.sleep(0.5)
port.write(b"LED2_ON\n")
time.sleep(2)
port.write(b"LED2_OFF\n")
```
**Arduino Sketch:**
```cpp
const int LED1 = 8, LED2 = 9;
void setup() {
Serial.begin(9600);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
}
void loop() {
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
if (cmd == "LED1_ON") digitalWrite(LED1, HIGH);
else if (cmd == "LED1_OFF") digitalWrite(LED1, LOW);
else if (cmd == "LED2_ON") digitalWrite(LED2, HIGH);
else if (cmd == "LED2_OFF") digitalWrite(LED2, LOW);
}
}
```
---
## 12. Boot Images
QEMU needs three files from the `/img/` directory to boot the Raspberry Pi 3:
| File | Size | Description |
| ---- | ---- | ----------- |
| `kernel8.img` | ~6 MB | ARM64 Linux kernel extracted from Raspberry Pi OS Trixie |
| `bcm271~1.dtb` | ~40 KB | Device tree binary — defines CPU, RAM, peripheral base addresses |
| `2025-12-04-raspios-trixie-armhf.img` | ~5.67 GB | Full Raspberry Pi OS SD card image (root filesystem) |
> The base SD image is **never modified**. Each session creates a **qcow2 copy-on-write overlay** (`overlay_<session_id>.qcow2`) that records only the changes made during that session. The overlay is automatically deleted when the session ends.
### Creating the Overlay at Runtime
```bash
# Backend does this automatically for each session:
qemu-img create -f qcow2 \
-b /img/2025-12-04-raspios-trixie-armhf.img \
/tmp/overlay_<session_id>.qcow2
```
---
## 13. QEMU Launch Command
```bash
qemu-system-aarch64 \
-M raspi3b \
-kernel /img/kernel8.img \
-dtb /img/bcm271~1.dtb \
-drive file=/tmp/overlay_<id>.qcow2,if=sd,format=qcow2 \
-m 1G \
-smp 4 \
-nographic \
-serial tcp:127.0.0.1:<serial_port>,server,nowait \
-serial tcp:127.0.0.1:<gpio_port>,server,nowait \
-append 'console=ttyAMA0 root=/dev/mmcblk0p2 rootwait rw \
dwc_otg.lpm_enable=0 quiet init=/bin/sh'
```
### Key Flags
| Flag | Value | Meaning |
| ---- | ----- | ------- |
| `-M raspi3b` | machine type | Emulate the Raspberry Pi 3B hardware |
| `-m 1G` | RAM | 1 GB RAM (matches real Pi 3B) |
| `-smp 4` | CPU cores | 4 ARM Cortex-A53 cores |
| `-nographic` | no display | No HDMI/video output — serial only |
| `-serial tcp:...:N,server,nowait` | first serial | ttyAMA0 (user serial) served on TCP port N |
| `-serial tcp:...:M,server,nowait` | second serial | ttyAMA1 (GPIO shim protocol) served on TCP port M |
| `-append ... init=/bin/sh` | kernel cmdline | Boot straight to a shell (skips systemd login) |
| `-drive ...,format=qcow2` | disk | qcow2 overlay over the base SD image |
---
## 14. Known Limitations
| Limitation | Detail |
| ---------- | ------ |
| Boot time | QEMU takes 25 seconds to start; the frontend shows a "booting" state during this time |
| No real PWM | `GPIO.PWM` simulates duty cycle as binary state (>50% = HIGH, ≤50% = LOW); no analog dimming |
| No I2C emulation | `smbus`, `smbus2`, `i2c_msg` — I2C bus transactions are not forwarded to virtual devices |
| No SPI emulation | Hardware SPI registers not forwarded; `spidev` library will fail |
| Single UART for GPIO | ttyAMA1 is reserved for the GPIO shim; scripts cannot use it for other serial devices |
| No GUI / display | HDMI output is disabled (`-nographic`); GUI Python libraries (Tkinter, pygame, etc.) will not work |
| No networking | QEMU does not expose a network interface; `requests`, `socket`, `urllib` will fail |
| No persistent state | The qcow2 overlay is deleted after shutdown; files written to the Pi OS do not survive a restart |
| Reset is reconnect | `resetBoard()` is not implemented; to restart the Pi, stop it and start it again |
| Session isolation | Each board instance creates an independent QEMU process; two Pi boards do not share any state |
| Resource usage | Each Pi instance launches a full QEMU process (~200 MB RAM); hosting many simultaneous sessions is resource-intensive |
---
## 15. Differences vs Other Emulators
| Aspect | Raspberry Pi 3B | Raspberry Pi Pico | ESP32 (Xtensa) | Arduino AVR |
| ------ | --------------- | ----------------- | -------------- | ----------- |
| Engine | QEMU raspi3b | rp2040js (browser) | QEMU lcgamboa (backend) | avr8js (browser) |
| Backend required | **Yes** (QEMU process) | No | Yes (QEMU process) | No |
| Language | **Python** | C++ (Arduino) | C++ (Arduino) | C++ (Arduino) |
| Compilation step | **No** | Yes (arduino-cli) | Yes (arduino-cli) | Yes (arduino-cli) |
| OS | **Raspberry Pi OS (Linux)** | None (bare metal) | None (ESP-IDF) | None (bare metal) |
| Boot time | ~25 s | Instant | ~12 s | Instant |
| GPIO protocol | Text over ttyAMA1 | MMIO direct | QEMU callbacks + WebSocket | Port listeners |
| Serial | ttyAMA0 (real UART) | UART0/1 (rp2040js) | UART0 (QEMU) | USART0 (avr8js) |
| I2C | Not forwarded to frontend | 2 buses + virtual devices | Emulated | Not emulated |
| PWM | Binary (no waveform) | Hardware PWM | LEDC (mapped) | Timer-based |
| Multi-board comms | Yes (serial bridge) | No | No | No |
| Oscilloscope | No | Yes (8 ns resolution) | No | Yes |
| CI tests | No | Yes (Vitest) | No | Yes (Vitest) |
| Disk image required | **Yes** (~5.67 GB) | No | No | No |
---
## 16. Key Files
| File | Description |
| ---- | ----------- |
| `backend/app/services/qemu_manager.py` | `QemuManager` — manages QEMU process lifecycle, TCP sockets, qcow2 overlays |
| `backend/app/services/gpio_shim.py` | `RPi.GPIO` drop-in replacement; speaks text protocol over ttyAMA1 |
| `backend/app/api/routes/simulation.py` | WebSocket endpoint `/api/simulation/ws/{client_id}` |
| `frontend/src/simulation/RaspberryPi3Bridge.ts` | WebSocket client; routes `serial_output`, `gpio_change`, `system` events |
| `frontend/src/store/useSimulatorStore.ts` | Board lifecycle, serial bridge to co-simulated AVR/Pico boards |
| `frontend/src/store/useVfsStore.ts` | Per-board virtual filesystem (Python script editor) |
| `frontend/src/utils/boardPinMapping.ts` | Physical pin → BCM GPIO number mapping table |
| `frontend/src/components/components-wokwi/RaspberryPi3.tsx` | Board React component (SVG, 40-pin header coordinates) |
| `frontend/src/components/components-wokwi/RaspberryPi3Element.ts` | Web Component for canvas rendering and wire endpoints |
| `frontend/src/types/board.ts` | `BoardKind` type, `FQBN = null` for Raspberry Pi 3 |
| `img/kernel8.img` | ARM64 kernel extracted from Raspberry Pi OS Trixie |
| `img/bcm271~1.dtb` | Device tree binary for BCM2837 |
| `img/2025-12-04-raspios-trixie-armhf.img` | Raspberry Pi OS SD image (~5.67 GB) — base for qcow2 overlays |

609
docs/RP2040_EMULATION.md Normal file
View File

@ -0,0 +1,609 @@
# RP2040 Emulation (Raspberry Pi Pico / Pico W)
> Status: **Functional** · In-browser emulation · No backend dependencies
> Engine: **rp2040js** — ARM Cortex-M0+ emulator in TypeScript
> Platform: **RP2040 @ 125 MHz** — dual-core ARM Cortex-M0+ (single core emulated)
> Available on: all platforms (Windows, macOS, Linux, Docker)
> Applies to: **Raspberry Pi Pico**, **Raspberry Pi Pico W**
---
## Table of Contents
1. [Overview](#1-overview)
2. [Supported Boards](#2-supported-boards)
3. [Emulator Architecture](#3-emulator-architecture)
4. [Emulated Memory and Peripherals](#4-emulated-memory-and-peripherals)
5. [Full Flow: Compile and Run](#5-full-flow-compile-and-run)
6. [Binary Format and Loading](#6-binary-format-and-loading)
7. [GPIO](#7-gpio)
8. [UART — Serial Monitor](#8-uart--serial-monitor)
9. [ADC — Analog Inputs](#9-adc--analog-inputs)
10. [I2C Virtual Devices](#10-i2c-virtual-devices)
11. [SPI](#11-spi)
12. [PWM](#12-pwm)
13. [Simulation Execution Loop](#13-simulation-execution-loop)
14. [Pin Mapping](#14-pin-mapping)
15. [Oscilloscope / Logic Analyzer](#15-oscilloscope--logic-analyzer)
16. [Known Limitations](#16-known-limitations)
17. [Tests](#17-tests)
18. [Differences vs Other Emulators](#18-differences-vs-other-emulators)
19. [Key Files](#19-key-files)
---
## 1. Overview
The **Raspberry Pi Pico** and **Pico W** boards use the **RP2040** microcontroller — a dual-core **ARM Cortex-M0+** chip designed by Raspberry Pi. Unlike the ESP32 (which requires QEMU running in the backend), RP2040 emulation runs **entirely in the browser** using the [rp2040js](https://github.com/wokwi/rp2040js) library (a local clone in `wokwi-libs/rp2040js/`).
### Emulation Engine Comparison
| Board | CPU | Engine |
| ----- | --- | ------ |
| Arduino Uno / Nano / Mega | AVR ATmega | avr8js (browser) |
| Raspberry Pi Pico / Pico W | RP2040 ARM Cortex-M0+ | **rp2040js (browser, no backend)** |
| ESP32-C3, XIAO-C3 | RISC-V RV32IMC | RiscVCore.ts (browser) |
| ESP32, ESP32-S3 | Xtensa LX6/LX7 | QEMU lcgamboa (backend WebSocket) |
### Advantages of the In-Browser Emulator
- **No network dependencies** — works offline, no WebSocket connection to any backend process
- **Instant startup** — emulation begins immediately after compilation (no process launch latency)
- **Testable with Vitest** — the same TypeScript code runs in both production and CI tests
- **Cross-platform** — identical behavior on Windows, macOS, Linux, and Docker
---
## 2. Supported Boards
| Board | arduino-cli FQBN | Built-in LED | Notes |
| ----- | ---------------- | ------------ | ----- |
| Raspberry Pi Pico | `rp2040:rp2040:rpipico` | GPIO 25 | Standard Pico |
| Raspberry Pi Pico W | `rp2040:rp2040:rpipicow` | GPIO 25 (via CYW43) | WiFi chip not emulated |
> **Pico W note:** The wireless chip (Infineon CYW43439) is not emulated. GPIO 25 drives the on-board LED in the same way as the standard Pico for simulation purposes.
---
## 3. Emulator Architecture
```text
Arduino Sketch (.ino)
▼ arduino-cli (backend, FQBN rp2040:rp2040:rpipico)
sketch.ino.bin ← raw ARM binary (no HEX format)
▼ base64 → frontend
compileBoardProgram(boardId, base64)
▼ RP2040Simulator.loadBinary(base64)
Uint8Array → copied into rp2040.flash at offset 0
▼ rp2040.loadBootrom(bootromB1)
Bootrom B1 revision loaded
▼ PC reset to 0x10000000 (FLASH_START_ADDRESS)
CortexM0Core.executeInstruction() ← requestAnimationFrame @ 60 FPS
│ 2,083,333 cycles/frame (125 MHz ÷ 60)
├── GPIO listener → PinManager → visual component update
├── UART0/UART1 byte → onSerialData → Serial Monitor
├── ADC channel read → channelValues[ch] → analogRead()
└── I2C bus events → virtual I2C device callbacks
```
### Main Classes
| Class | File | Responsibility |
| ----- | ---- | -------------- |
| `RP2040Simulator` | `simulation/RP2040Simulator.ts` | Main wrapper: bootrom, GPIO, UART, ADC, I2C, SPI, RAF loop |
| `RP2040` | `rp2040js` (library) | Full hardware model: CPU, peripherals, memory map |
| `CortexM0Core` | `rp2040js` (library) | ARM Cortex-M0+ instruction decoder and executor |
| `PinManager` | `simulation/PinManager.ts` | Routes GPIO events to visual components |
---
## 4. Emulated Memory and Peripherals
### Memory Map
| Region | Base Address | Size | Description |
| ------ | ------------ | ---- | ----------- |
| Flash (XIP) | `0x10000000` | 16 MB | Program storage (firmware binary) |
| SRAM | `0x20000000` | 264 KB | Data RAM (stack, variables) |
| USB DPRAM | `0x50100000` | 4 KB | USB dual-port RAM |
| Bootrom | `0x00000000` | 4 KB | B1 revision bootrom |
### Peripherals Emulated by rp2040js
| Peripheral | Details |
| ---------- | ------- |
| GPIO | All 30 pins (GPIO0GPIO29), input/output |
| UART0 | TX/RX, baud rate auto-detection |
| UART1 | TX/RX, forwarded to same `onSerialData` callback |
| I2C0 | Master mode, virtual device callbacks |
| I2C1 | Master mode, virtual device callbacks |
| SPI0 | Default loopback; custom handler supported |
| SPI1 | Default loopback; custom handler supported |
| ADC | 5 channels (GPIO2629 + internal temp sensor, ch4) |
| PWM | Available on any GPIO |
| Timers | Used internally for `delay()`, `millis()` |
| Watchdog | Present (reads return 0) |
| PLL / Clock | Simulated at fixed 125 MHz |
### Peripherals NOT Emulated
- **WiFi/BLE** (Pico W: CYW43439 wireless chip)
- **USB device stack** (USB device enumeration)
- **DMA transfers** (DMA control registers return 0)
- **PIO state machines** (PIO registers return 0)
> PIO in particular is a significant limitation — `PicoLED`, `WS2812` (NeoPixel), `DHT`, and other timing-critical libraries that rely on PIO will not function.
---
## 5. Full Flow: Compile and Run
### 5.1 Compile the Sketch
The backend compiles for the Pico using the earlephilhower arduino-pico core:
```bash
# First-time setup — install the RP2040 board manager:
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
# Compile for Raspberry Pi Pico:
arduino-cli compile \
--fqbn rp2040:rp2040:rpipico \
--output-dir build/ \
my_sketch/
# Output: build/my_sketch.ino.bin ← raw binary
```
The Velxio backend automatically finds `sketch.ino.bin` (or `sketch.ino.uf2` as fallback), encodes it as base64, and sends it to the frontend in `CompileResponse.binary_content`.
### 5.2 Serial Redirection (Important)
The backend **automatically prepends** `#define Serial Serial1` to `sketch.ino` for RP2040 boards:
```python
# backend/app/services/arduino_cli.py
if "rp2040" in board_fqbn and write_name == "sketch.ino":
content = "#define Serial Serial1\n" + content
```
**Why?** The RP2040 bootrom uses UART0 for its own communication. To avoid conflicts, the Arduino `Serial` object is remapped to UART1. This means `Serial.print()` in your sketch goes through UART1, which the emulator also captures and shows in the Serial Monitor.
### 5.3 Minimal Sketch for Raspberry Pi Pico
```cpp
// Blink the built-in LED (GPIO 25)
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
Serial.println("Pico started!");
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("LED ON");
delay(500);
digitalWrite(LED_BUILTIN, LOW);
Serial.println("LED OFF");
delay(500);
}
```
### 5.4 Analog Read Example
```cpp
// Read a potentiometer on GPIO 26 (ADC channel 0)
void setup() {
Serial.begin(115200);
}
void loop() {
int raw = analogRead(A0); // GPIO26 → ADC ch0, returns 04095
float voltage = raw * 3.3f / 4095.0f;
Serial.print("ADC: ");
Serial.print(raw);
Serial.print(" → ");
Serial.print(voltage);
Serial.println(" V");
delay(200);
}
```
---
## 6. Binary Format and Loading
The RP2040 uses a **raw binary format** (`.bin`), not the Intel HEX format used by AVR:
```typescript
// RP2040Simulator.loadBinary(base64: string)
loadBinary(base64: string): void {
const binary = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
// Copy directly into flash at offset 0
this.rp2040.flash.set(binary, 0);
// Load bootrom and reset PC to 0x10000000
this.rp2040.loadBootrom(bootromB1);
}
```
**Flash memory layout** (as seen by the CPU):
```text
0x10000000 ← FLASH_START_ADDRESS — firmware entry point (PC reset here)
0x10000100 ... program .text section
0x10xxxxxx ... .rodata, .data, constants
0x20000000 ← SRAM — stack, heap, .bss
```
The bootrom B1 (`rp2040-bootrom.ts`) contains the RP2040's factory-programmed ROM code, required for correct flash XIP (eXecute In Place) initialization.
---
## 7. GPIO
All 30 GPIO pins (GPIO0GPIO29) are emulated. Each pin has an event listener attached at startup:
```typescript
// RP2040Simulator.ts — gpio listener setup
for (let gpioIdx = 0; gpioIdx < 30; gpioIdx++) {
const gpio = this.rp2040.gpio[gpioIdx];
gpio.addListener((state: GPIOPinState) => {
const isHigh = state === GPIOPinState.High;
this.pinManager.triggerPinChange(gpioIdx, isHigh);
if (this.onPinChangeWithTime) {
const timeMs = this.rp2040.clock.timeUs / 1000;
this.onPinChangeWithTime(gpioIdx, isHigh, timeMs);
}
});
}
```
### GPIO Input (Push Buttons / Switches)
External components can drive GPIO pins from the UI:
```typescript
// Set a pin HIGH or LOW from the simulator canvas (e.g. button press)
simulator.setPinState(gpioPin: number, state: boolean): void
// → rp2040.gpio[gpioPin].setInputValue(state)
```
This is equivalent to the signal propagated by a physical wire from a button to the GPIO pin.
### LED_BUILTIN
- `LED_BUILTIN` = GPIO 25 on both Pico and Pico W
- A pin change listener on GPIO 25 drives the on-board LED component
- `digitalWrite(LED_BUILTIN, HIGH)` → GPIO 25 goes high → LED visual component turns on
---
## 8. UART — Serial Monitor
The RP2040 has two UART controllers. Both are emulated and forwarded to the Serial Monitor:
| Channel | TX Pin | RX Pin | Arduino Object | Notes |
| ------- | ------ | ------ | -------------- | ----- |
| UART0 | GPIO0 | GPIO1 | `Serial1` | Used by bootrom; Arduino `Serial` redirected away |
| UART1 | GPIO4 | GPIO5 | `Serial` (redirected) | Receives Arduino `Serial.print()` |
### Serial Output (TX → Monitor)
```typescript
// Both UART0 and UART1 forward bytes to the same callback:
this.rp2040.uart[0].onByte = (value: number) => {
this.onSerialData?.(String.fromCharCode(value));
};
this.rp2040.uart[1].onByte = (value: number) => {
this.onSerialData?.(String.fromCharCode(value));
};
```
### Serial Input (Monitor → Sketch)
```typescript
// Feed text from the Serial Monitor input box into the sketch:
simulator.serialWrite("COMMAND\n");
// → bytes queued in rp2040.uart[0].feedByte(charCode)
// → sketch reads them via Serial.read() / Serial.readString()
```
---
## 9. ADC — Analog Inputs
The RP2040 has a 12-bit ADC (04095) with 5 channels:
| ADC Channel | GPIO Pin | Arduino Alias | Default Value | Notes |
| ----------- | -------- | ------------- | ------------- | ----- |
| 0 | GPIO26 | A0 | 2048 (~1.65 V) | General purpose |
| 1 | GPIO27 | A1 | 2048 (~1.65 V) | General purpose |
| 2 | GPIO28 | A2 | 2048 (~1.65 V) | General purpose |
| 3 | GPIO29 | A3 | 2048 (~1.65 V) | General purpose |
| 4 | — (internal) | — | 876 (~27 °C) | Internal temperature sensor |
### Injecting ADC Values from UI Components
When a potentiometer is wired to an ADC pin on the canvas, the simulator canvas reads the potentiometer's value and injects it:
```typescript
// SimulatorCanvas — potentiometer input event
simulator.setADCValue(channel: number, value: number): void
// value range: 04095 (clamped automatically)
// → rp2040.adc.channelValues[channel] = value
```
`analogRead(A0)` in the sketch reads `adc.channelValues[0]` directly.
---
## 10. I2C Virtual Devices
The RP2040 emulator supports virtual I2C devices — software objects that respond to I2C bus transactions from the firmware:
```typescript
export interface RP2040I2CDevice {
address: number; // 7-bit I2C address
writeByte(value: number): boolean; // return true = ACK
readByte(): number;
stop?(): void;
}
```
### Default Virtual Devices (Auto-Registered)
Three virtual I2C devices are registered automatically when a Pico program is loaded:
| Device | Address | Bus | Description |
| ------ | ------- | --- | ----------- |
| `VirtualDS1307` | 0x68 | I2C0 | Real-time clock (DS1307 compatible) |
| `VirtualTempSensor` | 0x48 | I2C0 | Temperature sensor (LM75 / TMP102 compatible) |
| `I2CMemoryDevice` | 0x50 | I2C0 | 24C04 EEPROM (512 bytes) |
### Default I2C0 Pins
| Signal | GPIO |
| ------ | ---- |
| SDA | GPIO4 |
| SCL | GPIO5 |
### I2C Bus Event Flow
```text
firmware: Wire.beginTransmission(0x68)
▼ I2C master asserts START + address
rp2040.i2c[0].onConnect(address=0x68)
▼ device found in registry
ds1307.writeByte(register_address)
Wire.requestFrom(0x68, 7)
▼ 7 read calls
ds1307.readByte() × 7
▼ I2C STOP
ds1307.stop()
```
---
## 11. SPI
Both SPI buses implement a **loopback** by default — bytes transmitted by the master are immediately echoed back as received bytes:
```typescript
// Default: loopback
this.rp2040.spi[0].onTransmit = (value: number) => {
this.rp2040.spi[0].completeTransmit(value);
};
```
### Default SPI0 Pins
| Signal | GPIO |
| ------ | ---- |
| MISO | GPIO16 |
| CS | GPIO17 |
| SCK | GPIO18 |
| MOSI | GPIO19 |
### Custom SPI Handler
A custom handler can replace the loopback for simulating specific SPI peripherals (displays, sensors):
```typescript
simulator.setSPIHandler(bus: 0 | 1, handler: (value: number) => number): void
// handler receives TX byte, returns RX byte
```
---
## 12. PWM
PWM is available on any GPIO pin through the RP2040's hardware PWM slices. The rp2040js library emulates the PWM peripheral registers, so `analogWrite()` and `ledcWrite()` work at the firmware level.
Visual PWM feedback (LED dimming) in the simulator canvas uses the `onPwmChange` callback from `PinManager`, which receives the duty cycle as a 0.01.0 float and sets `el.style.opacity` on the LED element.
---
## 13. Simulation Execution Loop
The simulation runs at **60 FPS** using `requestAnimationFrame`. Each frame executes enough CPU cycles to match a 125 MHz clock:
```
F_CPU = 125,000,000 Hz
CYCLE_NANOS = 1e9 / F_CPU = 8 ns/cycle
CYCLES_PER_FRAME = F_CPU / 60 = 2,083,333 cycles/frame
```
### WFI Optimization
When the ARM core executes a **WFI** (Wait For Interrupt) instruction — which Arduino uses during `delay()` and `sleep()` — the loop skips ahead to the next pending timer alarm instead of executing millions of NOP-equivalent cycles:
```typescript
while (cyclesDone < cyclesTarget) {
if (core.waiting) {
// CPU is sleeping — jump to next alarm
const jump = clock.nanosToNextAlarm;
if (jump <= 0) break;
clock.tick(jump);
cyclesDone += Math.ceil(jump / CYCLE_NANOS);
} else {
// Execute one ARM instruction
const cycles = core.executeInstruction();
clock.tick(cycles * CYCLE_NANOS);
cyclesDone += cycles;
}
}
```
This allows `delay(1000)` to complete in microseconds of real time instead of simulating all 125 million cycles.
### Variable Speed
The simulation speed can be adjusted:
```typescript
simulator.setSpeed(speed: number): void
// speed: 0.1 (10% = very slow, for debugging) to 10.0 (10× faster)
```
---
## 14. Pin Mapping
The Pico has 40 physical pins. Below is the mapping from board pin names to GPIO numbers:
| Board Pin | GPIO | Function | Board Pin | GPIO | Function |
| --------- | ---- | -------- | --------- | ---- | -------- |
| GP0 | 0 | UART0 TX | GP15 | 15 | SPI1 TX |
| GP1 | 1 | UART0 RX | GP16 | 16 | SPI0 MISO |
| GP2 | 2 | I2C1 SDA | GP17 | 17 | SPI0 CS |
| GP3 | 3 | I2C1 SCL | GP18 | 18 | SPI0 SCK |
| GP4 | 4 | I2C0 SDA | GP19 | 19 | SPI0 MOSI |
| GP5 | 5 | I2C0 SCL | GP20 | 20 | — |
| GP6 | 6 | SPI0 SCK | GP21 | 21 | — |
| GP7 | 7 | SPI0 TX | GP22 | 22 | — |
| GP8 | 8 | UART1 TX | GP26 | 26 | ADC ch0 (A0) |
| GP9 | 9 | UART1 RX | GP27 | 27 | ADC ch1 (A1) |
| GP10 | 10 | SPI1 SCK | GP28 | 28 | ADC ch2 (A2) |
| GP11 | 11 | SPI1 TX | GP29 | 29 | ADC ch3 (A3) |
| GP12 | 12 | SPI1 RX | **GP25** | **25** | **LED_BUILTIN** |
| GP13 | 13 | SPI1 CS | — | — | VBUS, VSYS, 3V3, GND |
| GP14 | 14 | SPI1 SCK | — | — | RUN, ADC_VREF |
> All GPIO pins support PWM. GPIO 022 and 2629 are available for general-purpose digital I/O. GPIO 2325 are internal (power control, LED, SMPS mode).
---
## 15. Oscilloscope / Logic Analyzer
The RP2040 emulator provides timestamps for every GPIO transition using the internal simulation clock:
```typescript
// Callback fires on every GPIO state change
simulator.onPinChangeWithTime = (pin: number, state: boolean, timeMs: number) => {
// timeMs = rp2040.clock.timeUs / 1000
// Accurate to ~8 ns (one ARM clock cycle)
};
```
The oscilloscope store (`useOscilloscopeStore`) subscribes to this callback and records samples:
```text
GPIO event fired (pin=25, state=HIGH, timeMs=123.456)
useOscilloscopeStore.pushSample(channelId, 123.456, true)
Oscilloscope waveform display updates
```
This enables the built-in logic analyzer to display accurate waveforms for signals like PWM, UART bit patterns, and I2C clock/data.
---
## 16. Known Limitations
| Limitation | Detail |
| ---------- | ------ |
| Single-core only | RP2040 is dual-core; emulator runs core 0 only. Code that uses `multicore_launch_core1()` will not execute on core 1 |
| No PIO | PIO state machines return 0. Libraries that depend on PIO (WS2812 NeoPixels, DHT sensors, I2S audio, quadrature encoders) will not work |
| No WiFi (Pico W) | The CYW43439 wireless chip is not emulated; `WiFi.begin()` will not connect |
| No USB device | USB HID, CDC, and MIDI device modes are not emulated |
| No DMA | DMA transfers return without moving data; memcpy-based alternatives work fine |
| No hardware FPU | ARM Cortex-M0+ has no floating-point unit; float operations are emulated in software (slower, but correct) |
| Timing accuracy | Emulation runs at variable speed; `micros()` and `millis()` track simulated time, not wall-clock time |
| Flash writes | `LittleFS`, `EEPROM.commit()`, and other flash-write operations may not persist across simulated resets |
| Serial redirect | `Serial` is mapped to `Serial1` (UART1) at compile time; code that uses `Serial1` directly alongside `Serial` may behave unexpectedly |
---
## 17. Tests
RP2040 emulation tests are in `frontend/src/__tests__/`:
```bash
cd frontend
npm test -- RP2040
```
### Test Suites
| Suite | Tests | What It Verifies |
| ----- | ----- | ---------------- |
| Lifecycle | 4 | `create`, `loadBinary`, `start`, `stop`, `reset` idempotency |
| GPIO | 8 | All 30 pins, state injection, listener callbacks, multiple listeners |
| ADC | 3 | Channel access (04), value clamping to 04095, `analogRead` integration |
| Serial | 4 | `onSerialData` callback, `serialWrite` input feed, both UART channels |
| I2C | 5 | Virtual device registration, address matching, read/write/stop callbacks |
| SPI | 2 | Custom handler registration, loopback default |
| Bootrom | 1 | Loads without errors, PC resets to `0x10000000` |
---
## 18. Differences vs Other Emulators
| Aspect | Raspberry Pi Pico (RP2040) | Arduino AVR | ESP32-C3 (RISC-V) | ESP32 (Xtensa) |
| ------ | -------------------------- | ----------- | ------------------ | -------------- |
| Engine | `rp2040js` (browser) | `avr8js` (browser) | `RiscVCore.ts` (browser) | QEMU backend (WebSocket) |
| Backend dependency | **No** | **No** | **No** | Yes |
| Architecture | ARM Cortex-M0+ | AVR 8-bit | RISC-V RV32IMC | Xtensa LX6 |
| Clock speed | 125 MHz | 16 MHz | 160 MHz | 240 MHz |
| Binary format | `.bin` (raw) | `.hex` (Intel HEX) | `.bin` (merged flash) | `.bin` (merged flash) |
| Arduino framework | Full | Full | Partial (no FreeRTOS) | Full |
| Serial | UART0 + UART1 | USART0 | UART0 | UART0 |
| ADC | 4 external + 1 temp | 6 channels (10-bit) | Not emulated | Emulated |
| I2C | 2 buses + virtual devices | Not emulated | Not emulated | Emulated |
| SPI | 2 buses (loopback) | Not emulated | Not emulated | Emulated |
| PIO | **Not emulated** | N/A | N/A | N/A |
| PWM | All GPIO pins | Timer-based | Not emulated | LEDC (mapped) |
| Oscilloscope support | Yes (8 ns resolution) | Yes | Yes | No |
| CI tests | Yes (Vitest) | Yes | Yes | No |
---
## 19. Key Files
| File | Description |
| ---- | ----------- |
| `frontend/src/simulation/RP2040Simulator.ts` | Main emulator wrapper (GPIO, UART, ADC, I2C, SPI, RAF loop) |
| `frontend/src/simulation/rp2040-bootrom.ts` | RP2040 B1 bootrom binary (required for flash XIP) |
| `frontend/src/simulation/PinManager.ts` | Routes GPIO events to visual components on canvas |
| `frontend/src/store/useSimulatorStore.ts` | Zustand store — `compileBoardProgram()`, `startBoard()`, serial wiring |
| `frontend/src/components/simulator/SimulatorCanvas.tsx` | Canvas rendering, pin subscriptions, ADC/button event forwarding |
| `frontend/src/components/components-wokwi/PiPicoWElement.ts` | Pico W SVG board rendering and pin coordinate map |
| `frontend/src/__tests__/RP2040Simulator.test.ts` | Unit and integration tests |
| `backend/app/services/arduino_cli.py` | Compilation — detects RP2040 FQBN, encodes `.bin` as base64, prepends `Serial1` redirect |
| `wokwi-libs/rp2040js/` | Local clone of the rp2040js library (ARM Cortex-M0+ emulator) |

View File

@ -114,3 +114,47 @@ void loop() {
| 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. |
---
## Community & Links
- **GitHub:** [github.com/davidmonterocrespo24/velxio](https://github.com/davidmonterocrespo24/velxio) — source code, issues, pull requests
- **Discord:** [YOUR_DISCORD_INVITE_URL] — ask questions, share projects, report issues
- **Live Demo:** [velxio.dev](https://velxio.dev)
---
## Documentation
### Orientation
- [Introduction](./intro.md) — What is Velxio and why use it
- [Getting Started](./getting-started.md) — This page
### Architecture & Internals
- [Architecture](./ARCHITECTURE.md) — High-level project architecture
- [Emulator Architecture](./emulator.md) — How CPU emulation works layer by layer
- [Wokwi Libraries Integration](./WOKWI_LIBS.md) — Local wokwi-elements, avr8js, rp2040js
### Boards & Emulation
- [RP2040 Emulation](./RP2040_EMULATION.md) — Raspberry Pi Pico / Pico W in-browser emulator (ARM Cortex-M0+)
- [Raspberry Pi 3 Emulation](./RASPBERRYPI3_EMULATION.md) — BCM2837 / QEMU raspi3b, Python + GPIO shim
- [ESP32 Emulation](./ESP32_EMULATION.md) — Full Xtensa QEMU emulation (GPIO, ADC, PWM, WiFi, I2C, SPI, RMT)
- [RISC-V Emulation](./RISCV_EMULATION.md) — ESP32-C3 / XIAO-C3 in-browser emulator
### Components & Examples
- [Components Reference](./components.md) — All 48+ supported electronic components
- [Example Projects](./examples/README.md) — Built-in example gallery
### API & Integrations
- [MCP Server](./MCP.md) — Model Context Protocol server for AI agent integration
### Project Status
- [Roadmap](./roadmap.md) — Implemented, in-progress, and planned features
- [Setup Complete](./SETUP_COMPLETE.md) — Feature implementation status log

View File

@ -26,11 +26,44 @@ Write Arduino C++ code, compile it with a real `arduino-cli` backend, and simula
---
## Quick Links
## Documentation
### Getting Started
- [Getting Started](./getting-started.md) — Quick setup guide (hosted, Docker, manual)
- [Introduction](./intro.md) — Overview, supported boards, quick links
### Architecture & Internals
- [Architecture](./ARCHITECTURE.md) — High-level project architecture
- [Emulator Architecture](./emulator.md) — How CPU emulation works layer by layer
- [Wokwi Libraries Integration](./WOKWI_LIBS.md) — Local wokwi-elements, avr8js, rp2040js setup
### Boards & Emulation
- [RP2040 Emulation](./RP2040_EMULATION.md) — Raspberry Pi Pico / Pico W in-browser emulator (ARM Cortex-M0+)
- [Raspberry Pi 3 Emulation](./RASPBERRYPI3_EMULATION.md) — BCM2837 / QEMU raspi3b, Python + GPIO shim
- [ESP32 Emulation](./ESP32_EMULATION.md) — Full Xtensa QEMU emulation (GPIO, ADC, PWM, WiFi, I2C, SPI, RMT)
- [RISC-V Emulation](./RISCV_EMULATION.md) — ESP32-C3 / XIAO-C3 in-browser emulator
### Components & Examples
- [Components Reference](./components.md) — All 48+ supported electronic components
- [Example Projects](./examples/README.md) — Built-in example gallery
### API & Integrations
- [MCP Server](./MCP.md) — Model Context Protocol server for AI agent integration
### Project Status
- [Roadmap](./roadmap.md) — Implemented, in-progress, and planned features
- [Setup Complete](./SETUP_COMPLETE.md) — Feature implementation status log
---
## Community & Links
- [Getting Started](./getting-started.md)
- [Emulator Architecture](./emulator.md)
- [Components Reference](./components.md)
- [Roadmap](./roadmap.md)
- [Live Demo](https://velxio.dev)
- [GitHub Repository](https://github.com/davidmonterocrespo24/velxio)
- [Discord](YOUR_DISCORD_INVITE_URL) — Ask questions, share projects, report issues

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 912 KiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -238,13 +238,13 @@
/* ── Hero ─────────────────────────────────────────────── */
.landing-hero {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-columns: 1fr 1.3fr;
align-items: center;
gap: 64px;
padding: 100px 80px 100px 80px;
max-width: 1240px;
gap: 56px;
padding: 80px 80px 80px 80px;
max-width: 1400px;
margin: 0 auto;
min-height: 85vh;
min-height: 90vh;
}
.hero-left {
@ -387,10 +387,10 @@
.hero-preview-img {
width: 100%;
max-width: 560px;
max-width: 100%;
height: auto;
border-radius: var(--radius);
filter: drop-shadow(0 16px 48px rgba(0, 0, 0, 0.7));
filter: drop-shadow(0 24px 64px rgba(0, 0, 0, 0.75));
}
/* ── Sections ─────────────────────────────────────────── */
@ -898,8 +898,9 @@
/* ── Responsive ───────────────────────────────────────── */
@media (max-width: 1024px) {
.landing-hero {
gap: 48px;
padding: 80px 48px;
grid-template-columns: 1fr 1.2fr;
gap: 40px;
padding: 72px 48px;
}
.landing-section {