2026-05-09 19:39:31 +02:00

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


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 RS485 ETH

Schéma

Documentation technique :

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 / soleil12192.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 ^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) :

  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 :

#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)

[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-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.


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-timeWIFI_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
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.

S
Description
No description provided
Readme 4.1 MiB
Languages
C++ 43.8%
HTML 24.2%
JavaScript 16.4%
CSS 5.8%
Python 5.7%
Other 4.1%