Medidor de Energia Activa com Pzem04 atraves serial.write

Estava a realizar um trabalho de medição e registo da energia activa, e me deparei com uma pequena dificuldade. Precisava de registar no tempo em um (1) segundo as quatro (4) grandezas eléctricas (tensão, intensidade de corrente eléctrica, potência e energia activa) com o aparelho Pzem04.

Imaginemos que poderíamos medir o arranque de um motor indutor que na fase inicial existe o consumo de 3xIn e se vai ajustando ao valor nominal no tempo, com o objectivo de selecionar a proteção ideal de modo ajustar à curva do fusível ou do disjuntor.

As bibliotecas do Pzem04 são estáveis e não apresentam erros no registo das quatro 4 grandezas eléctricas mas demoram mais de um segundo, entretanto, encontrei no forum
Hex over TTL - Project Guidance - Arduino Forum
como comunicar com o Pzem04, tive o resultado surpreendente que poderia ler em 250ms, mas dando mais "folga" às comunicações entre o microprocessador e o Pzem04, aproximei o resultado em um segundo (variável timer), para as quatro variáveis, aconselho leituras durante 1 ou 5 minutos para sincronizar os diversos aparelhos, RTC, sdCard e o Pzem04.

realização do seguinte código:

#include <SoftwareSerial.h>
SoftwareSerial serial_(0, 2); // GPIO0=pinD3; GPIO2=pinD4 sSerial(2, 3) RX, TX pins https://forum.arduino.cc/index.php?topic=492614.0
const unsigned int baudRate = 9600; // 115200 https://github.com/olehs/PZEM004T
float pzA=1, pzV=0, pzI=0, pzP=0, pzE=0;
byte reqVn[]={0xB0,0xC0,0xA8,0x01,0x01,0x00,0x1A}; // B0 C0 A8 01 01 00 1A : (Computer sends a request to read the voltage value) : Hex request to send to meter
byte reqIn[]={0xB1,0xC0,0xA8,0x01,0x01,0x00,0x1B}; // B1 C0 A8 01 01 00 1B : current value
byte reqPn[]={0xB2,0xC0,0xA8,0x01,0x01,0x00,0x1C}; // B2 C0 A8 01 01 00 1C : Active power
byte reqEn[]={0xB3,0xC0,0xA8,0x01,0x01,0x00,0x1D}; // B3 C0 A8 01 01 00 1D : Energy kWh
byte  reqA[]={0xB4,0xC0,0xA8,0x01,0x01,0x00,0x1E}; // B4 C0 A8 01 01 00 1E : <=> address IPAddress ip(192,168,1,1);
byte  reqB[]={0xB5,0xC0,0xA8,0x01,0x01,0x00,0x1F}; //
byte  reqC[]={0xB6,0xC0,0xA8,0x01,0x01,0x00,0x1A}; //
byte  reqD[]={0xB7,0xC0,0xA8,0x01,0x01,0x00,0x1C}; //
byte  reqE[]={0xB7,0xC0,0xA8,0x01,0x01,0x00,0x1B}; //
byte  reqF[]={0xB7,0xC0,0xA8,0x01,0x01,0x00,0x1A}; //

// Set the module address Req B4 C0 A8 01 01 00 1E (Computer sends a request to set the address, 192.168.1.1)
// Set the module address resp A4 00 00 00 00 00 A4 (Meter reply the address was successfully set)
// Set the power alarm threshold Req B5 C0 A8 01 01 14 33 (computer sends a request to set a power alarm threshold)
// Set the power alarm threshold Resp A5 00 00 00 00 00 A56 (Meter reply the power alarm threshold was successfully set)

const unsigned int max_sRx = 50; // how much serial data we expect before a newline
unsigned long mil = millis(); static unsigned int posRx=0; String s="", s1="";
int timer=185; // 490 no erros. pode ir até 50ms, mas o ideal será 235ms(for 4units) or 190(for 5units) totalizando ~1s
byte tmp=5; // evitar erros a alta velocidade quando se aplica o Serial.println("");
unsigned long oldMil=mil+timer*4+500; byte sRx=0;

void setup() { delay(5000); // initialize serial:
  Serial.begin(115200); // Serial.begin(9600); comunicação com/usb
  serial_.begin(baudRate); // const unsigned int baudRate = 9600; comunicação com pzem
  wdt_enable(WDTO_500MS); // WDTO_15MS WDTO_30MS WDTO_250MS WDTO_500MS WDTO_1S WDTO_8S watchdog
  for (int i=0; i<=sizeof(reqA)-1; i++) { serial_.write(reqA[i]); s+=String(reqA[i],HEX)+":"; }
   s+=" "; posRx=0; sAvailable(timer); pzA=convNum(s,164); s+=" Adr="+String(pzA); Serial.println(s); delay(tmp); s="";
  if (pzA==164) {Serial.println(" PZEM04 exits "); } else Serial.println("PZEM04 no exits "); // 164(dec)=A4(hex)
}

float convNum(String st, byte adr) { // converter número
 float n=0; byte i0=0, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0; // year = str.substring(2, str.indexOf("/")).toInt();
  st=st.substring(st.indexOf(";")-3,st.length()); i0=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 0)"+st+"["+i0+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i1=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 1)"+st+"["+i1+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i2=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 2)"+st+"["+i2+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i3=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 3)"+st+"["+i3+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i4=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 4)"+st+"["+i4+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i5=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 5)"+st+"["+i5+"]");
  st=st.substring(st.indexOf(";")+1,st.length()); i6=st.substring(0,st.indexOf(";")).toInt(); // Serial.print(" 6)"+st+"["+i6+"]");
   // Serial.print("<"+String(i0)+";"+String(i1)+";"+String(i2)+";"+String(i3)+";"+String(i4)+";"+String(i5)+";"+String(i6)+">");
  if (i0==160) { n=i2+(float)i3/10; }           // tension=list[2]+list[3]/10
   else if (i0==161) { n=i2+(float)i3/100; }    // current=list[2]+list[3]/100
   else if (i0==162) { n=i1*256+i2; }             // power=list[1]*256+list[2]
   else if (i0==163) { n=i1*256*256+i2*256+i3; } // Energy=list[1]*256*256+list[2]*256+list[3]
   else if (i0==164) { n=i1*10000000+i2*1000000+i3*100000+i4*10000+i5*1000+i6; }
 return(n);
}

