# 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 ```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 ```bash 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 ```text 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 `SystemState` global ## É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 ```text 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) ```text 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) ```text 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 ```text [ 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) ```text http://192.168.4.1/update ``` * Upload fichier `.bin` depuis 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 ```cpp // 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 ```cpp 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 ```text 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 ```text 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`) ```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 ```cpp // Évaluation pilotée par millis(), jamais par delay() if (millis() - lastRuleEval > RULE_INTERVAL_MS) { evaluerRegles(); lastRuleEval = millis(); } ``` --- # 18. Gestion de l'énergie ## Mode JOUR ```text WiFi ON Serveur web ON Lecture Epever active (toutes les 5s) Règles actives ``` ## Mode NUIT ```text 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 ```text /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 ---