a8f0d6ccba
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>
573 lines
13 KiB
Markdown
573 lines
13 KiB
Markdown
# 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
|
|
|
|
---
|