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>
10 KiB
Plan de développement — Configuration EPEVER
Basé sur l'analyse de consigne_amelioration_epever_config_md.md et du code existant.
État actuel du projet (base de travail)
| Module | Fichier | État |
|---|---|---|
| Modbus RTU (lecture brute) | modbus_epever.cpp |
✅ Fonctionnel à 115 200 bauds |
| FC03 holding read | lireHoldingBruts() |
✅ Utilisé pour RTC |
| FC16 holding write | ecrireHoldingMultiplesBrut() |
✅ Utilisé pour RTC |
| Webserver REST | webserver.cpp |
✅ Pattern AsyncCallbackJsonWebHandler établi |
| Persistance LittleFS | rules.cpp → /rules.json |
✅ Patron JSON réutilisable |
| Persistance NVS | relais, noms, modbus, sleep |
✅ Patron Preferences établi |
| UI web (onglets) | data/index.html + app.js |
✅ Architecture multi-onglets existante |
Infrastructure à réutiliser sans modification : lireHoldingBruts(), ecrireHoldingMultiplesBrut(), lectureEnCours, AsyncCallbackJsonWebHandler, pattern LittleFS JSON.
Registres Epever cibles (Holding FC03 / FC16)
Deux blocs consécutifs lisibles en une seule trame chacun :
Bloc 1 — 0x9000 à 0x900D (14 registres, facteur ÷100 sauf 0x9000/9001)
| Registre | Clé JSON | Unité | Facteur | Min | Max | Écriture |
|---|---|---|---|---|---|---|
| 0x9000 | battery_type |
— | ×1 | 0 | 4 | ✅ (0=User,1=Sealed,2=GEL,3=Flooded,4=Li) |
| 0x9001 | battery_capacity |
Ah | ×1 | 1 | 9999 | ✅ |
| 0x9002 | temp_compensation |
mV/°C/2V | ×1 | -9 | 0 | ✅ |
| 0x9003 | overvoltage_disconnect |
V | ÷100 | 13.0 | 16.0 | ✅ |
| 0x9004 | charging_limit |
V | ÷100 | 13.0 | 15.5 | ✅ |
| 0x9005 | overvoltage_reconnect |
V | ÷100 | 13.0 | 16.0 | ✅ |
| 0x9006 | equalize_voltage |
V | ÷100 | 13.0 | 15.5 | ✅ |
| 0x9007 | boost_voltage |
V | ÷100 | 13.5 | 15.0 | ✅ |
| 0x9008 | float_voltage |
V | ÷100 | 13.0 | 14.5 | ✅ |
| 0x9009 | boost_reconnect |
V | ÷100 | 11.0 | 13.5 | ✅ |
| 0x900A | low_reconnect |
V | ÷100 | 10.0 | 13.5 | ✅ |
| 0x900B | undervolt_warning |
V | ÷100 | 10.0 | 13.0 | ✅ |
| 0x900C | low_disconnect |
V | ÷100 | 9.0 | 13.0 | ✅ |
| 0x900D | discharge_limit |
V | ÷100 | 9.0 | 13.0 | ✅ |
Bloc 2 — 0x906B à 0x906E (4 registres)
| Registre | Clé JSON | Unité | Facteur | Min | Max | Écriture |
|---|---|---|---|---|---|---|
| 0x906B | rated_voltage |
— | ×1 | 0 | 2 | ✅ (0=auto,1=12V,2=24V) |
| 0x906C | equalize_duration |
min | ×1 | 0 | 180 | ✅ |
| 0x906D | boost_duration |
min | ×1 | 10 | 180 | ✅ |
| 0x906E | bat_discharge_soc |
% | ×1 | 20 | 100 | ✅ |
Architecture cible — nouveaux fichiers uniquement
src/
epever_config.cpp ← NOUVEAU : logique lecture/écriture/persistance config
include/
epever_config.h ← NOUVEAU : struct EpeverRegDef, API publique
data/
epever_config.json ← NOUVEAU : sauvegarde locale LittleFS (généré à l'usage)
Fichiers modifiés (ajouts uniquement, pas de refactoring) :
| Fichier | Modification |
|---|---|
src/webserver.cpp |
+6 routes API /api/epever/config* |
src/main.cpp |
+1 appel initConfigEpever() dans setup() |
src/modbus_epever.cpp |
+1 fonction isModbusBusy() pour exposer lectureEnCours |
include/modbus_epever.h |
+1 déclaration isModbusBusy() |
data/index.html |
+1 onglet "Config EPEVER" |
data/app.js |
+section JS onglet config |
data/style.css |
+styles mineurs onglet config |
Fichiers non touchés : state.h, rules.cpp, historique.cpp, sleep.cpp, wifi_ap.cpp, buttons.cpp, ota.cpp.
Étapes de développement
Étape 1 — Abstraction registres (backend)
Créer include/epever_config.h et src/epever_config.cpp :
struct EpeverRegDef {
uint16_t reg;
const char *key;
float scale;
float valMin;
float valMax;
bool writable;
const char *unit;
const char *label;
};
Table statique des ~18 registres des deux blocs.
Pas d'effet sur le comportement du système — compilation et link uniquement.
Test : pio run — vérifier que la compilation passe.
Étape 2 — Lecture config Epever (backend)
Dans epever_config.cpp :
bool lireConfigEpever(); // lit les deux blocs, populate configCache
void getConfigJson(String &out); // sérialise + métadonnées (lastRead, rs485_ok, erreur)
Utilise lireHoldingBruts() existant. Même garde que reglerHorlogeEpever() :
if (isModbusBusy()) return false;
Cache en RAM (float configCache[18] + bool configValide).
Dans webserver.cpp :
GET /api/epever/config → lireConfigEpever() + getConfigJson()
Test : appel depuis navigateur, vérifier JSON avec valeurs cohérentes de l'Epever.
Étape 3 — Validation + écriture (backend)
Dans epever_config.cpp :
bool validerConfig(JsonObject obj, String &erreur);
bool ecrireConfigEpever(JsonObject obj); // valide puis FC16 par bloc
Règles de validation :
- plage min/max par registre (table)
- cohérence inter-registres :
float_voltage < boost_voltage < equalize_voltage < overvoltage_disconnect low_disconnect < low_reconnect < undervolt_warning
Écriture en deux passes (bloc 0x9000, puis bloc 0x906B) avec vérification lecture-retour.
Dans webserver.cpp :
POST /api/epever/config → body JSON → validerConfig() → ecrireConfigEpever()
Réponse : {"ok": true} ou {"ok": false, "erreur": "Float > Boost"}.
Test : modifier Float Voltage de 0.1V depuis Postman/curl, relire pour confirmer.
Étape 4 — Sauvegarde LittleFS (backend)
Dans epever_config.cpp :
bool sauvegarderConfigJson(); // écrit /epever_config.json depuis configCache
bool restaurerConfigJson(); // lit /epever_config.json → écriture Epever
bool exporterConfigJson(String &out);
bool importerConfigJson(const String &json);
Format fichier identique à l'API (même structure JSON).
Nouvelles routes :
GET /api/epever/config/saved → lire LittleFS
POST /api/epever/config/save → sauvegarderConfigJson()
POST /api/epever/config/restore → restaurerConfigJson() → ecrireConfigEpever()
Test : sauvegarder, modifier une valeur sur l'Epever, restaurer, relire.
Étape 5 — Interface web (frontend)
Nouvel onglet "Config EPEVER" dans index.html + section dans app.js.
Structure UI :
┌─────────────────────────────────────────────────┐
│ [Lire depuis Epever] [Sauvegarder] [Restaurer]│
│ Statut : ✅ Synchronisé — 09:14:22 │
├───────────────────────┬─────────────────────────┤
│ 🔋 Batterie │ ⚡ Tensions de charge │
│ Type [Sealed ▼] │ Boost [14.4 V] │
│ Capacité [100 Ah] │ Float [13.6 V] │
│ Tension [auto ▼] │ Equalize [14.6 V] │
├───────────────────────┼─────────────────────────┤
│ ⏱ Timing │ 🌡 Température │
│ Durée boost [120 min] │ Compensation [-3 mV/°C] │
│ Durée égal. [60 min] │ │
└───────────────────────┴─────────────────────────┘
[✏️ Écrire vers Epever] [⬇ Exporter JSON] [⬆ Importer]
Comportements :
- Lire : GET
/api/epever/config, remplit les champs - Écrire : confirmation modale → POST
/api/epever/config - Sauvegarder : POST
/api/epever/config/save - Restaurer : confirmation modale → POST
/api/epever/config/restore - Exporter : GET
/api/epever/config/saved,Blob+<a download> - Importer :
<input type="file">→ POST/api/epever/config
Aide intégrée (novice) : chaque paramètre a un title HTML + icône ℹ avec explication en français courte.
Exemples :
Tension Float : tension de maintien après charge complète. Une valeur trop haute fatigue la batterie ; trop basse ne la charge pas suffisamment. Typique : 13.5–13.8V pour plomb.
Test : vérifier sur mobile (WiFi AP), formulaire responsive, confirmation avant écriture.
Étape 6 — Gestion des erreurs UI
- Champ en rouge si valeur hors plage (validation JS temps réel)
- Alerte si cohérence inter-registres violée avant même d'écrire
- Toast de succès/erreur après écriture (3s)
- Indicateur
rs485_okdans le status bar de l'onglet - Affichage de la dernière erreur Modbus si
rs485_ok = false
Contraintes de non-régression
| Risque | Mitigation |
|---|---|
| Conflit accès Serial2 | isModbusBusy() — même garde que reglerHorlogeEpever() |
| Saturation LittleFS | /epever_config.json ≈ 500 octets — négligeable |
| Timeout HTTP long | lireHoldingBruts() sur 2 blocs ≈ 200ms — acceptable |
| Écriture valeur dangereuse | Double validation : JS (UX) + C++ (sécurité) |
| Régression onglets existants | Ajouts dans app.js isolés dans une section dédiée |
Dead code connu à ne pas toucher : les callbacks cbPV, cbLoad, cbSOC, cbStatus, cbEnergie, cbJourNuit dans modbus_epever.cpp sont inactifs mais laissés en place pour éviter le risque de régression.
Ordre d'implémentation recommandé
Étape 1 : EpeverRegDef + table registres (~1h) → pio run
Étape 2 : lireConfigEpever + GET /api/epever/config (~2h) → test JSON navigateur
Étape 3 : validation + POST /api/epever/config (~2h) → test curl
Étape 4 : LittleFS save/restore (~1h) → test navigateur
Étape 5 : UI onglet complet (~3h) → test mobile
Étape 6 : gestion erreurs UI + aide novice (~1h) → test cas d'erreur
Total estimé : ~10h de développement incrémental, chaque étape testable indépendamment.
Fonctionnalités futures (hors scope immédiat)
- Profils batterie prédéfinis : GEL, AGM, Sealed, Lithium (presets JSON)
- Rollback automatique si la tension batterie tombe anormalement après écriture
- Historique des modifications de config (date + delta)
- Synchronisation MQTT / Home Assistant
- Mode expert : affichage de tous les registres bruts