void sAvailable(int hora) { // serialAvailable
unsigned long mil_ = millis(); unsigned long milPre = mil_; String s0="", s1="";
 while (mil_-milPre<hora) {
  mil_=millis();
  while (serial_.available() > 0) // When Software serial is available
   { sRx=serial_.read();
   if (posRx < (max_sRx)) { s1+=String(sRx)+";"; posRx+=1; }
    s0+=String(sRx,HEX)+":";
   }
 } s="tx="+s+" rx="+s0+"=["+s1+"] ";
}

void loop() { mil=millis(); delay(1); // usar delay nas funções para evitar erros
  if (mil > oldMil) { s=""; oldMil=mil+timer*4+500; // min.total 2500=2.5s
    for (int i=0; i<=sizeof(reqA)-1; i++) { serial_.write(reqA[i]); s+=String(reqA[i],HEX)+":"; }
     s+=" "; posRx=0; sAvailable(timer); pzA=convNum(s,164); delay(tmp); s="";
     if (pzA==164) { /* Serial.println(" PZEM04 exits "); */ } else Serial.println("PZEM04 no exits {"+String(pzA)+"}, verify connection. "); // 164(dec)=A4(hex)
    for (int i=0; i<=sizeof(reqVn)-1; i++) { serial_.write(reqVn[i]); s+=String(reqVn[i],HEX)+":"; }
     s+=" "; posRx=0; sAvailable(timer); pzV=convNum(s,160); s+=" Vn="+String(pzV); Serial.println(s); delay(tmp); s="";
    for (int i=0; i<=sizeof(reqIn)-1; i++) { serial_.write(reqIn[i]); s+=String(reqIn[i],HEX)+":"; }
     s+=""; posRx=0; sAvailable(timer); pzI=convNum(s,161); s+="In="+String(pzI); Serial.println(s); delay(tmp); s="";
    for (int i=0; i<=sizeof(reqPn)-1; i++) { serial_.write(reqPn[i]); s+=String(reqPn[i],HEX)+":"; }
     s+=""; posRx=0;sAvailable(timer); pzP=convNum(s,162); s+="Pn="+String(pzP); Serial.println(s); delay(tmp); s="";
    for (int i=0; i<=sizeof(reqEn)-1; i++) { serial_.write(reqEn[i]); s+=String(reqEn[i],HEX)+":"; }
     s+=""; posRx=0; sAvailable(timer); pzE=convNum(s,163); s+="En="+String(pzE); Serial.println(s); delay(tmp); s="";
//  wdt_reset(); // reset o watchdog, ou seja se o programa craker e não ativar esse reset em 8segundos e o watchdog será ativado.
  Serial.println(" time="+String(long(millis()-mil))+" "); delay(tmp);
  }
}

Verificamos a medição da tensão, corrente, potência e energia activa.

Quando ligamos por exemplo uma lâmpada, obtemos o seguinte resultado:

report "lamp":
tx=b4:c0:a8:1:1:0:1e: rx=a4:0:0:0:0:0:a4:=[164;0;0;0;0;0;164;] Adr=164.00
PZEM04 exits
tx=b0:c0:a8:1:1:0:1a: rx=a0:0:ed:6:0:0:93:=[160;0;237;6;0;0;147;] Vn=237.60V
tx=b1:c0:a8:1:1:0:1b: rx=a1:0:0:8:0:0:a9:=[161;0;0;8;0;0;169;] In=0.08A
tx=b2:c0:a8:1:1:0:1c: rx=a2:0:c:0:0:0:ae:=[162;0;12;0;0;0;174;] Pn=12.00W
tx=b3:c0:a8:1:1:0:1d: rx=a3:0:1:ad:0:0:51:=[163;0;1;173;0;0;81;] En=429.00kWh
time=990

Quando está ligado o Pzem04 e não temos nada ligado, aparecem alguns erros.
lê a tensão e a energia activa, mas a comunicação baralha-se.

Quando o Pzem04 está ligado e sem carga, tem a seguinte descrição:
PZEM04 no exits, verify connection.
tx=b0:c0:a8:1:1:0:1a: rx==[] Vn=0.00V
tx=b1:c0:a8:1:1:0:1b: rx==[] In=0.00A
tx=b2:c0:a8:1:1:0:1c: rx==[] Pn=0.00W
tx=b3:c0:a8:1:1:0:1d: rx==[] En=0.00kWh

Quando utilizamos um "Ventilador":
B0:C0:A8:1:1:0:1A: 160 0 230 70 0 0 150 Vn=160;0;230;7;0;0;150;
B1:C0:A8:1:1:0:1B: 161 0 7 5 0 0 142 In=161;0;7;50;0;0;142;
B2:C0:A8:1:1:0:1C: 162 0 6 174 0 0 150 Pn=162;0;6;174;0;0;150;
B3:C0:A8:1:1:0:1D: 163 0 0 70 0 0 152 En=163;0;0;70;0;0;152;
Vn=230+7/10=231.7V
In=7+50/100=7.50A
Pn=6256+174=1710W
En=0
256256+0256+70=70kWh

ino_pzem_serial.ino (6.54 KB)