Note de ce sujet :
  • Moyenne : 1 (1 vote(s))
  • 1
  • 2
  • 3
  • 4
  • 5
Compatibilité compteur connecté EM06P (Meross, Refoss)
#21
Bonjour,
j'ai déplacé mon routeur à côté de ma box internet Wifi, le niveau WIFI est maintenant à -50dBm.
Maintenant, les pages s'affichent plus rapidement. La Led jaune pulse normalement toutes les 2s.
Cependant, lorsque je change entre les différentes pages, on dirait que j'arrive à surcharger le routeur et fini par planté.

J'ai fait un test, j'ai chargé la page "Donnée brutes", et je ne change plus de page.
Maintenant, j'ai la Led qui pulse normalement et l"ESP ON" est à 1h10min.

J'ai 2 mesures de températures et 1 action.

Avez-vous une idée de ce qu'il pourrait se passer?
Merci
Installation : 1 routeur fixe F1ATB en V17.19 en mode SSR pour chauffe-eau avec thermostat électronique
Photovoltaïque : 3 kWp : 3* (2 *500w + Micro onduleur Hoymiles HMS-1000W-2T) 
Domotique : F1atb + Home Assistant / REFOSS EM06P & MQTT
Répondre

#22
Bonjour,
le problème était les fichiers Export qui saturaient la mémoire et faisant plante l'ESP32.
J'ai sûrement du générer plein d'erreurs lorsque je faisais mes tests avec MQTT.

Maintenant, je suis surpris de la vitesse de chargement des pages du routeur :-)

Tout fonctionne bien, je n'ai plus qu'à m'atteler à la programmation des actions.

En espérant que mes recherches pourront en aider d'autres.

Encore merci André pour ton travail.
Installation : 1 routeur fixe F1ATB en V17.19 en mode SSR pour chauffe-eau avec thermostat électronique
Photovoltaïque : 3 kWp : 3* (2 *500w + Micro onduleur Hoymiles HMS-1000W-2T) 
Domotique : F1atb + Home Assistant / REFOSS EM06P & MQTT
Répondre

#23
Bonjour,
j'ai modifié le programme avec l'aide de l'IA Claude afin d'y intégrer mon compteur EM06P. Je me suis inspiré de ce qui était fait pour le Shelly Pro3Em.
J'ai fait les modifs en dur avec une pince voie 1 et l'autre voie 4; ca peut ne pas correspondre pour une autre personne.
Je mets ici le .ino concernant le compteur EM06P:
Code :
//*********************************************************************
// Variante EM06P                                                    *
// Adaptation basée sur un exemple réel de réponse JSON de l'EM06P :  *
//                                                                     *
// {"status":[                                                        *
//   {"id":1,"current":2.319,"voltage":228.614,"power":-155.988,      *
//    "pf":-0.29,"year_energy":294.99,"year_ret_energy":-259.035,     *
//    "month_energy":...,"week_energy":...,"day_energy":...},         *
//   {"id":2, ...}, ... jusqu'à {"id":6, ...}                         *
// ]}                                                                  *
//                                                                     *
// L'EM06P ne fournit PAS de compteur d'énergie cumulé depuis          *
// toujours (pas de "total_act_energy" façon Shelly). Le champ le     *
// plus proche est "year_energy" (kWh, flottant) mais il repart à     *
// zéro chaque 1er janvier.                                           *
// ********************************************************************
// * Paramétrage des voies pour le EM06P :                             *
// *   - EnphaseSerial (0 à 5) = numéro de la voie/pince du routeur   *
// *     (voie "M"). L'EM06P a 6 voies, mais leurs "id" JSON vont     *
// *     de 1 à 6 -> conversion +1 faite dans le code.                 *
// *   - VOIE_SECONDAIRE_EM06P_ID (constante ci-dessous) = voie "T"   *
// *     (ex: production PV). Fixée en dur à l'id 4 : adaptez cette   *
// *     valeur si votre câblage change.                               *
// *                                                                    *
// * IMPORTANT : contrairement au Shelly (qui nécessitait une requête *
// * HTTP séparée par voie, d'où l'ancienne logique d'alternance sur  *
// * plusieurs cycles), l'EM06P renvoie TOUTES les voies en une seule *
// * requête. On extrait donc M et T à CHAQUE appel, pour une         *
// * régulation plus réactive (plus d'alternance qui ne rafraîchissait*
// * la voie du routeur qu'une fois sur 5 cycles environ).            *
// ********************************************************************

