diff --git a/docs/img5.png b/docs/img5.png
new file mode 100644
index 0000000..5af5ac2
Binary files /dev/null and b/docs/img5.png differ
diff --git a/frontend/public/components-metadata.json b/frontend/public/components-metadata.json
index 605712c..ffdd945 100644
--- a/frontend/public/components-metadata.json
+++ b/frontend/public/components-metadata.json
@@ -1,6 +1,6 @@
{
"version": "1.0.0",
- "generatedAt": "2026-03-14T18:11:39.708Z",
+ "generatedAt": "2026-03-16T21:03:46.561Z",
"components": [
{
"id": "arduino-mega",
diff --git a/frontend/src/components/simulator/SimulatorCanvas.tsx b/frontend/src/components/simulator/SimulatorCanvas.tsx
index fa0ff72..2111b1b 100644
--- a/frontend/src/components/simulator/SimulatorCanvas.tsx
+++ b/frontend/src/components/simulator/SimulatorCanvas.tsx
@@ -900,7 +900,7 @@ export const SimulatorCanvas = () => {
return () => timers.forEach(t => clearTimeout(t));
}, [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
// jumps (indicating the user loaded a new circuit, not just added one part).
const prevComponentCountRef = useRef(-1);
@@ -919,9 +919,21 @@ export const SimulatorCanvas = () => {
if (!canvas) return;
const rect = canvas.getBoundingClientRect();
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 = {
- x: rect.width / 4 - boardPosition.x * currentZoom,
- y: rect.height / 4 - boardPosition.y * currentZoom,
+ x: rect.width / 2 - centerX * currentZoom,
+ y: rect.height / 2 - centerY * currentZoom,
};
panRef.current = newPan;
setPan(newPan);
diff --git a/frontend/src/data/examples.ts b/frontend/src/data/examples.ts
index fd2bf2f..0c205c2 100644
--- a/frontend/src/data/examples.ts
+++ b/frontend/src/data/examples.ts
@@ -652,8 +652,8 @@ void loop() {
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
-// Background color: dark blue
-#define BG_COLOR 0x0006
+// Background color: blue (visible on dark simulator canvas)
+#define BG_COLOR 0x001F
// Ball state
int ballX = 120, ballY = 200;
@@ -724,8 +724,8 @@ void loop() {
{
type: 'wokwi-ili9341',
id: 'tft1',
- x: 480,
- y: 60,
+ x: 300,
+ y: 30,
properties: {},
},
],
diff --git a/frontend/src/pages/DocsPage.tsx b/frontend/src/pages/DocsPage.tsx
index 85b1709..2b646fb 100644
--- a/frontend/src/pages/DocsPage.tsx
+++ b/frontend/src/pages/DocsPage.tsx
@@ -72,55 +72,55 @@ const NAV_ITEMS: NavItem[] = [
interface SectionMeta { title: string; description: string; }
const SECTION_META: Record = {
'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.',
},
'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.',
},
'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.',
},
'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.',
},
'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.',
},
'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.',
},
'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.",
},
'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.',
},
'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.',
},
'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.',
},
'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.',
},
'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.',
},
'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.',
},
};
@@ -133,16 +133,16 @@ const IntroSection: React.FC = () => (
Velxio is a fully local, open-source Arduino emulator that runs entirely in your browser.
Write Arduino C++ code, compile it with a real arduino-cli backend, and simulate it using
- true AVR8 / RP2040 CPU emulation — with 48+ interactive electronic components, all without installing
+ true AVR8 / RP2040 CPU emulation, with 48+ interactive electronic components, all without installing
any software on your machine.
Why Velxio?
- - No installation required — everything runs in the browser.
- - Real emulation — not a simplified model, but accurate AVR8 / RP2040 CPU emulation.
- - Interactive components — LEDs, buttons, potentiometers, displays, sensors, and more.
- - Open-source — inspect, modify, and self-host it yourself.
+ - No installation required: everything runs in the browser.
+ - Real emulation: not a simplified model, but accurate AVR8 / RP2040 CPU emulation.
+ - Interactive components: LEDs, buttons, potentiometers, displays, sensors, and more.
+ - Open-source: inspect, modify, and self-host it yourself.
Supported Boards
@@ -163,7 +163,7 @@ const IntroSection: React.FC = () => (
Live Demo:{' '}
velxio.dev
- {' '}— no installation needed, open the editor and start simulating immediately.
+ {' '}, no installation needed, open the editor and start simulating immediately.
);
@@ -176,7 +176,7 @@ const GettingStartedSection: React.FC = () => (
Option 1: Use the Hosted Version
- No installation needed — go to{' '}
+ No installation needed, go to{' '}
https://velxio.dev{' '}
and start coding immediately.
@@ -235,8 +235,8 @@ void loop() {
delay(500);
}`}
- - Click Compile — the backend calls
arduino-cli and returns a .hex file.
- - Click Run — the AVR8 emulator executes the compiled program.
+ - Click Compile: the backend calls
arduino-cli and returns a .hex file.
+ - Click Run: the AVR8 emulator executes the compiled program.
- Add components using the component picker (click the + button on the canvas).
- Connect wires by clicking a component pin and then another pin.
@@ -317,7 +317,7 @@ cpu.tick(); // advance peripheral timers and counters`}
| GPIO | PORTB (pins 8–13), PORTC (A0–A5), PORTD (pins 0–7) |
| Timer0 / Timer1 / Timer2 | millis(), delay(), PWM via analogWrite() |
- | USART | Full transmit and receive — powers the Serial Monitor |
+ | USART | Full transmit and receive, powers the Serial Monitor |
| ADC | 10-bit, 5 V reference on pins A0–A5 |
| SPI | Hardware SPI (enables ILI9341, SD card, etc.) |
| I2C (TWI) | Hardware I2C with virtual device bus |
@@ -392,7 +392,7 @@ const ComponentsSection: React.FC = () => (
Connecting Components
- - Click a pin on any component — a wire starts from that pin.
+ - Click a pin on any component, a wire starts from that pin.
- Click a destination pin to complete the connection.
- Wires are color-coded by signal type:
@@ -488,7 +488,7 @@ const RoadmapSection: React.FC = () => (
✅ Implemented
- Example — Blink LED from Scratch
+ Example: Blink LED from Scratch
{`// Step 1 — Create a circuit
{
"tool": "create_circuit",
@@ -1048,7 +1048,7 @@ const RiscVEmulationSection: React.FC = () => (
RISC-V Emulation (ESP32-C3)
ESP32-C3, XIAO ESP32-C3, and C3 SuperMini boards use a RISC-V RV32IMC core running at
- 160 MHz. Velxio emulates them entirely in the browser — no backend, no QEMU, no WebAssembly pipeline.
+ 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.
@@ -1095,9 +1095,9 @@ const RiscVEmulationSection: React.FC = () => (
ISA Support
- - RV32I — Full base integer instruction set (ALU, load/store, branches, JAL/JALR)
- - RV32M — Multiply/divide: MUL, MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU
- - RV32C — 16-bit compressed instructions: C.LI, C.ADDI, C.LUI, C.J, C.JAL, C.BEQZ,
+
- RV32I: Full base integer instruction set (ALU, load/store, branches, JAL/JALR)
+ - RV32M: Multiply/divide: MUL, MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU
+ - RV32C: 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
@@ -1165,14 +1165,14 @@ const Esp32EmulationSection: React.FC = () => (
ESP32 and ESP32-S3 boards use an Xtensa LX6 / LX7 architecture. Because no
production-quality Xtensa emulator is available as pure JavaScript, Velxio uses a
- QEMU-based backend for these boards — the lcgamboa fork with
+ QEMU-based backend for these boards, the lcgamboa fork with
libqemu-xtensa, compiled to a native binary and served by the FastAPI backend.
Note: This section applies only to ESP32 and ESP32-S3 (Xtensa).
For ESP32-C3, XIAO ESP32-C3, and C3 SuperMini (RISC-V), see{' '}
- RISC-V Emulation (ESP32-C3) in the sidebar — those boards run entirely in the browser.
+ RISC-V Emulation (ESP32-C3) in the sidebar, those boards run entirely in the browser.
How It Works
@@ -1219,12 +1219,12 @@ const Esp32EmulationSection: React.FC = () => (
Peripheral Support
- - GPIO — digital output / input, LED control
- - UART — Serial Monitor via
Serial.print()
- - I2C / SPI — peripheral communication
- - RMT / NeoPixel — addressable LED strips
- - LEDC / PWM — hardware PWM channels
- - WiFi — partial (connection events forwarded)
+ - GPIO: digital output / input, LED control
+ - UART: Serial Monitor via
Serial.print()
+ - I2C / SPI: peripheral communication
+ - RMT / NeoPixel: addressable LED strips
+ - LEDC / PWM: hardware PWM channels
+ - WiFi: partial (connection events forwarded)
Requirements
@@ -1253,7 +1253,7 @@ const Rp2040EmulationSection: React.FC = () => (
The Raspberry Pi Pico and Pico W are emulated entirely in the browser using{' '}
rp2040js,
- an open-source ARM Cortex-M0+ emulator. No QEMU or backend process is required — the binary runs at full speed inside a Web Worker.
+ an open-source ARM Cortex-M0+ emulator. No QEMU or backend process is required, the binary runs at full speed inside a Web Worker.
Supported Boards
@@ -1278,7 +1278,7 @@ const Rp2040EmulationSection: React.FC = () => (
Binary Format
The backend compiles the sketch with arduino-cli targeting rp2040:rp2040:rpipico and returns
- a raw ARM binary (.bin) encoded in base64. Unlike AVR, there is no Intel HEX — the binary is loaded
+ a raw ARM binary (.bin) encoded in base64. Unlike AVR, there is no Intel HEX, the binary is loaded
directly into the RP2040 flash at offset 0.
@@ -1316,11 +1316,11 @@ const Rp2040EmulationSection: React.FC = () => (
Known Limitations
- - Pico W wireless chip (CYW43439) is not emulated — WiFi/Bluetooth will not work
- - SPI loopback only — no real SPI device emulation
+ - Pico W wireless chip (CYW43439) is not emulated, WiFi/Bluetooth will not work
+ - SPI loopback only, no real SPI device emulation
- PWM produces correct frequency but no visual waveform on components
- DMA not emulated
- - Second CPU core (core 1) not emulated —
multicore_launch_core1() has no effect
+ - Second CPU core (core 1) not emulated,
multicore_launch_core1() has no effect
Full Documentation
@@ -1340,7 +1340,7 @@ const RaspberryPi3EmulationSection: React.FC = () => (
Raspberry Pi 3 Emulation (QEMU)
The Raspberry Pi 3B is emulated using QEMU 8.1.3 with -M raspi3b.
- This is the only board in Velxio that runs a full operating system — a real{' '}
+ This is the only board in Velxio that runs a full operating system, a real{' '}
Raspberry Pi OS (Trixie) image booted inside the emulator.
Users write Python scripts (not C++), which are executed by the real Python 3 interpreter inside the VM.
@@ -1364,8 +1364,8 @@ const RaspberryPi3EmulationSection: React.FC = () => (
QEMU exposes two UART channels to the backend:
- - ttyAMA0 — User serial: interactive terminal (Serial Monitor). The user's
print() output appears here.
- - ttyAMA1 — GPIO shim: carries a text protocol between the GPIO shim inside the VM and the backend.
+ - ttyAMA0: User serial: interactive terminal (Serial Monitor). The user's
print() output appears here.
+ - ttyAMA1: GPIO shim: carries a text protocol between the GPIO shim inside the VM and the backend.
RPi.GPIO Shim
@@ -1402,7 +1402,7 @@ const RaspberryPi3EmulationSection: React.FC = () => (
No I2C or SPI device emulation
No PWM waveform output to components
No networking (WiFi/Ethernet not emulated)
- Session state is not persisted — overlay is discarded on stop
+ Session state is not persisted, overlay is discarded on stop
Boot time is slow (~10–20 s) as a full OS must start
Requires the ~5.67 GB base SD image to be present on the server
diff --git a/frontend/src/pages/LandingPage.css b/frontend/src/pages/LandingPage.css
index c24df59..48bd0df 100644
--- a/frontend/src/pages/LandingPage.css
+++ b/frontend/src/pages/LandingPage.css
@@ -238,19 +238,23 @@
/* ── Hero ─────────────────────────────────────────────── */
.landing-hero {
display: grid;
- grid-template-columns: 1fr 1.3fr;
- align-items: center;
- gap: 56px;
- padding: 80px 80px 80px 80px;
- max-width: 1400px;
- margin: 0 auto;
- min-height: 90vh;
+ grid-template-columns: 1fr 1.2fr;
+ align-items: stretch;
+ gap: 0;
+ min-height: calc(100vh - 52px);
+ width: 100%;
+ max-width: none;
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
}
.hero-left {
display: flex;
flex-direction: column;
+ justify-content: center;
gap: 0;
+ padding: 80px 60px 80px 80px;
}
.hero-eyebrow {
@@ -370,11 +374,11 @@
display: none;
}
-/* Hero right — schematic */
+/* Hero right — image full-bleed */
.hero-right {
- display: flex;
- align-items: center;
- justify-content: center;
+ position: relative;
+ overflow: hidden;
+ min-height: calc(100vh - 52px);
}
.schematic-svg {
@@ -386,11 +390,12 @@
}
.hero-preview-img {
+ position: absolute;
+ inset: 0;
width: 100%;
- max-width: 100%;
- height: auto;
- border-radius: var(--radius);
- filter: drop-shadow(0 24px 64px rgba(0, 0, 0, 0.75));
+ height: 100%;
+ object-fit: cover;
+ object-position: center;
}
/* ── Sections ─────────────────────────────────────────── */
@@ -495,12 +500,12 @@
/* Apple product tile */
.board-card-sm {
background: #141416;
- border-radius: 20px;
- padding: 28px 14px 22px;
+ border-radius: 16px;
+ padding: 20px 14px 18px;
display: flex;
flex-direction: column;
align-items: center;
- gap: 14px;
+ gap: 10px;
transition: transform 0.32s cubic-bezier(0.34, 1.46, 0.64, 1),
box-shadow 0.32s ease;
cursor: default;
@@ -531,6 +536,32 @@
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 {
width: 80px;
height: auto;
@@ -898,9 +929,11 @@
/* ── Responsive ───────────────────────────────────────── */
@media (max-width: 1024px) {
.landing-hero {
- grid-template-columns: 1fr 1.2fr;
- gap: 40px;
- padding: 72px 48px;
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .hero-left {
+ padding: 72px 40px 72px 48px;
}
.landing-section {
@@ -925,13 +958,18 @@
@media (max-width: 900px) {
.landing-hero {
grid-template-columns: 1fr;
- padding: 64px 32px;
min-height: auto;
- gap: 44px;
+ }
+
+ .hero-left {
+ padding: 64px 32px;
+ order: 1;
}
.hero-right {
order: -1;
+ min-height: 55vw;
+ max-height: 480px;
}
.schematic-svg {
@@ -963,7 +1001,7 @@
display: none;
}
- .landing-hero {
+ .hero-left {
padding: 52px 20px;
}
diff --git a/frontend/src/pages/LandingPage.tsx b/frontend/src/pages/LandingPage.tsx
index d0f0183..32f4154 100644
--- a/frontend/src/pages/LandingPage.tsx
+++ b/frontend/src/pages/LandingPage.tsx
@@ -508,7 +508,6 @@ export const LandingPage: React.FC = () => {
{/* Hero */}
-
Open Source · Free · Runs Locally
Emulate Hardware.
In your browser.
@@ -526,14 +525,7 @@ export const LandingPage: React.FC = () => {
View on GitHub
-
- ATmega328p
- RP2040
- RV32IMC
- Xtensa LX6/LX7
- ARM Cortex-A53
- 48+ components
-
+

@@ -556,30 +548,24 @@ export const LandingPage: React.FC = () => {
-
-
-
+
Arduino Uno
ATmega328p · 32 KB
-
-
-
+
Arduino Nano
ATmega328p · 32 KB
-
-
-
+
Arduino Mega 2560
ATmega2560 · 256 KB
-
+
ATtiny85
-
ATtiny85 · 8 KB
+
AVR · 8 KB · DIP-8
@@ -592,12 +578,16 @@ export const LandingPage: React.FC = () => {
-

+
+

+
Raspberry Pi Pico
RP2040 · 264 KB RAM
-

+
+

+
Raspberry Pi Pico W
RP2040 + WiFi
@@ -612,22 +602,28 @@ export const LandingPage: React.FC = () => {
-

+
+

+
ESP32-C3 DevKit
RV32IMC · 4 MB flash
-

+
+

+
XIAO ESP32-C3
RV32IMC · compact
-

+
+

+
ESP32-C3 SuperMini
RV32IMC · mini form
-
+
CH32V003 (RISC-V)
RV32EC · 48 MHz
@@ -642,34 +638,44 @@ export const LandingPage: React.FC = () => {
-
-
+
+
ESP32 DevKit V1
LX6 · 4 MB flash
-

+
+

+
ESP32 DevKit C V4
LX6 · 4 MB flash
-

+
+

+
ESP32-CAM
LX6 · camera module
-

+
+

+
ESP32-S3 DevKit
LX7 · 4 MB flash
-

+
+

+
XIAO ESP32-S3
LX7 · compact
-

+
+

+
Arduino Nano ESP32
LX7 · Nano form
@@ -684,8 +690,8 @@ export const LandingPage: React.FC = () => {
-
-

+
+
Raspberry Pi 3B
Cortex-A53 · 1.2 GHz · Linux
diff --git a/frontend/src/simulation/parts/ComplexParts.ts b/frontend/src/simulation/parts/ComplexParts.ts
index ae54a9d..3c0b49b 100644
--- a/frontend/src/simulation/parts/ComplexParts.ts
+++ b/frontend/src/simulation/parts/ComplexParts.ts
@@ -586,6 +586,8 @@ const ili9341Simulation = {
const pinManager = (avrSimulator as any).pinManager;
const spi = (avrSimulator as any).spi;
+ console.log('[ILI9341] attachEvents called. spi=', !!spi, 'pinManager=', !!pinManager, 'cpu=', !!(avrSimulator as any).cpu);
+
if (!pinManager || !spi) {
console.warn('[ILI9341] pinManager or SPI peripheral not available');
return () => {};
@@ -622,6 +624,7 @@ const ili9341Simulation = {
let pendingFlush = false;
let rafId: number | null = null;
+ let flushCount = 0;
const scheduleFlush = () => {
if (rafId !== null) return;
rafId = requestAnimationFrame(() => {
@@ -629,6 +632,10 @@ const ili9341Simulation = {
if (pendingFlush && ctx && imageData) {
ctx.putImageData(imageData, 0, 0);
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 ─────────────────────────────────────
+ let ramwrPixelCount = 0;
const processCommand = (cmd: number) => {
currentCmd = cmd;
dataBytes = [];
@@ -688,11 +696,15 @@ const ili9341Simulation = {
pixelByteCount = 0;
if (cmd === 0x01) { // SWRESET – clear framebuffer
+ console.log('[ILI9341] SWRESET received, ctx=', !!ctx);
colStart = 0; colEnd = SCREEN_W - 1;
rowStart = 0; rowEnd = SCREEN_H - 1;
curX = 0; curY = 0;
imageData = null;
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);
scheduleFlush();
pixelByteCount = 0;
+ ramwrPixelCount++;
+ if (ramwrPixelCount === 1) {
+ console.log('[ILI9341] First pixel written: rgb565=', ((pixelHiByte << 8) | value).toString(16));
+ }
}
return;
}
@@ -727,7 +743,12 @@ const ili9341Simulation = {
// ── Intercept SPI ─────────────────────────────────────────────────
const prevOnByte = spi.onByte.bind(spi);
+ let spiByteCount = 0;
spi.onByte = (value: number) => {
+ spiByteCount++;
+ if (spiByteCount === 1) {
+ console.log('[ILI9341] First SPI byte! value=0x' + value.toString(16) + ' dcState=' + dcState);
+ }
if (!dcState) {
processCommand(value);
} else {
@@ -736,7 +757,8 @@ const ili9341Simulation = {
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 ───────────────────────────────────────────────────────
return () => {
diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts
index d780647..57bef45 100644
--- a/frontend/src/store/useSimulatorStore.ts
+++ b/frontend/src/store/useSimulatorStore.ts
@@ -497,10 +497,10 @@ export const useSimulatorStore = create
((set, get) => {
set((s) => {
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;
- return { boards, ...(isActive ? { running: true, serialMonitorOpen: true } : {}) };
+ return { boards, ...(isActive ? { running: true } : {}) };
});
},