Refactor code structure for improved readability and maintainability

pull/47/head
David Montero Crespo 2026-03-16 18:31:46 -03:00
parent 16164372cf
commit d35ad2d2f7
9 changed files with 217 additions and 139 deletions

BIN
docs/img5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

View File

@ -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",

View File

@ -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);

View File

@ -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: {},
}, },
], ],

View File

@ -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 813), PORTC (A0A5), PORTD (pins 07)</td></tr> <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>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 A0A5</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>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 (~1020 s) as a full OS must start</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> <li>Requires the ~5.67 GB base SD image to be present on the server</li>
</ul> </ul>

View File

@ -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;
} }

View File

@ -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>

View File

@ -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 () => {

View File

@ -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 } : {}) };
}); });
}, },