diff --git a/frontend/index.html b/frontend/index.html index f55563a..8866abb 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -18,6 +18,19 @@ + + + + diff --git a/test/esp32-emulator/README.md b/test/esp32-emulator/README.md new file mode 100644 index 0000000..064b7fd --- /dev/null +++ b/test/esp32-emulator/README.md @@ -0,0 +1,29 @@ +# ESP32 Emulation Test Suite + +This directory contains tests for ESP32 emulation using QEMU compiled to WebAssembly. + +## Structure + +- `sketches/` - Arduino sketches for ESP32 (copied from test/esp32/sketches) +- `binaries/` - Compiled firmware binaries (`.bin`, `.elf`) +- `qemu-config/` - QEMU machine configurations +- `scripts/` - Build and test scripts +- `web/` - HTML/JS wrapper for WebAssembly QEMU + +## Prerequisites + +1. QEMU fork built as WebAssembly (see `wokwi-libs/qemu-lcgamboa/`) +2. ESP32 toolchain (arduino-cli with esp32 platform) +3. Emscripten SDK (for building QEMU to WASM) + +## Initial Test + +The first test is to compile the blink sketch and run it in QEMU (native) to verify basic GPIO emulation. + +## Goals + +- Emulate GPIO pins (digitalWrite, digitalRead) +- Emulate Serial output (UART) +- Emulate WiFi (station and AP modes) +- Emulate other peripherals (SPI, I2C, ADC, etc.) +- Run entirely in browser via WebAssembly \ No newline at end of file diff --git a/test/esp32-emulator/binaries/.gitkeep b/test/esp32-emulator/binaries/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/esp32-emulator/qemu-wasm/Dockerfile b/test/esp32-emulator/qemu-wasm/Dockerfile new file mode 100644 index 0000000..003b6f6 --- /dev/null +++ b/test/esp32-emulator/qemu-wasm/Dockerfile @@ -0,0 +1,40 @@ +# Dockerfile to build QEMU for ESP32 with Emscripten +FROM emscripten/emsdk:latest AS builder + +WORKDIR /src + +# Clone QEMU fork (lcgamboa) +RUN git clone --depth 1 https://github.com/lcgamboa/qemu.git -b picsimlab-esp32 qemu +WORKDIR /src/qemu + +# Apply patches if needed (none for now) + +# Configure for Emscripten +# Use emconfigure wrapper to set up environment +RUN emconfigure ./configure \ + --target-list=xtensa-softmmu \ + --disable-system \ + --disable-user \ + --disable-tools \ + --disable-docs \ + --disable-guest-agent \ + --disable-slirp \ + --disable-werror \ + --enable-emscripten \ + --extra-cflags="-O2 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1" \ + --cross-prefix=em \ + --cpu=wasm32 + +# Build with emmake +RUN emmake make -j$(nproc) + +# The resulting binary will be qemu-system-xtensa.wasm? Actually QEMU builds to .js and .wasm +# We need to copy the generated files to output directory +RUN mkdir -p /output +RUN cp -r build/* /output/ + +# Also build the shared library version for easier integration +# (optional) + +FROM scratch AS export +COPY --from=builder /output/ / \ No newline at end of file diff --git a/test/esp32-emulator/sketches/blink.ino b/test/esp32-emulator/sketches/blink.ino new file mode 100644 index 0000000..6e619f6 --- /dev/null +++ b/test/esp32-emulator/sketches/blink.ino @@ -0,0 +1,20 @@ +// Simple ESP32 Blink Test +// Tests basic GPIO functionality + +#define LED_PIN 2 // Built-in LED on most ESP32 dev boards + +void setup() { + pinMode(LED_PIN, OUTPUT); + Serial.begin(115200); + Serial.println("ESP32 Blink Test Started"); +} + +void loop() { + digitalWrite(LED_PIN, HIGH); + Serial.println("LED ON"); + delay(1000); + + digitalWrite(LED_PIN, LOW); + Serial.println("LED OFF"); + delay(1000); +} \ No newline at end of file diff --git a/test/esp32-emulator/web/emulator.js b/test/esp32-emulator/web/emulator.js new file mode 100644 index 0000000..8892353 --- /dev/null +++ b/test/esp32-emulator/web/emulator.js @@ -0,0 +1,124 @@ +// Mock ESP32 Emulator using QEMU WebAssembly (placeholder) +// This is a temporary simulation until real QEMU WASM is integrated. + +class ESP32Emulator { + constructor() { + this.running = false; + this.interval = null; + this.gpioState = {}; + this.serialBuffer = ''; + this.canvas = document.getElementById('simulationCanvas'); + this.ctx = this.canvas.getContext('2d'); + this.ledX = 400; + this.ledY = 200; + this.ledRadius = 30; + this.ledOn = false; + this.initGPIO(); + this.renderCanvas(); + } + + initGPIO() { + // Initialize GPIO pins 0-39 + for (let i = 0; i < 40; i++) { + this.gpioState[i] = { mode: 'input', value: 0 }; + } + // Set pin 2 as output (built-in LED) + this.gpioState[2].mode = 'output'; + this.updateGPIOView(); + } + + updateGPIOView() { + const table = document.getElementById('gpioTable'); + table.innerHTML = ''; + for (let i = 0; i < 40; i++) { + const row = document.createElement('tr'); + row.innerHTML = `${i}${this.gpioState[i].mode}${this.gpioState[i].value}`; + table.appendChild(row); + } + } + + logSerial(message) { + const logDiv = document.getElementById('serialLog'); + logDiv.textContent += message + '\n'; + logDiv.scrollTop = logDiv.scrollHeight; + } + + renderCanvas() { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + // Draw LED + this.ctx.beginPath(); + this.ctx.arc(this.ledX, this.ledY, this.ledRadius, 0, Math.PI * 2); + this.ctx.fillStyle = this.ledOn ? '#ff4444' : '#444444'; + this.ctx.fill(); + this.ctx.strokeStyle = '#888'; + this.ctx.stroke(); + // Draw label + this.ctx.fillStyle = 'white'; + this.ctx.font = '16px Arial'; + this.ctx.textAlign = 'center'; + this.ctx.fillText('GPIO 2 (LED)', this.ledX, this.ledY + this.ledRadius + 20); + } + + start() { + if (this.running) return; + this.running = true; + this.logSerial('ESP32 Emulator started'); + this.interval = setInterval(() => { + // Simulate blink sketch: toggle GPIO2 every second + this.gpioState[2].value = this.gpioState[2].value ? 0 : 1; + this.ledOn = this.gpioState[2].value === 1; + this.updateGPIOView(); + this.renderCanvas(); + this.logSerial(this.ledOn ? 'LED ON' : 'LED OFF'); + }, 1000); + this.logSerial('Blink sketch running...'); + } + + stop() { + if (!this.running) return; + clearInterval(this.interval); + this.running = false; + this.logSerial('Emulator stopped'); + } + + reset() { + this.stop(); + this.initGPIO(); + this.ledOn = false; + this.renderCanvas(); + document.getElementById('serialLog').textContent = ''; + this.logSerial('ESP32 reset'); + } +} + +const emulator = new ESP32Emulator(); + +// UI functions +function loadEmulator() { + const status = document.getElementById('status'); + status.className = 'status running'; + status.textContent = 'Emulator loaded (mock)'; + document.getElementById('startBtn').disabled = false; + document.getElementById('stopBtn').disabled = false; + document.getElementById('resetBtn').disabled = false; + emulator.logSerial('QEMU WASM module loaded (simulated)'); +} + +function startEmulation() { + emulator.start(); +} + +function stopEmulation() { + emulator.stop(); +} + +function resetEmulation() { + emulator.reset(); +} + +// Initialize buttons state +window.onload = function() { + document.getElementById('startBtn').disabled = true; + document.getElementById('stopBtn').disabled = true; + document.getElementById('resetBtn').disabled = true; +}; \ No newline at end of file diff --git a/test/esp32-emulator/web/index.html b/test/esp32-emulator/web/index.html new file mode 100644 index 0000000..6b1b543 --- /dev/null +++ b/test/esp32-emulator/web/index.html @@ -0,0 +1,92 @@ + + + + + + ESP32 Emulator Test + + + +
+

ESP32 Emulator Test (QEMU + WebAssembly)

+

This test loads QEMU compiled to WebAssembly and runs a compiled ESP32 blink sketch.

+
Emulator not loaded
+
+ + + + +
+
+ +
+

Serial Output

+
+

GPIO State

+
+ + + + + + +
PinModeState
+
+
+ + + \ No newline at end of file diff --git a/test/esp32/sketches/blink.ino b/test/esp32/sketches/blink.ino new file mode 100644 index 0000000..6e619f6 --- /dev/null +++ b/test/esp32/sketches/blink.ino @@ -0,0 +1,20 @@ +// Simple ESP32 Blink Test +// Tests basic GPIO functionality + +#define LED_PIN 2 // Built-in LED on most ESP32 dev boards + +void setup() { + pinMode(LED_PIN, OUTPUT); + Serial.begin(115200); + Serial.println("ESP32 Blink Test Started"); +} + +void loop() { + digitalWrite(LED_PIN, HIGH); + Serial.println("LED ON"); + delay(1000); + + digitalWrite(LED_PIN, LOW); + Serial.println("LED OFF"); + delay(1000); +} \ No newline at end of file