24 KiB
ESP32 WiFi & Bluetooth Emulation
Velxio emula WiFi y Bluetooth (BLE) en el ESP32 usando el fork de QEMU de lcgamboa con soporte de red slirp. Cada instancia de emulación obtiene su propia red NAT aislada — ideal para múltiples usuarios simultáneos.
Tabla de Contenidos
- Arquitectura General
- Cómo Funciona
- Cómo Usar
- Ejemplos Completos
- Indicadores Visuales en el Editor
- Configuración de Red
- Limitaciones
- Archivos Modificados / Creados
- Tests
- Troubleshooting
Arquitectura General
┌──────────────────────────────────────────────────────────────────┐
│ FRONTEND (React) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────────┐ │
│ │ Monaco Editor│ │ SimulatorCanvas│ │ Serial Monitor │ │
│ │ (sketch.ino)│ │ WiFi/BLE icons│ │ (ESP-IDF logs) │ │
│ └──────┬──────┘ └──────┬───────┘ └──────────┬────────────┘ │
│ │ │ │ │
│ ┌──────┴──────────────────┴──────────────────────┴────────────┐ │
│ │ useSimulatorStore (Zustand) │ │
│ │ - WiFi auto-detection from sketch content │ │
│ │ - wifiStatus / bleStatus per board │ │
│ └──────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────┴──────────────────────────────────────┐ │
│ │ Esp32Bridge (WebSocket) │ │
│ │ - wifiEnabled flag en start_esp32 payload │ │
│ │ - onWifiStatus / onBleStatus callbacks │ │
│ └──────────────────────┬──────────────────────────────────────┘ │
└─────────────────────────┼────────────────────────────────────────┘
│ WebSocket
┌─────────────────────────┼────────────────────────────────────────┐
│ BACKEND (FastAPI) │
│ │ │
│ ┌──────────────────────┴──────────────────────────────────────┐ │
│ │ simulation.py (WebSocket handler) │ │
│ │ - Lee wifi_enabled del payload │ │
│ │ - Asigna puerto dinámico para hostfwd │ │
│ └──────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────┴──────────────────────────────────────┐ │
│ │ esp32_worker.py / esp_qemu_manager.py │ │
│ │ - Lanza QEMU con -nic user,model=esp32_wifi,... │ │
│ │ - Captura serial output (UART0) │ │
│ └──────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────┴──────────┐ ┌────────────────────────┐ │
│ │ wifi_status_parser.py │ │ iot_gateway.py │ │
│ │ - Parsea logs ESP-IDF │ │ - Proxy HTTP reverso │ │
│ │ - Emite wifi_status/ble_status │ │ - Browser → ESP32:80 │ │
│ └─────────────────────────────────┘ └────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────┼────────────────────────────────────────┐
│ QEMU (lcgamboa fork) │
│ │ │
│ ┌──────────────────────┴──────────────────────────────────────┐ │
│ │ esp32_wifi_ap.c — Access Points emulados: │ │
│ │ • "Velxio-GUEST" (ch 6, -20 dBm, open) │ │
│ │ • "PICSimLabWifi" (ch 1, -25 dBm) │ │
│ │ • "Espressif" (ch 5, -30 dBm) │ │
│ │ • "MasseyWifi" (ch 10, -40 dBm) │ │
│ ├──────────────────────────────────────────────────────────────┤ │
│ │ Slirp (user-mode NAT) │ │
│ │ • Red: 192.168.4.0/24 │ │
│ │ • ESP32 IP: 192.168.4.15 (static, matches slirp DHCP) │ │
│ │ • Gateway: 192.168.4.2 │ │
│ │ • Internet: acceso completo vía NAT del host │ │
│ └──────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
Cómo Funciona
Red WiFi Virtual
Cada instancia de QEMU ejecuta su propia red WiFi emulada usando slirp (user-mode networking):
- Sin configuración de red del host — no requiere TAP, bridge, ni permisos de administrador
- Aislamiento por usuario — cada sesión de emulación tiene su propia red
192.168.4.0/24 - Acceso a internet — el ESP32 emulado puede hacer peticiones HTTP, DNS, etc. vía NAT del host
- SSID principal:
Velxio-GUEST(canal 6, abierto, sin contraseña)
QEMU emula la capa MAC 802.11 completa: beacons, scan, asociación y DHCP. El firmware ESP-IDF del ESP32 interactúa con el hardware WiFi emulado exactamente como lo haría con hardware real.
Detección Automática de WiFi
Cuando presionas "Run" en el editor, Velxio escanea automáticamente tu código buscando patrones WiFi:
// Patrones detectados:
'#include <WiFi.h>'
'#include <esp_wifi.h>'
'#include "WiFi.h"'
'WiFi.begin('
Si se detecta cualquiera de estos, se activa wifi_enabled=true automáticamente — no necesitas configurar nada.
Flujo de Estado WiFi
Sketch ejecuta WiFi.begin("Velxio-GUEST", "")
│
▼
QEMU UART0: "I (432) wifi:wifi sta start"
│
▼
Backend: wifi_status_parser → { status: "initializing" }
│
▼
WebSocket → Frontend: wifi_status event
│
▼
QEMU UART0: "I (800) wifi:connected with Velxio-GUEST, aid = 1"
│
▼
Backend: { status: "connected", ssid: "Velxio-GUEST" }
│
▼
QEMU UART0: "I (1200) esp_netif_handlers: sta ip: 192.168.4.15"
│
▼
Backend: { status: "got_ip", ip: "192.168.4.15" }
│
▼
SimulatorCanvas: ícono WiFi cambia a verde ✓
IoT Gateway (Servidor HTTP)
Cuando tu sketch ejecuta un WebServer en el ESP32, Velxio crea un proxy HTTP reverso para que puedas acceder desde tu navegador:
- QEMU inicia con
hostfwd=tcp::{puerto}-192.168.4.15:80 - El backend asigna un puerto dinámico libre
- Un proxy en
/api/gateway/{client_id}/reenvía peticiones al ESP32 - Tu navegador puede interactuar con el servidor del ESP32
Browser → http://localhost:8001/api/gateway/board-1/
→ Proxy → http://127.0.0.1:{hostfwd_port}/
→ QEMU → ESP32 WebServer (192.168.4.15:80)
→ "<h1>Hola desde ESP32 🚀</h1>"
Bluetooth Low Energy (BLE)
Velxio detecta el uso de BLE en tu sketch y muestra el estado de inicialización:
// Patrones BLE detectados:
'#include <BLEDevice.h>'
'#include <esp_bt.h>'
'BLEDevice::init('
El estado BLE se muestra en el canvas del simulador (ícono Bluetooth azul).
Nota: BLE es solo detección — el firmware inicializa BLE correctamente pero la comunicación BLE real (scan, connect, notify) no está emulada. Esto se debe a que el fork de QEMU de lcgamboa no implementa VHCI (Virtual HCI controller).
Cómo Usar
WiFi Básico
- Escribe tu sketch usando
#include <WiFi.h> - Usa el SSID
Velxio-GUEST(sin contraseña) - Presiona Run — WiFi se activa automáticamente
- Observa el Serial Monitor — verás los logs de conexión ESP-IDF
- Mira el ícono WiFi en el canvas del simulador
#include <WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.begin("Velxio-GUEST", "");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConectado!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
void loop() {
delay(1000);
}
Salida esperada en Serial Monitor:
I (432) wifi:wifi sta start
I (500) wifi:new:Velxio-GUEST, old: , ASSOC
I (800) wifi:connected with Velxio-GUEST, aid = 1, channel 6
I (1200) esp_netif_handlers: sta ip: 192.168.4.15, mask: 255.255.255.0
...
Conectado!
IP: 192.168.4.15
Servidor HTTP
#include <WiFi.h>
#include <WebServer.h>
const char* ssid = "Velxio-GUEST";
const char* password = "";
WebServer server(80);
void handleRoot() {
server.send(200, "text/html", "<h1>Hola desde ESP32 🚀</h1>");
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Conectando");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConectado!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.begin();
Serial.println("Servidor HTTP iniciado");
}
void loop() {
server.handleClient();
}
Una vez que el Serial Monitor muestre "Servidor HTTP iniciado", puedes acceder al servidor del ESP32 a través del IoT Gateway.
BLE Advertise
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
void setup() {
Serial.begin(115200);
BLEDevice::init("Velxio-ESP32");
BLEServer *pServer = BLEDevice::createServer();
BLEAdvertising *pAdv = BLEDevice::getAdvertising();
pAdv->start();
Serial.println("BLE advertising started");
}
void loop() {
delay(2000);
}
BLE se inicializa correctamente, pero no hay comunicación BLE real emulada.
Ejemplos Completos
Velxio incluye 4 ejemplos pre-cargados accesibles desde la galería de ejemplos:
1. WiFi Scan
Escanea las redes WiFi disponibles en el entorno emulado.
#include <WiFi.h>
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);
int n = WiFi.scanNetworks();
Serial.println("Networks found:");
for (int i = 0; i < n; i++) {
Serial.printf("%d: %s (%d dBm)\n", i+1, WiFi.SSID(i).c_str(), WiFi.RSSI(i));
}
}
void loop() { delay(10000); }
Salida esperada:
Networks found:
1: Velxio-GUEST (-20 dBm)
2: PICSimLabWifi (-25 dBm)
3: Espressif (-30 dBm)
4: MasseyWifi (-40 dBm)
2. WiFi Connect
Conecta a Velxio-GUEST y muestra la información de red.
#include <WiFi.h>
void setup() {
Serial.begin(115200);
Serial.print("Connecting to WiFi");
WiFi.begin("Velxio-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) {
delay(100);
Serial.print(".");
}
Serial.println(" Connected!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
void loop() { delay(1000); }
3. HTTP WebServer
Servidor web completo accesible desde el navegador vía IoT Gateway.
#include <WiFi.h>
#include <WebServer.h>
WebServer server(80);
void setup() {
Serial.begin(115200);
WiFi.begin("Velxio-GUEST", "", 6);
while (WiFi.status() != WL_CONNECTED) delay(100);
server.on("/", []() {
server.send(200, "text/html", "<h1>Hello from ESP32!</h1>");
});
server.begin();
Serial.print("Server at: ");
Serial.println(WiFi.localIP());
}
void loop() { server.handleClient(); }
4. BLE Advertise
Inicializa BLE y comienza advertising (detección solamente).
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
void setup() {
Serial.begin(115200);
BLEDevice::init("Velxio-ESP32");
BLEServer *pServer = BLEDevice::createServer();
BLEAdvertising *pAdv = BLEDevice::getAdvertising();
pAdv->start();
Serial.println("BLE advertising started");
}
void loop() { delay(2000); }
Indicadores Visuales en el Editor
El canvas del simulador muestra íconos de estado para WiFi y BLE junto al board ESP32:
Ícono WiFi
| Color | Estado | Significado |
|---|---|---|
| Gris | — | WiFi no activo (sketch sin WiFi) |
| Naranja (pulsante) | initializing |
WiFi inicializándose |
| Naranja | connected |
Conectado al AP, esperando IP |
| Verde | got_ip |
Conectado con IP asignada |
| Gris | disconnected |
Desconectado |
Tooltip: muestra SSID e IP cuando está conectado.
Ícono BLE
| Color | Estado | Significado |
|---|---|---|
| Gris | — | BLE no activo |
| Azul | initialized |
BLE controlador inicializado |
| Índigo | advertising |
BLE advertising activo |
Configuración de Red
| Parámetro | Valor |
|---|---|
| SSID | Velxio-GUEST |
| Contraseña | (vacía — red abierta) |
| Canal | 6 |
| Seguridad | Open (sin cifrado) |
| Subred | 192.168.4.0/24 |
| IP del ESP32 | 192.168.4.15 |
| Gateway | 192.168.4.2 |
| DNS | Proporcionado por slirp |
| BSSID | 42:13:37:55:aa:01 |
| MAC ESP32 | 24:0a:c4:00:01:10 (default) |
Redes adicionales visibles en scan
| SSID | Canal | Señal |
|---|---|---|
| Velxio-GUEST | 6 | -20 dBm |
| PICSimLabWifi | 1 | -25 dBm |
| Espressif | 5 | -30 dBm |
| MasseyWifi | 10 | -40 dBm |
Boards soportados
| Board | NIC Model QEMU | FQBN |
|---|---|---|
| ESP32 | esp32_wifi |
esp32:esp32:esp32 |
| ESP32-S3 | esp32_wifi |
esp32:esp32:esp32s3 |
| ESP32-C3 | esp32c3_wifi |
esp32:esp32:esp32c3 |
Limitaciones
WiFi
| Limitación | Detalle |
|---|---|
| SSID fijo | Debes usar "Velxio-GUEST" (sin contraseña). No puedes crear tu propio AP ni usar otro SSID para conectar. |
| Sin WPA/WPA2 | La red es abierta. El firmware puede intentar cifrado pero no se verificará. |
| Sin ICMP (ping) | ping no funciona — es una limitación de slirp. Usa TCP/HTTP para verificar conectividad. |
| MAC fija | Todas las instancias usan 24:0a:c4:00:01:10 por defecto. Configurable vía eFuse emulado pero no expuesto en UI. |
| Sin modo AP | El ESP32 no puede crear su propio Access Point — solo modo Station (STA). |
| Sin ESP-NOW | Comunicación peer-to-peer entre ESP32s no soportada. |
| Sin mDNS funcional | mDNS.begin("esp32") puede compilar pero no resuelve nombres en la red emulada. |
| Puerto HTTP 80 solamente | El hostfwd solo mapea al puerto 80 del ESP32. Servidores en otros puertos no son accesibles vía IoT Gateway. |
Bluetooth / BLE
| Limitación | Detalle |
|---|---|
| Solo detección | BLE se inicializa correctamente (el firmware ejecuta BLEDevice::init(), crea servicios y characteristics) pero no hay comunicación real. |
| Sin VHCI | El fork de QEMU de lcgamboa no implementa Virtual HCI. No hay transporte BLE real entre host y emulador. |
| Sin scan BLE | BLEScan no encontrará dispositivos. |
| Sin notify/indicate | Las characteristics se crean pero los callbacks de notify/indicate no se disparan. |
| Sin Classic Bluetooth | Solo BLE es detectado. Bluetooth Classic (SPP, A2DP, etc.) no está soportado. |
Generales
| Limitación | Detalle |
|---|---|
| Requiere rebuild QEMU | Para cambiar los APs disponibles o la configuración de red, necesitas recompilar libqemu-xtensa con build_libqemu-esp32.sh. |
| Un servidor HTTP por instancia | Cada sesión de emulación soporta un solo servidor HTTP (puerto 80). |
| Latencia de red | Las peticiones HTTP a través del IoT Gateway tienen latencia adicional por el doble proxy (browser → backend → QEMU → ESP32). |
Archivos Modificados / Creados
QEMU (C)
| Archivo | Cambio |
|---|---|
wokwi-libs/qemu-lcgamboa/hw/misc/esp32_wifi_ap.c |
Añadido SSID "Velxio-GUEST" al array de access points |
Backend (Python)
| Archivo | Cambio |
|---|---|
backend/app/services/espidf_compiler.py |
NUEVO — Compilador ESP-IDF: traduce sketches Arduino WiFi/WebServer a ESP-IDF C nativo, compila con cmake+ninja, merge flash image |
backend/app/services/esp-idf-template/ |
NUEVO — Template ESP-IDF (CMakeLists.txt, sdkconfig.defaults, main.c/cpp, partitions.csv) |
backend/app/api/routes/compile.py |
Ruta ESP32 boards a ESP-IDF compiler cuando disponible |
backend/app/services/esp32_worker.py |
Añadidos args -nic WiFi al lanzar QEMU, IP estática 192.168.4.15 |
backend/app/services/esp32_lib_manager.py |
Parámetros wifi_enabled/wifi_hostfwd_port en start_instance(), integración del parser serial |
backend/app/services/esp_qemu_manager.py |
Parámetros WiFi en start_instance() y _boot(), args -nic en subprocess |
backend/app/api/routes/simulation.py |
Handler WebSocket extrae wifi_enabled, asigna puerto dinámico, _find_free_port() |
backend/app/api/routes/iot_gateway.py |
NUEVO — Proxy HTTP reverso para servidores ESP32 |
backend/app/services/wifi_status_parser.py |
NUEVO — Parser de logs ESP-IDF para eventos WiFi/BLE |
backend/app/main.py |
Registrado router del IoT Gateway |
Frontend (TypeScript/React)
| Archivo | Cambio |
|---|---|
frontend/src/simulation/Esp32Bridge.ts |
Interfaces WifiStatus/BleStatus, propiedad wifiEnabled, callbacks onWifiStatus/onBleStatus |
frontend/src/store/useSimulatorStore.ts |
Auto-detección WiFi, estado wifiStatus/bleStatus por board |
frontend/src/components/simulator/SimulatorCanvas.tsx |
Íconos SVG de WiFi/BLE con estados y tooltips |
frontend/src/components/simulator/SimulatorCanvas.css |
Estilos CSS para badges WiFi/BLE |
frontend/src/types/board.ts |
Interfaces WifiStatus/BleStatus, campos en BoardInstance |
frontend/src/data/examples.ts |
4 ejemplos nuevos: WiFi Scan, WiFi Connect, HTTP Server, BLE Advertise |
Tests
Frontend (Vitest)
| Test File | Tests | Descripción |
|---|---|---|
esp32-wifi-bluetooth.test.ts |
14 | Esp32Bridge WiFi flag, status events, auto-detección, BLE detection |
esp32-wifi-compile.test.ts |
11 | Validación de sketches ejemplo, FQBN mapping |
esp32-wifi-webserver-integration.test.ts |
31 | Pipeline completo: sketch → auto-detect → bridge → status → serial → gateway |
esp32c3-wifi-bluetooth.test.ts |
29 | ESP32-C3 bridge, variant mapping, WiFi/BLE status, QEMU NIC config |
Ejecutar:
cd frontend
npm test -- --run src/__tests__/esp32-wifi-bluetooth.test.ts
npm test -- --run src/__tests__/esp32-wifi-compile.test.ts
npm test -- --run src/__tests__/esp32-wifi-webserver-integration.test.ts
npm test -- --run src/__tests__/esp32c3-wifi-bluetooth.test.ts
Backend (pytest)
| Test File | Tests | Descripción |
|---|---|---|
test_esp32_wifi.py |
17 | NIC arg injection, EspQemuManager params, free port allocation |
test_esp32c3_wifi.py |
18 | RISC-V binary, C3 machine, NIC model, hostfwd, serial parser |
test_wifi_status_parser.py |
14 | Parser WiFi/BLE: sta_start, connected, got_ip, disconnect, BLE init/advertising |
test_esp32_wifi_webserver.py |
23 | Integration: sketch structure, auto-detect, NIC args, serial parsing, gateway URL |
Ejecutar:
cd backend
python -m pytest tests/test_esp32_wifi.py tests/test_esp32c3_wifi.py tests/test_wifi_status_parser.py tests/test_esp32_wifi_webserver.py -v
Total: 157 tests específicos de WiFi/BLE
Troubleshooting
El ícono WiFi no aparece
- Verifica que tu board sea ESP32, ESP32-S3 o ESP32-C3
- Asegúrate de que tu sketch incluya
#include <WiFi.h>o useWiFi.begin(
WiFi se queda en "initializing"
- El firmware debe usar el SSID
"Velxio-GUEST"— otros SSIDs no funcionarán - No uses contraseña:
WiFi.begin("Velxio-GUEST", "") - Verifica en el Serial Monitor que QEMU muestra los logs ESP-IDF de WiFi
No puedo acceder al servidor HTTP del ESP32
- Espera a que el Serial Monitor muestre "Server at:" o similar
- El IoT Gateway solo funciona con servidores en puerto 80
- Accede vía
/api/gateway/{client_id}/no directamente al IP 192.168.4.15
BLE no funciona completamente
- BLE solo es detección — la inicialización funciona pero no hay comunicación real
- El ícono BLE se pondrá azul cuando
BLEDevice::init()se ejecute - Para BLE real necesitarías un QEMU con soporte VHCI (no disponible en lcgamboa fork)
ping no funciona
- Es una limitación de slirp — usa
HTTPClientoWiFiClientpara verificar conectividad - Ejemplo:
http.begin("http://httpbin.org/get")funciona,ping google.comno
La compilación falla
- ESP-IDF (producción): Los sketches ESP32 se compilan ahora con ESP-IDF 4.4.7 en lugar de arduino-cli. El backend traduce automáticamente Arduino WiFi/WebServer a ESP-IDF C nativo. Verifica que
IDF_PATHesté configurado. - Fallback arduino-cli: Si ESP-IDF no está disponible, se usa arduino-cli. Instala el core:
arduino-cli core install esp32:esp32@2.0.17 - El FQBN correcto es
esp32:esp32:esp32(noesp32:esp32:esp32dev)