// id JSON (1 à 6) de la voie secondaire T - à adapter si besoin
const int VOIE_SECONDAIRE_EM06P_ID = 4;

// Extrait le sous-objet JSON {"id":N, ...} d'une chaîne "status":[...]
// Hypothèse : les objets de voie ne contiennent que des champs scalaires
// (pas de sous-objet imbriqué), donc la première "}" après le "id":N
// correspond bien à la fin de l'objet de cette voie.
String ExtraitVoieEM06P(int id, String &data) {
  String cle = "\"id\":" + String(id) + ",";
  int debut = data.indexOf(cle);
  if (debut < 0) return "";
  debut = data.lastIndexOf("{", debut);
  int fin = data.indexOf("}", debut);
  if (fin < 0) return "";
  return data.substring(debut, fin + 1);
}

// Extrait les grandeurs (Pw, voltage, pf, énergies jour/année) d'un
// sous-objet de voie déjà isolé (résultat de ExtraitVoieEM06P).
void LectureVoieEM06P(String &tmp, float &Pw, float &voltage, float &pf,
                       long &energieSoutireeWh, long &energieInjecteeWh,
                       long &jourSoutireeWh, long &jourInjecteeWh) {
  // Convention confirmée par observation terrain : power négatif = injection
  // réelle, power positif = soutirage réel. Pas d'inversion nécessaire.
  Pw = ValJson("power", tmp);
  voltage = ValJson("voltage", tmp);
  pf = abs(ValJson("pf", tmp));
  if (pf > 1) pf = 1;

  // Compteur d'énergie "année" en kWh -> converti en Wh (cf. limitation
  // en tête de fichier : ce compteur repart à zéro chaque année).
  float yearEnergy = ValJson("year_energy", tmp);
  float yearRetEnergy = ValJson("year_ret_energy", tmp);
  energieSoutireeWh = (long)(abs(yearEnergy) * 1000.0);
  energieInjecteeWh = (long)(abs(yearRetEnergy) * 1000.0);

  // Compteur d'énergie "jour" (kWh -> Wh), fourni directement par l'EM06P,
  // remis à zéro chaque minuit par l'appareil lui-même : utilisé tel quel,
  // sans calcul de delta (voir EnergieQuotidienne() dans le sketch principal).
  float dayEnergy = ValJson("day_energy", tmp);
  float dayRetEnergy = ValJson("day_ret_energy", tmp);
  jourSoutireeWh = (long)(abs(dayEnergy) * 1000.0);
  jourInjecteeWh = (long)(abs(dayRetEnergy) * 1000.0);
}

