velxio/frontend
David Montero Crespo b35b5aead4 fix(firmware): explain the blank-NVS WiFi failure, and detect WiFi in stripped images (#270)
The reporter sent his three Visuino binaries, and running them settles what his
error was. It was never the network name, and it was not his delay() theory
either.

His first binary reproduces his exact report on the in-browser engine —
`[40][E][STA.cpp:357] connect(): STA connect failed! 0x300a: ESP_ERR_WIFI_SSID`,
timestamps and all — and his latest binary connects and prints its lease, the
same `NAN WiFi stop` line and IP his real-hardware screenshot shows. Same
emulator, same day. The difference is inside the firmware: the failing build
reaches esp_wifi_connect with no usable SSID, and a sketch calling
WiFi.begin() with NO arguments on this simulator produces his error verbatim —
verified. On his real DevKit that same call works, because begin() with no
credentials reuses whatever some earlier firmware saved in NVS, and his chip's
flash had a network in it. The simulator's flash starts blank. Nothing in the
output said any of that, and it cost him days.

So the serial monitor now says it: when `0x300a: ESP_ERR_WIFI_SSID` scrolls
past, one [velxio] line explains the NVS mechanism and gives the literal fix
(`WiFi.begin("Velxio-GUEST", "")`). Wired at the serial batcher, which both
backends feed; it fires once per run, re-arming naturally because a fresh run
starts a fresh log. The delay() theory was measured and does not hold — the
same sketch with and without delay(1) both associate, 48s vs 52s on the
engine, 8.0s vs 8.2s on QEMU.

His binaries also exposed d64b6796's detection as built on sand: it keyed on
Arduino's log strings ('WiFiGeneric', 'STA.cpp'), which vanish when a build
lowers its debug level — his latest image contains neither, so the note would
have stayed silent on precisely the firmware it was written for. The markers
are now IDF driver names (net80211, phy_init, esp_wifi, wifi_init), chosen by
measuring a blink build against a WiFi build: each is absent from the former
and present in the latter. WIFI_INIT looked obvious and is in BOTH, so it is
not on the list. Checked against all three of his images: WiFi detected, his
correct SSID found, note rightly silent.

Separate finding, out of scope here: his merged images reboot-loop on the QEMU
machine (flash-init assert). His build carries CONFIG_SPIRAM=y — the core 3.x
precompiled libs enable it regardless of the PSRAM=disabled menu choice — and
our own build with PSRAM enabled fails the same way on QEMU, which models no
PSRAM chip ('PSRAM ID read error: 0x00000000'). velxio.dev routes ESP32 to the
in-browser engine, where his images run, so this affects only the self-hosted
QEMU path.
2026-08-26 04:51:16 +02:00
..
public chore(examples): refresh 1 thumb file(s) [auto] 2026-08-24 23:10:22 +02:00
scripts prerender: SSR bodies were never injected; examples gallery renders on the server 2026-08-18 18:10:36 +02:00
src fix(firmware): explain the blank-NVS WiFi failure, and detect WiFi in stripped images (#270) 2026-08-26 04:51:16 +02:00
.env.production
…
.gitignore
…
.prettierignore
…
.prettierrc.json
…
Dockerfile
…
README.md
…
eslint.config.js
…
esp32 plan.md
…
index.html seo/analytics: real GA attribution, tight robots.txt, no build-date lastmod, GA only on velxio.dev 2026-08-18 16:04:48 +02:00
nginx.conf
…
package.json test: declare `canvas` so the jsdom tests get a real 2D context 2026-08-18 02:05:01 +02:00
tsconfig.app.json
…
tsconfig.json
…
tsconfig.node.json
…
vite.config.ts
…
vitest.config.ts test(config): stop running the rsynced copy of the pro overlay 2026-08-25 00:13:35 +02:00

README.md

Arduino Emulator - Frontend

React + TypeScript + Vite frontend for the Arduino emulator with visual simulator and code editor.

Features

  • Monaco Code Editor - Full VSCode-like Arduino code editing experience
  • Dynamic Component System - 48+ wokwi-elements components with search and categories
  • Visual Simulator Canvas - Interactive drag-and-drop circuit builder
  • Component Property Dialog - Single-click component interaction (rotate, delete, view pins)
  • Segment-Based Wire Editing - Drag wire segments perpendicular to orientation (like Wokwi)
  • Real AVR8 Emulation - Actual ATmega328p emulation using avr8js
  • Pin Management - Automatic pin mapping and state synchronization
  • Grid Snapping - 20px grid alignment for clean circuit layouts

Tech Stack

  • React 18 - UI framework
  • TypeScript - Static typing
  • Vite 5 - Build tool and dev server
  • Monaco Editor - Code editor (VSCode engine)
  • Zustand - State management
  • Axios - HTTP client for backend API
  • avr8js - AVR8 CPU emulator (npm package)
  • @wokwi/elements - Electronic web components (npm package)

Development

Prerequisites

Install Dependencies

npm install

Run Development Server

npm run dev

The app will be available at http://localhost:5173

Build for Production

npm run build

Output will be in the dist/ directory.

Lint

npm run lint

Project Structure

frontend/
├── src/
│   ├── components/
│   │   ├── velxio-components/    # React wrappers for wokwi-elements + Velxio-original parts
│   │   ├── editor/               # Monaco Editor components
│   │   │   ├── CodeEditor.tsx
│   │   │   └── EditorToolbar.tsx
│   │   └── simulator/            # Simulation canvas components
│   │       ├── SimulatorCanvas.tsx
│   │       ├── WireLayer.tsx
│   │       ├── WireRenderer.tsx
│   │       ├── PinOverlay.tsx
│   │       ├── ComponentPropertyDialog.tsx
│   │       ├── ComponentPickerModal.tsx
│   │       └── ComponentPalette.tsx
│   ├── simulation/
│   │   ├── AVRSimulator.ts       # AVR8 CPU wrapper
│   │   └── PinManager.ts         # Pin mapping and callbacks
│   ├── store/
│   │   ├── useEditorStore.ts     # Code editor state
│   │   └── useSimulatorStore.ts  # Simulation state
│   ├── services/
│   │   ├── api.ts                # Backend API client
│   │   └── ComponentRegistry.ts  # Component metadata
│   ├── types/                    # TypeScript definitions
│   ├── utils/
│   │   ├── hexParser.ts          # Intel HEX parser
│   │   ├── wirePathGenerator.ts  # Wire SVG path generation
│   │   └── wireSegments.ts       # Segment-based wire editing
│   ├── App.tsx                   # Main app component
│   └── main.tsx                  # Entry point
├── public/                       # Static assets
├── vite.config.ts               # Vite configuration
└── package.json

Key Architecture Patterns

State Management (Zustand)

Two main stores:

  • useEditorStore - Code content, theme, compilation state
  • useSimulatorStore - Simulation running state, components, wires, compiled hex

Wokwi Libraries

@wokwi/elements, avr8js and rp2040js are regular npm dependencies resolved from node_modules like any other package — no local clones required.

AVR Simulation Loop

  • Runs at ~60 FPS using requestAnimationFrame
  • Executes ~267,000 CPU cycles per frame (16MHz / 60fps)
  • Port listeners fire when GPIO registers change
  • PinManager routes pin states to component callbacks

Component System

Components are Web Components from wokwi-elements:

  1. React wrappers in velxio-components/
  2. Dynamic loading via ComponentRegistry
  3. Pin info extracted from component metadata
  4. State updates via refs and callbacks

Wire Editing System

Segment-based editing (like Wokwi):

  • Wires consist of orthogonal segments (horizontal/vertical)
  • Drag segments perpendicular to orientation:
    • Horizontal segments: move up/down (ns-resize)
    • Vertical segments: move left/right (ew-resize)
  • Local preview state during drag (requestAnimationFrame)
  • Store update only on mouse up with grid snapping (20px)

Performance Optimizations

  • requestAnimationFrame for smooth wire dragging
  • Local state for real-time previews
  • Memoized path generation and segment computation
  • Store updates batched at interaction completion

API Integration

Backend endpoints (http://localhost:8001):

  • POST /api/compile - Compile Arduino code to .hex
  • GET /api/compile/status/{task_id} - Check compilation status
  • GET /api/compile/download/{filename} - Download compiled .hex

See backend documentation for API details.

Component Development

Adding a New Component Type

  1. Check if wokwi-elements has the component:

    ls ../third-party/wokwi-elements/src/
    
  2. Create React wrapper in src/components/velxio-components/:

    import React, { useRef, useEffect } from 'react';
    
    export const WokwiMyComponent: React.FC<Props> = ({ ... }) => {
      const elementRef = useRef<any>(null);
    
      useEffect(() => {
        if (elementRef.current) {
          elementRef.current.setAttribute('prop', value);
        }
      }, [value]);
    
      return <wokwi-my-component ref={elementRef} />;
    };
    
  3. Add to ComponentRegistry metadata

  4. Use in SimulatorCanvas or make available in ComponentPalette

Troubleshooting

Monaco Editor Not Loading

  • Check if monaco-editor is installed
  • Verify Vite worker configuration in vite.config.ts

Components Not Rendering

  • Ensure wokwi-elements is built: cd ../third-party/wokwi-elements && npm run build
  • Check browser console for Web Component registration errors
  • Verify Vite alias paths in vite.config.ts

Wire Editing Performance Issues

  • Ensure requestAnimationFrame is being used
  • Check that store updates only happen on mouse up, not during drag
  • Verify no unnecessary re-renders with React DevTools

Pin Alignment Issues

  • Pin coordinates from wokwi-elements are in CSS pixels
  • Do NOT multiply by MM_TO_PX conversion factor
  • Verify component position + pin offset calculation

Compilation Fails

  • Check backend is running at http://localhost:8001
  • Verify arduino-cli is installed and arduino:avr core is available
  • Check CORS configuration in backend

References