velxio/frontend/src/pages/DocsPage.tsx

1583 lines
68 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 = () => (
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61-.546-1.385-1.335-1.755-1.335-1.755-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 21.795 24 17.295 24 12c0-6.63-5.37-12-12-12z" />
</svg>
);
/* ── 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<SectionId, SectionMeta> = {
'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 = () => (
<div className="docs-section">
<span className="docs-label">// overview</span>
<h1>Introduction</h1>
<p>
<strong>Velxio</strong> is a fully local, open-source Arduino emulator that runs entirely in your browser.
Write Arduino C++ code, compile it with a real <code>arduino-cli</code> backend, and simulate it using
true AVR8 / RP2040 CPU emulation with 48+ interactive electronic components, all without installing
any software on your machine.
</p>
<h2>Why Velxio?</h2>
<ul>
<li><strong>No installation required</strong> everything runs in the browser.</li>
<li><strong>Real emulation</strong> not a simplified model, but accurate AVR8 / RP2040 CPU emulation.</li>
<li><strong>Interactive components</strong> LEDs, buttons, potentiometers, displays, sensors, and more.</li>
<li><strong>Open-source</strong> inspect, modify, and self-host it yourself.</li>
</ul>
<h2>Supported Boards</h2>
<table>
<thead>
<tr><th>Board</th><th>CPU</th><th>Emulator</th></tr>
</thead>
<tbody>
<tr><td>Arduino Uno</td><td>ATmega328p @ 16 MHz</td><td>avr8js</td></tr>
<tr><td>Arduino Nano</td><td>ATmega328p @ 16 MHz</td><td>avr8js</td></tr>
<tr><td>Arduino Mega</td><td>ATmega2560 @ 16 MHz</td><td>avr8js</td></tr>
<tr><td>Raspberry Pi Pico</td><td>RP2040 @ 133 MHz</td><td>rp2040js</td></tr>
<tr><td>ESP32-C3 / XIAO C3 / C3 SuperMini</td><td>RV32IMC @ 160 MHz</td><td>Esp32C3Simulator (browser)</td></tr>
<tr><td>ESP32 / ESP32-S3</td><td>Xtensa LX6/LX7 @ 240 MHz</td><td>QEMU (lcgamboa)</td></tr>
</tbody>
</table>
<div className="docs-callout">
<strong>Live Demo:</strong>{' '}
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">velxio.dev</a>
{' '} no installation needed, open the editor and start simulating immediately.
</div>
</div>
);
const GettingStartedSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// setup</span>
<h1>Getting Started</h1>
<p>Follow these steps to simulate your first Arduino sketch.</p>
<h2>Option 1: Use the Hosted Version</h2>
<p>
No installation needed go to{' '}
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">https://velxio.dev</a>{' '}
and start coding immediately.
</p>
<h2>Option 2: Self-Host with Docker</h2>
<p>Run a single Docker command to start a fully local instance:</p>
<pre><code>{`docker run -d \\
--name velxio \\
-p 3080:80 \\
-v $(pwd)/data:/app/data \\
ghcr.io/davidmonterocrespo24/velxio:master`}</code></pre>
<p>Then open <strong>http://localhost:3080</strong> in your browser.</p>
<h2>Option 3: Manual Setup (Development)</h2>
<p><strong>Prerequisites:</strong> Node.js 18+, Python 3.12+, <code>arduino-cli</code></p>
<h3>1. Clone the repository</h3>
<pre><code>{`git clone https://github.com/davidmonterocrespo24/velxio.git
cd velxio`}</code></pre>
<h3>2. Start the backend</h3>
<pre><code>{`cd backend
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8001`}</code></pre>
<h3>3. Start the frontend</h3>
<pre><code>{`cd frontend
npm install
npm run dev`}</code></pre>
<p>Open <strong>http://localhost:5173</strong>.</p>
<h3>4. Set up arduino-cli (first time)</h3>
<pre><code>{`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`}</code></pre>
<h2>Your First Simulation</h2>
<ol>
<li><strong>Open the editor</strong> at <a href="https://velxio.dev/editor" target="_blank" rel="noopener noreferrer">velxio.dev/editor</a>.</li>
<li><strong>Select a board</strong> from the toolbar (e.g., <em>Arduino Uno</em>).</li>
<li><strong>Write Arduino code</strong> in the Monaco editor, for example:</li>
</ol>
<pre><code>{`void setup() {
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH);
delay(500);
digitalWrite(13, LOW);
delay(500);
}`}</code></pre>
<ol start={4}>
<li><strong>Click Compile</strong> the backend calls <code>arduino-cli</code> and returns a <code>.hex</code> file.</li>
<li><strong>Click Run</strong> the AVR8 emulator executes the compiled program.</li>
<li><strong>Add components</strong> using the component picker (click the <strong>+</strong> button on the canvas).</li>
<li><strong>Connect wires</strong> by clicking a component pin and then another pin.</li>
</ol>
<h2>Troubleshooting</h2>
<table>
<thead>
<tr><th>Problem</th><th>Solution</th></tr>
</thead>
<tbody>
<tr>
<td><code>arduino-cli: command not found</code></td>
<td>Install <code>arduino-cli</code> and add it to your PATH.</td>
</tr>
<tr>
<td>LED doesn't blink</td>
<td>Check the browser console for port listener errors; verify pin assignment in the component property dialog.</td>
</tr>
<tr>
<td>Serial Monitor is empty</td>
<td>Ensure <code>Serial.begin()</code> is called inside <code>setup()</code> before any <code>Serial.print()</code>.</td>
</tr>
<tr>
<td>Compilation errors</td>
<td>Check the compilation console at the bottom of the editor for full <code>arduino-cli</code> output.</td>
</tr>
</tbody>
</table>
</div>
);
const EmulatorSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// internals</span>
<h1>Emulator Architecture</h1>
<p>
Velxio uses <strong>real CPU emulation</strong> rather than a simplified model.
This document describes how each layer of the simulation works.
</p>
<h2>High-Level Data Flow</h2>
<pre><code>{`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`}</code></pre>
<h2>AVR8 Emulation (Arduino Uno / Nano / Mega)</h2>
<p>
The AVR backend uses <a href="https://github.com/wokwi/avr8js" target="_blank" rel="noopener noreferrer">avr8js</a>,
which implements a complete ATmega328p / ATmega2560 processor.
</p>
<h3>Execution Loop</h3>
<p>Each animation frame executes approximately 267,000 CPU cycles (16 MHz ÷ 60 FPS):</p>
<pre><code>{`avrInstruction(cpu); // decode and execute one AVR instruction
cpu.tick(); // advance peripheral timers and counters`}</code></pre>
<h3>Supported Peripherals</h3>
<table>
<thead>
<tr><th>Peripheral</th><th>Details</th></tr>
</thead>
<tbody>
<tr><td>GPIO</td><td>PORTB (pins 813), PORTC (A0A5), PORTD (pins 07)</td></tr>
<tr><td>Timer0 / Timer1 / Timer2</td><td><code>millis()</code>, <code>delay()</code>, PWM via <code>analogWrite()</code></td></tr>
<tr><td>USART</td><td>Full transmit and receive — powers the Serial Monitor</td></tr>
<tr><td>ADC</td><td>10-bit, 5 V reference on pins A0A5</td></tr>
<tr><td>SPI</td><td>Hardware SPI (enables ILI9341, SD card, etc.)</td></tr>
<tr><td>I2C (TWI)</td><td>Hardware I2C with virtual device bus</td></tr>
</tbody>
</table>
<h3>Pin Mapping</h3>
<table>
<thead>
<tr><th>Arduino Pin</th><th>AVR Port</th><th>Bit</th></tr>
</thead>
<tbody>
<tr><td>07</td><td>PORTD</td><td>07</td></tr>
<tr><td>813</td><td>PORTB</td><td>05</td></tr>
<tr><td>A0A5</td><td>PORTC</td><td>05</td></tr>
</tbody>
</table>
<h2>RP2040 Emulation (Raspberry Pi Pico)</h2>
<p>
The RP2040 backend uses <a href="https://github.com/wokwi/rp2040js" target="_blank" rel="noopener noreferrer">rp2040js</a>.
</p>
<ul>
<li>Real RP2040 emulation at 133 MHz</li>
<li>UART0 serial output displayed in the Serial Monitor</li>
<li>12-bit ADC on GPIO 2629 (A0A3) with 3.3 V reference</li>
</ul>
<h2>HEX File Format</h2>
<p>Arduino compilation produces <strong>Intel HEX</strong> format. The parser in <code>hexParser.ts</code>:</p>
<ol>
<li>Reads lines starting with <code>:</code></li>
<li>Extracts the address, record type, and data bytes</li>
<li>Returns a <code>Uint8Array</code> of program bytes</li>
<li><code>AVRSimulator</code> converts this to a <code>Uint16Array</code> (16-bit words, little-endian)</li>
</ol>
<h2>Key Source Files</h2>
<table>
<thead>
<tr><th>File</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr><td><code>frontend/src/simulation/AVRSimulator.ts</code></td><td>AVR8 CPU emulator wrapper</td></tr>
<tr><td><code>frontend/src/simulation/PinManager.ts</code></td><td>Maps Arduino pins to UI components</td></tr>
<tr><td><code>frontend/src/utils/hexParser.ts</code></td><td>Intel HEX parser</td></tr>
<tr><td><code>frontend/src/components/simulator/SimulatorCanvas.tsx</code></td><td>Canvas rendering</td></tr>
<tr><td><code>backend/app/services/arduino_cli.py</code></td><td>arduino-cli wrapper</td></tr>
<tr><td><code>backend/app/api/routes/compile.py</code></td><td>Compilation API endpoint</td></tr>
</tbody>
</table>
</div>
);
const ComponentsSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// reference</span>
<h1>Components Reference</h1>
<p>
Velxio ships with <strong>48+ interactive electronic components</strong> powered by{' '}
<a href="https://github.com/wokwi/wokwi-elements" target="_blank" rel="noopener noreferrer">wokwi-elements</a>.
All components can be placed on the simulation canvas, connected with wires, and interact with your Arduino sketch in real time.
</p>
<h2>Adding Components</h2>
<ol>
<li>Click the <strong>+</strong> button on the simulation canvas.</li>
<li>Use <strong>search</strong> or browse by <strong>category</strong> in the component picker.</li>
<li>Click a component to place it on the canvas.</li>
<li><strong>Drag</strong> to reposition; click to open the <strong>Property Dialog</strong>.</li>
</ol>
<h2>Connecting Components</h2>
<ol>
<li>Click a <strong>pin</strong> on any component — a wire starts from that pin.</li>
<li>Click a <strong>destination pin</strong> to complete the connection.</li>
<li>Wires are <strong>color-coded</strong> by signal type:</li>
</ol>
<table>
<thead>
<tr><th>Color</th><th>Signal Type</th></tr>
</thead>
<tbody>
<tr><td><span className="wire-dot" style={{ background: '#ef4444' }} /> Red</td><td>VCC (power)</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#374151' }} /> Black</td><td>GND (ground)</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#3b82f6' }} /> Blue</td><td>Analog</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#22c55e' }} /> Green</td><td>Digital</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#a855f7' }} /> Purple</td><td>PWM</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#eab308' }} /> Gold</td><td>I2C (SDA/SCL)</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#f97316' }} /> Orange</td><td>SPI (MOSI/MISO/SCK)</td></tr>
<tr><td><span className="wire-dot" style={{ background: '#06b6d4' }} /> Cyan</td><td>USART (TX/RX)</td></tr>
</tbody>
</table>
<h2>Component Categories</h2>
<h3>Output</h3>
<table>
<thead><tr><th>Component</th><th>Description</th></tr></thead>
<tbody>
<tr><td>LED</td><td>Single LED with configurable color</td></tr>
<tr><td>RGB LED</td><td>Three-color LED (red, green, blue channels)</td></tr>
<tr><td>7-Segment Display</td><td>Single digit numeric display</td></tr>
<tr><td>LCD 16×2</td><td>2-line character LCD (I2C or parallel)</td></tr>
<tr><td>LCD 20×4</td><td>4-line character LCD</td></tr>
<tr><td>ILI9341 TFT</td><td>240×320 color TFT display (SPI)</td></tr>
<tr><td>Buzzer</td><td>Passive piezo buzzer</td></tr>
<tr><td>NeoPixel</td><td>Individually addressable RGB LED strip</td></tr>
</tbody>
</table>
<h3>Input</h3>
<table>
<thead><tr><th>Component</th><th>Description</th></tr></thead>
<tbody>
<tr><td>Push Button</td><td>Momentary push button</td></tr>
<tr><td>Slide Switch</td><td>SPDT slide switch</td></tr>
<tr><td>Potentiometer</td><td>Analog voltage divider (ADC input)</td></tr>
<tr><td>Rotary Encoder</td><td>Incremental rotary encoder</td></tr>
<tr><td>Keypad 4×4</td><td>16-button matrix keypad</td></tr>
<tr><td>Joystick</td><td>Dual-axis analog joystick</td></tr>
</tbody>
</table>
<h3>Sensors</h3>
<table>
<thead><tr><th>Component</th><th>Description</th></tr></thead>
<tbody>
<tr><td>HC-SR04</td><td>Ultrasonic distance sensor</td></tr>
<tr><td>DHT22</td><td>Temperature and humidity sensor</td></tr>
<tr><td>PIR Motion</td><td>Passive infrared motion sensor</td></tr>
<tr><td>Photoresistor</td><td>Light-dependent resistor (LDR)</td></tr>
<tr><td>IR Receiver</td><td>38 kHz infrared receiver</td></tr>
</tbody>
</table>
<h3>Passive Components</h3>
<table>
<thead><tr><th>Component</th><th>Description</th></tr></thead>
<tbody>
<tr><td>Resistor</td><td>Standard resistor (configurable value)</td></tr>
<tr><td>Capacitor</td><td>Electrolytic capacitor</td></tr>
<tr><td>Inductor</td><td>Coil inductor</td></tr>
</tbody>
</table>
<h2>Component Properties</h2>
<p>Each component has a <strong>Property Dialog</strong> accessible by clicking it on the canvas:</p>
<table>
<thead><tr><th>Property</th><th>Description</th></tr></thead>
<tbody>
<tr><td>Arduino Pin</td><td>The digital or analog pin this component is connected to</td></tr>
<tr><td>Color</td><td>Visual color (LEDs, wires)</td></tr>
<tr><td>Value</td><td>Component value (e.g., resistance in Ω)</td></tr>
<tr><td>Rotation</td><td>Rotate in 90° increments</td></tr>
<tr><td>Delete</td><td>Remove the component from the canvas</td></tr>
</tbody>
</table>
</div>
);
const RoadmapSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// future</span>
<h1>Roadmap</h1>
<p>Features that are implemented, in progress, and planned for future releases of Velxio.</p>
<h2>✅ Implemented</h2>
<ul>
<li>Monaco Editor with C++ syntax highlighting, autocomplete, and minimap</li>
<li>Multi-file workspace — create, rename, delete, and switch between files</li>
<li>Arduino compilation via <code>arduino-cli</code> (multi-file sketch support)</li>
<li>Real ATmega328p / ATmega2560 emulation at 16 MHz via avr8js</li>
<li>Full GPIO, Timers, USART, ADC, SPI, I2C support</li>
<li>Real RP2040 emulation at 133 MHz via rp2040js</li>
<li>48+ wokwi-elements components with component picker</li>
<li>Wire creation, orthogonal routing, and segment-based editing</li>
<li>Serial Monitor with auto baud-rate detection and send support</li>
<li>Library Manager (browse and install Arduino libraries)</li>
<li>Email/password and Google OAuth authentication</li>
<li>Project save, update, delete with permanent URLs</li>
<li>8 built-in example projects</li>
<li>Single-container Docker image published to GHCR + Docker Hub</li>
</ul>
<h2>🔄 In Progress</h2>
<ul>
<li>Functional wire connections — electrical signal routing and validation</li>
<li>Wire connection error handling — detect short circuits and invalid connections</li>
</ul>
<h2>🗓 Planned — Near-Term</h2>
<ul>
<li>Undo / redo for code edits and canvas changes</li>
<li>Export / import projects as <code>.zip</code> files</li>
<li>More boards — ESP32, Arduino Leonardo</li>
<li>Breadboard — place components with automatic wire routing</li>
</ul>
<h2>🗓 Planned — Mid-Term</h2>
<ul>
<li>TypeDoc API documentation — auto-generated from source code</li>
<li>GitHub Pages docs site — automatic deployment on push to <code>main</code></li>
<li>More sensor simulations — HC-SR04, DHT22, IR receiver</li>
<li>EEPROM emulation — persistent read/write across simulation restarts</li>
<li>Oscilloscope component — plot analog pin voltages over time</li>
</ul>
<h2>🗓 Planned — Long-Term</h2>
<ul>
<li>Multiplayer — share and co-edit simulations in real time</li>
<li>Embedded tutorial system — step-by-step guided projects inside the editor</li>
<li>Custom component SDK — define new components with a JSON/TypeScript API</li>
<li>Mobile / tablet support — responsive layout for touch devices</li>
</ul>
<div className="docs-callout">
<strong>Want to contribute?</strong>{' '}
Feature requests, bug reports, and pull requests are welcome at{' '}
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer">github.com/davidmonterocrespo24/velxio</a>.
</div>
</div>
);
/* ── Architecture Section ─────────────────────────────── */
const ArchitectureSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// system design</span>
<h1>Project Architecture</h1>
<p>
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.
</p>
<h2>High-Level Overview</h2>
<pre><code>{`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`}</code></pre>
<h2>Data Flows</h2>
<h3>1. Compilation</h3>
<pre><code>{`Click "Compile"
→ EditorToolbar reads all workspace files
→ POST /api/compile/ { files, board_fqbn }
→ Backend: ArduinoCLIService writes temp dir
→ arduino-cli compile --fqbn <board> --output-dir build/
→ Returns hex_content (Intel HEX string)
→ useSimulatorStore.setCompiledHex() → loadHex()`}</code></pre>
<h3>2. Simulation Loop</h3>
<pre><code>{`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`}</code></pre>
<h3>3. Input Components</h3>
<pre><code>{`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`}</code></pre>
<h2>Key Frontend Stores (Zustand)</h2>
<table>
<thead>
<tr><th>Store</th><th>Key State</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr><td><code>useEditorStore</code></td><td>files[], activeFileId</td><td>Multi-file Monaco workspace</td></tr>
<tr><td><code>useSimulatorStore</code></td><td>simulator, components, wires, running</td><td>Simulation + canvas state</td></tr>
<tr><td><code>useAuthStore</code></td><td>user, token</td><td>Auth (persisted localStorage)</td></tr>
<tr><td><code>useProjectStore</code></td><td>projectId, slug</td><td>Currently open project</td></tr>
</tbody>
</table>
<h2>Backend Routes</h2>
<table>
<thead>
<tr><th>Route</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>POST /api/compile/</code></td><td>Compile sketch files → Intel HEX / UF2</td></tr>
<tr><td><code>GET /api/compile/boards</code></td><td>List available boards</td></tr>
<tr><td><code>GET/POST /api/auth/*</code></td><td>Email/password + Google OAuth</td></tr>
<tr><td><code>GET/POST /api/projects/*</code></td><td>CRUD project persistence (SQLite)</td></tr>
<tr><td><code>GET /api/libraries/*</code></td><td>Arduino Library Manager integration</td></tr>
<tr><td><code>GET /health</code></td><td>Health check endpoint</td></tr>
</tbody>
</table>
<h2>Wire System</h2>
<p>Wires are stored as objects with start/end endpoints tied to component pin positions:</p>
<pre><code>{`{
id: string
start: { componentId, pinName, x, y }
end: { componentId, pinName, x, y }
color: string
signalType: 'digital' | 'analog' | 'power-vcc' | 'power-gnd'
}`}</code></pre>
<ul>
<li>Orthogonal routing — no diagonal segments</li>
<li>Segment drag — drag perpendicular to segment orientation</li>
<li>Auto-update — wire positions recalculate when components move</li>
<li>Grid snapping — 20 px grid for all wire endpoints</li>
</ul>
<div className="docs-callout">
<strong>Full details:</strong>{' '}
See{' '}
<a href={`${GITHUB_URL}/blob/master/docs/ARCHITECTURE.md`} target="_blank" rel="noopener noreferrer">
docs/ARCHITECTURE.md
</a>{' '}
in the repository.
</div>
</div>
);
/* ── Wokwi Libraries Section ──────────────────────────── */
const WokwiLibsSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// open-source libs</span>
<h1>Wokwi Libraries</h1>
<p>
Velxio uses official Wokwi open-source repositories cloned locally in <code>wokwi-libs/</code>.
This gives you up-to-date, compatible emulation engines and visual components without npm registry
dependencies.
</p>
<h2>Cloned Repositories</h2>
<table>
<thead>
<tr><th>Library</th><th>Location</th><th>Purpose</th></tr>
</thead>
<tbody>
<tr>
<td><a href="https://github.com/wokwi/wokwi-elements" target="_blank" rel="noopener noreferrer">wokwi-elements</a></td>
<td><code>wokwi-libs/wokwi-elements/</code></td>
<td>48+ Lit Web Components (LEDs, LCDs, sensors, buttons…)</td>
</tr>
<tr>
<td><a href="https://github.com/wokwi/avr8js" target="_blank" rel="noopener noreferrer">avr8js</a></td>
<td><code>wokwi-libs/avr8js/</code></td>
<td>ATmega328p / ATmega2560 CPU emulator at 16 MHz</td>
</tr>
<tr>
<td><a href="https://github.com/wokwi/rp2040js" target="_blank" rel="noopener noreferrer">rp2040js</a></td>
<td><code>wokwi-libs/rp2040js/</code></td>
<td>Raspberry Pi Pico (RP2040) emulator</td>
</tr>
</tbody>
</table>
<h2>Vite Configuration</h2>
<p><code>frontend/vite.config.ts</code> uses path aliases so imports resolve to the local builds:</p>
<pre><code>{`resolve: {
alias: {
'avr8js': '../wokwi-libs/avr8js/dist/esm',
'@wokwi/elements': '../wokwi-libs/wokwi-elements/dist/esm',
},
}`}</code></pre>
<h2>Updating the Libraries</h2>
<h3>All at once (recommended)</h3>
<pre><code>{`# Windows
update-wokwi-libs.bat`}</code></pre>
<h3>Manually</h3>
<pre><code>{`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`}</code></pre>
<h3>After updating wokwi-elements</h3>
<p>Regenerate component metadata so new components appear in the picker:</p>
<pre><code>{`cd frontend
npx tsx ../scripts/generate-component-metadata.ts`}</code></pre>
<h2>Available Wokwi Components (48)</h2>
<table>
<thead>
<tr><th>Category</th><th>Components</th></tr>
</thead>
<tbody>
<tr><td>Boards</td><td>Arduino Uno, Mega, Nano, ESP32 DevKit</td></tr>
<tr><td>Sensors</td><td>DHT22, HC-SR04, PIR, Photoresistor, NTC, Joystick</td></tr>
<tr><td>Displays</td><td>LCD 16×2, LCD 20×4, 7-Segment</td></tr>
<tr><td>Input</td><td>Push button, 6mm button, Slide switch, DIP switch 8, Potentiometer</td></tr>
<tr><td>Output</td><td>LED, RGB LED, LED bar graph, Buzzer, NeoPixel</td></tr>
<tr><td>Motors</td><td>Servo, Stepper motor</td></tr>
<tr><td>Passive</td><td>Resistor, Slide potentiometer, LED ring, Matrix keypad</td></tr>
<tr><td>Other</td><td>IR receiver, DS1307 RTC, breadboards, etc.</td></tr>
</tbody>
</table>
<h2>How avr8js Powers the Simulation</h2>
<pre><code>{`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);
}`}</code></pre>
<div className="docs-callout">
<strong>Full details:</strong>{' '}
See{' '}
<a href={`${GITHUB_URL}/blob/master/docs/WOKWI_LIBS.md`} target="_blank" rel="noopener noreferrer">
docs/WOKWI_LIBS.md
</a>{' '}
in the repository.
</div>
</div>
);
/* ── MCP Server Section ───────────────────────────────── */
const McpSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// AI integration</span>
<h1>MCP Server</h1>
<p>
Velxio exposes a{' '}
<a href="https://modelcontextprotocol.io/" target="_blank" rel="noopener noreferrer">
Model Context Protocol
</a>{' '}
(MCP) server that lets AI agents (Claude, Cursor, and others) create circuits,
generate code, and compile Arduino sketches directly.
</p>
<h2>Available Tools</h2>
<table>
<thead>
<tr><th>Tool</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>compile_project</code></td><td>Compile Arduino sketch files → Intel HEX / binary</td></tr>
<tr><td><code>run_project</code></td><td>Compile and mark artifact as simulation-ready</td></tr>
<tr><td><code>import_wokwi_json</code></td><td>Parse a Wokwi <code>diagram.json</code> → Velxio circuit</td></tr>
<tr><td><code>export_wokwi_json</code></td><td>Serialise a Velxio circuit → Wokwi <code>diagram.json</code></td></tr>
<tr><td><code>create_circuit</code></td><td>Create a new circuit definition</td></tr>
<tr><td><code>update_circuit</code></td><td>Merge changes into an existing circuit</td></tr>
<tr><td><code>generate_code_files</code></td><td>Generate starter <code>.ino</code> code from a circuit</td></tr>
</tbody>
</table>
<h2>Transport Options</h2>
<h3>1. stdio — Claude Desktop / CLI agents</h3>
<pre><code>{`cd backend
python mcp_server.py`}</code></pre>
<p>Claude Desktop config (<code>~/.claude/claude_desktop_config.json</code>):</p>
<pre><code>{`{
"mcpServers": {
"velxio": {
"command": "python",
"args": ["/absolute/path/to/velxio/backend/mcp_server.py"]
}
}
}`}</code></pre>
<h3>2. SSE / HTTP — Cursor IDE / web agents</h3>
<pre><code>{`cd backend
python mcp_sse_server.py --port 8002`}</code></pre>
<p>MCP client config:</p>
<pre><code>{`{
"mcpServers": {
"velxio": { "url": "http://localhost:8002/sse" }
}
}`}</code></pre>
<h2>Circuit Data Format</h2>
<p>Velxio circuits are plain JSON objects:</p>
<pre><code>{`{
"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" }
]
}`}</code></pre>
<h3>Supported Board FQBNs</h3>
<table>
<thead>
<tr><th>Board</th><th>FQBN</th></tr>
</thead>
<tbody>
<tr><td>Arduino Uno</td><td><code>arduino:avr:uno</code></td></tr>
<tr><td>Arduino Mega</td><td><code>arduino:avr:mega</code></td></tr>
<tr><td>Arduino Nano</td><td><code>arduino:avr:nano</code></td></tr>
<tr><td>Raspberry Pi Pico</td><td><code>rp2040:rp2040:rpipico</code></td></tr>
</tbody>
</table>
<h2>Example — Blink LED from Scratch</h2>
<pre><code>{`// 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": "<result from Step 1>",
"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"
}
}`}</code></pre>
<h2>Setup</h2>
<pre><code>{`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`}</code></pre>
<div className="docs-callout">
<strong>Full reference:</strong>{' '}
See{' '}
<a href={`${GITHUB_URL}/blob/master/docs/MCP.md`} target="_blank" rel="noopener noreferrer">
docs/MCP.md
</a>{' '}
in the repository.
</div>
</div>
);
/* ── Setup / Project Status Section ──────────────────── */
const SetupSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// project status</span>
<h1>Project Status</h1>
<p>A comprehensive overview of all features currently implemented in Velxio.</p>
<h2>AVR Emulation (avr8js)</h2>
<table>
<thead><tr><th>Feature</th><th>Status</th></tr></thead>
<tbody>
<tr><td>ATmega328p CPU at 16 MHz</td><td>✅ Working</td></tr>
<tr><td>Timer0, Timer1, Timer2</td><td>✅ Working</td></tr>
<tr><td>USART (Serial)</td><td>✅ Working</td></tr>
<tr><td>ADC (<code>analogRead</code>)</td><td>✅ Working</td></tr>
<tr><td>Full GPIO (PORTB / PORTC / PORTD)</td><td>✅ Working</td></tr>
<tr><td>~60 FPS loop (267k cycles/frame)</td><td>✅ Working</td></tr>
<tr><td>Speed control (0.1× 10×)</td><td>✅ Working</td></tr>
<tr><td>PWM monitoring (6 channels)</td><td>✅ Working</td></tr>
<tr><td>External pin injection (inputs)</td><td>✅ Working</td></tr>
</tbody>
</table>
<h2>Component System (48+)</h2>
<table>
<thead><tr><th>Feature</th><th>Status</th></tr></thead>
<tbody>
<tr><td>Automatic discovery via AST</td><td>✅ 48 components detected</td></tr>
<tr><td>ComponentPickerModal with search</td><td>✅ Working</td></tr>
<tr><td>9 categories with filters</td><td>✅ Working</td></tr>
<tr><td>Generic DynamicComponent renderer</td><td>✅ Working</td></tr>
<tr><td>Drag-and-drop on canvas</td><td>✅ Working</td></tr>
<tr><td>Rotation (90° increments)</td><td>✅ Working</td></tr>
<tr><td>Properties dialog (single-click)</td><td>✅ Working</td></tr>
<tr><td>Pin overlay (clickable cyan dots)</td><td>✅ Working</td></tr>
</tbody>
</table>
<h2>Interactive Parts (16 simulated)</h2>
<table>
<thead><tr><th>Part</th><th>Type</th><th>Status</th></tr></thead>
<tbody>
<tr><td>LED</td><td>Output</td><td>✅</td></tr>
<tr><td>RGB LED</td><td>Output (digital + PWM)</td><td>✅</td></tr>
<tr><td>LED Bar Graph (10 LEDs)</td><td>Output</td><td>✅</td></tr>
<tr><td>7-Segment Display</td><td>Output</td><td>✅</td></tr>
<tr><td>Pushbutton</td><td>Input</td><td>✅</td></tr>
<tr><td>Pushbutton 6mm</td><td>Input</td><td>✅</td></tr>
<tr><td>Slide Switch</td><td>Input</td><td>✅</td></tr>
<tr><td>DIP Switch 8</td><td>Input</td><td>✅</td></tr>
<tr><td>Potentiometer</td><td>Input (ADC)</td><td>✅</td></tr>
<tr><td>Slide Potentiometer</td><td>Input (ADC)</td><td>✅</td></tr>
<tr><td>Photoresistor</td><td>Input / Output</td><td>✅</td></tr>
<tr><td>Analog Joystick</td><td>Input (ADC + digital)</td><td>✅</td></tr>
<tr><td>Servo</td><td>Output</td><td>✅</td></tr>
<tr><td>Buzzer</td><td>Output (Web Audio)</td><td>✅</td></tr>
<tr><td>LCD 1602</td><td>Output (full HD44780)</td><td>✅</td></tr>
<tr><td>LCD 2004</td><td>Output (full HD44780)</td><td>✅</td></tr>
</tbody>
</table>
<h2>Wire System</h2>
<table>
<thead><tr><th>Feature</th><th>Status</th></tr></thead>
<tbody>
<tr><td>Pin-to-pin creation with click</td><td>✅ Working</td></tr>
<tr><td>Real-time preview (green dashed)</td><td>✅ Working</td></tr>
<tr><td>Orthogonal routing (no diagonals)</td><td>✅ Working</td></tr>
<tr><td>Segment editing (perpendicular drag)</td><td>✅ Working</td></tr>
<tr><td>8 colours by signal type</td><td>✅ Working</td></tr>
<tr><td>Auto-update when moving components</td><td>✅ Working</td></tr>
<tr><td>Grid snapping (20 px)</td><td>✅ Working</td></tr>
<tr><td>Wire selection and deletion</td><td>✅ Working</td></tr>
</tbody>
</table>
<h2>Example Projects (8)</h2>
<table>
<thead><tr><th>Example</th><th>Category</th><th>Difficulty</th></tr></thead>
<tbody>
<tr><td>Blink LED</td><td>Basics</td><td>Beginner</td></tr>
<tr><td>Traffic Light</td><td>Basics</td><td>Beginner</td></tr>
<tr><td>Button Control</td><td>Basics</td><td>Beginner</td></tr>
<tr><td>Fade LED (PWM)</td><td>Basics</td><td>Beginner</td></tr>
<tr><td>Serial Hello World</td><td>Communication</td><td>Beginner</td></tr>
<tr><td>RGB LED Colors</td><td>Basics</td><td>Intermediate</td></tr>
<tr><td>Simon Says Game</td><td>Games</td><td>Advanced</td></tr>
<tr><td>LCD 20×4 Display</td><td>Displays</td><td>Intermediate</td></tr>
</tbody>
</table>
<h2>Troubleshooting</h2>
<table>
<thead><tr><th>Problem</th><th>Solution</th></tr></thead>
<tbody>
<tr>
<td>Components not displayed</td>
<td><pre style={{margin:0}}><code>cd wokwi-libs/wokwi-elements{'\n'}npm run build</code></pre></td>
</tr>
<tr>
<td><code>Cannot find module 'avr8js'</code></td>
<td><pre style={{margin:0}}><code>cd wokwi-libs/avr8js{'\n'}npm install && npm run build</code></pre></td>
</tr>
<tr>
<td>LED doesn't blink</td>
<td>Compile first, then click Run. Check pin assignment in the component property dialog.</td>
</tr>
<tr>
<td>New component not in picker</td>
<td><pre style={{margin:0}}><code>cd frontend{'\n'}npx tsx ../scripts/generate-component-metadata.ts</code></pre></td>
</tr>
</tbody>
</table>
<div className="docs-callout">
<strong>Full status:</strong>{' '}
See{' '}
<a href={`${GITHUB_URL}/blob/master/docs/SETUP_COMPLETE.md`} target="_blank" rel="noopener noreferrer">
docs/SETUP_COMPLETE.md
</a>{' '}
in the repository.
</div>
</div>
);
const RiscVEmulationSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// risc-v</span>
<h1>RISC-V Emulation (ESP32-C3)</h1>
<p>
ESP32-C3, XIAO ESP32-C3, and C3 SuperMini boards use a <strong>RISC-V RV32IMC</strong> 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.
</p>
<h2>Supported Boards</h2>
<table>
<thead>
<tr><th>Board</th><th>CPU</th><th>Flash</th><th>RAM</th></tr>
</thead>
<tbody>
<tr><td>ESP32-C3</td><td>RV32IMC @ 160 MHz</td><td>4 MB</td><td>384 KB DRAM</td></tr>
<tr><td>XIAO ESP32-C3</td><td>RV32IMC @ 160 MHz</td><td>4 MB</td><td>384 KB DRAM</td></tr>
<tr><td>C3 SuperMini</td><td>RV32IMC @ 160 MHz</td><td>4 MB</td><td>384 KB DRAM</td></tr>
</tbody>
</table>
<h2>Memory Map</h2>
<table>
<thead>
<tr><th>Region</th><th>Base Address</th><th>Size</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td>IROM (Flash)</td><td><code>0x42000000</code></td><td>4 MB</td><td>Code stored in flash</td></tr>
<tr><td>DROM (Flash R/O)</td><td><code>0x3C000000</code></td><td>4 MB</td><td>Read-only data in flash</td></tr>
<tr><td>DRAM</td><td><code>0x3FC80000</code></td><td>384 KB</td><td>Data RAM (stack + heap)</td></tr>
<tr><td>IRAM</td><td><code>0x4037C000</code></td><td>384 KB</td><td>Instruction RAM (copied from flash)</td></tr>
<tr><td>UART0</td><td><code>0x60000000</code></td><td>1 KB</td><td>Serial port 0 (GPIO 20/21)</td></tr>
<tr><td>GPIO</td><td><code>0x60004000</code></td><td>512 B</td><td>GPIO output / input / enable</td></tr>
</tbody>
</table>
<h2>ISA Support</h2>
<ul>
<li><strong>RV32I</strong> Full base integer instruction set (ALU, load/store, branches, JAL/JALR)</li>
<li><strong>RV32M</strong> Multiply/divide: MUL, MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU</li>
<li><strong>RV32C</strong> 16-bit compressed instructions: C.LI, C.ADDI, C.LUI, C.J, C.JAL, C.BEQZ,
C.BNEZ, C.MV, C.ADD, C.JR, C.JALR, C.LW, C.SW, C.LWSP, C.SWSP, C.SLLI, C.ADDI4SPN</li>
</ul>
<h2>Compilation Flow</h2>
<p>When you click <strong>Compile + Run</strong> for an ESP32-C3 board:</p>
<ol>
<li>The backend compiles your sketch with <code>arduino-cli</code> using the <code>esp32:esp32</code> core.</li>
<li>The resulting binary is a <strong>merged 4 MB flash image</strong>: bootloader at <code>0x1000</code>,
partition table at <code>0x8000</code>, application at <code>0x10000</code>.</li>
<li>The frontend's <code>esp32ImageParser.ts</code> finds the app at offset <code>0x10000</code>,
reads the 24-byte ESP32 image header (magic <code>0xE9</code>), and extracts all segments
(load address + data).</li>
<li>Each segment is written into the correct memory region (IROM, DROM, DRAM, or IRAM) of
the <code>Esp32C3Simulator</code>.</li>
<li>The CPU starts executing from the entry point specified in the image header.</li>
</ol>
<h2>GPIO Registers</h2>
<table>
<thead>
<tr><th>Register</th><th>Offset</th><th>Description</th></tr>
</thead>
<tbody>
<tr><td><code>GPIO_OUT_REG</code></td><td><code>+0x04</code></td><td>Current output value</td></tr>
<tr><td><code>GPIO_OUT_W1TS</code></td><td><code>+0x08</code></td><td>Set bits (write 1 to set)</td></tr>
<tr><td><code>GPIO_OUT_W1TC</code></td><td><code>+0x0C</code></td><td>Clear bits (write 1 to clear)</td></tr>
<tr><td><code>GPIO_ENABLE_REG</code></td><td><code>+0x20</code></td><td>Output enable</td></tr>
<tr><td><code>GPIO_IN_REG</code></td><td><code>+0x3C</code></td><td>Input pin states</td></tr>
</tbody>
</table>
<h2>UART0</h2>
<p>
Writing a byte to <code>0x60000000</code> (UART0 FIFO) triggers the <code>onSerialData</code> callback,
which streams characters to the Serial Monitor. Reading from the same address pops from the receive
FIFO (used by <code>Serial.read()</code>). The UART status register always returns 0 (TX ready).
</p>
<h2>Key Source Files</h2>
<table>
<thead>
<tr><th>File</th><th>Role</th></tr>
</thead>
<tbody>
<tr><td><code>simulation/RiscVCore.ts</code></td><td>RV32IMC interpreter (step, MMIO hooks)</td></tr>
<tr><td><code>simulation/Esp32C3Simulator.ts</code></td><td>ESP32-C3 peripherals, memory map, lifecycle</td></tr>
<tr><td><code>utils/esp32ImageParser.ts</code></td><td>Parses merged flash image, extracts segments</td></tr>
</tbody>
</table>
<div className="docs-callout">
<strong>Full details:</strong>{' '}
<a href={`${GITHUB_URL}/blob/master/docs/RISCV_EMULATION.md`} target="_blank" rel="noopener noreferrer">
docs/RISCV_EMULATION.md
</a>{' '}
in the repository.
</div>
</div>
);
const Esp32EmulationSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// xtensa</span>
<h1>ESP32 Emulation (Xtensa)</h1>
<p>
ESP32 and ESP32-S3 boards use an <strong>Xtensa LX6 / LX7</strong> architecture. Because no
production-quality Xtensa emulator is available as pure JavaScript, Velxio uses a
<strong> QEMU-based backend</strong> for these boards — the lcgamboa fork with
libqemu-xtensa, compiled to a native binary and served by the FastAPI backend.
</p>
<div className="docs-callout">
<strong>Note:</strong> This section applies only to <strong>ESP32</strong> and <strong>ESP32-S3</strong> (Xtensa).
For ESP32-C3, XIAO ESP32-C3, and C3 SuperMini (RISC-V), see{' '}
<strong>RISC-V Emulation (ESP32-C3)</strong> in the sidebar — those boards run entirely in the browser.
</div>
<h2>How It Works</h2>
<ol>
<li>Arduino sketch is compiled by <code>arduino-cli</code> to an ESP32 <code>.bin</code> flash image.</li>
<li>Frontend sends the binary to the backend via WebSocket (<code>Esp32Bridge</code>).</li>
<li>Backend spawns a QEMU process with the lcgamboa Xtensa plugin, loads the image.</li>
<li>GPIO and UART events are forwarded over the WebSocket back to the browser.</li>
<li>Frontend updates component states (LEDs, display, etc.) in real time.</li>
</ol>
<h2>Supported Boards</h2>
<table>
<thead>
<tr><th>Board</th><th>CPU</th><th>Emulation</th></tr>
</thead>
<tbody>
<tr><td>ESP32</td><td>Xtensa LX6 dual-core @ 240 MHz</td><td>QEMU (lcgamboa)</td></tr>
<tr><td>ESP32-S3</td><td>Xtensa LX7 dual-core @ 240 MHz</td><td>QEMU (lcgamboa)</td></tr>
</tbody>
</table>
<h2>Peripheral Support</h2>
<ul>
<li><strong>GPIO</strong> — digital output / input, LED control</li>
<li><strong>UART</strong> — Serial Monitor via <code>Serial.print()</code></li>
<li><strong>I2C / SPI</strong> — peripheral communication</li>
<li><strong>RMT / NeoPixel</strong> — addressable LED strips</li>
<li><strong>LEDC / PWM</strong> — hardware PWM channels</li>
<li><strong>WiFi</strong> — partial (connection events forwarded)</li>
</ul>
<h2>Requirements</h2>
<p>
QEMU-based emulation requires the Velxio backend to be running. This means it works with
the <strong>hosted version</strong> at{' '}
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">velxio.dev</a>{' '}
and with <strong>Docker self-hosting</strong>, but not in a pure static frontend deployment.
</p>
<div className="docs-callout">
<strong>Full details:</strong>{' '}
<a href={`${GITHUB_URL}/blob/master/docs/ESP32_EMULATION.md`} target="_blank" rel="noopener noreferrer">
docs/ESP32_EMULATION.md
</a>{' '}
in the repository.
</div>
</div>
);
/* ── RP2040 Emulation Section ─────────────────────────── */
const Rp2040EmulationSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// arm cortex-m0+</span>
<h1>RP2040 Emulation (Raspberry Pi Pico)</h1>
<p>
The Raspberry Pi Pico and Pico W are emulated entirely in the browser using{' '}
<a href="https://github.com/wokwi/rp2040js" target="_blank" rel="noopener noreferrer">rp2040js</a>,
an open-source ARM Cortex-M0+ emulator. No QEMU or backend process is required — the binary runs at full speed inside a Web Worker.
</p>
<h2>Supported Boards</h2>
<div className="docs-board-gallery">
<div className="docs-board-card">
<img src="/boards/pi-pico.svg" alt="Raspberry Pi Pico" />
<span>Raspberry Pi Pico</span>
</div>
<div className="docs-board-card">
<img src="/boards/pi-pico-w.svg" alt="Raspberry Pi Pico W" />
<span>Raspberry Pi Pico W</span>
</div>
</div>
<table>
<thead><tr><th>Board</th><th>FQBN</th><th>Built-in LED</th></tr></thead>
<tbody>
<tr><td>Raspberry Pi Pico</td><td><code>rp2040:rp2040:rpipico</code></td><td>GPIO 25</td></tr>
<tr><td>Raspberry Pi Pico W</td><td><code>rp2040:rp2040:rpipicow</code></td><td>GPIO 25 (via CYW43)</td></tr>
</tbody>
</table>
<h2>Binary Format</h2>
<p>
The backend compiles the sketch with <code>arduino-cli</code> targeting <code>rp2040:rp2040:rpipico</code> and returns
a raw ARM binary (<code>.bin</code>) encoded in base64. Unlike AVR, there is no Intel HEX — the binary is loaded
directly into the RP2040 flash at offset 0.
</p>
<p>
The backend also prepends <code>#define Serial Serial1</code> to the sketch so that Arduino <code>Serial</code> calls
are redirected to UART0 (the virtual serial port streamed to the Serial Monitor).
</p>
<h2>Peripherals</h2>
<table>
<thead><tr><th>Peripheral</th><th>Support</th><th>Notes</th></tr></thead>
<tbody>
<tr><td>GPIO (30 pins)</td><td>Full</td><td>Digital read/write, pull-up/down</td></tr>
<tr><td>UART0 / UART1</td><td>Full</td><td>Serial Monitor via UART0</td></tr>
<tr><td>ADC (ch 03)</td><td>Full</td><td>GPIO 2629; ch 4 = temperature sensor</td></tr>
<tr><td>I2C0 / I2C1</td><td>Partial</td><td>DS1307 RTC, TempSensor, EEPROM virtual devices</td></tr>
<tr><td>SPI0 / SPI1</td><td>Loopback</td><td>TX looped back to RX</td></tr>
<tr><td>PWM</td><td>Frequency only</td><td>No waveform output to components</td></tr>
<tr><td>Timer / Alarm</td><td>Full</td><td>Used by <code>delay()</code> and <code>millis()</code></td></tr>
</tbody>
</table>
<h2>WFI Optimisation</h2>
<p>
When the CPU executes a <strong>WFI</strong> (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 <code>delay()</code> calls.
</p>
<h2>Simulation Loop</h2>
<p>
The simulation runs inside <code>requestAnimationFrame</code> at ~60 FPS.
Each frame executes approximately <strong>2,200,000</strong> CPU cycles (133 MHz / 60 fps).
GPIO listeners fire whenever a pin state changes and update the visual components on the canvas.
</p>
<h2>Known Limitations</h2>
<ul>
<li>Pico W wireless chip (CYW43439) is not emulated — WiFi/Bluetooth will not work</li>
<li>SPI loopback only — no real SPI device emulation</li>
<li>PWM produces correct frequency but no visual waveform on components</li>
<li>DMA not emulated</li>
<li>Second CPU core (core 1) not emulated — <code>multicore_launch_core1()</code> has no effect</li>
</ul>
<h2>Full Documentation</h2>
<p>
See the complete technical reference:{' '}
<a href="https://github.com/davidmonterocrespo24/velxio/blob/master/docs/RP2040_EMULATION.md" target="_blank" rel="noopener noreferrer">
RP2040_EMULATION.md
</a>
</p>
</div>
);
/* ── Raspberry Pi 3 Emulation Section ─────────────────── */
const RaspberryPi3EmulationSection: React.FC = () => (
<div className="docs-section">
<span className="docs-label">// qemu raspi3b</span>
<h1>Raspberry Pi 3 Emulation (QEMU)</h1>
<p>
The Raspberry Pi 3B is emulated using <strong>QEMU 8.1.3</strong> with <code>-M raspi3b</code>.
This is the only board in Velxio that runs a full operating system — a real{' '}
<strong>Raspberry Pi OS (Trixie)</strong> image booted inside the emulator.
Users write Python scripts (not C++), which are executed by the real Python 3 interpreter inside the VM.
</p>
<h2>Supported Boards</h2>
<table>
<thead><tr><th>Board</th><th>QEMU Machine</th><th>CPU</th></tr></thead>
<tbody>
<tr><td>Raspberry Pi 3B</td><td><code>raspi3b</code></td><td>BCM2837, 4× Cortex-A53 @ 1.2 GHz</td></tr>
</tbody>
</table>
<h2>Dual-Channel Serial Architecture</h2>
<p>
QEMU exposes two UART channels to the backend:
</p>
<ul>
<li><strong>ttyAMA0</strong> — User serial: interactive terminal (Serial Monitor). The user's <code>print()</code> output appears here.</li>
<li><strong>ttyAMA1</strong> GPIO shim: carries a text protocol between the GPIO shim inside the VM and the backend.</li>
</ul>
<h2>RPi.GPIO Shim</h2>
<p>
A custom <code>RPi.GPIO</code> shim is injected at <code>/usr/local/lib/python3.11/dist-packages/RPi/GPIO.py</code>
inside the VM. When user code calls <code>GPIO.output(pin, value)</code>, the shim writes
a line like <code>GPIO 17 1</code> to <strong>ttyAMA1</strong>.
The backend reads this, fires a <code>gpio_change</code> WebSocket event, and the frontend
updates the visual component on the canvas.
</p>
<p>
The reverse also works: the frontend can send <code>SET 17 1</code> via WebSocket backend ttyAMA1 shim user code reads it via <code>GPIO.input(17)</code>.
</p>
<h2>Virtual File System (VFS)</h2>
<p>
Each Raspberry Pi board instance has its own VFS that maps to files inside the running VM.
The default tree is:
</p>
<pre><code>{`/home/pi/
├── script.py ← user's main Python script
└── lib/
└── helper.py ← optional helper library`}</code></pre>
<h2>Overlay Images</h2>
<p>
The base Raspberry Pi OS SD image is never modified. Each session creates a fresh
<strong> qcow2 overlay</strong> on top of the base image, so all runtime changes are
isolated and discarded when the session ends.
</p>
<h2>Known Limitations</h2>
<ul>
<li>No I2C or SPI device emulation</li>
<li>No PWM waveform output to components</li>
<li>No networking (WiFi/Ethernet not emulated)</li>
<li>Session state is not persisted overlay is discarded on stop</li>
<li>Boot time is slow (~1020 s) as a full OS must start</li>
<li>Requires the ~5.67 GB base SD image to be present on the server</li>
</ul>
<h2>Full Documentation</h2>
<p>
See the complete technical reference:{' '}
<a href="https://github.com/davidmonterocrespo24/velxio/blob/master/docs/RASPBERRYPI3_EMULATION.md" target="_blank" rel="noopener noreferrer">
RASPBERRYPI3_EMULATION.md
</a>
</p>
</div>
);
const SECTION_MAP: Record<SectionId, React.FC> = {
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 <head> 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 = <E extends Element>(selector: string, attr: string): [E | null, string] => {
const el = document.querySelector<E>(selector);
return [el, el?.getAttribute(attr) ?? ''];
};
const [descEl, origDesc] = captureAttr<HTMLMetaElement>('meta[name="description"]', 'content');
const [canonicalEl, origCanonical] = captureAttr<HTMLLinkElement>('link[rel="canonical"]', 'href');
const [ogTitleEl, origOgTitle] = captureAttr<HTMLMetaElement>('meta[property="og:title"]', 'content');
const [ogDescEl, origOgDesc] = captureAttr<HTMLMetaElement>('meta[property="og:description"]', 'content');
const [ogUrlEl, origOgUrl] = captureAttr<HTMLMetaElement>('meta[property="og:url"]', 'content');
const [twTitleEl, origTwTitle] = captureAttr<HTMLMetaElement>('meta[name="twitter:title"]', 'content');
const [twDescEl, origTwDesc] = captureAttr<HTMLMetaElement>('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<HTMLMetaElement>(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<HTMLLinkElement>('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 (
<div className="docs-page">
<AppHeader />
<div className="docs-mobile-bar">
<button
className="docs-sidebar-toggle"
onClick={() => setSidebarOpen((v) => !v)}
aria-label="Toggle sidebar"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<line x1="3" y1="6" x2="21" y2="6" />
<line x1="3" y1="12" x2="21" y2="12" />
<line x1="3" y1="18" x2="21" y2="18" />
</svg>
</button>
<span className="docs-mobile-bar-title">Documentation</span>
</div>
<div className="docs-body">
{/* Sidebar */}
<aside className={`docs-sidebar${sidebarOpen ? ' docs-sidebar--open' : ''}`}>
<div className="docs-sidebar-title">Documentation</div>
<nav className="docs-sidebar-nav">
{NAV_ITEMS.map((item) => (
<Link
key={item.id}
to={`/docs/${item.id}`}
className={`docs-sidebar-item${activeSection === item.id ? ' docs-sidebar-item--active' : ''}`}
onClick={() => setSidebarOpen(false)}
>
{item.label}
</Link>
))}
</nav>
<div className="docs-sidebar-divider" />
<div className="docs-sidebar-title docs-sidebar-title--pages">Pages</div>
<nav className="docs-sidebar-nav">
<Link to="/" className="docs-sidebar-item docs-sidebar-link" onClick={() => setSidebarOpen(false)}>Home</Link>
<Link to="/editor" className="docs-sidebar-item docs-sidebar-link" onClick={() => setSidebarOpen(false)}>Editor</Link>
<Link to="/examples" className="docs-sidebar-item docs-sidebar-link" onClick={() => setSidebarOpen(false)}>Examples</Link>
</nav>
<div className="docs-sidebar-footer">
<a href={GITHUB_URL} target="_blank" rel="noopener noreferrer" className="docs-sidebar-gh">
<IcoGitHub /> View on GitHub
</a>
</div>
</aside>
{/* Main content */}
<main className="docs-main">
<ActiveContent />
{/* Prev / Next navigation */}
<div className="docs-pagination">
{activeIdx > 0 && (
<Link
to={`/docs/${NAV_ITEMS[activeIdx - 1].id}`}
className="docs-pagination-btn docs-pagination-btn--prev"
onClick={() => window.scrollTo(0, 0)}
>
{NAV_ITEMS[activeIdx - 1].label}
</Link>
)}
{activeIdx < NAV_ITEMS.length - 1 && (
<Link
to={`/docs/${NAV_ITEMS[activeIdx + 1].id}`}
className="docs-pagination-btn docs-pagination-btn--next"
onClick={() => window.scrollTo(0, 0)}
>
{NAV_ITEMS[activeIdx + 1].label}
</Link>
)}
</div>
</main>
</div>
</div>
);
};