void LectureEM06P() {
  String EM06P_Data = "";
  String tmp;

  WiFiClient clientESP_RMS;
  String host = IP2String(RMSextIP);

  const int NB_VOIES = 6;  // EM06P : 6 voies monophasées indépendantes
  int idM = (EnphaseSerial.toInt() % NB_VOIES) + 1;  // id JSON de la voie routeur (1 à 6)
  int p;
  unsigned long timeout = millis();

  // id=65535 = id spécial qui renvoie le statut des 6 voies en une requête
  String url = "/rpc/Em.Status.Get?id=65535";

  if (!clientESP_RMS.connect(host.c_str(), 80, 3000)) {
    delay(500);
    if (!clientESP_RMS.connect(host.c_str(), 80, 3000)) {
      delay(100);
      clientESP_RMS.stop();
      StockMessage("connection to EM06P failed : " + host);
      delay(200);
      return;
    }
  }

  clientESP_RMS.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");

  // Lecture robuste : on attend la fermeture de la connexion par le serveur
  // (Connection: close) plutôt que de s'arrêter dès qu'il n'y a momentanément
  // plus de données disponibles - la réponse peut arriver en plusieurs paquets.
  bool gotData = false;
  timeout = millis();
  while (millis() - timeout < 5000) {
    while (clientESP_RMS.available()) {
      EM06P_Data += (char)clientESP_RMS.read();
      timeout = millis();  // on relance le délai tant que des données arrivent
      gotData = true;
    }
    if (!clientESP_RMS.connected() && !clientESP_RMS.available()) {
      break;  // le serveur a fermé la connexion et il n'y a plus rien à lire
    }
  }
  clientESP_RMS.stop();
  if (!gotData) {
    StockMessage("client EM06P Timeout ! : " + host);
    return;
  }
  p = EM06P_Data.indexOf("{");
  if (p < 0) {
    StockMessage("EM06P : aucune accolade '{' trouvée dans la réponse");
    filtre_puissance();
    PuissanceRecue = true;
    return;
  }
  EM06P_Data = EM06P_Data.substring(p);

  String brute = "";

  // --- Voie M (routeur) --- lue à chaque appel
  tmp = ExtraitVoieEM06P(idM, EM06P_Data);
  if (tmp != "") {
    brute += "<strong>EM06P voie M (id " + String(idM) + ")</strong><br>" + tmp + "<br>";

    float Pw, voltage, pf;
    long energieSoutireeWh, energieInjecteeWh, jourSoutireeWh, jourInjecteeWh;
    LectureVoieEM06P(tmp, Pw, voltage, pf, energieSoutireeWh, energieInjecteeWh, jourSoutireeWh, jourInjecteeWh);

    if (Pw >= 0) {
      PuissanceS_M_inst = Pw;
      PuissanceI_M_inst = 0;
      PVAS_M_inst = (pf > 0.01) ? PfloatMax(Pw / pf) : 0;
      PVAI_M_inst = 0;
    } else {
      PuissanceS_M_inst = 0;
      PuissanceI_M_inst = -Pw;
      PVAI_M_inst = (pf > 0.01) ? PfloatMax(-Pw / pf) : 0;
      PVAS_M_inst = 0;
    }
    Energie_M_Soutiree = energieSoutireeWh;
    Energie_M_Injectee = energieInjecteeWh;
    EnergieJour_M_Soutiree = jourSoutireeWh;
    EnergieJour_M_Injectee = jourInjecteeWh;
    PowerFactor_M = pf;
    Tension_M = voltage;
    Pva_valide = true;
  } else {
    StockMessage("EM06P : voie M (id " + String(idM) + ") introuvable dans la réponse JSON");
  }

  // --- Voie T (secondaire, ex: PV) --- lue à chaque appel elle aussi
  tmp = ExtraitVoieEM06P(VOIE_SECONDAIRE_EM06P_ID, EM06P_Data);
  if (tmp != "") {
    brute += "<strong>EM06P voie T (id " + String(VOIE_SECONDAIRE_EM06P_ID) + ")</strong><br>" + tmp;

    float Pw, voltage, pf;
    long energieSoutireeWh, energieInjecteeWh, jourSoutireeWh, jourInjecteeWh;
    LectureVoieEM06P(tmp, Pw, voltage, pf, energieSoutireeWh, energieInjecteeWh, jourSoutireeWh, jourInjecteeWh);

    if (LissageLong) {
      PwMoy2 = 0.2 * Pw + 0.8 * PwMoy2;
      pfMoy2 = 0.2 * pf + 0.8 * pfMoy2;
      Pw = PwMoy2;
      pf = pfMoy2;
    }
    if (Pw >= 0) {
      PuissanceS_T_inst = Pw;
      PuissanceI_T_inst = 0;
      PVAS_T_inst = (pf > 0.01) ? PfloatMax(Pw / pf) : 0;
      PVAI_T_inst = 0;
    } else {
      PuissanceS_T_inst = 0;
      PuissanceI_T_inst = -Pw;
      PVAI_T_inst = (pf > 0.01) ? PfloatMax(-Pw / pf) : 0;
      PVAS_T_inst = 0;
    }
    Energie_T_Soutiree = energieSoutireeWh;
    Energie_T_Injectee = energieInjecteeWh;
    EnergieJour_T_Soutiree = jourSoutireeWh;
    EnergieJour_T_Injectee = jourInjecteeWh;
    PowerFactor_T = pf;
    Tension_T = voltage;
  } else {
    StockMessage("EM06P : voie T (id " + String(VOIE_SECONDAIRE_EM06P_ID) + ") introuvable dans la réponse JSON");
  }

  ShEm_dataBrute = brute;

  filtre_puissance();
  PuissanceRecue = true;
  EnergieActiveValide = true;  // les deux voies sont lues à chaque appel, plus besoin d'alternance
  if (cptLEDyellow > 30) {
    cptLEDyellow = 4;
  }
}


