# 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` : ```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` : ```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()` : ```cpp 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` : ```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` : ```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` + `` - **Importer** : `` → 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_ok` dans 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