Files
kc868-a2_solar/README.md
T
2026-05-09 19:30:36 +02:00

279 lines
11 KiB
Markdown
Raw 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)
**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`).
### 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 |
---
## 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
---
## Licence
Usage personnel — aucune licence définie.