Mes valeurs sont bien remontés sur la page d'accueil.
Je pense qu'il faut encore nettoyer le programme.
Cependant, la régulation semble moins stable que lorsque j'avais mes données de compteurs reçues via MQTT (voir PJ).
Je ne peux plus tester à cette heure, à voir les prochains jours.
Est-ce que quelqu'un a une idée d'où pourrait venir le problème?

Voici tout le programme en téléchargement:
https://www.grosfichiers.com/iauCewnwBBa


En espérant que ma version puisse aider un autre utilisateur d'EM06P ;-)


Pièces jointes Miniature(s)
   
Installation : 1 routeur fixe F1ATB en V17.19 en mode SSR pour chauffe-eau avec thermostat électronique
Photovoltaïque : 3 kWp : 3* (2 *500w + Micro onduleur Hoymiles HMS-1000W-2T) 
Domotique : F1atb + Home Assistant / REFOSS EM06P & MQTT
Répondre

#24
Salut,

bien vue l'adaptation, 'cest presque un Shelly.

à cet endroit du code:
  p = EM06P_Data.indexOf("{");
  if (p < 0) {
    StockMessage("EM06P : aucune accolade '{' trouvée dans la réponse");
    filtre_puissance();
    PuissanceRecue = true;
    return;
  }

les données puissance seraient invalides donc si tu veux garder le reset au cas ou tu ne reçois pas les données il faudrait sûrement juste faire le return sans mettre PuissanceRecue a true et sans filtre_puissance().
Je suis aussi mauvais qu'une IA, mes réponses sont très incertaines.
Répondre

#25
Salut,
je réussis à lire les données de mon compteur avec un taux de rafraîchissement d'environ 5 secondes. Je ne sais pas si cela vient d'une limite de ce compteur ou le code.
Serait-ce suffisant pour la régulation?
En comparaison, quel est le taux de rafraîchissement d'un Shelly ?
Merci
Installation : 1 routeur fixe F1ATB en V17.19 en mode SSR pour chauffe-eau avec thermostat électronique
Photovoltaïque : 3 kWp : 3* (2 *500w + Micro onduleur Hoymiles HMS-1000W-2T) 
Domotique : F1atb + Home Assistant / REFOSS EM06P & MQTT
Répondre

#26
salut

le shelly est amplement sous la seconde, je crois que le linky est toute les 2s.
Répondre

#27
Bonjour,
j'ai modifié le code et me voici avec un rafraichissement des données toutes les 0,5sec. 
Maintenant, la régulation semble beaucoup plus stable et la navigation entre les pages est bien plus fluide.
Cela pourrait aider pour tout le monde.
Si André veut ajouter tout cela dans son programme, ca serait super.

Voici les fichiers:
https://www.grosfichiers.com/X5Kfp7tF4Y7

Claude m'a résumé les modification du code :
# Adaptation du routeur solaire F1ATB au compteur Meross/Refoss EM06P

Résumé des évolutions et corrections apportées, dans l'ordre chronologique.

## 1. Contexte et objectif de départ

Le programme gérait initialement un compteur **Shelly Pro EM**. L'objectif était
de l'adapter pour fonctionner avec un compteur **Meross/Refoss EM06P**, qui
utilise le même style de protocole JSON-RPC en HTTP, mais avec :
- une structure JSON différente (tableau plat de 6 voies indépendantes,
  identifiées par `"id":1` à `"id":6`, plutôt que des objets nommés
  `"em1:0"`, `"em1:1"`... comme chez Shelly) ;
- des noms de champs différents (`power` au lieu de `act_power`, etc.) ;
- pas de compteur d'énergie cumulé "depuis toujours" (pas de
  `total_act_energy`), seulement des compteurs `day_energy` / `week_energy`
  / `month_energy` / `year_energy` (et leurs équivalents `_ret_energy` pour
  le sens inverse), tous remis à zéro périodiquement par l'appareil
  lui-même.

## 2. Découverte de l'API réelle

