Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.
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 |
|---|---|---|
![]() |
![]() |
![]() |
Documentation technique :
- KC868-A2 Schematic — schéma électronique complet de la carte
- Epever Modbus Protocol v2.5 — 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.localconfigurable 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 | ^7.0 | Sérialisation JSON |
| AsyncTCP | git | TCP non bloquant |
| ESPAsyncWebServer | git | Serveur HTTP async |
| ElegantOTA | ^3.1 | OTA via navigateur |
| modbus-esp8266 | ^4.1 | Modbus RTU (utilisé pour les fonctions bas niveau) |
| 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 — 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
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) :
- Sélectionner Firmware → uploader
.pio/build/kc868_a2/firmware.bin - Sélectionner Filesystem → uploader
.pio/build/kc868_a2/littlefs.bin
Configuration
WiFi AP (compile-time)
Dans include/config.h :
#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é :
25s 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
.confWireGuard dans git (il est dans.gitignore).
Exemple de fichier .conf (généré par wg-easy ou wg CLI)
[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 :
# 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-Arduinon'expose pas encore laPresharedKeydans 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.
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-Arduinon'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-esp8266est 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_PASSWORDetOTA_PASSWORDdansconfig.hen clair ; les déplacer dans un fichiersecrets.hexclu 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
qemuet les scripts Python du dossieremulator/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 |
| Forum KinCony | forum.kincony.com |
| GitHub KinCony (officiel) | github.com/hzkincony |
| Profil ESPHome | devices.esphome.io — KC868-A2 |
| Template Tasmota | templates.blakadder.com — KC868-A2 |
| Scripts démo Arduino | github.com/playfultechnology/kincony |
Matériel — Epever Tracer
| Ressource | Lien |
|---|---|
| Documentation Modbus v2.5 | MODBUS-Protocol-v25.pdf (inclus dans ce repo) |
| Documentation Tracer-AN Series | epever.com — Tracer-AN v1.0 |
| Registres Modbus — discussion avancée | diysolarforum.com — Epever registers |
| Carte complète des registres (Python) | github.com/kasbert/epsolar-tracer |
| Intégration Home Assistant | community.home-assistant.io — Epever Modbus |
Topics GitHub epever |
github.com/topics/epever |
Projets similaires (ESP32 + Epever + Modbus)
| Projet | Description |
|---|---|
| Solar-Tracer-Blynk-V3 | ESP32 + Epever RS485 → Blynk / Home Assistant / MQTT |
| Tracer-RS485-Modbus-Blynk-V2 | ESP8266 + Epever RS485 → Blynk (version remaniée) |
| Tracer-RS485-Modbus-CustomAPI | ESP8266 + Epever RS485 → API personnalisée |
| 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 |
| Référence Arduino | arduino.cc — WireGuard-ESP32 |
| Guide démarrage (DeepWiki) | deepwiki.com — Getting Started |
| Exemple avec interface web | github.com/andr13/ESP32-Web-WireGuard |
Bibliothèques et outils
| Ressource | Lien |
|---|---|
| PlatformIO | platformio.org |
| ESPAsyncWebServer | github.com/me-no-dev/ESPAsyncWebServer |
| ElegantOTA | github.com/ayushsharma82/ElegantOTA |
| ArduinoJson | arduinojson.org |
| modbus-esp8266 | github.com/emelianov/modbus-esp8266 |
Licence
Usage personnel — aucune licence définie.




