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>
271 lines
10 KiB
Markdown
271 lines
10 KiB
Markdown
# 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` + `<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_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
|