La documentation officielle (`docs.refoss.net`) est un site JavaScript non
lisible automatiquement. La structure réelle de l'API a été déterminée à
partir d'un relevé JSON fourni par l'utilisateur, puis confirmée par un test
direct :
```
GET http://<IP>/rpc/Em.Status.Get?id=65535
```
`id=65535` est un identifiant spécial qui renvoie le statut des 6 voies en
une seule requête (pas besoin d'interroger voie par voie).

## 3. Écriture de la fonction `LectureEM06P()`

Nouvelle fonction créée sur le modèle de `LectureShellyProEm()`, avec :
- une fonction `ExtraitVoieEM06P(id, data)` qui isole le sous-objet JSON
  d'une voie donnée dans le tableau `"status":[...]` ;
- lecture de `power`, `voltage`, `pf`, `year_energy`/`year_ret_energy` et
  `day_energy`/`day_ret_energy` par voie.

## 4. Bugs corrigés dans le code

### 4.1 Lecture réseau peu robuste
La boucle de lecture HTTP initiale s'arrêtait dès qu'il n'y avait
momentanément plus de données disponibles, alors que la réponse peut arriver
en plusieurs paquets réseau séparés (en-têtes puis corps). Corrigée avec une
boucle qui relance le délai d'attente à chaque octet reçu.

### 4.2 Bug dans `ValJson()` (fonction générique de parsing JSON)
`ValJson()` retournait **0** lorsque le champ demandé était le **dernier**
d'un objet JSON (donc suivi directement de `}` sans virgule après) : le code
faisait `min(position_virgule, position_accolade)`, et quand la virgule est
absente (`indexOf` renvoie -1), `min(x, -1)` vaut toujours -1, ce qui faisait
échouer l'extraction. Ce bug touchait spécifiquement `day_ret_energy`
(toujours en dernière position dans chaque objet voie de l'EM06P), d'où
"Energie Active du jour → Injectée" bloquée à 0. Corrigé, et potentiellement
utile aussi pour d'autres sources utilisant cette même fonction.

### 4.3 Calcul de l'énergie du jour inadapté
`EnergieQuotidienne()` calculait l'énergie du jour par **delta** par rapport
à une baseline mémorisée à minuit (`EAS_M_J0` etc.), ce qui suppose un
compteur strictement croissant. L'EM06P n'ayant pas ce type de compteur,
cette logique produisait des valeurs incohérentes. Corrigé en excluant
`Source == "EM06P"` de ce calcul par delta, et en utilisant directement les
champs `day_energy`/`day_ret_energy` fournis par l'appareil (déjà remis à
zéro par l'appareil lui-même chaque minuit).

### 4.4 Convention de signe puissance/énergie
Une inversion de signe avait été appliquée par erreur suite à une déduction
indirecte, puis annulée après confirmation terrain : sur ce compteur,
**puissance positive = soutirage réel, puissance négative = injection
réelle** (convention standard, pas d'inversion nécessaire). Confirmé par
observation directe en situation d'injection puis de soutirage réel.

### 4.5 Voie secondaire (T) mal choisie, lue une fois sur cinq
- La voie secondaire était calculée comme "voie principale + 1", sans lien
  avec l'installation réelle (PV câblé sur la voie 4). Fixée en dur sur
  `VOIE_SECONDAIRE_EM06P_ID = 4`.
- Le code alternait la lecture entre voie M et voie T sur plusieurs cycles
  (`ShEm_comptage_appels`), hérité du Shelly qui nécessitait une requête HTTP
  séparée par voie. Or l'EM06P renvoie déjà les 6 voies en une seule requête
  : le code a été restructuré pour extraire M **et** T de la même réponse à
  **chaque** appel, supprimant l'alternance et améliorant la réactivité de
  la régulation.

### 4.6 Sources d'affichage oubliant `"EM06P"`
Plusieurs endroits du code testaient explicitement `Source_data ==
"ShellyEm" || Source_data == "ShellyPro"` sans inclure `"EM06P"`, empêchant
l'affichage correct de la voie secondaire :
- `AdaptationSource()` (page Paramètres, JS) : champs de nom de la seconde
  sonde restaient masqués.
- `PageBruteJS2` (switch de la page Données Brutes) : aucune donnée EM06P
  affichée du tout (tombait dans le `default`).
- `handleAjaxData()` (serveur, deux occurrences) : envoyait `"Fake"` au lieu
  des vraies données de la voie T vers la page d'accueil.
- `handleMainJS1()` (serveur) : variable `biSonde` restait à `false`,
  masquant la colonne entière sur la page d'accueil même avec de bonnes
  données reçues.

Toutes ces conditions ont été complétées avec `Source_data == "EM06P"` /
`Source == "EM06P"`.

### 4.7 Titre de la page "Données Brutes"
Le titre "Données Shelly Em" était en dur dans le HTML (`PageBrute.h`),
partagé par Shelly et EM06P. Ajout d'un `id="titreShellyEm"` sur ce `<div>`,
mis à jour dynamiquement en JS selon la source (`case "EM06P":` distinct de
`case "ShellyEm"/"ShellyPro":`).

### 4.8 Fuite mémoire / fragmentation heap
La lecture réseau construisait la réponse **caractère par caractère** avec
un `String` Arduino (`EM06P_Data += (char)...`), ce qui peut déclencher une
réallocation complète à chaque ajout. Répété toutes les 300-600ms en continu,
ça fragmentait sévèrement la mémoire (RAM libre minimum tombée à ~200
octets, causant des pages web inaccessibles / timeouts). Corrigé avec
`EM06P_Data.reserve(2000)` en début de fonction, pour allouer le buffer une
seule fois.

### 4.9 Délai artificiel de ~5,5 secondes par lecture (cause principale de l'instabilité de régulation)
Un diagnostic de timing (ajouté avec compteur d'appels lents, moyenne et
écart-type calculés sur des fenêtres de 200 lectures) a révélé que **100 %**
des lectures dépassaient 1 seconde, avec une moyenne très stable autour de
5 500ms (écart-type faible, donc un phénomène régulier, pas aléatoire). Des
tests `curl` directs depuis un PC sur le même réseau ont montré que le
compteur répondait en réalité en 50-400ms — innocentant le matériel.

**Cause identifiée** : la boucle de lecture attendait que
`WiFiClient::connected()` devienne `false` pour considérer la lecture
terminée. Sur ESP32, cette fonction est connue pour mettre plusieurs
secondes à détecter la fermeture réelle d'une connexion TCP par le serveur,
même quand toutes les données ont déjà été reçues.

**Correction** : lecture désormais basée sur l'en-tête HTTP
`Content-Length` — la fonction s'arrête dès que le nombre d'octets annoncé a
été reçu, sans attendre la fermeture formelle de la connexion. Résultat
observé : temps de lecture moyen passé de ~5 500ms à **~1-460ms** (mesuré
via `Charge coeur 0` sur la page Données ESP32 et les messages `EM06P
DIAG`).

## 5. Outil de diagnostic ajouté

Un système de journalisation périodique (`EM06P DIAG`, toutes les 200
lectures) a été ajouté dans `LectureEM06P()`, donnant :
- nombre de lectures dépassant 1 seconde sur la fenêtre ;
- durée moyenne ;
- écart-type ;
- durée maximale observée.

Les compteurs sont remis à zéro à chaque rapport (fenêtre glissante), pour
rester représentatifs de l'état récent plutôt que d'une moyenne diluée
depuis le démarrage.

## 6. Correctif watchdog sur données invalides

Dans le cas où aucune donnée JSON valide n'est reçue (pas d'accolade `{`
trouvée), le code appelait auparavant quand même `filtre_puissance()` et
positionnait `PuissanceRecue = true` — ce qui réinitialisait à tort le
watchdog de sécurité (`WDT_TIMEOUT`, 180s) même en cas d'échec de lecture
réel et prolongé. Corrigé : ces deux appels sont retirés dans cette branche
d'erreur spécifique, pour que le reset de sécurité fonctionne correctement
en cas de panne durable du compteur.

## 7. État actuel

- Lecture HTTP rapide et fiable (~1-460ms au lieu de ~5 500ms).
- Mémoire stable dans le temps (plus de fuite/fragmentation).
- Voies M (routeur) et T (PV, voie 4) lues à chaque cycle, sans alternance.
- Affichage correct sur toutes les pages (Accueil, Données Brutes,
  Paramètres) pour la source EM06P.
- Énergies jour/total cohérentes avec les données réelles de l'appareil.
- Tests de stabilité de la régulation solaire en cours sur plusieurs jours,
  suite à la correction du délai de lecture (point 4.9), qui était très
  probablement la cause principale de l'instabilité observée initialement.


Pièces jointes Miniature(s)
       
Installation : 1 routeur fixe F1ATB en V17.19 en mode SSR pour chauffe-eau avec thermostat électronique
Photovoltaïque : 3 kWp : 3* (2 *500w + Micro onduleur Hoymiles HMS-1000W-2T) 
Domotique : F1atb + Home Assistant / REFOSS EM06P & MQTT
Répondre

#28
Bonjour,
voici ma dernière version du programme. C'est très fluide au niveau affichage et la régulation est stable
J'ai réussi à résoudre le problème où parfois la page ne s'affiche pas ou même, certains graphiques ne s'affichaient pas.
La modification ne serait pas lié à mon compteur EM06P, mais sur le programme en général donc cela pourrait aider tous les utilisateurs.
Je vous laisse essayer.

Voici les fichiers:
https://www.grosfichiers.com/qdBvGGvxahm


Voici le résumé des modifications:
## 1. Correctif watchdog sur données EM06P invalides

Dans `LectureEM06P()`, quand aucune donnée JSON valide n'est reçue (pas
d'accolade `{` trouvée dans la réponse), le code appelait quand même
`filtre_puissance()` et positionnait `PuissanceRecue = true` — ce qui
réinitialisait à tort le watchdog de sécurité (`WDT_TIMEOUT`, 180s) même en
cas d'échec de lecture réel et prolongé.

**Corrigé** : ces deux appels sont retirés dans cette branche d'erreur
spécifique, pour que le reset de sécurité fonctionne correctement en cas de
panne durable du compteur.

## 2. Bug découvert et corrigé sur l'export d'historique annuel (indépendant de l'EM06P)

En travaillant sur l'affichage de la page d'accueil, un problème séparé a
été identifié : certains graphiques (notamment l'historique annuel)
n'apparaissaient pas de façon fiable, avec une erreur navigateur du type
`Content-Length header of network response exceeds response Body` sur
l'endpoint `/ajax_histo1an`.

### Cause
La fonction `envoyerHistoriqueEnergie()` construisait sa réponse JSON via
`JsonDocument` (ArduinoJson v7), en ajoutant une par une toutes les lignes
de plusieurs fichiers CSV (`doc["EnergieJour"].add(ligne)`). ArduinoJson v7
ne permettant pas de réserver une capacité à l'avance (contrairement à la
v6), ces nombreuses petites allocations successives fragmentaient
progressivement la mémoire disponible de l'ESP32. Après plusieurs dizaines
de minutes à quelques heures de fonctionnement, la mémoire libre minimale
chutait jusqu'à des valeurs critiques (~200-800 octets observés), provoquant
des réponses vides, des pages incomplètes, voire des redémarrages
inattendus de l'ESP32.

Ce problème est **préexistant et indépendant de l'EM06P** : il touche un
endpoint générique utilisé quel que soit le compteur configuré (Shelly y
compris).

### Tentative initiale (abandonnée)
Une première correction a consisté à construire le JSON complet en mémoire
puis à l'envoyer en une seule fois (`send()` avec `Content-Length`
automatique). Cette approche a **provoqué un redémarrage de l'ESP32**
(mémoire libre minimale tombée à 216-256 octets) car elle demandait un pic
de mémoire encore plus important que l'original. Cette version a été
abandonnée et retirée.

### Correctif retenu
Réécriture de `envoyerHistoriqueEnergie()` sans `JsonDocument` : le JSON est
désormais construit **manuellement, ligne par ligne**, et écrit directement
dans le flux de sortie HTTP chunked (via la classe existante
`ChunkedWriter`) au fur et à mesure de la lecture des fichiers CSV, sans
jamais garder l'ensemble des données en mémoire. Le mécanisme d'envoi
chunked d'origine (`setContentLength(CONTENT_LENGTH_UNKNOWN)`,
`Transfer-Encoding: chunked`) a été conservé intact.

**Résultat observé** : mémoire libre minimale stable autour de 44 000-47 000
octets sur plus de 9h d'uptime (contre des chutes à 200-800 octets avant),
endpoint `/ajax_histo1an` fonctionnel de façon fiable, tous les graphiques
de la page d'accueil s'affichant correctement sur plusieurs rechargements
successifs.
Installation : 1 routeur fixe F1ATB en V17.19 en mode SSR pour chauffe-eau avec thermostat électronique
Photovoltaïque : 3 kWp : 3* (2 *500w + Micro onduleur Hoymiles HMS-1000W-2T) 
Domotique : F1atb + Home Assistant / REFOSS EM06P & MQTT
Répondre



Atteindre :


Utilisateur(s) parcourant ce sujet :
1 visiteur(s)

Moteur MyBB, © 2002-2026 Melroy van den Berg.