Fonctionnalités : - Lecture RS485 Modbus Epever Tracer 4210N (115200 bps, FC03/FC04/FC16) - Moteur de règles JSON (LittleFS) — commande automatique des relais - Interface web mobile-first (dashboard, règles, config, historique, EPEVER, debug) - WiFi AP+STA simultanés avec reconnexion automatique et portail captif - mDNS configurable (pv.local par défaut) - Configuration registres EPEVER depuis l'UI (18 registres holding) - Historique basse/haute résolution avec graphes canvas - VPN WireGuard optionnel (désactivé par défaut, config via UI) - OTA firmware + filesystem via ElegantOTA - Deep sleep / économie d'énergie Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 KiB
consigne.md
Projet : Contrôleur solaire autonome KC868-A2 + Epever 4210N
Rôle de l'assistant
Tu es un expert en développement d'objets IoT ESP32 avec PlatformIO. Tu emploieras le français pour la discussion. Les commentaires de code seront en français.
1. Objectif du projet
Développer un système autonome basé sur une carte Kincony KC868-A2 (ESP32) permettant :
- Lecture des données d'un régulateur solaire Epever 4210N via RS485 Modbus
- Pilotage de 2 relais
- Gestion de 2 boutons via contacts secs DI1 / DI2
- Hébergement d'une interface web responsive adaptée smartphone
- Fonctionnement autonome via point d'accès WiFi ESP32
- Mise à jour firmware OTA via interface web
- Moteur de règles programmable depuis l'interface web
- Gestion d'un mode économie d'énergie / sleep
- Système robuste tolérant aux erreurs RS485
Le système doit fonctionner sans internet.
2. Hardware utilisé
Carte principale
- Kincony KC868-A2
- ESP32-WROOM-32E intégré
- 2 relais
- RS485 intégré (MAX13487 — direction automatique, pas de pin DE/RE à gérer)
- Entrées contacts secs DI1 / DI2
- Alimentation 12V
Régulateur solaire
- Epever Tracer 4210N
- Communication Modbus RS485 via RJ45
3. GPIO KC868-A2
Extraits du schéma bloc officiel de la carte :
| Fonction | GPIO | Notes |
|---|---|---|
| Relay 1 | GPIO15 | Actif haut |
| Relay 2 | GPIO2 | ⚠️ Pin de boot — doit être HIGH au démarrage |
| RS485 TXD | GPIO32 | Vers MAX13487 |
| RS485 RXD | GPIO35 | Depuis MAX13487 (input only) |
| DI1 | GPIO36 | Input only, pull-up interne |
| DI2 | GPIO39 | Input only, pull-up interne |
| SDA (I2C) | GPIO4 | Disponible si besoin |
| SCL (I2C) | GPIO5 | Disponible si besoin |
| 1-Wire / DTH1 | GPIO27 | Disponible si besoin |
| DTH2 / LED | GPIO26 | Disponible si besoin |
⚠️ GPIO2 : relais 2 est sur ce pin de boot. Le relais ne doit pas forcer GPIO2 à LOW au démarrage sous peine de bloquer l'ESP32 en mode flash.
⚠️ GPIO35 et GPIO36 / GPIO39 : ces pins sont en entrée uniquement (input-only) sur l'ESP32 — pas de sortie possible.
Tous les GPIO sont en logique 3,3V.
4. Développement logiciel
Environnement
- Visual Studio Code
- PlatformIO
- Framework Arduino ESP32
Langages
- C++
- HTML / CSS / JavaScript
5. Configuration PlatformIO
platformio.ini
[env:kc868_a2]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
board_build.filesystem = littlefs
lib_deps =
bblanchon/ArduinoJson
emelianov/modbus-esp8266
me-no-dev/ESPAsyncWebServer
me-no-dev/AsyncTCP
ayushsharma82/AsyncElegantOTA
Commandes utiles
pio run # Compiler
pio run --target upload # Compiler et flasher
pio run --target monitor # Moniteur série
pio run --target uploadfs # Uploader le filesystem LittleFS (/data)
pio run --target clean # Nettoyer le build
6. Architecture générale
Epever 4210N
│
└── RS485 (Modbus RTU) ──► ESP32 KC868-A2
│
┌───────────┼───────────┐
▼ ▼ ▼
Moteur règles Relais 1/2 Boutons DI1/DI2
│
▼
Interface Web (WiFi AP 192.168.4.1)
│
▼
Smartphone utilisateur
7. Ordre de développement validé
Étape 1 — WiFi + Interface Web + OTA
- Point d'accès WiFi actif
- Serveur web async avec pages statiques depuis LittleFS
- OTA via
/update
Étape 2 — Pilotage relais
- API REST ON/OFF relais 1 et 2
- Retour état relais dans le dashboard
Étape 3 — Gestion boutons DI1 / DI2
- Lecture non-bloquante avec anti-rebond
- DI1 : bascule mode auto / manuel
- DI2 : commande manuelle relais
Étape 4 — Lecture RS485 Epever
- Initialisation Serial2 (GPIO32 TX, GPIO35 RX)
- Lecture périodique non-bloquante des registres Epever
- Mise à jour du
SystemStateglobal
Étape 5 — Moteur de règles
- Chargement règles depuis LittleFS (
rules.json) - Évaluation périodique des conditions
- Application des actions sur les relais
Étape 6 — Gestion sleep / économie d'énergie
- Détection mode JOUR / NUIT via état Epever
- Deep sleep la nuit, réveil périodique
8. Câblage
8.1 RS485
EPEVER RS485 A (D+) ───── KC868-A2 A1 / RXI
EPEVER RS485 B (D-) ───── KC868-A2 B1 / TXO
EPEVER GND ───── KC868-A2 GND (recommandé)
⚠️ Ne jamais connecter une alimentation provenant du RJ45 Epever.
8.2 Boutons (contacts secs)
DI1 ----[Bouton]---- GND
DI2 ----[Bouton]---- GND
- DI1 : mode auto / manuel
- DI2 : commande manuelle relais
8.3 Relais
- Relay 1 (GPIO15) → charge 1
- Relay 2 (GPIO2) → charge 2
9. WiFi
Mode point d'accès (AP)
SSID : KC868_SOLAR
IP : 192.168.4.1
Le smartphone se connecte directement à la carte, sans routeur ni internet.
10. Interface Web
Objectifs
- Responsive smartphone (mobile-first)
- Interface légère — pas de framework lourd (pas de React, Vue, etc.)
- Mise à jour dynamique par fetch JSON sans rechargement de page
- Vanilla JS + CSS simple
Sections
[ Dashboard ]
- Tension batterie (V)
- Tension PV (V)
- Courant PV (A)
- Etat jour / nuit
- Etat relais 1 et 2
- Etat RS485 (OK / erreur)
[ Commandes ]
- ON/OFF relais 1 et 2
- Bascule mode auto / manuel
[ Programmation ]
- Liste des règles actives
- Ajout / suppression règle
[ Configuration ]
- Activation sleep
- Seuils batterie
- Intervalle de lecture RS485
- Temporisations règles
[ Firmware ]
- OTA update (upload .bin)
[ Debug ]
- Logs série
- Erreurs RS485
- Etats DI1 / DI2
11. OTA (mise à jour firmware)
http://192.168.4.1/update
- Upload fichier
.bindepuis l'interface web - Redémarrage automatique après mise à jour
- Protection par mot de passe
12. Communication Modbus Epever
Paramètres
- Baudrate : 9600
- Adresse esclave : 1
- Modbus RTU (half-duplex)
- Serial2 : TX=GPIO32, RX=GPIO35
Registres utiles
| Donnée | Registre | Unité | Facteur |
|---|---|---|---|
| Tension PV | 0x3100 | V | ÷100 |
| Courant PV | 0x3101 | A | ÷100 |
| Puissance PV | 0x3102 | W | ÷100 |
| Tension batterie | 0x3104 | V | ÷100 |
| Courant de charge | 0x3106 | A | ÷100 |
| Etat jour/nuit | 0x200C | bit 0 = nuit | — |
| Etat charge batterie | 0x3201 | bits | — |
Timing de lecture
- Intervalle recommandé : toutes les 5 secondes
- Timeout réponse : 200 ms maximum
- Retry : 2 tentatives maximum
13. Gestion des erreurs RS485
Règle critique
Une erreur RS485 ne doit jamais bloquer :
- le serveur web
- le pilotage relais
- les boutons
- le moteur de règles
Interdit
// JAMAIS de boucle bloquante
while (!response) { }
delay(x);
Comportement attendu
- Lecture périodique pilotée par
millis() - Timeout court (200 ms)
- Maximum 2 retries
- En cas d'échec : conserver la dernière valeur valide, passer
rs485_ok = false - L'interface web affiche l'état RS485
14. Etat système global
struct SystemState {
// Données Epever
float battery; // Tension batterie (V)
float pv; // Tension PV (V)
float pvCurrent; // Courant PV (A)
bool sun; // true = jour
// Relais
bool relay1;
bool relay2;
// Boutons
bool di1;
bool di2;
// Santé RS485
bool rs485_ok;
unsigned long last_update; // millis() de la dernière lecture OK
};
15. Pilotage relais
- ON/OFF manuel depuis l'interface web
- Pilotage automatique via moteur de règles
- Pilotage manuel via boutons DI1/DI2
- État affiché en temps réel dans le dashboard
16. Boutons DI1 / DI2
- Anti-rebond logiciel (50 ms)
- DI1 : bascule mode auto ↔ manuel
- DI2 : en mode manuel, toggle relais 1
- En mode auto : les règles reprennent le contrôle
17. Moteur de règles
Structure logique
SI (conditions)
ALORS (action)
AVEC (délai optionnel en secondes)
Conditions possibles
- Soleil (jour/nuit)
- Batterie > seuil
- Batterie < seuil
- Etat relais
- Etat DI
Actions possibles
- Activer relais 1 ou 2
- Désactiver relais 1 ou 2
Exemple règle
SI soleil ET batterie > 13V → ALORS relais1 ON
SI soleil ET batterie > 13V → ATTENDRE 3600s → ALORS relais2 ON
Format JSON (stocké dans LittleFS rules.json)
[
{
"id": 1,
"enabled": true,
"sun": true,
"battery_min": 13.0,
"battery_max": 0,
"relay": 1,
"state": true,
"delay": 0
}
]
| Champ | Type | Description |
|---|---|---|
id |
int | Identifiant unique |
enabled |
bool | Règle active ou non |
sun |
bool | Condition soleil (true=jour, false=nuit) |
battery_min |
float | Seuil min batterie en V (0 = ignoré) |
battery_max |
float | Seuil max batterie en V (0 = ignoré) |
relay |
int | Numéro relais (1 ou 2) |
state |
bool | true = ON, false = OFF |
delay |
int | Délai avant action (secondes) |
Timing des règles
// Évaluation pilotée par millis(), jamais par delay()
if (millis() - lastRuleEval > RULE_INTERVAL_MS) {
evaluerRegles();
lastRuleEval = millis();
}
18. Gestion de l'énergie
Mode JOUR
WiFi ON
Serveur web ON
Lecture Epever active (toutes les 5s)
Règles actives
Mode NUIT
WiFi OFF
Serveur web OFF
Deep sleep
Réveil périodique (configurable, ex: 10 min)
Lecture Epever au réveil
Si toujours nuit → retour deep sleep
Paramètres configurables
- Activation / désactivation du sleep
- Intervalle de réveil (secondes)
- Seuil de détection soleil (valeur registre 0x200C)
- Mode : deep sleep ou light sleep
Contraintes deep sleep
- Deep sleep = reboot complet de l'ESP32
- UART indisponible pendant le sleep
- Les relais conservent leur état (verrouillage mécanique)
- Variables RAM perdues au réveil → sauvegarder en RTC memory si besoin
19. Consommation estimée
| Mode | Consommation |
|---|---|
| Normal | 1.8 à 4 W |
| Sleep | 0.1 à 0.6 W |
20. Structure projet PlatformIO
/src
main.cpp ← boucle principale, init, dispatcher
wifi.cpp ← AP WiFi
webserver.cpp ← serveur HTTP async, endpoints REST
ota.cpp ← mise à jour OTA
modbus.cpp ← lecture RS485 Epever non-bloquante
relais.cpp ← contrôle GPIO relais
rules.cpp ← évaluation moteur de règles
sleep.cpp ← gestion deep sleep
buttons.cpp ← lecture DI1/DI2 avec anti-rebond
/include
config.h ← constantes GPIO, SSID, intervalles
state.h ← déclaration SystemState
/data
index.html ← interface web principale
style.css
app.js ← fetch JSON, mise à jour dynamique
rules.json ← règles persistées (LittleFS)
21. Contraintes de conception
Non-bloquant partout
Toute la logique doit utiliser millis() et des machines à états. Jamais de delay() ni de boucle d'attente.
RS485 half-duplex
- Le MAX13487 gère automatiquement la direction (pas de pin DE)
- Ne pas lire trop fréquemment : respecter l'intervalle de 5 secondes
- Bien gérer le timeout de 200 ms par requête Modbus
GPIO2 (Relay 2)
- GPIO2 est un pin de strapping de boot sur ESP32
- S'assurer que le relais ne tire pas GPIO2 à LOW au démarrage
LittleFS
- Le filesystem doit être uploadé séparément :
pio run --target uploadfs - La partition LittleFS doit être configurée dans
platformio.ini
22. Evolutions possibles
- MQTT / Home Assistant
- Historique graphique (Chart.js)
- Mode STA (connexion au routeur existant)
- Accès distant VPN
- Conditions avancées ET / OU dans les règles
- Gestion des priorités entre règles
- Notifications push
23. Objectif final
Le système final doit être :
- autonome — fonctionne sans internet ni serveur externe
- robuste — tolérant aux erreurs RS485 et aux redémarrages
- configurable — réglable depuis un smartphone
- optimisé énergie — mode sleep la nuit
- extensible — architecture modulaire facilitant les ajouts
- maintenable — code commenté en français, structure claire