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 ;-)