# Émulateur QEMU ESP32 — KC868-A2

Émulation du firmware sur QEMU ESP32 (fork Espressif, GPL).  
Interface de débogage 3 volets : GPIO / terminal série / webserver ESP32.

## Architecture

```
Docker
├── qemu-system-xtensa    (ESP32 firmware + WiFi + réseau)
│     UART0 → stdout      (logs Serial.print → terminal)
│     UART2 → TCP:1235    (Modbus RTU ← stub Python)
│     NIC   → slirp       (hostfwd port 10080 → ESP32:80)
│
├── modbus_stub.py         (émulateur Modbus RTU slave Epever)
│     Se connecte à TCP:1235, répond aux FC04 avec données simulées
│
└── server.py              (interface web débogage, port 8888)
      /          → UI 3 volets
      /serial    → SSE flux UART0
      /api/*     → proxy vers webserver ESP32 (port 10080)
```

## Prérequis

- Docker + Docker Compose
- Firmware compilé avec PlatformIO : `pio run`

## Lancement

### Option 1 — Serveur de simulation (recommandé)

Sert les vrais fichiers `data/` + simule tous les `/api/*` en mémoire.
L'état des relais, règles et config sleep sont modifiables via l'interface.
L'historique s'alimente toutes les 5 secondes (= 5 min en temps réel).

```bash
# Sans Docker (Python 3 requis) :
cd emulator && python3 sim.py

# Avec Docker :
cd emulator && docker compose up sim
```

Accès : **http://localhost:8087**

---

### Option 2 — Émulateur QEMU (boot séquence + terminal série)

Exécute le vrai binaire compilé. Montre le boot ESP32 et les logs Serial.
Le firmware crashe au démarrage de WiFi (hardware non émulé) — normal.

```bash
# 1. Compiler le firmware
pio run && pio run -t buildfs

# 2. Copier les binaires
cp .pio/build/kc868_a2/*.bin emulator/firmware/

# 3. Démarrer
cd emulator && docker compose up --build emulator
```

Accès :
- **Interface de débogage** : http://localhost:8888
- **WebServer ESP32** : http://localhost:10080 (si WiFi démarre)

## Mise à jour de la version QEMU

Si le téléchargement échoue, vérifier la dernière version disponible sur :
https://github.com/espressif/qemu/releases

Modifier `QEMU_TAG` et `QEMU_ARCHIVE` dans le `Dockerfile`.

## Correctif registres WiFi (LoadStorePIFAddrError)

Le QEMU Espressif standard n'émule pas les registres matériels WiFi modem
(`0x60033C00`). Sans correctif, le firmware crash en boucle avec
`LoadStorePIFAddrError` dès l'initialisation WiFi.

**Solution** : le `Dockerfile` utilise un build multi-étapes :
1. **`rom-extractor`** — télécharge le binaire pré-compilé, extrait les ROM blobs ESP32 (fichiers binaires propriétaires)
2. **`qemu-builder`** — clone le source Espressif QEMU, applique `wifi_stub_patch.py`, compile uniquement la cible xtensa
3. **Image finale** — ROM blobs de l'étape 1 + binaire patché de l'étape 2

`wifi_stub_patch.py` injecte un appel `create_unimplemented_device()` dans
`hw/xtensa/esp32.c` qui mappe silencieusement la plage `0x60033C00–0x60043BFF`
(64 Ko, WiFi MAC + baseband) : toutes les lectures retournent 0, les écritures
sont ignorées, plus de fault CPU.

> **Note** : le build initial prend 15–30 min (compilation QEMU). Docker met
> les layers en cache — les rebuilds suivants sont instantanés.

## Limites connues

| Fonctionnalité | État |
|---|---|
| WiFi AP mode (softAP) | Registres stubés (pas de WiFi réel) — le firmware démarre |
| Webserver ESP32 (port 80) | Accessible via hostfwd → port 10080 si WiFi s'initialise |
| Modbus RS485 | Émulé par `modbus_stub.py` (données sinusoïdales) |
| GPIO physiques (DI1/DI2) | Non émulés — toujours à 0 |
| Deep sleep | Non supporté dans QEMU |
| OTA | Non testé |

## Modbus simulé

Le stub `modbus_stub.py` répond aux lectures FC04 des registres Epever Tracer 4210N :

| Registre | Valeur simulée |
|---|---|
| 0x3100 PV tension | ~18.72 V (variation sinusoïdale) |
| 0x3101 PV courant | ~4.20 A |
| 0x3104 Batterie   | ~13.45 V |
| 0x310E Load       | 26.80 W |
| 0x311A SOC        | 75 % |
| 0x3200 Statut     | Float charge |
| 0x200C Jour/Nuit  | Jour |

## Arrêt

```bash
docker compose down
```

Pour quitter la console QEMU (dans le terminal) : `Ctrl+A` puis `X`.
