velxio/docs/ESP32_WIFI_BLUETOOTH.md

603 lines
24 KiB
Markdown

# 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](#arquitectura-general)
- [Cómo Funciona](#cómo-funciona)
- [Red WiFi Virtual](#red-wifi-virtual)
- [Detección Automática de WiFi](#detección-automática-de-wifi)
- [Flujo de Estado WiFi](#flujo-de-estado-wifi)
- [IoT Gateway (Servidor HTTP)](#iot-gateway-servidor-http)
- [Bluetooth Low Energy (BLE)](#bluetooth-low-energy-ble)
- [Cómo Usar](#cómo-usar)
- [WiFi Básico](#wifi-básico)
- [Servidor HTTP](#servidor-http)
- [BLE Advertise](#ble-advertise)
- [Ejemplos Completos](#ejemplos-completos)
- [1. WiFi Scan](#1-wifi-scan)
- [2. WiFi Connect](#2-wifi-connect)
- [3. HTTP WebServer](#3-http-webserver)
- [4. BLE Advertise](#4-ble-advertise)
- [Indicadores Visuales en el Editor](#indicadores-visuales-en-el-editor)
- [Configuración de Red](#configuración-de-red)
- [Limitaciones](#limitaciones)
- [Archivos Modificados / Creados](#archivos-modificados--creados)
- [Tests](#tests)
- [Troubleshooting](#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:
```typescript
// 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:
1. QEMU inicia con `hostfwd=tcp::{puerto}-192.168.4.15:80`
2. El backend asigna un puerto dinámico libre
3. Un proxy en `/api/gateway/{client_id}/` reenvía peticiones al ESP32
4. 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:
```typescript
// 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
1. **Escribe tu sketch** usando `#include <WiFi.h>`
2. **Usa el SSID `Velxio-GUEST`** (sin contraseña)
3. **Presiona Run** — WiFi se activa automáticamente
4. **Observa el Serial Monitor** — verás los logs de conexión ESP-IDF
5. **Mira el ícono WiFi** en el canvas del simulador
```cpp
#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
```cpp
#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
```cpp
#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.
```cpp
#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.
```cpp
#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.
```cpp
#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).
```cpp
#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:**
```bash
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:**
```bash
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 use `WiFi.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 `HTTPClient` o `WiFiClient` para verificar conectividad
- Ejemplo: `http.begin("http://httpbin.org/get")` funciona, `ping google.com` no
### 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_PATH` esté configurado.
- **Fallback arduino-cli**: Si ESP-IDF no está disponible, se usa arduino-cli. Instala el core:
```bash
arduino-cli core install esp32:esp32@2.0.17
```
- El FQBN correcto es `esp32:esp32:esp32` (no `esp32:esp32:esp32dev`)