Files
kc868-a2_solar/plan.md
T
gilles a8f0d6ccba Initial commit — KC868-A2 contrôleur solaire ESP32
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>
2026-05-09 19:25:01 +02:00

271 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.513.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