Refactor code structure for improved readability and maintainability
parent
16164372cf
commit
d35ad2d2f7
Binary file not shown.
|
After Width: | Height: | Size: 198 KiB |
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"generatedAt": "2026-03-14T18:11:39.708Z",
|
"generatedAt": "2026-03-16T21:03:46.561Z",
|
||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"id": "arduino-mega",
|
"id": "arduino-mega",
|
||||||
|
|
|
||||||
|
|
@ -900,7 +900,7 @@ export const SimulatorCanvas = () => {
|
||||||
return () => timers.forEach(t => clearTimeout(t));
|
return () => timers.forEach(t => clearTimeout(t));
|
||||||
}, [components, recalculateAllWirePositions]);
|
}, [components, recalculateAllWirePositions]);
|
||||||
|
|
||||||
// Auto-pan to keep the board visible after a project import/load.
|
// Auto-pan to keep the board and all components visible after a project import/load.
|
||||||
// We track the previous component count and only re-center when the count
|
// We track the previous component count and only re-center when the count
|
||||||
// jumps (indicating the user loaded a new circuit, not just added one part).
|
// jumps (indicating the user loaded a new circuit, not just added one part).
|
||||||
const prevComponentCountRef = useRef(-1);
|
const prevComponentCountRef = useRef(-1);
|
||||||
|
|
@ -919,9 +919,21 @@ export const SimulatorCanvas = () => {
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
const rect = canvas.getBoundingClientRect();
|
const rect = canvas.getBoundingClientRect();
|
||||||
const currentZoom = zoomRef.current;
|
const currentZoom = zoomRef.current;
|
||||||
|
|
||||||
|
// Compute the centroid of all world-space elements (board + extra components)
|
||||||
|
// so that the auto-pan keeps everything visible, not just the board.
|
||||||
|
const allX = [boardPositionRef.current.x, ...componentsRef.current.map((c) => c.x)];
|
||||||
|
const allY = [boardPositionRef.current.y, ...componentsRef.current.map((c) => c.y)];
|
||||||
|
const minX = Math.min(...allX);
|
||||||
|
const maxX = Math.max(...allX);
|
||||||
|
const minY = Math.min(...allY);
|
||||||
|
const maxY = Math.max(...allY);
|
||||||
|
const centerX = (minX + maxX) / 2;
|
||||||
|
const centerY = (minY + maxY) / 2;
|
||||||
|
|
||||||
const newPan = {
|
const newPan = {
|
||||||
x: rect.width / 4 - boardPosition.x * currentZoom,
|
x: rect.width / 2 - centerX * currentZoom,
|
||||||
y: rect.height / 4 - boardPosition.y * currentZoom,
|
y: rect.height / 2 - centerY * currentZoom,
|
||||||
};
|
};
|
||||||
panRef.current = newPan;
|
panRef.current = newPan;
|
||||||
setPan(newPan);
|
setPan(newPan);
|
||||||
|
|
|
||||||
|
|
@ -652,8 +652,8 @@ void loop() {
|
||||||
|
|
||||||
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
|
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
|
||||||
|
|
||||||
// Background color: dark blue
|
// Background color: blue (visible on dark simulator canvas)
|
||||||
#define BG_COLOR 0x0006
|
#define BG_COLOR 0x001F
|
||||||
|
|
||||||
// Ball state
|
// Ball state
|
||||||
int ballX = 120, ballY = 200;
|
int ballX = 120, ballY = 200;
|
||||||
|
|
@ -724,8 +724,8 @@ void loop() {
|
||||||
{
|
{
|
||||||
type: 'wokwi-ili9341',
|
type: 'wokwi-ili9341',
|
||||||
id: 'tft1',
|
id: 'tft1',
|
||||||
x: 480,
|
x: 300,
|
||||||
y: 60,
|
y: 30,
|
||||||
properties: {},
|
properties: {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -72,55 +72,55 @@ const NAV_ITEMS: NavItem[] = [
|
||||||
interface SectionMeta { title: string; description: string; }
|
interface SectionMeta { title: string; description: string; }
|
||||||
const SECTION_META: Record<SectionId, SectionMeta> = {
|
const SECTION_META: Record<SectionId, SectionMeta> = {
|
||||||
'intro': {
|
'intro': {
|
||||||
title: 'Introduction — Velxio Documentation',
|
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.',
|
description: 'Learn about Velxio, the free open-source Arduino emulator with real AVR8 and RP2040 CPU emulation and 48+ interactive electronic components.',
|
||||||
},
|
},
|
||||||
'getting-started': {
|
'getting-started': {
|
||||||
title: 'Getting Started — Velxio Documentation',
|
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.',
|
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': {
|
'emulator': {
|
||||||
title: 'Emulator Architecture — Velxio Documentation',
|
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.',
|
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': {
|
'riscv-emulation': {
|
||||||
title: 'RISC-V Emulation (ESP32-C3) — Velxio Documentation',
|
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.',
|
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': {
|
'esp32-emulation': {
|
||||||
title: 'ESP32 Emulation (Xtensa) — Velxio Documentation',
|
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.',
|
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': {
|
'components': {
|
||||||
title: 'Components Reference — Velxio Documentation',
|
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.',
|
description: 'Full reference for all 48+ interactive electronic components in Velxio: LEDs, displays, sensors, buttons, potentiometers, and more. Includes wiring and property details.',
|
||||||
},
|
},
|
||||||
'roadmap': {
|
'roadmap': {
|
||||||
title: 'Roadmap — Velxio Documentation',
|
title: 'Roadmap | Velxio Documentation',
|
||||||
description: "Velxio's feature roadmap: what's implemented, what's in progress, and what's planned for future releases.",
|
description: "Velxio's feature roadmap: what's implemented, what's in progress, and what's planned for future releases.",
|
||||||
},
|
},
|
||||||
'architecture': {
|
'architecture': {
|
||||||
title: 'Project Architecture — Velxio Documentation',
|
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.',
|
description: 'Detailed overview of the Velxio system architecture: frontend, backend, AVR8 emulation pipeline, data flows, Zustand stores, and wire system.',
|
||||||
},
|
},
|
||||||
'wokwi-libs': {
|
'wokwi-libs': {
|
||||||
title: 'Wokwi Libraries — Velxio Documentation',
|
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.',
|
description: 'How Velxio integrates the official Wokwi open-source libraries: avr8js, wokwi-elements, and rp2040js. Covers configuration, updates, and the 48 available components.',
|
||||||
},
|
},
|
||||||
'mcp': {
|
'mcp': {
|
||||||
title: 'MCP Server — Velxio Documentation',
|
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.',
|
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': {
|
'setup': {
|
||||||
title: 'Project Status — Velxio Documentation',
|
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.',
|
description: 'Complete status of all implemented Velxio features: AVR emulation, component system, wire system, code editor, example projects, and next steps.',
|
||||||
},
|
},
|
||||||
'rp2040-emulation': {
|
'rp2040-emulation': {
|
||||||
title: 'RP2040 Emulation (Raspberry Pi Pico) — Velxio Documentation',
|
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.',
|
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': {
|
'raspberry-pi3-emulation': {
|
||||||
title: 'Raspberry Pi 3 Emulation (QEMU) — Velxio Documentation',
|
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.',
|
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.',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -133,16 +133,16 @@ const IntroSection: React.FC = () => (
|
||||||
<p>
|
<p>
|
||||||
<strong>Velxio</strong> is a fully local, open-source Arduino emulator that runs entirely in your browser.
|
<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
|
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
|
true AVR8 / RP2040 CPU emulation, with 48+ interactive electronic components, all without installing
|
||||||
any software on your machine.
|
any software on your machine.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Why Velxio?</h2>
|
<h2>Why Velxio?</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>No installation required</strong> — everything runs in the browser.</li>
|
<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>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>Interactive components</strong>: LEDs, buttons, potentiometers, displays, sensors, and more.</li>
|
||||||
<li><strong>Open-source</strong> — inspect, modify, and self-host it yourself.</li>
|
<li><strong>Open-source</strong>: inspect, modify, and self-host it yourself.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Supported Boards</h2>
|
<h2>Supported Boards</h2>
|
||||||
|
|
@ -163,7 +163,7 @@ const IntroSection: React.FC = () => (
|
||||||
<div className="docs-callout">
|
<div className="docs-callout">
|
||||||
<strong>Live Demo:</strong>{' '}
|
<strong>Live Demo:</strong>{' '}
|
||||||
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">velxio.dev</a>
|
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">velxio.dev</a>
|
||||||
{' '}— no installation needed, open the editor and start simulating immediately.
|
{' '}, no installation needed, open the editor and start simulating immediately.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -176,7 +176,7 @@ const GettingStartedSection: React.FC = () => (
|
||||||
|
|
||||||
<h2>Option 1: Use the Hosted Version</h2>
|
<h2>Option 1: Use the Hosted Version</h2>
|
||||||
<p>
|
<p>
|
||||||
No installation needed — go to{' '}
|
No installation needed, go to{' '}
|
||||||
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">https://velxio.dev</a>{' '}
|
<a href="https://velxio.dev" target="_blank" rel="noopener noreferrer">https://velxio.dev</a>{' '}
|
||||||
and start coding immediately.
|
and start coding immediately.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -235,8 +235,8 @@ void loop() {
|
||||||
delay(500);
|
delay(500);
|
||||||
}`}</CodeBlock>
|
}`}</CodeBlock>
|
||||||
<ol start={4}>
|
<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 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>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>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>
|
<li><strong>Connect wires</strong> by clicking a component pin and then another pin.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
@ -317,7 +317,7 @@ cpu.tick(); // advance peripheral timers and counters`}</CodeBlock>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td>GPIO</td><td>PORTB (pins 8–13), PORTC (A0–A5), PORTD (pins 0–7)</td></tr>
|
<tr><td>GPIO</td><td>PORTB (pins 8–13), PORTC (A0–A5), PORTD (pins 0–7)</td></tr>
|
||||||
<tr><td>Timer0 / Timer1 / Timer2</td><td><code>millis()</code>, <code>delay()</code>, PWM via <code>analogWrite()</code></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>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 A0–A5</td></tr>
|
<tr><td>ADC</td><td>10-bit, 5 V reference on pins A0–A5</td></tr>
|
||||||
<tr><td>SPI</td><td>Hardware SPI (enables ILI9341, SD card, etc.)</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>
|
<tr><td>I2C (TWI)</td><td>Hardware I2C with virtual device bus</td></tr>
|
||||||
|
|
@ -392,7 +392,7 @@ const ComponentsSection: React.FC = () => (
|
||||||
|
|
||||||
<h2>Connecting Components</h2>
|
<h2>Connecting Components</h2>
|
||||||
<ol>
|
<ol>
|
||||||
<li>Click a <strong>pin</strong> on any component — a wire starts from that pin.</li>
|
<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>Click a <strong>destination pin</strong> to complete the connection.</li>
|
||||||
<li>Wires are <strong>color-coded</strong> by signal type:</li>
|
<li>Wires are <strong>color-coded</strong> by signal type:</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
@ -488,7 +488,7 @@ const RoadmapSection: React.FC = () => (
|
||||||
<h2>✅ Implemented</h2>
|
<h2>✅ Implemented</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Monaco Editor with C++ syntax highlighting, autocomplete, and minimap</li>
|
<li>Monaco Editor with C++ syntax highlighting, autocomplete, and minimap</li>
|
||||||
<li>Multi-file workspace — create, rename, delete, and switch between files</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>Arduino compilation via <code>arduino-cli</code> (multi-file sketch support)</li>
|
||||||
<li>Real ATmega328p / ATmega2560 emulation at 16 MHz via avr8js</li>
|
<li>Real ATmega328p / ATmega2560 emulation at 16 MHz via avr8js</li>
|
||||||
<li>Full GPIO, Timers, USART, ADC, SPI, I2C support</li>
|
<li>Full GPIO, Timers, USART, ADC, SPI, I2C support</li>
|
||||||
|
|
@ -505,33 +505,33 @@ const RoadmapSection: React.FC = () => (
|
||||||
|
|
||||||
<h2>🔄 In Progress</h2>
|
<h2>🔄 In Progress</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Functional wire connections — electrical signal routing and validation</li>
|
<li>Functional wire connections, electrical signal routing and validation</li>
|
||||||
<li>Wire connection error handling — detect short circuits and invalid connections</li>
|
<li>Wire connection error handling, detect short circuits and invalid connections</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>🗓 Planned — Near-Term</h2>
|
<h2>🗓 Planned: Near-Term</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Undo / redo for code edits and canvas changes</li>
|
<li>Undo / redo for code edits and canvas changes</li>
|
||||||
<li>Export / import projects as <code>.zip</code> files</li>
|
<li>Export / import projects as <code>.zip</code> files</li>
|
||||||
<li>More boards — ESP32, Arduino Leonardo</li>
|
<li>More boards, ESP32, Arduino Leonardo</li>
|
||||||
<li>Breadboard — place components with automatic wire routing</li>
|
<li>Breadboard, place components with automatic wire routing</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>🗓 Planned — Mid-Term</h2>
|
<h2>🗓 Planned: Mid-Term</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>TypeDoc API documentation — auto-generated from source code</li>
|
<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>GitHub Pages docs site, automatic deployment on push to <code>main</code></li>
|
||||||
<li>More sensor simulations — HC-SR04, DHT22, IR receiver</li>
|
<li>More sensor simulations, HC-SR04, DHT22, IR receiver</li>
|
||||||
<li>EEPROM emulation — persistent read/write across simulation restarts</li>
|
<li>EEPROM emulation, persistent read/write across simulation restarts</li>
|
||||||
<li>Oscilloscope component — plot analog pin voltages over time</li>
|
<li>Oscilloscope component, plot analog pin voltages over time</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>🗓 Planned — Long-Term</h2>
|
<h2>🗓 Planned: Long-Term</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Multiplayer — share and co-edit simulations in real time</li>
|
<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>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>Custom component SDK, define new components with a JSON/TypeScript API</li>
|
||||||
<li>Mobile / tablet support — responsive layout for touch devices</li>
|
<li>Mobile / tablet support, responsive layout for touch devices</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className="docs-callout">
|
<div className="docs-callout">
|
||||||
|
|
@ -635,10 +635,10 @@ const ArchitectureSection: React.FC = () => (
|
||||||
signalType: 'digital' | 'analog' | 'power-vcc' | 'power-gnd'
|
signalType: 'digital' | 'analog' | 'power-vcc' | 'power-gnd'
|
||||||
}`}</CodeBlock>
|
}`}</CodeBlock>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Orthogonal routing — no diagonal segments</li>
|
<li>Orthogonal routing, no diagonal segments</li>
|
||||||
<li>Segment drag — drag perpendicular to segment orientation</li>
|
<li>Segment drag, drag perpendicular to segment orientation</li>
|
||||||
<li>Auto-update — wire positions recalculate when components move</li>
|
<li>Auto-update, wire positions recalculate when components move</li>
|
||||||
<li>Grid snapping — 20 px grid for all wire endpoints</li>
|
<li>Grid snapping, 20 px grid for all wire endpoints</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div className="docs-callout">
|
<div className="docs-callout">
|
||||||
|
|
@ -796,7 +796,7 @@ const McpSection: React.FC = () => (
|
||||||
|
|
||||||
<h2>Transport Options</h2>
|
<h2>Transport Options</h2>
|
||||||
|
|
||||||
<h3>1. stdio — Claude Desktop / CLI agents</h3>
|
<h3>1. stdio: Claude Desktop / CLI agents</h3>
|
||||||
<CodeBlock language="bash">{`cd backend
|
<CodeBlock language="bash">{`cd backend
|
||||||
python mcp_server.py`}</CodeBlock>
|
python mcp_server.py`}</CodeBlock>
|
||||||
<p>Claude Desktop config (<code>~/.claude/claude_desktop_config.json</code>):</p>
|
<p>Claude Desktop config (<code>~/.claude/claude_desktop_config.json</code>):</p>
|
||||||
|
|
@ -809,7 +809,7 @@ python mcp_server.py`}</CodeBlock>
|
||||||
}
|
}
|
||||||
}`}</CodeBlock>
|
}`}</CodeBlock>
|
||||||
|
|
||||||
<h3>2. SSE / HTTP — Cursor IDE / web agents</h3>
|
<h3>2. SSE / HTTP: Cursor IDE / web agents</h3>
|
||||||
<CodeBlock language="bash">{`cd backend
|
<CodeBlock language="bash">{`cd backend
|
||||||
python mcp_sse_server.py --port 8002`}</CodeBlock>
|
python mcp_sse_server.py --port 8002`}</CodeBlock>
|
||||||
<p>MCP client config:</p>
|
<p>MCP client config:</p>
|
||||||
|
|
@ -847,7 +847,7 @@ python mcp_sse_server.py --port 8002`}</CodeBlock>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Example — Blink LED from Scratch</h2>
|
<h2>Example: Blink LED from Scratch</h2>
|
||||||
<CodeBlock language="json">{`// Step 1 — Create a circuit
|
<CodeBlock language="json">{`// Step 1 — Create a circuit
|
||||||
{
|
{
|
||||||
"tool": "create_circuit",
|
"tool": "create_circuit",
|
||||||
|
|
@ -1048,7 +1048,7 @@ const RiscVEmulationSection: React.FC = () => (
|
||||||
<h1>RISC-V Emulation (ESP32-C3)</h1>
|
<h1>RISC-V Emulation (ESP32-C3)</h1>
|
||||||
<p>
|
<p>
|
||||||
ESP32-C3, XIAO ESP32-C3, and C3 SuperMini boards use a <strong>RISC-V RV32IMC</strong> core running at
|
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.
|
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.
|
The emulator is written in pure TypeScript and runs at real-time speeds.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
@ -1095,9 +1095,9 @@ const RiscVEmulationSection: React.FC = () => (
|
||||||
|
|
||||||
<h2>ISA Support</h2>
|
<h2>ISA Support</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>RV32I</strong> — Full base integer instruction set (ALU, load/store, branches, JAL/JALR)</li>
|
<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>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,
|
<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>
|
C.BNEZ, C.MV, C.ADD, C.JR, C.JALR, C.LW, C.SW, C.LWSP, C.SWSP, C.SLLI, C.ADDI4SPN</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
@ -1165,14 +1165,14 @@ const Esp32EmulationSection: React.FC = () => (
|
||||||
<p>
|
<p>
|
||||||
ESP32 and ESP32-S3 boards use an <strong>Xtensa LX6 / LX7</strong> architecture. Because no
|
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
|
production-quality Xtensa emulator is available as pure JavaScript, Velxio uses a
|
||||||
<strong> QEMU-based backend</strong> for these boards — the lcgamboa fork with
|
<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.
|
libqemu-xtensa, compiled to a native binary and served by the FastAPI backend.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="docs-callout">
|
<div className="docs-callout">
|
||||||
<strong>Note:</strong> This section applies only to <strong>ESP32</strong> and <strong>ESP32-S3</strong> (Xtensa).
|
<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{' '}
|
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.
|
<strong>RISC-V Emulation (ESP32-C3)</strong> in the sidebar, those boards run entirely in the browser.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>How It Works</h2>
|
<h2>How It Works</h2>
|
||||||
|
|
@ -1219,12 +1219,12 @@ const Esp32EmulationSection: React.FC = () => (
|
||||||
|
|
||||||
<h2>Peripheral Support</h2>
|
<h2>Peripheral Support</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>GPIO</strong> — digital output / input, LED control</li>
|
<li><strong>GPIO</strong>: digital output / input, LED control</li>
|
||||||
<li><strong>UART</strong> — Serial Monitor via <code>Serial.print()</code></li>
|
<li><strong>UART</strong>: Serial Monitor via <code>Serial.print()</code></li>
|
||||||
<li><strong>I2C / SPI</strong> — peripheral communication</li>
|
<li><strong>I2C / SPI</strong>: peripheral communication</li>
|
||||||
<li><strong>RMT / NeoPixel</strong> — addressable LED strips</li>
|
<li><strong>RMT / NeoPixel</strong>: addressable LED strips</li>
|
||||||
<li><strong>LEDC / PWM</strong> — hardware PWM channels</li>
|
<li><strong>LEDC / PWM</strong>: hardware PWM channels</li>
|
||||||
<li><strong>WiFi</strong> — partial (connection events forwarded)</li>
|
<li><strong>WiFi</strong>: partial (connection events forwarded)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Requirements</h2>
|
<h2>Requirements</h2>
|
||||||
|
|
@ -1253,7 +1253,7 @@ const Rp2040EmulationSection: React.FC = () => (
|
||||||
<p>
|
<p>
|
||||||
The Raspberry Pi Pico and Pico W are emulated entirely in the browser using{' '}
|
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>,
|
<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.
|
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>
|
</p>
|
||||||
|
|
||||||
<h2>Supported Boards</h2>
|
<h2>Supported Boards</h2>
|
||||||
|
|
@ -1278,7 +1278,7 @@ const Rp2040EmulationSection: React.FC = () => (
|
||||||
<h2>Binary Format</h2>
|
<h2>Binary Format</h2>
|
||||||
<p>
|
<p>
|
||||||
The backend compiles the sketch with <code>arduino-cli</code> targeting <code>rp2040:rp2040:rpipico</code> and returns
|
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
|
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.
|
directly into the RP2040 flash at offset 0.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -1316,11 +1316,11 @@ const Rp2040EmulationSection: React.FC = () => (
|
||||||
|
|
||||||
<h2>Known Limitations</h2>
|
<h2>Known Limitations</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Pico W wireless chip (CYW43439) is not emulated — WiFi/Bluetooth will not work</li>
|
<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>SPI loopback only, no real SPI device emulation</li>
|
||||||
<li>PWM produces correct frequency but no visual waveform on components</li>
|
<li>PWM produces correct frequency but no visual waveform on components</li>
|
||||||
<li>DMA not emulated</li>
|
<li>DMA not emulated</li>
|
||||||
<li>Second CPU core (core 1) not emulated — <code>multicore_launch_core1()</code> has no effect</li>
|
<li>Second CPU core (core 1) not emulated, <code>multicore_launch_core1()</code> has no effect</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Full Documentation</h2>
|
<h2>Full Documentation</h2>
|
||||||
|
|
@ -1340,7 +1340,7 @@ const RaspberryPi3EmulationSection: React.FC = () => (
|
||||||
<h1>Raspberry Pi 3 Emulation (QEMU)</h1>
|
<h1>Raspberry Pi 3 Emulation (QEMU)</h1>
|
||||||
<p>
|
<p>
|
||||||
The Raspberry Pi 3B is emulated using <strong>QEMU 8.1.3</strong> with <code>-M raspi3b</code>.
|
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{' '}
|
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.
|
<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.
|
Users write Python scripts (not C++), which are executed by the real Python 3 interpreter inside the VM.
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -1364,8 +1364,8 @@ const RaspberryPi3EmulationSection: React.FC = () => (
|
||||||
QEMU exposes two UART channels to the backend:
|
QEMU exposes two UART channels to the backend:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>ttyAMA0</strong> — User serial: interactive terminal (Serial Monitor). The user's <code>print()</code> output appears here.</li>
|
<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>
|
<li><strong>ttyAMA1</strong>: GPIO shim: carries a text protocol between the GPIO shim inside the VM and the backend.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>RPi.GPIO Shim</h2>
|
<h2>RPi.GPIO Shim</h2>
|
||||||
|
|
@ -1402,7 +1402,7 @@ const RaspberryPi3EmulationSection: React.FC = () => (
|
||||||
<li>No I2C or SPI device emulation</li>
|
<li>No I2C or SPI device emulation</li>
|
||||||
<li>No PWM waveform output to components</li>
|
<li>No PWM waveform output to components</li>
|
||||||
<li>No networking (WiFi/Ethernet not emulated)</li>
|
<li>No networking (WiFi/Ethernet not emulated)</li>
|
||||||
<li>Session state is not persisted — overlay is discarded on stop</li>
|
<li>Session state is not persisted, overlay is discarded on stop</li>
|
||||||
<li>Boot time is slow (~10–20 s) as a full OS must start</li>
|
<li>Boot time is slow (~10–20 s) as a full OS must start</li>
|
||||||
<li>Requires the ~5.67 GB base SD image to be present on the server</li>
|
<li>Requires the ~5.67 GB base SD image to be present on the server</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -238,19 +238,23 @@
|
||||||
/* ── Hero ─────────────────────────────────────────────── */
|
/* ── Hero ─────────────────────────────────────────────── */
|
||||||
.landing-hero {
|
.landing-hero {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1.3fr;
|
grid-template-columns: 1fr 1.2fr;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
gap: 56px;
|
gap: 0;
|
||||||
padding: 80px 80px 80px 80px;
|
min-height: calc(100vh - 52px);
|
||||||
max-width: 1400px;
|
width: 100%;
|
||||||
margin: 0 auto;
|
max-width: none;
|
||||||
min-height: 90vh;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-left {
|
.hero-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
|
padding: 80px 60px 80px 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-eyebrow {
|
.hero-eyebrow {
|
||||||
|
|
@ -370,11 +374,11 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hero right — schematic */
|
/* Hero right — image full-bleed */
|
||||||
.hero-right {
|
.hero-right {
|
||||||
display: flex;
|
position: relative;
|
||||||
align-items: center;
|
overflow: hidden;
|
||||||
justify-content: center;
|
min-height: calc(100vh - 52px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.schematic-svg {
|
.schematic-svg {
|
||||||
|
|
@ -386,11 +390,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-preview-img {
|
.hero-preview-img {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
height: 100%;
|
||||||
height: auto;
|
object-fit: cover;
|
||||||
border-radius: var(--radius);
|
object-position: center;
|
||||||
filter: drop-shadow(0 24px 64px rgba(0, 0, 0, 0.75));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Sections ─────────────────────────────────────────── */
|
/* ── Sections ─────────────────────────────────────────── */
|
||||||
|
|
@ -495,12 +500,12 @@
|
||||||
/* Apple product tile */
|
/* Apple product tile */
|
||||||
.board-card-sm {
|
.board-card-sm {
|
||||||
background: #141416;
|
background: #141416;
|
||||||
border-radius: 20px;
|
border-radius: 16px;
|
||||||
padding: 28px 14px 22px;
|
padding: 20px 14px 18px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 14px;
|
gap: 10px;
|
||||||
transition: transform 0.32s cubic-bezier(0.34, 1.46, 0.64, 1),
|
transition: transform 0.32s cubic-bezier(0.34, 1.46, 0.64, 1),
|
||||||
box-shadow 0.32s ease;
|
box-shadow 0.32s ease;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
|
|
@ -531,6 +536,32 @@
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Uniform image container — all cards same height */
|
||||||
|
.board-img-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 110px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-img-box img {
|
||||||
|
max-width: 90px;
|
||||||
|
max-height: 110px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-img-box .board-svg {
|
||||||
|
max-width: 110px;
|
||||||
|
max-height: 100px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.board-img-sm {
|
.board-img-sm {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|
@ -898,9 +929,11 @@
|
||||||
/* ── Responsive ───────────────────────────────────────── */
|
/* ── Responsive ───────────────────────────────────────── */
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.landing-hero {
|
.landing-hero {
|
||||||
grid-template-columns: 1fr 1.2fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 40px;
|
}
|
||||||
padding: 72px 48px;
|
|
||||||
|
.hero-left {
|
||||||
|
padding: 72px 40px 72px 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing-section {
|
.landing-section {
|
||||||
|
|
@ -925,13 +958,18 @@
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.landing-hero {
|
.landing-hero {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
padding: 64px 32px;
|
|
||||||
min-height: auto;
|
min-height: auto;
|
||||||
gap: 44px;
|
}
|
||||||
|
|
||||||
|
.hero-left {
|
||||||
|
padding: 64px 32px;
|
||||||
|
order: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-right {
|
.hero-right {
|
||||||
order: -1;
|
order: -1;
|
||||||
|
min-height: 55vw;
|
||||||
|
max-height: 480px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.schematic-svg {
|
.schematic-svg {
|
||||||
|
|
@ -963,7 +1001,7 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing-hero {
|
.hero-left {
|
||||||
padding: 52px 20px;
|
padding: 52px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -508,7 +508,6 @@ export const LandingPage: React.FC = () => {
|
||||||
{/* Hero */}
|
{/* Hero */}
|
||||||
<section className="landing-hero">
|
<section className="landing-hero">
|
||||||
<div className="hero-left">
|
<div className="hero-left">
|
||||||
<p className="hero-eyebrow">Open Source · Free · Runs Locally</p>
|
|
||||||
<h1 className="hero-title">
|
<h1 className="hero-title">
|
||||||
Emulate Hardware.<br />
|
Emulate Hardware.<br />
|
||||||
<span className="hero-accent">In your browser.</span>
|
<span className="hero-accent">In your browser.</span>
|
||||||
|
|
@ -526,14 +525,7 @@ export const LandingPage: React.FC = () => {
|
||||||
View on GitHub
|
View on GitHub
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p className="hero-specs">
|
|
||||||
<span className="spec-pill">ATmega328p</span>
|
|
||||||
<span className="spec-pill">RP2040</span>
|
|
||||||
<span className="spec-pill">RV32IMC</span>
|
|
||||||
<span className="spec-pill">Xtensa LX6/LX7</span>
|
|
||||||
<span className="spec-pill">ARM Cortex-A53</span>
|
|
||||||
<span className="spec-pill">48+ components</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="hero-right">
|
<div className="hero-right">
|
||||||
<img src="/image.png" alt="Velxio simulator preview" className="hero-preview-img" />
|
<img src="/image.png" alt="Velxio simulator preview" className="hero-preview-img" />
|
||||||
|
|
@ -556,30 +548,24 @@ export const LandingPage: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="boards-row">
|
<div className="boards-row">
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<div className="board-element-wrap board-element-uno">
|
<div className="board-img-box"><BoardUno /></div>
|
||||||
<wokwi-arduino-uno />
|
|
||||||
</div>
|
|
||||||
<span className="board-name-sm">Arduino Uno</span>
|
<span className="board-name-sm">Arduino Uno</span>
|
||||||
<span className="board-chip-sm">ATmega328p · 32 KB</span>
|
<span className="board-chip-sm">ATmega328p · 32 KB</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<div className="board-element-wrap board-element-nano">
|
<div className="board-img-box"><BoardNano /></div>
|
||||||
<wokwi-arduino-nano />
|
|
||||||
</div>
|
|
||||||
<span className="board-name-sm">Arduino Nano</span>
|
<span className="board-name-sm">Arduino Nano</span>
|
||||||
<span className="board-chip-sm">ATmega328p · 32 KB</span>
|
<span className="board-chip-sm">ATmega328p · 32 KB</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<div className="board-element-wrap board-element-mega">
|
<div className="board-img-box"><BoardMega /></div>
|
||||||
<wokwi-arduino-mega />
|
|
||||||
</div>
|
|
||||||
<span className="board-name-sm">Arduino Mega 2560</span>
|
<span className="board-name-sm">Arduino Mega 2560</span>
|
||||||
<span className="board-chip-sm">ATmega2560 · 256 KB</span>
|
<span className="board-chip-sm">ATmega2560 · 256 KB</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<BoardATtiny85 />
|
<div className="board-img-box"><BoardATtiny85 /></div>
|
||||||
<span className="board-name-sm">ATtiny85</span>
|
<span className="board-name-sm">ATtiny85</span>
|
||||||
<span className="board-chip-sm">ATtiny85 · 8 KB</span>
|
<span className="board-chip-sm">AVR · 8 KB · DIP-8</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -592,12 +578,16 @@ export const LandingPage: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="boards-row">
|
<div className="boards-row">
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/pi-pico.svg" alt="Raspberry Pi Pico" className="board-img-sm" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/pi-pico.svg" alt="Raspberry Pi Pico" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">Raspberry Pi Pico</span>
|
<span className="board-name-sm">Raspberry Pi Pico</span>
|
||||||
<span className="board-chip-sm">RP2040 · 264 KB RAM</span>
|
<span className="board-chip-sm">RP2040 · 264 KB RAM</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/pi-pico-w.svg" alt="Raspberry Pi Pico W" className="board-img-sm" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/pi-pico-w.svg" alt="Raspberry Pi Pico W" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">Raspberry Pi Pico W</span>
|
<span className="board-name-sm">Raspberry Pi Pico W</span>
|
||||||
<span className="board-chip-sm">RP2040 + WiFi</span>
|
<span className="board-chip-sm">RP2040 + WiFi</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -612,22 +602,28 @@ export const LandingPage: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="boards-row">
|
<div className="boards-row">
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/esp32-c3.svg" alt="ESP32-C3" className="board-img-sm board-img-tall" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/esp32-c3.svg" alt="ESP32-C3" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">ESP32-C3 DevKit</span>
|
<span className="board-name-sm">ESP32-C3 DevKit</span>
|
||||||
<span className="board-chip-sm">RV32IMC · 4 MB flash</span>
|
<span className="board-chip-sm">RV32IMC · 4 MB flash</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/xiao-esp32-c3.svg" alt="XIAO ESP32-C3" className="board-img-sm" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/xiao-esp32-c3.svg" alt="XIAO ESP32-C3" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">XIAO ESP32-C3</span>
|
<span className="board-name-sm">XIAO ESP32-C3</span>
|
||||||
<span className="board-chip-sm">RV32IMC · compact</span>
|
<span className="board-chip-sm">RV32IMC · compact</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/esp32c3-supermini.svg" alt="ESP32-C3 SuperMini" className="board-img-sm" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/esp32c3-supermini.svg" alt="ESP32-C3 SuperMini" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">ESP32-C3 SuperMini</span>
|
<span className="board-name-sm">ESP32-C3 SuperMini</span>
|
||||||
<span className="board-chip-sm">RV32IMC · mini form</span>
|
<span className="board-chip-sm">RV32IMC · mini form</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<BoardCH32V003 />
|
<div className="board-img-box"><BoardCH32V003 /></div>
|
||||||
<span className="board-name-sm">CH32V003 (RISC-V)</span>
|
<span className="board-name-sm">CH32V003 (RISC-V)</span>
|
||||||
<span className="board-chip-sm">RV32EC · 48 MHz</span>
|
<span className="board-chip-sm">RV32EC · 48 MHz</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -642,34 +638,44 @@ export const LandingPage: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="boards-row">
|
<div className="boards-row">
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<div className="board-element-wrap board-element-esp32">
|
<div className="board-img-box">
|
||||||
<wokwi-esp32-devkit-v1 />
|
<img src="/boards/esp32-devkit-c-v4.svg" alt="ESP32 DevKit V1" className="board-img-sm" />
|
||||||
</div>
|
</div>
|
||||||
<span className="board-name-sm">ESP32 DevKit V1</span>
|
<span className="board-name-sm">ESP32 DevKit V1</span>
|
||||||
<span className="board-chip-sm">LX6 · 4 MB flash</span>
|
<span className="board-chip-sm">LX6 · 4 MB flash</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/esp32-devkit-c-v4.svg" alt="ESP32 DevKit C V4" className="board-img-sm board-img-tall" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/esp32-devkit-c-v4.svg" alt="ESP32 DevKit C V4" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">ESP32 DevKit C V4</span>
|
<span className="board-name-sm">ESP32 DevKit C V4</span>
|
||||||
<span className="board-chip-sm">LX6 · 4 MB flash</span>
|
<span className="board-chip-sm">LX6 · 4 MB flash</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/esp32-cam.svg" alt="ESP32-CAM" className="board-img-sm board-img-tall" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/esp32-cam.svg" alt="ESP32-CAM" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">ESP32-CAM</span>
|
<span className="board-name-sm">ESP32-CAM</span>
|
||||||
<span className="board-chip-sm">LX6 · camera module</span>
|
<span className="board-chip-sm">LX6 · camera module</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/esp32-s3.svg" alt="ESP32-S3" className="board-img-sm board-img-tall" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/esp32-s3.svg" alt="ESP32-S3" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">ESP32-S3 DevKit</span>
|
<span className="board-name-sm">ESP32-S3 DevKit</span>
|
||||||
<span className="board-chip-sm">LX7 · 4 MB flash</span>
|
<span className="board-chip-sm">LX7 · 4 MB flash</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/xiao-esp32-s3.svg" alt="XIAO ESP32-S3" className="board-img-sm" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/xiao-esp32-s3.svg" alt="XIAO ESP32-S3" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">XIAO ESP32-S3</span>
|
<span className="board-name-sm">XIAO ESP32-S3</span>
|
||||||
<span className="board-chip-sm">LX7 · compact</span>
|
<span className="board-chip-sm">LX7 · compact</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<img src="/boards/arduino-nano-esp32.svg" alt="Arduino Nano ESP32" className="board-img-sm board-img-tall" />
|
<div className="board-img-box">
|
||||||
|
<img src="/boards/arduino-nano-esp32.svg" alt="Arduino Nano ESP32" className="board-img-sm" />
|
||||||
|
</div>
|
||||||
<span className="board-name-sm">Arduino Nano ESP32</span>
|
<span className="board-name-sm">Arduino Nano ESP32</span>
|
||||||
<span className="board-chip-sm">LX7 · Nano form</span>
|
<span className="board-chip-sm">LX7 · Nano form</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -684,8 +690,8 @@ export const LandingPage: React.FC = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="boards-row">
|
<div className="boards-row">
|
||||||
<div className="board-card-sm">
|
<div className="board-card-sm">
|
||||||
<div className="board-img-wrap">
|
<div className="board-img-box">
|
||||||
<img src={raspberryPi3Svg} alt="Raspberry Pi 3" className="board-img-pi3" />
|
<img src={raspberryPi3Svg} alt="Raspberry Pi 3B" className="board-img-sm" />
|
||||||
</div>
|
</div>
|
||||||
<span className="board-name-sm">Raspberry Pi 3B</span>
|
<span className="board-name-sm">Raspberry Pi 3B</span>
|
||||||
<span className="board-chip-sm">Cortex-A53 · 1.2 GHz · Linux</span>
|
<span className="board-chip-sm">Cortex-A53 · 1.2 GHz · Linux</span>
|
||||||
|
|
|
||||||
|
|
@ -586,6 +586,8 @@ const ili9341Simulation = {
|
||||||
const pinManager = (avrSimulator as any).pinManager;
|
const pinManager = (avrSimulator as any).pinManager;
|
||||||
const spi = (avrSimulator as any).spi;
|
const spi = (avrSimulator as any).spi;
|
||||||
|
|
||||||
|
console.log('[ILI9341] attachEvents called. spi=', !!spi, 'pinManager=', !!pinManager, 'cpu=', !!(avrSimulator as any).cpu);
|
||||||
|
|
||||||
if (!pinManager || !spi) {
|
if (!pinManager || !spi) {
|
||||||
console.warn('[ILI9341] pinManager or SPI peripheral not available');
|
console.warn('[ILI9341] pinManager or SPI peripheral not available');
|
||||||
return () => {};
|
return () => {};
|
||||||
|
|
@ -622,6 +624,7 @@ const ili9341Simulation = {
|
||||||
let pendingFlush = false;
|
let pendingFlush = false;
|
||||||
let rafId: number | null = null;
|
let rafId: number | null = null;
|
||||||
|
|
||||||
|
let flushCount = 0;
|
||||||
const scheduleFlush = () => {
|
const scheduleFlush = () => {
|
||||||
if (rafId !== null) return;
|
if (rafId !== null) return;
|
||||||
rafId = requestAnimationFrame(() => {
|
rafId = requestAnimationFrame(() => {
|
||||||
|
|
@ -629,6 +632,10 @@ const ili9341Simulation = {
|
||||||
if (pendingFlush && ctx && imageData) {
|
if (pendingFlush && ctx && imageData) {
|
||||||
ctx.putImageData(imageData, 0, 0);
|
ctx.putImageData(imageData, 0, 0);
|
||||||
pendingFlush = false;
|
pendingFlush = false;
|
||||||
|
flushCount++;
|
||||||
|
if (flushCount === 1) console.log('[ILI9341] First canvas flush complete');
|
||||||
|
} else if (pendingFlush) {
|
||||||
|
console.warn('[ILI9341] Flush skipped: ctx=', !!ctx, 'imageData=', !!imageData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -681,6 +688,7 @@ const ili9341Simulation = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── Command / data processing ─────────────────────────────────────
|
// ── Command / data processing ─────────────────────────────────────
|
||||||
|
let ramwrPixelCount = 0;
|
||||||
const processCommand = (cmd: number) => {
|
const processCommand = (cmd: number) => {
|
||||||
currentCmd = cmd;
|
currentCmd = cmd;
|
||||||
dataBytes = [];
|
dataBytes = [];
|
||||||
|
|
@ -688,11 +696,15 @@ const ili9341Simulation = {
|
||||||
pixelByteCount = 0;
|
pixelByteCount = 0;
|
||||||
|
|
||||||
if (cmd === 0x01) { // SWRESET – clear framebuffer
|
if (cmd === 0x01) { // SWRESET – clear framebuffer
|
||||||
|
console.log('[ILI9341] SWRESET received, ctx=', !!ctx);
|
||||||
colStart = 0; colEnd = SCREEN_W - 1;
|
colStart = 0; colEnd = SCREEN_W - 1;
|
||||||
rowStart = 0; rowEnd = SCREEN_H - 1;
|
rowStart = 0; rowEnd = SCREEN_H - 1;
|
||||||
curX = 0; curY = 0;
|
curX = 0; curY = 0;
|
||||||
imageData = null;
|
imageData = null;
|
||||||
if (ctx) ctx.clearRect(0, 0, SCREEN_W, SCREEN_H);
|
if (ctx) ctx.clearRect(0, 0, SCREEN_W, SCREEN_H);
|
||||||
|
} else if (cmd === 0x2C) { // RAMWR
|
||||||
|
ramwrPixelCount = 0;
|
||||||
|
console.log('[ILI9341] RAMWR received, ctx=', !!ctx, 'col=', colStart, '-', colEnd, 'row=', rowStart, '-', rowEnd);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -706,6 +718,10 @@ const ili9341Simulation = {
|
||||||
writePixel(pixelHiByte, value);
|
writePixel(pixelHiByte, value);
|
||||||
scheduleFlush();
|
scheduleFlush();
|
||||||
pixelByteCount = 0;
|
pixelByteCount = 0;
|
||||||
|
ramwrPixelCount++;
|
||||||
|
if (ramwrPixelCount === 1) {
|
||||||
|
console.log('[ILI9341] First pixel written: rgb565=', ((pixelHiByte << 8) | value).toString(16));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -727,7 +743,12 @@ const ili9341Simulation = {
|
||||||
// ── Intercept SPI ─────────────────────────────────────────────────
|
// ── Intercept SPI ─────────────────────────────────────────────────
|
||||||
const prevOnByte = spi.onByte.bind(spi);
|
const prevOnByte = spi.onByte.bind(spi);
|
||||||
|
|
||||||
|
let spiByteCount = 0;
|
||||||
spi.onByte = (value: number) => {
|
spi.onByte = (value: number) => {
|
||||||
|
spiByteCount++;
|
||||||
|
if (spiByteCount === 1) {
|
||||||
|
console.log('[ILI9341] First SPI byte! value=0x' + value.toString(16) + ' dcState=' + dcState);
|
||||||
|
}
|
||||||
if (!dcState) {
|
if (!dcState) {
|
||||||
processCommand(value);
|
processCommand(value);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -736,7 +757,8 @@ const ili9341Simulation = {
|
||||||
spi.completeTransfer(0xFF); // Unblock CPU immediately
|
spi.completeTransfer(0xFF); // Unblock CPU immediately
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(`[ILI9341] SPI simulation ready. DC→pin${pinDC}`);
|
// Verify the override was applied (same object ref check)
|
||||||
|
console.log(`[ILI9341] SPI simulation ready. DC→pin${pinDC}. spi.onByte overridden=${spi.onByte !== prevOnByte}`);
|
||||||
|
|
||||||
// ── Cleanup ───────────────────────────────────────────────────────
|
// ── Cleanup ───────────────────────────────────────────────────────
|
||||||
return () => {
|
return () => {
|
||||||
|
|
|
||||||
|
|
@ -497,10 +497,10 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
||||||
|
|
||||||
set((s) => {
|
set((s) => {
|
||||||
const boards = s.boards.map((b) =>
|
const boards = s.boards.map((b) =>
|
||||||
b.id === boardId ? { ...b, running: true, serialMonitorOpen: true } : b
|
b.id === boardId ? { ...b, running: true } : b
|
||||||
);
|
);
|
||||||
const isActive = s.activeBoardId === boardId;
|
const isActive = s.activeBoardId === boardId;
|
||||||
return { boards, ...(isActive ? { running: true, serialMonitorOpen: true } : {}) };
|
return { boards, ...(isActive ? { running: true } : {}) };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue