Note de ce sujet :
  • Moyenne : 5 (4 vote(s))
  • 1
  • 2
  • 3
  • 4
  • 5
Plus d'accès aux données de la passerelle Enphase Envoy
Bonsoir,
Pour info.
Sur l'appli Emphase, j'ai ce message "difficultés avec le statut en direct, nos équipes travaillent sur ce problème "
Répondre

(19-06-2026, 04:08 PM)ROPA 59 a écrit : Pour aider ..
ne faudrait il pas limiter / ralentir les demandes  de l'ESP32  sur l'Enphase ?
Comparativement au monitor de l'application d'Enphase qui doit être +- 3 minutes entre chaque information  ( vraiment trop longue Smile 
a+

Bonsoir,

Pour info sur l'application Enphase le monitoring est par défaut à 15 min, je sais qu'il peuvent le baisser sur demande, il faudra que je leur demande lundi pour voir. Pour info toujours en 5167.
Répondre

Bonsoir,

J'ai fait des tests avec succes, pour l'instant j'ai augmenter le temps de polling dans Solar_Router.ino:

      if (Source == "Enphase") {
        LectureEnphase();
        LastRMS_Millis = millis();
        PeriodeProgMillis = 2000 + ralenti;  //On s'adapte à la vitesse réponse Envoy-S metered
      }

Et en partant du script posté + IA, la fonction mise a jour (a tester sur longue durée):

void LectureEnphase()
{
  static NetworkClient *clientFirmV5 = nullptr;
  char host[16];
  snprintf(host, sizeof(host), "%lu.%lu.%lu.%lu", (RMSextIP >> 24) & 0xFF, (RMSextIP >> 16) & 0xFF, (RMSextIP >> 8) & 0xFF, RMSextIP & 0xFF);
  char baseRequest[50];
  snprintf(baseRequest, sizeof(baseRequest), "/ivp/meters/readings");

  JsonDocument filter;
  filter[0]["activePower"] = true;
  filter[0]["apparentPower"] = true;
  filter[0]["voltage"] = true;
  filter[0]["current"] = true;
  filter[0]["freq"] = true;
  filter[0]["actEnergyDlvd"] = true;
  filter[1]["activePower"] = true;
  filter[1]["apparentPower"] = true;
  filter[1]["voltage"] = true;
  filter[1]["current"] = true;
  filter[1]["freq"] = true;
  filter[1]["actEnergyDlvd"] = true;
  JsonDocument doc;
  DeserializationError error = DeserializationError::EmptyInput;
  static uint32_t lastTokenUpdate = 0;
  constexpr uint32_t TOKEN_REFRESH_MS = 30UL * 24UL * 60UL * 60UL * 1000UL;
  if (lastTokenUpdate == 0)
    lastTokenUpdate = millis();
  if ((uint32_t)(millis() - lastTokenUpdate) > TOKEN_REFRESH_MS)
  {
    lastTokenUpdate = millis();
    Setup_Enphase();
  }

  static uint32_t lastCall = 0;
  if ((uint32_t)(millis() - lastCall) < 2000)
  {
    return; // min 2s entre requêtes
  }
  lastCall = millis();

  {
    NetworkClientSecure client;
    client.setInsecure();
    client.setTimeout(8000);
    // StockMessage("Envoy HTTPS connect...");
    if (!client.connect(host, 443))
    {
      TelnetPrintln("TLS FAIL");
      return;
    }

    client.print(
        String("GET ") + baseRequest + " HTTP/1.1\r\n" +
        "Host: " + host + "\r\n" +
        "Accept: application/json\r\n" +
        "Authorization: Bearer " + TokenEnphase + "\r\n" +
        "Connection: close\r\n\r\n");

    String statusLine = client.readStringUntil('\n');
    statusLine.trim();
    TelnetPrintln("HTTP: " + statusLine);
    if (statusLine.indexOf("200") < 0)
    {
      TelnetPrintln("Envoy refused request");
      client.stop();
      return;
    }
    // On passe toutes les lignes de header restantes jusqu'à tomber sur la ligne vide (\r)
    while (client.connected())
    {
      String line = client.readStringUntil('\n');
      if (line == "\r" || line == "")
      {
        break; // Fin des headers atteinte, le JSON commence juste après
      }
    }

    // On attend un poil que les paquets TLS arrivent
    uint32_t startCheck = millis();
    while (client.available() == 0 && (millis() - startCheck) < 2000)
    {
      delay(10);
    }
    String jsonPayload = "";
    while (client.available() > 0 || client.connected())
    {
      if (client.available() > 0)
      {
        jsonPayload += (char)client.read();
      }
      else
      {
        delay(1); // Laisse respirer le stack TLS de l'ESP32
      }
    }
    client.stop(); // Fermeture propre immédiate du socket

    if (jsonPayload.length() == 0)
    {
      TelnetPrintln("JSON ERR: Payload vide");
      return;
    }
    error = deserializeJson(doc, static_cast<const String &>(jsonPayload), DeserializationOption::Filter(filter));
    if (error)
    {
      TelnetPrintln("JSON ERR: " + String(error.c_str()));
      return;
    }
    TelnetPrintln("JSON OK");
  }

  float PactReseau = 0.0f;
  float PvaReseau = 0.0f;
  long whDlvdCum = 0L;
  if (!error)
  {
    TelnetPrintln("doc[0][activePower] : OK");
    PactProd = doc[0]["activePower"] | 0.0f;
    PactReseau = doc[1]["activePower"] | 0.0f;
    PactConso_M = PactReseau - PactProd;
    PvaReseau = doc[1]["apparentPower"] | 0.0f;
    whDlvdCum = doc[1]["actEnergyDlvd"] | 0L;
    Tension_M = doc[1]["voltage"] | 0.0f;
    Intensite_M = doc[1]["current"] | 0.0f;
    Frequence = doc[1]["freq"];
    Tension_M1 = doc[1]["channels"][0]["voltage"] | 0.0f;
    Tension_M2 = doc[1]["channels"][1]["voltage"] | 0.0f;
    Tension_M3 = doc[1]["channels"][2]["voltage"] | 0.0f;
    Intensite_M1 = doc[1]["channels"][0]["current"] | 0.0f;
    Intensite_M2 = doc[1]["channels"][1]["current"] | 0.0f;
    Intensite_M3 = doc[1]["channels"][2]["current"] | 0.0f;
    if (Tension_M > 600.0f)
      Tension_M /= 3.0f;
    if (Tension_M3 > 200.0f)
      Intensite_M /= 3.0f;
  }
  else
  {
    TelnetPrintln("doc[0][activePower] :unknown");
    PactProd = 0.0f;
    PactConso_M = 0.0f;
    PactReseau = 0.0f;
    Tension_M = 0.0f;
    Intensite_M = 0.0f;
    Frequence = 0.0f;
    Tension_M1 = 0.0f;
    Tension_M2 = 0.0f;
    Tension_M3 = 0.0f;
    Intensite_M1 = 0.0f;
    Intensite_M2 = 0.0f;
    Intensite_M3 = 0.0f;
  }

  // Calcul injection / soutirage
  PactReseau = PfloatMax(PactReseau);
  if (PactReseau < 0)
  {
    PuissanceS_M_inst = 0;
    PuissanceI_M_inst = int(-PactReseau);
  }
  else
  {
    PuissanceI_M_inst = 0;
    PuissanceS_M_inst = int(PactReseau);
  }
  // Calcul VA (Puissance apparente)
  PvaReseau = PfloatMax(PvaReseau);
  if (PvaReseau < 0)
  {
    PVAS_M_inst = 0;
    PVAI_M_inst = int(-PvaReseau);
  }
  else
  {
    PVAI_M_inst = 0;
    PVAS_M_inst = int(PvaReseau);
  }
  Pva_valide = true;
  filtre_puissance();
  // Facteur de puissance (Cos phi)
  float PowerFactor = 0.0f;
  if ((PVA_M_moy) != 0)
  {
    PowerFactor = floor(100.0f * fabsf(Puissance_M_moy) / PVA_M_moy) / 100.0f;
    PowerFactor = min(PowerFactor, 1.0f);
  }
  PowerFactor_M = PowerFactor;
  // Index compteurs énergies cumulés
  if (whDlvdCum != 0)
  {
    if (LastwhDlvdCum == 0)
      LastwhDlvdCum = whDlvdCum;
    long DeltaWh = whDlvdCum - LastwhDlvdCum;
    LastwhDlvdCum = whDlvdCum;
    if (DeltaWh < 0)
    {
      Energie_M_Injectee = Energie_M_Injectee - DeltaWh;
    }
    else
    {
      Energie_M_Soutiree = Energie_M_Soutiree + DeltaWh;
    }
  }
  // Validation des flags et reset des surveillances
  EnergieActiveValide = true;
  if (PactReseau != 0 || PvaReseau != 0)
    PuissanceRecue = true;
  if (cptLEDyellow > 30)
    cptLEDyellow = 4;
}


Bon week-end
Répondre

Le endpoint /ivp/meters/reports/consumption ne fonctionne plus suite à la dernière mise a jour, ce qui fait planter le routeur.

On peut utiliser à la place le endpoint/ivp/meters/reports qui fonctionne toujours 

Petite optimisation à faire : un nouveau token est généré à chaque allumage de l'esp32 alors qu'il suffirait de le stocker et de vérifier sa validité avant de l'utiliser. Pour info un token est valable 1 an.
Répondre

(20-06-2026, 12:20 AM)lbourdel@yahoo.fr a écrit : Bonsoir,

J'ai fait des tests avec succes, pour l'instant j'ai augmenter le temps de polling dans Solar_Router.ino:

      if (Source == "Enphase") {
        LectureEnphase();
        LastRMS_Millis = millis();
        PeriodeProgMillis = 2000 + ralenti;  //On s'adapte à la vitesse réponse Envoy-S metered
      }

Et en partant du script posté + IA, la fonction mise a jour (a tester sur longue durée):

Code :
void LectureEnphase()
{
  static NetworkClient *clientFirmV5 = nullptr;
  char host[16];
  snprintf(host, sizeof(host), "%lu.%lu.%lu.%lu", (RMSextIP >> 24) & 0xFF, (RMSextIP >> 16) & 0xFF, (RMSextIP >> 8) & 0xFF, RMSextIP & 0xFF);
  char baseRequest[50];
  snprintf(baseRequest, sizeof(baseRequest), "/ivp/meters/readings");

  JsonDocument filter;
  filter[0]["activePower"] = true;
  filter[0]["apparentPower"] = true;
  filter[0]["voltage"] = true;
  filter[0]["current"] = true;
  filter[0]["freq"] = true;
  filter[0]["actEnergyDlvd"] = true;
  filter[1]["activePower"] = true;
  filter[1]["apparentPower"] = true;
  filter[1]["voltage"] = true;
  filter[1]["current"] = true;
  filter[1]["freq"] = true;
  filter[1]["actEnergyDlvd"] = true;
  JsonDocument doc;
  DeserializationError error = DeserializationError::EmptyInput;
  static uint32_t lastTokenUpdate = 0;
  constexpr uint32_t TOKEN_REFRESH_MS = 30UL * 24UL * 60UL * 60UL * 1000UL;
  if (lastTokenUpdate == 0)
    lastTokenUpdate = millis();
  if ((uint32_t)(millis() - lastTokenUpdate) > TOKEN_REFRESH_MS)
  {
    lastTokenUpdate = millis();
    Setup_Enphase();
  }

  static uint32_t lastCall = 0;
  if ((uint32_t)(millis() - lastCall) < 2000)
  {
    return; // min 2s entre requêtes
  }
  lastCall = millis();

  {
    NetworkClientSecure client;
    client.setInsecure();
    client.setTimeout(8000);
    // StockMessage("Envoy HTTPS connect...");
    if (!client.connect(host, 443))
    {
      TelnetPrintln("TLS FAIL");
      return;
    }

    client.print(
        String("GET ") + baseRequest + " HTTP/1.1\r\n" +
        "Host: " + host + "\r\n" +
        "Accept: application/json\r\n" +
        "Authorization: Bearer " + TokenEnphase + "\r\n" +
        "Connection: close\r\n\r\n");

    String statusLine = client.readStringUntil('\n');
    statusLine.trim();
    TelnetPrintln("HTTP: " + statusLine);
    if (statusLine.indexOf("200") < 0)
    {
      TelnetPrintln("Envoy refused request");
      client.stop();
      return;
    }
    // On passe toutes les lignes de header restantes jusqu'à tomber sur la ligne vide (\r)
    while (client.connected())
    {
      String line = client.readStringUntil('\n');
      if (line == "\r" || line == "")
      {
        break; // Fin des headers atteinte, le JSON commence juste après
      }
    }

    // On attend un poil que les paquets TLS arrivent
    uint32_t startCheck = millis();
    while (client.available() == 0 && (millis() - startCheck) < 2000)
    {
      delay(10);
    }
    String jsonPayload = "";
    while (client.available() > 0 || client.connected())
    {
      if (client.available() > 0)
      {
        jsonPayload += (char)client.read();
      }
      else
      {
        delay(1); // Laisse respirer le stack TLS de l'ESP32
      }
    }
    client.stop(); // Fermeture propre immédiate du socket

    if (jsonPayload.length() == 0)
    {
      TelnetPrintln("JSON ERR: Payload vide");
      return;
    }
    error = deserializeJson(doc, static_cast<const String &>(jsonPayload), DeserializationOption::Filter(filter));
    if (error)
    {
      TelnetPrintln("JSON ERR: " + String(error.c_str()));
      return;
    }
    TelnetPrintln("JSON OK");
  }

  float PactReseau = 0.0f;
  float PvaReseau = 0.0f;
  long whDlvdCum = 0L;
  if (!error)
  {
    TelnetPrintln("doc[0][activePower] : OK");
    PactProd = doc[0]["activePower"] | 0.0f;
    PactReseau = doc[1]["activePower"] | 0.0f;
    PactConso_M = PactReseau - PactProd;
    PvaReseau = doc[1]["apparentPower"] | 0.0f;
    whDlvdCum = doc[1]["actEnergyDlvd"] | 0L;
    Tension_M = doc[1]["voltage"] | 0.0f;
    Intensite_M = doc[1]["current"] | 0.0f;
    Frequence = doc[1]["freq"];
    Tension_M1 = doc[1]["channels"][0]["voltage"] | 0.0f;
    Tension_M2 = doc[1]["channels"][1]["voltage"] | 0.0f;
    Tension_M3 = doc[1]["channels"][2]["voltage"] | 0.0f;
    Intensite_M1 = doc[1]["channels"][0]["current"] | 0.0f;
    Intensite_M2 = doc[1]["channels"][1]["current"] | 0.0f;
    Intensite_M3 = doc[1]["channels"][2]["current"] | 0.0f;
    if (Tension_M > 600.0f)
      Tension_M /= 3.0f;
    if (Tension_M3 > 200.0f)
      Intensite_M /= 3.0f;
  }
  else
  {
    TelnetPrintln("doc[0][activePower] :unknown");
    PactProd = 0.0f;
    PactConso_M = 0.0f;
    PactReseau = 0.0f;
    Tension_M = 0.0f;
    Intensite_M = 0.0f;
    Frequence = 0.0f;
    Tension_M1 = 0.0f;
    Tension_M2 = 0.0f;
    Tension_M3 = 0.0f;
    Intensite_M1 = 0.0f;
    Intensite_M2 = 0.0f;
    Intensite_M3 = 0.0f;
  }

  // Calcul injection / soutirage
  PactReseau = PfloatMax(PactReseau);
  if (PactReseau < 0)
  {
    PuissanceS_M_inst = 0;
    PuissanceI_M_inst = int(-PactReseau);
  }
  else
  {
    PuissanceI_M_inst = 0;
    PuissanceS_M_inst = int(PactReseau);
  }
  // Calcul VA (Puissance apparente)
  PvaReseau = PfloatMax(PvaReseau);
  if (PvaReseau < 0)
  {
    PVAS_M_inst = 0;
    PVAI_M_inst = int(-PvaReseau);
  }
  else
  {
    PVAI_M_inst = 0;
    PVAS_M_inst = int(PvaReseau);
  }
  Pva_valide = true;
  filtre_puissance();
  // Facteur de puissance (Cos phi)
  float PowerFactor = 0.0f;
  if ((PVA_M_moy) != 0)
  {
    PowerFactor = floor(100.0f * fabsf(Puissance_M_moy) / PVA_M_moy) / 100.0f;
    PowerFactor = min(PowerFactor, 1.0f);
  }
  PowerFactor_M = PowerFactor;
  // Index compteurs énergies cumulés
  if (whDlvdCum != 0)
  {
    if (LastwhDlvdCum == 0)
      LastwhDlvdCum = whDlvdCum;
    long DeltaWh = whDlvdCum - LastwhDlvdCum;
    LastwhDlvdCum = whDlvdCum;
    if (DeltaWh < 0)
    {
      Energie_M_Injectee = Energie_M_Injectee - DeltaWh;
    }
    else
    {
      Energie_M_Soutiree = Energie_M_Soutiree + DeltaWh;
    }
  }
  // Validation des flags et reset des surveillances
  EnergieActiveValide = true;
  if (PactReseau != 0 || PvaReseau != 0)
    PuissanceRecue = true;
  if (cptLEDyellow > 30)
    cptLEDyellow = 4;
}



Bon week-end


Je n'ai pas encore testé ton code, mais peux tu préciser de quel "script posté" tu parles ? Script d'origine, un autre...

De plus, pour la lisibilité ne pas hésiter à utiliser les balises "code" pour poster tes sources (comme je viens de le faire au dessus)



some time later Smile Smile Smile Smile Smile Smile 

Je viens de tester ton code, et pour moi cela à l'air de fonctionner.

La puissance produite est ok (100 w à cette heure çi Smile )

Je laisse un peu tourner, pour comparer les valeurs avec mes autres outils, mais cela semble cohérent.
Pour rappel, Je suis en v8.3.5528 sur la passerelle et je suis parti de la RMS v17.21
--------------------------------------------------------------
ESP32 (v117,20 et IP fixe) + sonde température + SSR -- Cumulus/Chauffe-Eau
Source données serveur Enphase 7.

Répondre

@Alain_C38

Je suis parti du code de Michy en page 15 avec le fichier attaché    

Comme constaté par Michy, le fait d'appeler l'ancien endpoint /ivp/meters/reports/consumption fait bien planter la passerelle, il faut attendre un certain temps pour qu'elle revienne fonctionnelle
Sans doute la derniere mise a jour qui est buggé

En attendant une correction d'enphase, on peut utiliser/ivp/meters/readings

Comme remonté dans le fil de discussion, le token est valable 1 an, donc il serait bon de sauver le token et le timestamp de peremption dans le file system afin de ne pas en demander de nouveaux (lors d'une perte de connection Wifi, reboot, ..).

Je regarderai comment mettre cette optim en place

J'attend vos retours suite à ma proposition de correction
Répondre

La puissance réseau public (Pw) à l'air ok
Puissance produite : OK
Y'a que puissance consommée qui me parait étrange. Elle semble correspondre aux 2 précédentes sommées sans tenir compte du signe ?
--------------------------------------------------------------
ESP32 (v117,20 et IP fixe) + sonde température + SSR -- Cumulus/Chauffe-Eau
Source données serveur Enphase 7.

Répondre

Bonjour, 

  Question aux utilisateurs de système Enphase:

  Y a t'il encore des passerelles qui tourne avec un firmware V5 ? (=> en mode http (sans s) donc sans utilisation de Token)

  De ce que je comprend : A ce jour on est sur firmware V8 et les mises a jour sont piloté par Enphase ...

  => l'exception serait une passerelle non connecté a internet, ou une version hardware ancienne qui ne permet pas de monter en firmware V8
Merci André Smile ,
Routeur V17.19 (since V2.01) / Source UxI / 5 actions

Si les réponses que je propose bénévolement sur ce forum ne vous plaisent pas, ignorez-les simplement sans me jeter la pierre ! (Ou ne posez pas de question)
Répondre

(20-06-2026, 07:52 AM)Alain_C38 a écrit : La puissance réseau public (Pw) à l'air ok
Puissance produite : OK
Y'a que puissance consommée qui me parait étrange. Elle semble correspondre aux 2 précédentes sommées sans tenir compte du signe ?


J'ai mis a jour ma fonction, effectivement la Pconsommé etait mal calcullé

J'ai modifier la facon de stocker le Json car il fait 18k, avant on arrivait pas a recuperer les index energie.
Peut etre quelque ajustement a apporter car l'energie calculé est a partir du boot, peut etre veut on garder depuis la mise en service de la passerelle?

Fichier updaté en attaché

PS: fonction pour firmware V7 en monophasé ONLY

Laurent


Pièces jointes
.txt   Source_EnphaseEnvoy.txt (Taille : 6.18 Ko / Téléchargements : 12)
Répondre

(20-06-2026, 10:14 AM)lbourdel@yahoo.fr a écrit :
(20-06-2026, 07:52 AM)Alain_C38 a écrit : La puissance réseau public (Pw) à l'air ok
Puissance produite : OK
Y'a que puissance consommée qui me parait étrange. Elle semble correspondre aux 2 précédentes sommées sans tenir compte du signe ?


J'ai mis a jour ma fonction, effectivement la Pconsommé etait mal calcullé

J'ai modifier la facon de stocker le Json car il fait 18k, avant on arrivait pas a recuperer les index energie.
Peut etre quelque ajustement a apporter car l'energie calculé est a partir du boot, peut etre veut on garder depuis la mise en service de la passerelle?

Fichier updaté en attaché

PS: fonction pour firmware V7 en monophasé ONLY

Laurent

Bonjour Laurent,

Je ne comprends pas.

Le version que tu viens de proposer ressemble à ceci :

Code :
void LectureEnphase()
{
  static long LastwhDlvdCum = 0L;
  static long LastwhRcvdCum = 0L;
  char host[16];
  snprintf(host, sizeof(host), "%lu.%lu.%lu.%lu", (RMSextIP >> 24) & 0xFF, (RMSextIP >> 16) & 0xFF, (RMSextIP >> 8) & 0xFF, RMSextIP & 0xFF);

  char baseRequest[50];
  snprintf(baseRequest, sizeof(baseRequest), "/ivp/meters/readings");

  // ============================================================
  // CORRECTION 1 : Initialisation explicite du filtre Envoy V7
  // ============================================================
  StaticJsonDocument<2048> filter;
  // En configurant l'index [0], ArduinoJson v6 va appliquer
  // ce masque à TOUS les objets du tableau principal.
  filter[0]["activePower"] = true;
  filter[0]["apparentPower"] = true;
  filter[0]["voltage"] = true;
  filter[0]["current"] = true;
  filter[0]["freq"] = true;
  filter[0]["actEnergyDlvd"] = true; // Sera extrait partout où il existe !
  filter[0]["actEnergyRcvd"] = true; // Sera extrait partout où il existe !

  DynamicJsonDocument doc(13000); // 13000 octets pour le JSON complet (V7) avec les index énergétiques
  DeserializationError error = DeserializationError::EmptyInput;

  static uint32_t lastTokenUpdate = 0;
  constexpr uint32_t TOKEN_REFRESH_MS = 30UL * 24UL * 60UL * 60UL * 1000UL;

  if (lastTokenUpdate == 0)
    lastTokenUpdate = millis();

  if ((uint32_t)(millis() - lastTokenUpdate) > TOKEN_REFRESH_MS)
  {
    lastTokenUpdate = millis();
    Setup_Enphase();
  }

  static uint32_t lastCall = 0;
  if ((uint32_t)(millis() - lastCall) < 2000)
  {
    return; // min 2s entre requêtes
  }
  lastCall = millis();

  {
    NetworkClientSecure client;
    client.setInsecure();
    client.setTimeout(8000);

    // StockMessage("Envoy HTTPS connect...");

    if (!client.connect(host, 443))
    {
      TelnetPrintln("TLS FAIL");
      return;
    }

    client.print(
        String("GET ") + baseRequest + " HTTP/1.1\r\n" +
        "Host: " + host + "\r\n" +
        "Accept: application/json\r\n" +
        "Authorization: Bearer " + TokenEnphase + "\r\n" +
        "Connection: close\r\n\r\n");

    String statusLine = client.readStringUntil('\n');
    statusLine.trim();
    TelnetPrintln("HTTP: " + statusLine);

    if (statusLine.indexOf("200") < 0)
    {
      TelnetPrintln("Envoy refused request");
      client.stop();
      return;
    }

    // On passe toutes les lignes de header restantes jusqu'à tomber sur la ligne vide (\r)
    while (client.connected())
    {
      String line = client.readStringUntil('\n');
      if (line == "\r" || line == "")
      {
        break; // Fin des headers atteinte, le JSON commence juste après
      }
    }

    // On attend un poil que les paquets TLS arrivent
    uint32_t startCheck = millis();
    while (client.available() == 0 && (millis() - startCheck) < 2000)
    {
      delay(10);
    }

    String jsonPayload = "";
    while (client.available() > 0 || client.connected())
    {
      if (client.available() > 0)
      {
        jsonPayload += (char)client.read();
      }
      else
      {
        delay(1); // Laisse respirer le stack TLS de l'ESP32
      }
    }

    client.stop(); // Fermeture propre immédiate du socket

    if (jsonPayload.length() == 0)
    {
      TelnetPrintln("JSON ERR: Payload vide");
      return;
    }

    error = deserializeJson(doc, static_cast<const String &>(jsonPayload), DeserializationOption::Filter(filter));

    if (error)
    {
      TelnetPrintln("JSON ERR: " + String(error.c_str()));
      return;
    }

    TelnetPrintln("JSON OK");
  }

  // --- LOGIQUE DE TRAITEMENT ET DISPATCH DES DONNÉES ---
  float PactReseau = 0.0f;
  float PvaReseau = 0.0f;
  long whDlvdCum = 0L;
  long whRcvdCum = 0L; // <--- AJOUT CRITIQUE POUR LE FIRMWARE V7

  if (!error)
  {
    PactProd = doc[0]["activePower"] | 0.0f;
    PactReseau = doc[1]["activePower"] | 0.0f;
    PactConso_M = PactReseau + PactProd;

    PvaReseau = doc[1]["apparentPower"] | 0.0f;
    // Extraction des index énergétiques du bloc réseau [1]
    whDlvdCum = (long)doc[1]["actEnergyDlvd"].as<double>();
    whRcvdCum = (long)doc[1]["actEnergyRcvd"].as<double>();

    Tension_M = doc[1]["voltage"] | 0.0f;
    Intensite_M = doc[1]["current"] | 0.0f;
    Frequence = doc[1]["freq"];
  }
  else
  {
    PactProd = 0.0f;
    PactConso_M = 0.0f;
    PactReseau = 0.0f;
    Tension_M = 0.0f;
    Intensite_M = 0.0f;
    Frequence = 0.0f;
  }

  // Calcul injection / soutirage inst.
  PactReseau = PfloatMax(PactReseau);
  if (PactReseau < 0.0f)
  {
    PuissanceS_M_inst = 0;
    PuissanceI_M_inst = (int)(-PactReseau);
  }
  else
  {
    PuissanceI_M_inst = 0;
    PuissanceS_M_inst = (int)(PactReseau);
  }

  // Calcul VA (Puissance apparente)
  // TRÈS IMPORTANT : Sur l'Envoy V7, apparentPower est TOUJOURS positif dans le JSON !
  PvaReseau = PfloatMax(PvaReseau);
  if (PactReseau < 0.0f)
  { // On se base sur le signe de la puissance active
    PVAS_M_inst = 0;
    PVAI_M_inst = (int)(PvaReseau);
  }
  else
  {
    PVAI_M_inst = 0;
    PVAS_M_inst = (int)(PvaReseau);
  }
  Pva_valide = true;
  filtre_puissance();

  // Facteur de puissance (Cos phi)
  float PowerFactor = 0.0f;
  if (PVA_M_moy != 0)
  {
    PowerFactor = floor(100.0f * fabsf(Puissance_M_moy) / (float)PVA_M_moy) / 100.0f;
    PowerFactor = min(PowerFactor, 1.0f);
  }
  PowerFactor_M = PowerFactor;

  if (whDlvdCum != 0)
  {
    if (LastwhDlvdCum == 0)
      LastwhDlvdCum = whDlvdCum;
    long DeltaWhSoutire = whDlvdCum - LastwhDlvdCum;
    LastwhDlvdCum = whDlvdCum;
    if (DeltaWhSoutire > 0)
    {
      Energie_M_Soutiree += DeltaWhSoutire;
    }
  }

  if (whRcvdCum != 0)
  {
    if (LastwhRcvdCum == 0)
      LastwhRcvdCum = whRcvdCum;
    long DeltaWhInjecte = whRcvdCum - LastwhRcvdCum;
    LastwhRcvdCum = whRcvdCum;
    if (DeltaWhInjecte > 0)
    {
      Energie_M_Injectee += DeltaWhInjecte;
    }
  }

  // Validation des flags et reset des surveillances
  EnergieActiveValide = true;
  if (PactReseau != 0.0f || PvaReseau != 0.0f)
    PuissanceRecue = true;
  if (cptLEDyellow > 30)
    cptLEDyellow = 4;
}


ALors que ce que celle que tu as postée il y a une hier/cette nuit ressemblait à cela :
Code :
void LectureEnphase()
{
  static NetworkClient *clientFirmV5 = nullptr;
  char host[16];
  snprintf(host, sizeof(host), "%lu.%lu.%lu.%lu", (RMSextIP >> 24) & 0xFF, (RMSextIP >> 16) & 0xFF, (RMSextIP >> 8) & 0xFF, RMSextIP & 0xFF);
  char baseRequest[50];
  snprintf(baseRequest, sizeof(baseRequest), "/ivp/meters/readings");

  JsonDocument filter;
  filter[0]["activePower"] = true;
  filter[0]["apparentPower"] = true;
  filter[0]["voltage"] = true;
  filter[0]["current"] = true;
  filter[0]["freq"] = true;
  filter[0]["actEnergyDlvd"] = true;
  filter[1]["activePower"] = true;
  filter[1]["apparentPower"] = true;
  filter[1]["voltage"] = true;
  filter[1]["current"] = true;
  filter[1]["freq"] = true;
  filter[1]["actEnergyDlvd"] = true;
  JsonDocument doc;
  DeserializationError error = DeserializationError::EmptyInput;
  static uint32_t lastTokenUpdate = 0;
  constexpr uint32_t TOKEN_REFRESH_MS = 30UL * 24UL * 60UL * 60UL * 1000UL;
  if (lastTokenUpdate == 0)
    lastTokenUpdate = millis();
  if ((uint32_t)(millis() - lastTokenUpdate) > TOKEN_REFRESH_MS)
  {
    lastTokenUpdate = millis();
    Setup_Enphase();
  }

  static uint32_t lastCall = 0;
  if ((uint32_t)(millis() - lastCall) < 2000)
  {
    return; // min 2s entre requêtes
  }
  lastCall = millis();

  {
    NetworkClientSecure client;
    client.setInsecure();
    client.setTimeout(8000);
    // StockMessage("Envoy HTTPS connect...");
    if (!client.connect(host, 443))
    {
      TelnetPrintln("TLS FAIL");
      return;
    }

    client.print(
        String("GET ") + baseRequest + " HTTP/1.1\r\n" +
        "Host: " + host + "\r\n" +
        "Accept: application/json\r\n" +
        "Authorization: Bearer " + TokenEnphase + "\r\n" +
        "Connection: close\r\n\r\n");

    String statusLine = client.readStringUntil('\n');
    statusLine.trim();
    TelnetPrintln("HTTP: " + statusLine);
    if (statusLine.indexOf("200") < 0)
    {
      TelnetPrintln("Envoy refused request");
      client.stop();
      return;
    }
    // On passe toutes les lignes de header restantes jusqu'à tomber sur la ligne vide (\r)
    while (client.connected())
    {
      String line = client.readStringUntil('\n');
      if (line == "\r" || line == "")
      {
        break; // Fin des headers atteinte, le JSON commence juste après
      }
    }

    // On attend un poil que les paquets TLS arrivent
    uint32_t startCheck = millis();
    while (client.available() == 0 && (millis() - startCheck) < 2000)
    {
      delay(10);
    }
    String jsonPayload = "";
    while (client.available() > 0 || client.connected())
    {
      if (client.available() > 0)
      {
        jsonPayload += (char)client.read();
      }
      else
      {
        delay(1); // Laisse respirer le stack TLS de l'ESP32
      }
    }
    client.stop(); // Fermeture propre immédiate du socket

    if (jsonPayload.length() == 0)
    {
      TelnetPrintln("JSON ERR: Payload vide");
      return;
    }
    error = deserializeJson(doc, static_cast<const String &>(jsonPayload), DeserializationOption::Filter(filter));
    if (error)
    {
      TelnetPrintln("JSON ERR: " + String(error.c_str()));
      return;
    }
    TelnetPrintln("JSON OK");
  }

  float PactReseau = 0.0f;
  float PvaReseau = 0.0f;
  long whDlvdCum = 0L;
  if (!error)
  {
    TelnetPrintln("doc[0][activePower] : OK");
    PactProd = doc[0]["activePower"] | 0.0f;
    PactReseau = doc[1]["activePower"] | 0.0f;
    PactConso_M = PactReseau - PactProd;
    PvaReseau = doc[1]["apparentPower"] | 0.0f;
    whDlvdCum = doc[1]["actEnergyDlvd"] | 0L;
    Tension_M = doc[1]["voltage"] | 0.0f;
    Intensite_M = doc[1]["current"] | 0.0f;
    Frequence = doc[1]["freq"];
    Tension_M1 = doc[1]["channels"][0]["voltage"] | 0.0f;
    Tension_M2 = doc[1]["channels"][1]["voltage"] | 0.0f;
    Tension_M3 = doc[1]["channels"][2]["voltage"] | 0.0f;
    Intensite_M1 = doc[1]["channels"][0]["current"] | 0.0f;
    Intensite_M2 = doc[1]["channels"][1]["current"] | 0.0f;
    Intensite_M3 = doc[1]["channels"][2]["current"] | 0.0f;
    if (Tension_M > 600.0f)
      Tension_M /= 3.0f;
    if (Tension_M3 > 200.0f)
      Intensite_M /= 3.0f;
  }
  else
  {
    TelnetPrintln("doc[0][activePower] :unknown");
    PactProd = 0.0f;
    PactConso_M = 0.0f;
    PactReseau = 0.0f;
    Tension_M = 0.0f;
    Intensite_M = 0.0f;
    Frequence = 0.0f;
    Tension_M1 = 0.0f;
    Tension_M2 = 0.0f;
    Tension_M3 = 0.0f;
    Intensite_M1 = 0.0f;
    Intensite_M2 = 0.0f;
    Intensite_M3 = 0.0f;
  }

  // Calcul injection / soutirage
  PactReseau = PfloatMax(PactReseau);
  if (PactReseau < 0)
  {
    PuissanceS_M_inst = 0;
    PuissanceI_M_inst = int(-PactReseau);
  }
  else
  {
    PuissanceI_M_inst = 0;
    PuissanceS_M_inst = int(PactReseau);
  }
  // Calcul VA (Puissance apparente)
  PvaReseau = PfloatMax(PvaReseau);
  if (PvaReseau < 0)
  {
    PVAS_M_inst = 0;
    PVAI_M_inst = int(-PvaReseau);
  }
  else
  {
    PVAI_M_inst = 0;
    PVAS_M_inst = int(PvaReseau);
  }
  Pva_valide = true;
  filtre_puissance();
  // Facteur de puissance (Cos phi)
  float PowerFactor = 0.0f;
  if ((PVA_M_moy) != 0)
  {
    PowerFactor = floor(100.0f * fabsf(Puissance_M_moy) / PVA_M_moy) / 100.0f;
    PowerFactor = min(PowerFactor, 1.0f);
  }
  PowerFactor_M = PowerFactor;
  // Index compteurs énergies cumulés
  if (whDlvdCum != 0)
  {
    if (LastwhDlvdCum == 0)
      LastwhDlvdCum = whDlvdCum;
    long DeltaWh = whDlvdCum - LastwhDlvdCum;
    LastwhDlvdCum = whDlvdCum;
    if (DeltaWh < 0)
    {
      Energie_M_Injectee = Energie_M_Injectee - DeltaWh;
    }
    else
    {
      Energie_M_Soutiree = Energie_M_Soutiree + DeltaWh;
    }
  }
  // Validation des flags et reset des surveillances
  EnergieActiveValide = true;
  if (PactReseau != 0 || PvaReseau != 0)
    PuissanceRecue = true;
  if (cptLEDyellow > 30)
    cptLEDyellow = 4;
}

C'est cette dernière qui tourne chez moi

********** Edit quelques minutes plus tard **************

Je viens de passer à ta dernière version. Les puissances semblent ok.

Le facteur de puissance est négatif. C'est possible ????? Je viens de regarder la définition et ça parait pas possible Sad

Je ne sais pas non plus dire si les valeurs Enregies Actives/soutirées sont réaliste. Comment faire ? ANDRE Help
--------------------------------------------------------------
ESP32 (v117,20 et IP fixe) + sonde température + SSR -- Cumulus/Chauffe-Eau
Source données serveur Enphase 7.

Répondre



Atteindre :


Utilisateur(s) parcourant ce sujet :
lahuchette, Lepelot, 9 visiteur(s)

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