Files
gilles a53cb124d1 README : exemple config WireGuard, section RS485, liens fabricant et projets similaires
- Exemple de fichier .conf WireGuard annoté + commandes génération clés
- Paragraphe complet sur la résolution des 6 problèmes RS485 (baudrate,
  buffer, lib, registres énergie, puissance 32 bits, détection jour/nuit)
- Tables de liens : KC868-A2 (fabricant, forum, ESPHome, Tasmota),
  Epever (doc officielle, registres, HA), projets GitHub similaires,
  WireGuard ESP32, bibliothèques et outils

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 19:34:50 +02:00

420 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# KC868-A2 — Contrôleur Solaire ESP32
Système embarqué autonome de supervision et de pilotage d'une installation solaire.
Fonctionne sans internet, sans cloud, sans application mobile — tout passe par une interface web hébergée sur l'ESP32.
![KC868-A2 board](photo/board.png)
---
## Matériel
| Composant | Référence |
|-----------|-----------|
| Contrôleur | **Kincony KC868-A2** (ESP32-WROOM-32) |
| Régulateur solaire | **Epever Tracer 4210N** (MPPT 40A) |
| Communication | RS485 Modbus RTU |
| Interface | RJ45 8P8C côté Epever |
### Brochage RS485
```
Epever RJ45 (vue de face, languette bas) KC868-A2
Pin 3 / 4 → RS485-B (D-) → B
Pin 5 / 6 → RS485-A (D+) → A+
Pin 7 / 8 → GND → GND (optionnel)
Pin 1 / 2 → +7.5V ⚠ NE PAS CONNECTER
```
| Vue d'ensemble | Port RS485 | Port Ethernet |
|:-:|:-:|:-:|
| ![Carte](photo/carte.png) | ![RS485](photo/rs485.png) | ![ETH](photo/eth.png) |
![Schéma](photo/schema.png)
**Documentation technique :**
- [KC868-A2 Schematic](KC868-A2-schematic.pdf) — schéma électronique complet de la carte
- [Epever Modbus Protocol v2.5](MODBUS-Protocol-v25.pdf) — registres et protocole de communication du régulateur
**Paramètres Modbus :** esclave 1, 115200 bps, 8N1, RTU, half-duplex.
### GPIO ESP32
| Signal | Pin | Note |
|--------|-----|------|
| Relais 1 | GPIO 15 | |
| Relais 2 | GPIO 2 | Pin de strapping boot — reste HIGH au démarrage |
| RS485 TX | GPIO 32 | |
| RS485 RX | GPIO 35 | Input only |
| Entrée DI1 | GPIO 36 | Input only |
| Entrée DI2 | GPIO 39 | Input only |
---
## Fonctionnalités
### Acquisition de données (Modbus RTU)
- Tension / courant / puissance panneau solaire (PV)
- Tension / courant / puissance sortie de charge (load)
- Tension, SOC (%), température et statut batterie
- Énergie générée/consommée (jour et total, kWh)
- Détection jour/nuit et horloge interne de l'Epever
- Fréquence de lecture configurable (mode jour / mode nuit)
### Commande
- **2 relais** commandés manuellement ou automatiquement
- **2 entrées numériques** (DI1, DI2) — contacts secs
- État des relais sauvegardé en NVS (survit aux coupures)
### Moteur de règles
Règles JSON stockées dans LittleFS. Chaque règle combine :
- Déclencheurs : ensoleillement (jour/nuit), état DI1/DI2
- Conditions : seuil batterie min/max, seuil PV min/max
- Action : relais ON/OFF avec délai optionnel et hystérésis
### Interface web (mobile-first)
Accessible sur `http://192.168.4.1` (AP) ou `http://pv.local` (mDNS) :
| Onglet | Contenu |
|--------|---------|
| Dashboard | Relais, entrées, PV, batterie, load, énergie |
| Règles | Liste, ajout, activation/désactivation, suppression |
| Config | Relais manuels, noms, sleep, OTA, WiFi, WireGuard VPN |
| Historique | Graphes 4h (1 min) et 30h (5 min) exportables CSV |
| Config EPEVER | Lecture/écriture des 18 registres holding du régulateur |
| Debug | Journal série en mémoire |
### Réseau
- **Mode AP permanent** : `kc868-a2` / `soleil12``192.168.4.1`
- **Mode STA optionnel** : connexion à un réseau WiFi existant (scan + NVS)
- **Portail captif** : iOS/Android/Windows ouvrent l'UI automatiquement
- **mDNS** : `pv.local` configurable depuis l'interface
### VPN WireGuard *(optionnel, désactivé par défaut)*
Tunnel chiffré vers un serveur WireGuard distant.
Configuration complète depuis l'onglet Config — clés, endpoint, IP locale, keepalive.
Actif uniquement quand la STA WiFi est connectée ; l'accès local via AP reste toujours disponible.
### Deep sleep
Mode économie d'énergie configurable : l'ESP32 se rendort entre les cycles de mesure la nuit (PV < seuil), se réveille périodiquement et évalue les règles.
### OTA
Mise à jour firmware et filesystem via `http://192.168.4.1/update` (ElegantOTA).
---
## Architecture logicielle
```
src/
├── main.cpp — Init + boucle principale (non bloquante)
├── modbus_epever.cpp — Lecture RS485 Modbus RTU (FC02/03/04/16), CRC manuel
├── epever_config.cpp — Lecture/écriture des 18 registres holding de configuration
├── webserver.cpp — Serveur HTTP async + 30+ routes REST
├── wifi_ap.cpp — WiFi AP+STA, mDNS, portail captif, reconnexion auto
├── wireguard_vpn.cpp — Tunnel WireGuard (optionnel)
├── rules.cpp — Moteur de règles JSON
├── historique.cpp — Historique RAM (hires 4h) + LittleFS (lores 30h)
├── sleep.cpp — Deep sleep + restauration état relais
├── buttons.cpp — Entrées numériques DI1/DI2 avec anti-rebond
├── ota.cpp — ElegantOTA async
└── debug_log.cpp — Journal en mémoire (ring buffer)
include/ — Headers correspondants + config.h + state.h
data/ — Assets web (LittleFS)
├── index.html — SPA 6 onglets
├── app.js — Logique JS (~1100 lignes)
└── style.css — Thème sombre mobile-first
```
**Principe fondamental :** aucun `delay()` ni boucle bloquante.
Tout le timing passe par `millis()`. Les lectures RS485 ont un timeout court ; en cas d'erreur, le dernier état valide est conservé.
### API REST (sélection)
| Méthode | Endpoint | Description |
|---------|----------|-------------|
| GET | `/api/state` | État système complet (JSON) |
| POST | `/api/relay/1/on` | Relais 1 ON |
| POST | `/api/relay/1/toggle` | Toggle + sauvegarde NVS |
| GET | `/api/rules` | Liste des règles |
| POST | `/api/rules` | Ajout de règle (JSON) |
| GET | `/api/epever/config` | Lecture config EPEVER depuis le régulateur |
| POST | `/api/epever/config` | Écriture config EPEVER |
| GET | `/api/wifi/status` | Statut AP + STA |
| GET | `/api/wifi/scan` | Scan réseaux disponibles |
| POST | `/api/wifi/sta` | Connexion à un réseau WiFi |
| GET | `/api/mdns` | Hostname mDNS courant |
| POST | `/api/mdns` | Modification hostname mDNS |
| GET | `/api/wireguard` | Config et statut WireGuard |
| POST | `/api/wireguard` | Sauvegarde config WireGuard |
| GET | `/api/history/hires` | Historique 4h (1 min/point) |
| GET | `/api/history/csv` | Export CSV (30h) |
| GET | `/api/sleep` | Config deep sleep |
| POST | `/api/modbus` | Intervalles de lecture Modbus |
---
## Bibliothèques utilisées
| Bibliothèque | Version | Rôle |
|-------------|---------|------|
| [ArduinoJson](https://arduinojson.org/) | ^7.0 | Sérialisation JSON |
| [AsyncTCP](https://github.com/me-no-dev/AsyncTCP) | git | TCP non bloquant |
| [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) | git | Serveur HTTP async |
| [ElegantOTA](https://github.com/ayushsharma82/ElegantOTA) | ^3.1 | OTA via navigateur |
| [modbus-esp8266](https://github.com/emelianov/modbus-esp8266) | ^4.1 | Modbus RTU (utilisé pour les fonctions bas niveau) |
| [WireGuard-ESP32-Arduino](https://github.com/ciniml/WireGuard-ESP32-Arduino) | git | VPN WireGuard (kc868_a2 uniquement) |
Intégrées dans le framework ESP32 Arduino : `WiFi`, `DNSServer`, `ESPmDNS`, `Preferences` (NVS), `LittleFS`.
---
## Outils
- **[PlatformIO](https://platformio.org/)** — build, flash, monitor
- **Framework** : Arduino pour ESP32 (espressif32)
- **Filesystem** : LittleFS
---
## Build et flash
### Prérequis
- PlatformIO Core ou PlatformIO IDE (VS Code)
- Câble USB pour le premier flash, WiFi pour les suivants (OTA)
### Commandes
```bash
pio run # Compiler
pio run -e kc868_a2 --target upload # Flasher par USB
pio run -e kc868_a2 --target uploadfs # Flasher le filesystem (LittleFS)
pio run --target monitor # Moniteur série 115200 bps
pio run -e qemu # Build émulateur (sans WireGuard ni WiFi)
```
### OTA (après premier flash)
Ouvrir `http://192.168.4.1/update` (ou `http://pv.local/update`) :
1. Sélectionner **Firmware** → uploader `.pio/build/kc868_a2/firmware.bin`
2. Sélectionner **Filesystem** → uploader `.pio/build/kc868_a2/littlefs.bin`
---
## Configuration
### WiFi AP (compile-time)
Dans `include/config.h` :
```cpp
#define WIFI_SSID "kc868-a2"
#define WIFI_PASSWORD "soleil12"
```
### WiFi STA (runtime)
Onglet Config → section Connexion WiFi → scanner, choisir le réseau, saisir le mot de passe.
### Connexion WiFi STA + nom mDNS (runtime)
Onglet Config → section Connexion WiFi.
### VPN WireGuard (runtime)
Onglet Config → section VPN WireGuard :
- Coller la clé privée de l'interface ESP32
- Coller la clé publique du serveur
- Renseigner l'endpoint (`mon.domaine.org`) et le port (`51820`)
- IP locale WireGuard (ex. `10.8.0.16`)
- Keepalive recommandé : `25` s pour maintenir le NAT actif
- Activer et sauvegarder
> **Sécurité :** les clés sont stockées dans la NVS flash de l'ESP32. Ne jamais committer le fichier `.conf` WireGuard dans git (il est dans `.gitignore`).
#### Exemple de fichier `.conf` (généré par wg-easy ou `wg` CLI)
```ini
[Interface]
PrivateKey = <clé privée ESP32 — générer avec : wg genkey>
Address = 10.8.0.x/24
MTU = 1420
[Peer]
PublicKey = <clé publique du serveur WireGuard>
PresharedKey = <optionnel — générer avec : wg genpsk>
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Endpoint = mon.domaine.org:51820
```
**Générer une paire de clés :**
```bash
# Clé privée
wg genkey | tee privatekey
# Clé publique correspondante
cat privatekey | wg pubkey
# Clé pré-partagée (optionnel)
wg genpsk
```
> **Note :** la bibliothèque `WireGuard-ESP32-Arduino` n'expose pas encore la `PresharedKey` dans son API Arduino. Le tunnel fonctionne sans PSK — configurer le peer serveur en conséquence ou attendre un patch de la lib.
### Deep sleep
Onglet Config → Mode économie d'énergie : activer, régler l'intervalle de réveil et le seuil PV.
---
## Registres Modbus lus
| Registre | Données | Format |
|----------|---------|--------|
| 0x3100 | Tension PV | U16 × 0.01 V |
| 0x3101 | Courant PV | U16 × 0.01 A |
| 0x3104 | Tension batterie | U16 × 0.01 V |
| 0x3105 | Courant batterie | S16 × 0.01 A |
| 0x3106 | Tension load | U16 × 0.01 V |
| 0x3107 | Courant load | U16 × 0.01 A |
| 0x310C | Température batterie | S16 × 0.01 °C |
| 0x311A | SOC batterie | U16 % |
| 0x3201 | Statut batterie | bitfield |
| 0x3302 | Énergie générée jour | U32 × 0.01 kWh |
| 0x3304 | Énergie générée total | U32 × 0.01 kWh |
| 0x3306 | Énergie consommée jour | U32 × 0.01 kWh |
| 0x3308 | Énergie consommée total | U32 × 0.01 kWh |
| 0x200C | État jour/nuit | bitfield |
| 0x9013 | Horloge RTC | 3 × U16 |
---
## Résolution des problèmes RS485
La communication avec l'Epever a nécessité plusieurs corrections non documentées dans les projets existants. Ce résumé évite de retomber dans les mêmes pièges.
### Problème 1 — Baudrate incorrect (bloquant)
L'Epever Tracer 4210N communique à **115 200 bps** et non à 9 600 bps comme indiqué par défaut dans la plupart des bibliothèques et exemples en ligne. Aucune réponse RS485 n'est obtenue tant que le baudrate est incorrect.
**Correction :** `#define MODBUS_BAUDRATE 115200` et une sonde de démarrage (`probeRegistreBatterie()`) qui tente 9 600 puis 115 200 et confirme la réponse par CRC.
### Problème 2 — Bibliothèque ModbusRTU inadaptée
La bibliothèque `modbus-esp8266` avec ses callbacks asynchrones gérait mal le timing half-duplex à 115 200 bds : trames corrompues, échos non consommés, timeouts aléatoires.
**Correction :** abandon du mode callback, passage en **Serial2 direct** avec CRC Modbus calculé manuellement. La bibliothèque reste uniquement pour initialiser l'UART (`mb.begin()` / `mb.master()`).
### Problème 3 — Buffer RX pollué entre requêtes
Sans purge, les octets résiduels d'une réponse précédente (ou du bruit de ligne) polluaient la trame suivante et généraient des CRC invalides en cascade.
**Correction :** appel systématique de `viderRx()` avant chaque envoi — lit et journalise tous les octets présents dans le FIFO Serial2.
### Problème 4 — Registres énergie mal adressés
La plupart des projets open source lisent l'énergie à partir de `0x3300`. Le protocole Modbus v2.5 d'Epever indique que les compteurs kWh démarrent à **`0x3302`** (génération du jour) — décalage de 2 registres sur tous les offsets.
### Problème 5 — Puissance charge sur 32 bits
Le registre `0x310E` (puissance sortie load) est sur **32 bits** (word L + word H en `0x310F`). Une lecture 16 bits donnait des valeurs erronées dès que la puissance dépassait 655 W.
### Problème 6 — Détection jour/nuit incohérente
Le registre FC02 `0x200C` retourne parfois `Nuit` alors que le panneau produit clairement (transition rapide matin/soir).
**Correction :** logique hybride — `sun = !nuit_registre || pv > 2.0 V`. Si le panneau produit plus de 2 V, on considère qu'il fait jour quel que soit le registre.
### Tableau récapitulatif
| Problème | Cause racine | Correction appliquée |
|----------|-------------|---------------------|
| Aucune réponse | Baudrate 9 600 au lieu de 115 200 | `MODBUS_BAUDRATE 115200` + sonde boot |
| Trames corrompues | Buffer RX non purgé | `viderRx()` avant chaque trame |
| Timeouts aléatoires | Lib asynchrone inadaptée | Serial2 direct + CRC manuel |
| Énergie kWh = 0 | Base registre `0x3300` au lieu de `0x3302` | Correction offsets (doc v2.5) |
| Puissance load erronée | Lecture 16 bits au lieu de 32 bits | `(reg[3] << 16 \| reg[2]) × 0.01` |
| Soleil incohérent | FC02 seul, pas de fallback | Hybride : registre + tension PV |
> Le détail complet avec les extraits de code est dans [`debug_rs485.md`](debug_rs485.md).
---
## TODO
### Fonctionnalités
- [ ] **Publication MQTT** — envoi périodique de l'état système vers un broker MQTT (Home Assistant, Mosquitto…). Topics suggérés : `solar/state`, `solar/relay/1`, etc.
- [ ] **Version firmware dans l'UI** — afficher le numéro de version (défini dans `config.h`) sur le dashboard et dans l'en-tête de l'API `/api/state`
- [ ] **Améliorations interface web** — graphes plein écran, export PDF, notifications push navigateur (PWA), mode nuit/jour automatique de l'UI
- [ ] **Support PSK WireGuard** — la bibliothèque `WireGuard-ESP32-Arduino` n'expose pas encore la preshared key dans son API Arduino ; patcher la lib ou utiliser un fork qui le supporte
- [ ] **Alertes** — notification (email, webhook, MQTT) quand la batterie est basse, le RS485 déconnecté, ou un relais en défaut
- [ ] **Gestion multi-règles avancée** — priorité entre règles, règles avec plage horaire
### Code / qualité
- [ ] **Audit code mort** — vérifier si `modbus-esp8266` est encore utile (le code Modbus utilise du Serial2 raw + CRC manuel depuis la correction du RS485 ; les fonctions de la lib pourraient être inutilisées)
- [ ] **`backup/`** — le dossier est un instantané manuel ; le supprimer du repo une fois que l'historique git remplit ce rôle
- [ ] **Secrets compile-time**`WIFI_PASSWORD` et `OTA_PASSWORD` dans `config.h` en clair ; les déplacer dans un fichier `secrets.h` exclu du repo
- [ ] **Tests unitaires** — couvrir le moteur de règles et le décodage Modbus avec des tests PlatformIO (dossier `test/`)
- [ ] **Emulateur QEMU** — vérifier que le build `qemu` et les scripts Python du dossier `emulator/` sont encore synchronisés avec l'état actuel du firmware
---
---
## Ressources et liens utiles
### Matériel — KC868-A2
| Ressource | Lien |
|-----------|------|
| Page produit officielle | [kincony.com — KC868-A2](https://www.kincony.com/esp32-4g-relay.html) |
| Forum KinCony | [forum.kincony.com](https://www.kincony.com/forum/) |
| GitHub KinCony (officiel) | [github.com/hzkincony](https://github.com/hzkincony) |
| Profil ESPHome | [devices.esphome.io — KC868-A2](https://devices.esphome.io/devices/kincony-kc868-a2/) |
| Template Tasmota | [templates.blakadder.com — KC868-A2](https://templates.blakadder.com/kincony_KC868-A2.html) |
| Scripts démo Arduino | [github.com/playfultechnology/kincony](https://github.com/playfultechnology/kincony) |
### Matériel — Epever Tracer
| Ressource | Lien |
|-----------|------|
| Documentation Modbus v2.5 | [MODBUS-Protocol-v25.pdf](MODBUS-Protocol-v25.pdf) *(inclus dans ce repo)* |
| Documentation Tracer-AN Series | [epever.com — Tracer-AN v1.0](https://www.epever.com/upload/cert/file/1811/Tracer-AN-SMS-EL-V1.0.pdf) |
| Registres Modbus — discussion avancée | [diysolarforum.com — Epever registers](https://diysolarforum.com/threads/epever-tracer-modbus-registers-digging-deeper.108305/) |
| Carte complète des registres (Python) | [github.com/kasbert/epsolar-tracer](https://github.com/kasbert/epsolar-tracer/blob/master/pyepsolartracer/registers.py) |
| Intégration Home Assistant | [community.home-assistant.io — Epever Modbus](https://community.home-assistant.io/t/epever-modbus-rs-485-config/214397) |
| Topics GitHub `epever` | [github.com/topics/epever](https://github.com/topics/epever) |
### Projets similaires (ESP32 + Epever + Modbus)
| Projet | Description |
|--------|-------------|
| [Solar-Tracer-Blynk-V3](https://github.com/Bettapro/Solar-Tracer-Blynk-V3) | ESP32 + Epever RS485 → Blynk / Home Assistant / MQTT |
| [Tracer-RS485-Modbus-Blynk-V2](https://github.com/tekk/Tracer-RS485-Modbus-Blynk-V2) | ESP8266 + Epever RS485 → Blynk (version remaniée) |
| [Tracer-RS485-Modbus-CustomAPI](https://github.com/DeltaLima/Tracer-RS485-Modbus-CustomAPI) | ESP8266 + Epever RS485 → API personnalisée |
| [EPSolar_Tracer](https://github.com/alexnathanson/EPSolar_Tracer) | Plusieurs implémentations de communication avec les Tracer |
### WireGuard sur ESP32
| Ressource | Lien |
|-----------|------|
| Bibliothèque utilisée | [github.com/ciniml/WireGuard-ESP32-Arduino](https://github.com/ciniml/WireGuard-ESP32-Arduino) |
| Référence Arduino | [arduino.cc — WireGuard-ESP32](https://www.arduino.cc/reference/en/libraries/wireguard-esp32/) |
| Guide démarrage (DeepWiki) | [deepwiki.com — Getting Started](https://deepwiki.com/ciniml/WireGuard-ESP32-Arduino/2-getting-started) |
| Exemple avec interface web | [github.com/andr13/ESP32-Web-WireGuard](https://github.com/andr13/ESP32-Web-WireGuard) |
### Bibliothèques et outils
| Ressource | Lien |
|-----------|------|
| PlatformIO | [platformio.org](https://platformio.org/) |
| ESPAsyncWebServer | [github.com/me-no-dev/ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) |
| ElegantOTA | [github.com/ayushsharma82/ElegantOTA](https://github.com/ayushsharma82/ElegantOTA) |
| ArduinoJson | [arduinojson.org](https://arduinojson.org/) |
| modbus-esp8266 | [github.com/emelianov/modbus-esp8266](https://github.com/emelianov/modbus-esp8266) |
---
## Licence
Usage personnel — aucune licence définie.