From d81aa76260bc6e02d694ca3f09d202bd4426d6da Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Wed, 4 Mar 2026 18:39:02 -0300 Subject: [PATCH] feat: complete project setup with Wokwi libraries integration and documentation - Removed outdated WOKWI_LIBS.md and replaced with updated documentation. - Added ARCHITECTURE.md to describe project structure and data flow. - Created SETUP_COMPLETE.md for installation and configuration instructions. - Implemented automatic update script for Wokwi libraries. - Updated frontend components to utilize local Wokwi libraries. - Enhanced AVRSimulator to manage peripherals more efficiently. - Added example screenshot generation instructions for better documentation. - Updated components metadata and ensured proper integration with Vite. --- doc/ARCHITECTURE.md | 321 ++++++++++++++++++++ SETUP_COMPLETE.md => doc/SETUP_COMPLETE.md | 0 WOKWI_LIBS.md => doc/WOKWI_LIBS.md | 0 frontend/public/components-metadata.json | 2 +- frontend/src/simulation/AVRSimulator.ts | 32 +- frontend/src/simulation/parts/BasicParts.ts | 1 - scripts/generate-example-screenshots.md | 104 +++++++ 7 files changed, 443 insertions(+), 17 deletions(-) create mode 100644 doc/ARCHITECTURE.md rename SETUP_COMPLETE.md => doc/SETUP_COMPLETE.md (100%) rename WOKWI_LIBS.md => doc/WOKWI_LIBS.md (100%) create mode 100644 scripts/generate-example-screenshots.md diff --git a/doc/ARCHITECTURE.md b/doc/ARCHITECTURE.md new file mode 100644 index 00000000..22afff0f --- /dev/null +++ b/doc/ARCHITECTURE.md @@ -0,0 +1,321 @@ +# Project Architecture - Arduino Emulator + +## Overview + +This project is a fully local Arduino emulator using official Wokwi repositories for maximum compatibility. + +``` +┌─────────────────────────────────────────────────────────────┐ +│ USER (Browser) │ +│ http://localhost:5173 │ +└──────────────────────────┬──────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ FRONTEND (React + Vite) │ +│ │ +│ ┌────────────────┐ ┌──────────────────┐ ┌─────────────┐ │ +│ │ Monaco Editor │ │ Zustand Store │ │ Simulator │ │ +│ │ (Code Edit) │ │ (State Mgmt) │ │ Canvas │ │ +│ └────────────────┘ └──────────────────┘ └─────────────┘ │ +│ │ │ │ │ +│ └───────────────────┴──────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────────────────────────────────────┐ │ +│ │ Wokwi Components Integration │ │ +│ │ (wokwi-elements + avr8js from local repos) │ │ +│ └──────────────────────────────────────────────────────┘ │ +└──────────────────────────┬──────────────────────────────────┘ + │ HTTP (axios) + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ BACKEND (FastAPI + Python) │ +│ http://localhost:8000 │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ POST /api/compile │ │ +│ │ - Receives Arduino code (.ino) │ │ +│ │ - Compiles with arduino-cli │ │ +│ │ - Returns .hex file │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Arduino CLI Service │ │ +│ │ (Invokes arduino-cli as subprocess) │ │ +│ └─────────────────────────────────────────────────────┘ │ +└──────────────────────────┬──────────────────────────────────┘ + │ + ▼ + ┌──────────────────────┐ + │ arduino-cli │ + │ (Local system) │ + └──────────────────────┘ +``` + +## Data Flow: Compilation and Simulation + +### 1. Code Editing +``` +User writes code + ↓ +Monaco Editor + ↓ +Zustand (useEditorStore) + ↓ +State: code +``` + +### 2. Compilation +``` +Click "Compile" + ↓ +EditorToolbar.tsx → compileCode() + ↓ +Axios POST → http://localhost:8000/api/compile + ↓ +Backend: ArduinoCLIService.compile() + ↓ +arduino-cli compile --fqbn arduino:avr:uno + ↓ +Generates .hex file in temp directory + ↓ +Backend reads .hex and returns content + ↓ +Frontend: useSimulatorStore.setCompiledHex() +``` + +### 3. Simulation (Currently simplified) +``` +Click "Run" + ↓ +useSimulatorStore.setRunning(true) + ↓ +SimulatorCanvas: useEffect detects running=true + ↓ +setInterval every 1000ms + ↓ +updateComponentState('led-builtin', !state) + ↓ +wokwi-led component updates visually +``` + +### 4. Real Simulation (Coming with avr8js) +``` +Compiled .hex file + ↓ +AVRSimulator.loadHex(hex) + ↓ +hexParser → Uint16Array (program memory) + ↓ +CPU = new CPU(program) + ↓ +Click "Run" → AVRSimulator.start() + ↓ +requestAnimationFrame loop + ↓ +CPU.tick() × 267,000 cycles/frame + ↓ +Writes to PORTB/PORTC/PORTD + ↓ +Write hooks → PinManager.updatePort() + ↓ +PinManager notifies callbacks + ↓ +Components update visual state +``` + +## Key Components + +### Frontend + +#### 1. Stores (Zustand) +- **[useEditorStore.ts](frontend/src/store/useEditorStore.ts)** + - `code`: Current source code + - `theme`: Editor theme (dark/light) + - `setCode()`: Update code + +- **[useSimulatorStore.ts](frontend/src/store/useSimulatorStore.ts)** + - `running`: Simulation state + - `compiledHex`: Compiled hex file + - `components`: List of electronic components + - `setCompiledHex()`: Save hex + - `updateComponentState()`: Update LED/component + +#### 2. UI Components +- **[CodeEditor.tsx](frontend/src/components/editor/CodeEditor.tsx)** + - Monaco Editor wrapper + - C++ syntax highlighting + - Auto-completion + +- **[EditorToolbar.tsx](frontend/src/components/editor/EditorToolbar.tsx)** + - Buttons: Compile, Run, Stop + - Compilation state handling + - Error/success messages + +- **[SimulatorCanvas.tsx](frontend/src/components/simulator/SimulatorCanvas.tsx)** + - Renders Arduino Uno + - Renders components (LEDs) + - Simulation loop + +#### 3. Wokwi Component Wrappers +- **[LED.tsx](frontend/src/components/components-wokwi/LED.tsx)** + - React wrapper for `` + - Props: color, value, x, y + +- **[ArduinoUno.tsx](frontend/src/components/components-wokwi/ArduinoUno.tsx)** + - React wrapper for `` + - Internal LED control (pin 13) + +- **[Resistor.tsx](frontend/src/components/components-wokwi/Resistor.tsx)** + - React wrapper for `` + - Props: value (ohms) + +- **[Pushbutton.tsx](frontend/src/components/components-wokwi/Pushbutton.tsx)** + - React wrapper for `` + - Events: onPress, onRelease + +### Backend + +#### 1. API Routes +- **[compile.py](backend/app/api/routes/compile.py)** + - `POST /api/compile`: Compile code + - `GET /api/compile/boards`: List boards + +#### 2. Services +- **[arduino_cli.py](backend/app/services/arduino_cli.py)** + - `compile()`: Compile sketch with arduino-cli + - `list_boards()`: Get available boards + - Temporary directory management + +### Wokwi Libraries (Cloned Locally) + +#### 1. wokwi-elements +- **Location**: `wokwi-libs/wokwi-elements/` +- **Build**: `dist/esm/` and `dist/cjs/` +- **Components**: 50+ electronic elements +- **Technology**: Lit (Web Components) + +#### 2. avr8js +- **Location**: `wokwi-libs/avr8js/` +- **Build**: `dist/esm/` and `dist/cjs/` +- **Functionality**: Complete ATmega328p emulator +- **Supports**: CPU, Timers, USART, GPIO, ADC, etc. + +#### 3. rp2040js +- **Location**: `wokwi-libs/rp2040js/` +- **Future use**: Raspberry Pi Pico support + +## Vite Integration + +### Alias Configuration +```typescript +// vite.config.ts +resolve: { + alias: { + 'avr8js': path.resolve(__dirname, '../wokwi-libs/avr8js/dist/esm'), + '@wokwi/elements': path.resolve(__dirname, '../wokwi-libs/wokwi-elements/dist/esm'), + }, +} +``` + +This allows: +- Use local repos instead of npm +- Easy updates with `git pull` +- Modify source code if needed + +## Technology Stack + +### Frontend +| Technology | Version | Purpose | +|------------|---------|-----------| +| React | 19.2 | UI framework | +| Vite | 7.3 | Build tool & dev server | +| TypeScript | 5.9 | Static typing | +| Monaco Editor | 4.7 | Code editor (VSCode) | +| Zustand | 5.0 | State management | +| Axios | 1.13 | HTTP client | +| wokwi-elements | 1.9.2 | Electronic components | +| avr8js | 0.21.0 | AVR8 emulator | + +### Backend +| Technology | Version | Purpose | +|------------|---------|-----------| +| Python | 3.12+ | Runtime | +| FastAPI | 0.115 | Web framework | +| Uvicorn | 0.32 | ASGI server | +| SQLAlchemy | 2.0 | ORM (future) | +| aiosqlite | 0.20 | Async DB (future) | + +### External Tools +| Tool | Purpose | +|------|---------| +| arduino-cli | Arduino compiler | +| Git | Version control for Wokwi libs | + +## Architecture Advantages + +### ✅ Separation of Concerns +- **Frontend**: UI, UX, visualization +- **Backend**: Compilation, business logic +- **Wokwi Libs**: Emulation and components (maintained by Wokwi) + +### ✅ Wokwi Compatibility +- Official repositories = same functionality +- Automatic updates with `git pull` +- New components available immediately + +### ✅ Scalability +- Frontend can easily add more components +- Backend can add more endpoints (projects, sensors) +- Wokwi libs update independently + +### ✅ Local Development +- No internet required to work +- Local compilation with arduino-cli +- Local database (SQLite) + +## Upcoming Improvements + +### Phase 2: Real Emulation (avr8js) +``` +[ ] Implement AVRSimulator.ts +[ ] Intel HEX file parser +[ ] PinManager with write hooks +[ ] Integrate CPU execution loop +[ ] Map Arduino pins to components +``` + +### Phase 3: More Components +``` +[ ] Integrate more wokwi-elements +[ ] Buttons, potentiometers +[ ] Sensors (DHT22, HC-SR04) +[ ] Displays (LCD, 7-segment) +``` + +### Phase 4: Persistence +``` +[ ] SQLite database +[ ] SQLAlchemy models +[ ] Project CRUD +[ ] Save circuits as JSON +``` + +### Phase 5: Advanced Features +``` +[ ] Serial Monitor +[ ] Visual wiring (drag & drop) +[ ] Multiple boards (Mega, Nano, ESP32) +[ ] Export to Wokwi.com +``` + +## References + +- [Wokwi Elements Repo](https://github.com/wokwi/wokwi-elements) +- [AVR8js Repo](https://github.com/wokwi/avr8js) +- [Wokwi Simulator](https://wokwi.com) +- [Arduino CLI](https://arduino.github.io/arduino-cli/) +- [FastAPI Docs](https://fastapi.tiangolo.com/) +- [Vite Docs](https://vitejs.dev/) diff --git a/SETUP_COMPLETE.md b/doc/SETUP_COMPLETE.md similarity index 100% rename from SETUP_COMPLETE.md rename to doc/SETUP_COMPLETE.md diff --git a/WOKWI_LIBS.md b/doc/WOKWI_LIBS.md similarity index 100% rename from WOKWI_LIBS.md rename to doc/WOKWI_LIBS.md diff --git a/frontend/public/components-metadata.json b/frontend/public/components-metadata.json index b990c8b1..0c5f5488 100644 --- a/frontend/public/components-metadata.json +++ b/frontend/public/components-metadata.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "generatedAt": "2026-03-04T16:36:16.155Z", + "generatedAt": "2026-03-04T21:29:42.875Z", "components": [ { "id": "arduino-mega", diff --git a/frontend/src/simulation/AVRSimulator.ts b/frontend/src/simulation/AVRSimulator.ts index 73819cb2..19acf47b 100644 --- a/frontend/src/simulation/AVRSimulator.ts +++ b/frontend/src/simulation/AVRSimulator.ts @@ -1,4 +1,4 @@ -import { CPU, AVRTimer, timer0Config, timer1Config, timer2Config, AVRUSART, usart0Config, AVRIOPort, portBConfig, portCConfig, portDConfig, avrInstruction, PinState, AVRADC, adcConfig } from 'avr8js'; +import { CPU, AVRTimer, timer0Config, timer1Config, timer2Config, AVRUSART, usart0Config, AVRIOPort, portBConfig, portCConfig, portDConfig, avrInstruction, AVRADC, adcConfig } from 'avr8js'; import { PinManager } from './PinManager'; import { hexToUint8Array } from '../utils/hexParser'; @@ -27,10 +27,8 @@ const PWM_PINS = [ export class AVRSimulator { private cpu: CPU | null = null; - private timer0: AVRTimer | null = null; - private timer1: AVRTimer | null = null; - private timer2: AVRTimer | null = null; - private usart: AVRUSART | null = null; + /** Peripherals kept alive by reference so GC doesn't collect their CPU hooks */ + private peripherals: unknown[] = []; private portB: AVRIOPort | null = null; private portC: AVRIOPort | null = null; private portD: AVRIOPort | null = null; @@ -73,11 +71,13 @@ export class AVRSimulator { // Initialize CPU (ATmega328p @ 16MHz) this.cpu = new CPU(this.program); - // Initialize peripherals - this.timer0 = new AVRTimer(this.cpu, timer0Config); - this.timer1 = new AVRTimer(this.cpu, timer1Config); - this.timer2 = new AVRTimer(this.cpu, timer2Config); - this.usart = new AVRUSART(this.cpu, usart0Config, 16000000); // 16MHz + // Initialize peripherals (kept alive so their CPU hooks are not GC'd) + this.peripherals = [ + new AVRTimer(this.cpu, timer0Config), + new AVRTimer(this.cpu, timer1Config), + new AVRTimer(this.cpu, timer2Config), + new AVRUSART(this.cpu, usart0Config, 16000000), + ]; // Initialize ADC (analogRead support) this.adc = new AVRADC(this.cpu, adcConfig); @@ -93,7 +93,7 @@ export class AVRSimulator { // Set up pin change hooks this.setupPinHooks(); - console.log('AVR CPU initialized successfully (ADC + Timer1/Timer2 enabled)'); + console.log(`AVR CPU initialized (${this.peripherals.length} peripherals, ADC + Timer1/Timer2 enabled)`); } /** @@ -225,10 +225,12 @@ export class AVRSimulator { console.log('Resetting AVR CPU...'); this.cpu = new CPU(this.program); - this.timer0 = new AVRTimer(this.cpu, timer0Config); - this.timer1 = new AVRTimer(this.cpu, timer1Config); - this.timer2 = new AVRTimer(this.cpu, timer2Config); - this.usart = new AVRUSART(this.cpu, usart0Config, 16000000); + this.peripherals = [ + new AVRTimer(this.cpu, timer0Config), + new AVRTimer(this.cpu, timer1Config), + new AVRTimer(this.cpu, timer2Config), + new AVRUSART(this.cpu, usart0Config, 16000000), + ]; this.adc = new AVRADC(this.cpu, adcConfig); this.portB = new AVRIOPort(this.cpu, portBConfig); diff --git a/frontend/src/simulation/parts/BasicParts.ts b/frontend/src/simulation/parts/BasicParts.ts index 445411da..2c4cf1e8 100644 --- a/frontend/src/simulation/parts/BasicParts.ts +++ b/frontend/src/simulation/parts/BasicParts.ts @@ -1,5 +1,4 @@ import { PartSimulationRegistry } from './PartSimulationRegistry'; -import type { AVRSimulator } from '../AVRSimulator'; /** * Basic Pushbutton implementation (full-size) diff --git a/scripts/generate-example-screenshots.md b/scripts/generate-example-screenshots.md new file mode 100644 index 00000000..d99e06db --- /dev/null +++ b/scripts/generate-example-screenshots.md @@ -0,0 +1,104 @@ +# Generar Screenshots de Ejemplos + +Debido a restricciones CORS del navegador, no podemos capturar el canvas automáticamente con `toDataURL()`. En su lugar, necesitamos screenshots manuales. + +## Método Rápido: Captura de Pantalla + +### 1. Preparar el Navegador + +```bash +cd frontend +npm run dev +``` + +Abre http://localhost:5173 + +### 2. Para Cada Ejemplo + +1. Ve a http://localhost:5173/examples +2. Haz click en un ejemplo (ej: "Blink LED") +3. Espera a que se cargue en el editor +4. Ajusta el zoom del canvas si es necesario para que el circuito se vea bien centrado + +### 3. Capturar Solo el Canvas + +**Windows (Win + Shift + S):** +- Presiona `Win + Shift + S` +- Selecciona el área del simulador (panel derecho) +- La captura se guarda en el portapapeles + +**Guardar la imagen:** +1. Abre Paint o cualquier editor +2. Pega (Ctrl + V) +3. Guarda como PNG en `doc/examples/[nombre-ejemplo].png` + +### 4. Nombres de Archivo + +Usa el ID del ejemplo: + +- `blink-led.png` - Solo Arduino +- `traffic-light.png` - Arduino + 3 LEDs (rojo, amarillo, verde) +- `button-control.png` - Arduino + botón + LED +- `fade-led.png` - Arduino + LED azul +- `serial-hello.png` - Solo Arduino +- `rgb-led.png` - Arduino + LED RGB +- `simon-says.png` - Arduino + 4 LEDs + 4 botones + +### 5. Actualizar el Código + +Una vez tengas las imágenes, edita `frontend/src/data/examples.ts`: + +```typescript +{ + id: 'blink-led', + title: 'Blink LED', + description: 'Classic Arduino blink example - toggle an LED on and off', + category: 'basics', + difficulty: 'beginner', + thumbnail: '/doc/examples/blink-led.png', // ← Agregar esta línea + code: `...`, + // ... resto +} +``` + +## Alternativa: Script Browser DevTools + +Si prefieres algo más automatizado: + +1. Abre el ejemplo en http://localhost:5173 +2. Abre DevTools (F12) +3. Ve a la consola +4. Ejecuta: + +```javascript +// Tomar screenshot del canvas del simulador +const simulatorPanel = document.querySelector('.simulator-panel'); +const rect = simulatorPanel.getBoundingClientRect(); + +// Usar la API de screenshot del navegador (Chrome/Edge) +chrome.tabs.captureVisibleTab(null, {format: 'png'}, function(dataUrl) { + const link = document.createElement('a'); + link.download = 'example.png'; + link.href = dataUrl; + link.click(); +}); +``` + +**Nota:** Este método requiere una extensión del navegador. + +## Dimensiones Recomendadas + +- **Ancho**: 600-800px +- **Alto**: 400-600px +- **Formato**: PNG +- **Fondo**: El mismo del canvas (#1e1e1e) + +## Resultado Esperado + +Cada tarjeta en `/examples` mostrará la screenshot real del circuito en lugar del placeholder genérico. + +## Limpieza (Opcional) + +Una vez tengas las screenshots, puedes eliminar: +- `frontend/src/utils/captureCanvasPreview.ts` (ya no se necesita) +- `frontend/src/utils/generateExamplePreview.tsx` (SVG antiguo)