mandulaj / PZEM-004T-v30

Arduino library for the Updated PZEM-004T v3.0 Power and Energy meter
MIT License
256 stars 108 forks source link

esp8266 wemos d1 bug?? ver 1.1 and 1.2 #71

Open JoeleDixie opened 2 years ago

JoeleDixie commented 2 years ago

Library compiled with arduino ide with wemos d1 esp8266 software serial to pins 14/12, in a random way the readings of the pzem are distorted as shown in the graph and go to zero Blue line VAC, red line power ... X axis time line ( hours). If I connect the pzem with modbus library everything is ok ... If you need more info I'll give it to you. Thanks

grafico_pzem

mandulaj commented 2 years ago

Interesting, could you elaborate please. What do you mean with "modbus library". Could you provide a link to it?

To me it looks like some measurements are dropping out (perhaps transmission error, which is not surprising when using Software serial). However the PZEMs helpfully attach a CRC to all the messages. Thus the library should be able to detect corrupt data.

In case the CRC verification fails, this library will return Nan as the measurement values. You should always check the returned value with the isnan function in order to make sure that the data is valid like this:

float voltage = pzem.voltage();
if(isnan(voltage)){
    // Something went wrong, perhaps try reading again?
} else {
    // voltage should be valid, we can use it
   processValue(voltage); // ....
}

Could you perhaps provide some snippets of your code?

JoeleDixie commented 2 years ago

Hi , this is the code with your library and i have error


void read_pzemdata() {

  voltage = pzem.voltage();
  power = pzem.power();
  energy = pzem.energy();
  pf = pzem.pf(); 

if ( !isnan(voltage) ) {
    Serial.print("Voltage: "); Serial.print(voltage); 
Serial.println(" V");
  }
 else
 {
    voltage = 0;
    Serial.println("Error reading voltage");
  }

  if ( !isnan(power) ) {
    Serial.print("Power: "); Serial.print(power); 
Serial.println(" W");
  } 
else
 {
    power = 0 ;
    Serial.println("Error reading power");
  }

  if ( !isnan(energy) ) {
    Serial.print("Energy: "); 
Serial.print(energy, 3); 
Serial.println(" KWh");
  } 
else
 {
    energy = 0;
    Serial.println("Error reading energy");
  }

  if ( !isnan(pf) ) {
    Serial.print("PF: "); Serial.println(pf);
  } 
else
 {
    pf = 0 ;
    Serial.println("Error reading power factor");
  }
  Serial.println("====================================================");

}

This code is ModBusMaster.h library from https://github.com/4-20ma/ModbusMaster and not have error

void read_pzemdata() {

  result_node = node.readInputRegisters(0x0000, 10);

  if (result_node == node.ku8MBSuccess)
  {
 voltage = (node.getResponseBuffer(0x00) / 10.0f);
    power = (node.getResponseBuffer(0x03) / 10.0f);
    energy = (node.getResponseBuffer(0x05) / 1000.000f);
     pf = (node.getResponseBuffer(0x08) / 100.0f);
      }
if ( !isnan(voltage) ) {
    Serial.print("Voltage: "); 
    Serial.print(voltage); 
   Serial.println(" V");
  } else {

    Serial.println("Error reading voltage");
  }

  if ( !isnan(power) ) {
 Serial.print("Power: ");
 Serial.print(power);
 Serial.println(" W");
  } else {

    Serial.println("Error reading power");
  }

  if ( !isnan(energy) ) {
 Serial.print("Energy: "); 
 Serial.print(energy, 3);
 Serial.println(" kWh");
  } else {

    Serial.println("Error reading energy");
  }

  if ( !isnan(pf) ) {
    Serial.print("PF: "); 
    Serial.println(pf);
  } else {

    Serial.println("Error reading power factor");
  }
  Serial.println("====================================================");

}
mandulaj commented 2 years ago

I fixed the formatting for you :wink: :+1:

Just one thing, you have not included the initialization of pzem and node. Are you using SoftwareSerial for both? (or at least the non-Hardware serial pins).

You also said you encounter an error. Does that mean the library just returns a wrong value, or is actually the message Serial.println("Error reading voltage"); printed? In other words, does the library return a NAN when you encounter an error?

I mean in the PZEM snippet, you are setting the value of the measurements to 0 when you hit an error.

else
{
    power = 0 ; // <-------------- Power set to 0 here if power was NAN...
    Serial.println("Error reading power");
}

So the behavior you describe in the first comment makes perfect sense. From time to time, there will be an error in the transmission, and your code will set 0 as the value if an error is detected. Its unfortunate, but perfectly normal, especially for SoftwareSerial. That's why there is a CRC checksum included.

What you have to do is something like this:

// Try MAX_TRIES to read the values
for (int num_tries = 0; num_tries < MAX_TRIES; num_tries++){

    // Try reading
    voltage = pzem.voltage();
    power = pzem.power();
    energy = pzem.energy();
    pf = pzem.pf(); 

   if(!isnan(voltage) && !isnan(power) && !isnan(energy) && !isnan(pf)){
     // We have a valid value....
      break;
   }

  sleep(200); // Library returned an error, wait for a bit to retry
}

if (isnan(voltage)) {
   // We have tried MAX_TRIES times and still got an error, something is seriously wrong... Perhaps the wires are not connected?
} else {
   // All good, we can now us the voltage, power, energy....

}

I suspect that the Modbus library might be doing something like that in the background. Or it just returns the last best value. I would have to check the source. But let me know what exactly you mean when you say I get an error (Does the library return an error, or does it return a wrong value). Because the two are very different. The first is expected behavior, the second a bug!

JoeleDixie commented 2 years ago

Hi, thanks for the formatting 👍

Are you using SoftwareSerial for both? (or at least the non-Hardware serial pins).

Yes SoftwareSerial for both!

Initialization of node and pzem /// your library /// #include <PZEM004Tv30.h> #include <SoftwareSerial.h> #define D5 (14) #define D6 (12) SoftwareSerial pzem_Ser_V3(D5, D6); PZEM004Tv3 pzem(pzem_Ser_V3); /// modbus library /// #include <ModbusMaster.h> #include <SoftwareSerial.h> #define D5 (14) #define D6 (12) SoftwareSerial pzem_Ser_modbus(D5, D6); ModbusMaster node; node.begin(1,pzem_Ser_modbus);

You also said you encounter an error. Does that mean the library just returns a wrong value, or is actually the message Serial.println("Error reading voltage"); printed? In other words, does the library return a NAN when you encounter an error?

Yes NAN

I have disabled the check and attach the debug file tabulate with a space ---> example saved file on server time VAC Pow pf 02:17:34 229.20 284 0.87 correct all 02:17:41 nan 286 0.87 error voltage 02:39:47 nan 293 nan error voltage & pf 03:20:23 nan 239 0.84 error voltage ////////.....

modbus library file on server

00:00:01 234.60 340 0.80 00:01:07 234.90 339 0.80 00:01:17 235.20 340 0.80 00:01:27 235.20 340 0.80 ....... 00:03:07 232.40 340 0.81 00:03:17 232.30 340 0.81 00:03:27 232.30 340 0.81 ........ 00:10:37 231.80 338 0.81 00:10:47 231.90 338 0.81 00:10:57 231.70 338 0.81 00:11:07 231.40 338 0.81

mandulaj commented 2 years ago

OK, that's interesting. It does indeed look like a problem with the library. I will have a look into it.

What is weird is that only the voltage and pf are being affected (at least in this example) while the VAC looks fine. Normally when ever you read a value, such as the voltage with pzem.voltage(), all the values are updated at once. Then there is a mechanism to prevent updating more than once every 200ms. So basically in the code:

  voltage = pzem.voltage();
  power = pzem.power();
  energy = pzem.energy();
  pf = pzem.pf(); 

... the values are updated only during the first call. The subsequent calls skip the actual update and return the last cached value. So if say there was an error during the voltage, it will be NAN. Then there will be a second update attempt on power. But if that succeeds, the rest should also be valid. Which does not explain your example, where voltage and pf had an error.

As I said, it is very weird and thus interesting. I will try to reproduce it here.

JoeleDixie commented 2 years ago

This is the complete code for testing :-)

#include <PZEM004Tv30.h>  //////// https://github.com/mandulaj/PZEM-004T-v30
#include <SoftwareSerial.h>
#include <Arduino.h>
#include <stdlib.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#define D5 (14)
#define D6 (12)
SoftwareSerial pzem_Ser_V3(D5, D6);
PZEM004Tv30 pzem(pzem_Ser_V3);
float voltage, current, power, energy, frequency, pf ;
uint16_t data[6];
long interv_6s = 6000, prev_6s ;
boolean PzemReset = false;
bool buf_state = false;
int result;
int ora_reset = 0000; /// reset pzem time

/////////////////////////////////////////////////////////////////////////////////////////

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
  Serial.begin(9600);
  delay(1000);
  Serial.print("Inizio setup....");
  Serial.println("Start PZEM serial");
 }

void loop() {
  connectWifiReliable();// check wifi connection
    if (millis() - prev_6s > interv_6s) { 
    prev_6s = millis();
    read_pzemdata();     //// read data every 6 second
    /// compose a get form to send to local webserver
    String buf_1;
    buf_1 += F("http://192.168.1.1/inverter/file.php?vac=");
    buf_1 += String(voltage);
    buf_1 += F("&watt=");
    buf_1 += String(power);
    buf_1 += F("&energia=");
    buf_1 += String(energy);
    buf_1 += F("&pf=");
    buf_1 += String(pf);
    //-----------------------------------------------------------------------

    if ((WiFi.status() == WL_CONNECTED)) {
      WiFiClient client;
      HTTPClient http;
      Serial.print("[HTTP] begin...\n");
      if (http.begin(client, buf_1)) {  // HTTP
        Serial.print("[HTTP] GET...\n");
        // start connection and send HTTP header
        int httpCode = http.GET();
        // httpCode will be negative on error
        if (httpCode > 0) {
          // HTTP header has been send and Server response header has been handled
          Serial.printf("[HTTP] GET... code: %d\n", httpCode);
          // file found at server
          if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
            String payload = http.getString();
            result = payload.toInt();  //Converts string to integer);
            if (result > ora_reset)PzemReset = false;
            // serial debug/////////////////////////////////
            Serial.print(result);
            Serial.println(" <<<<< Integer time from server");
            Serial.print("PzemResettato ---> ");
            Serial.println(PzemReset);
            Serial.println(buf_1);
            Serial.println(WiFi.localIP());
            Serial.println(WiFi.macAddress());
            Serial.println("===================================================");
           ///////////////////////////////////////////////
           lampeggio_led();
            reset_pzem();

          }
        } else {

          Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());

        }

        http.end();
      } else {
        Serial.printf("[HTTP} Unable to connect\n");
      }
    }
  }
}

void read_pzemdata() {

  voltage = pzem.voltage();
  if ( !isnan(voltage) ) {
    Serial.print("Voltage: "); Serial.print(voltage); Serial.println("V");
  } 

  power = pzem.power();
  if ( !isnan(power) ) {
    Serial.print("Power: "); Serial.print(power); Serial.println("W");
  } 

  energy = pzem.energy();
  if ( !isnan(energy) ) {
    Serial.print("Energy: "); Serial.print(energy, 3); Serial.println("kWh");
  } 

  pf = pzem.pf();
  if ( !isnan(pf) ) {
    Serial.print("PF: "); Serial.println(pf);
  } 
  Serial.println("====================================================");

}

void lampeggio_led () {
  digitalWrite(LED_BUILTIN, 0);
  delay(20);
  digitalWrite(LED_BUILTIN, 1);

}

void accendi_led () {
  digitalWrite(LED_BUILTIN, 0);

}

void reset_pzem () {

  if ( result == ora_reset  && PzemReset == false ) {
    Serial.println(result);
    pzem.resetEnergy();   //// questo resetta l'energia memorizzata in KWH
    Serial.println ("Pzem Resettato!");
    delay (700);
    pzem.resetEnergy();   //// questo resetta l'energia memorizzata in KWH
    Serial.println ("Pzem Resettato!");
    delay (700);
    pzem.resetEnergy();   //// questo resetta l'energia memorizzata in KWH
    PzemReset = true;
    Serial.println ("Pzem Resettato!");
  }

}
//////////////////////////////////////////////////////////////////
int connectWifiReliable()
{
  while (WiFi.status() != WL_CONNECTED)
  {
    //digitalWrite(LED_BUILTIN, HIGH);

     WiFi.mode(WIFI_STA);
     WiFi.begin("wifi_ssid", "password");
     WiFi.hostname("Host_Name");
    int count = 0;
    while (WiFi.status() != WL_CONNECTED)
    {
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
      delay(100);

      if (count++ > 50
         )
      {
        //digitalWrite(LED_BUILTIN, HIGH);
        WiFi.disconnect();
        while (1); // give up and reboot
      }
    }
  }

  //digitalWrite(LED_BUILTIN, LOW);
  return 1;
}
sergiocntr commented 2 years ago

Hi , I had no problem with this library for years with Wemos d1 mini, I think that there is something in the modbus version that fix your code ,maybe some delay. I have a working code here https://github.com/sergiocntr/energyMain so feel free to use some part for your checks or/and ask questions.

mandulaj commented 2 years ago

@sergiocntr, I think your repository is private since the link leads to a 404.

I have recently released a new version of the library with some major changes v1.1.2. It is possible that something broke. Could you perhaps verify if you are seeing any problems with the uptodate version in your application?

sergiocntr commented 2 years ago

oh ,really sorry now is public (I'm a little ashamed of my bad code )

@mandulaj It's all fine with your release 1.1.2

Thanks for your work. Sergio

JoeleDixie commented 2 years ago

Update! I believe there is some conflict with the Esp8266 board. As the graph shows, the only card that does not cause the defect is version 3.0.0, the other versions randomly cause read errors red peaks to zero (NAN). IMG_20210729_231542

mandulaj commented 2 years ago

I am sorry @JoeleDixie, could you clarify what you are referring to the version of? Is that the version of the PZEM board? Also, do you see the same problem ( with versions 3.0.1 and 3.0.2) when using the ModbusMaster library or is the problem only present when using the PZEM library. What I want to clarify is if the problem is related to the PZEM library or if it is purely result of the PZEM boards.

JoeleDixie commented 2 years ago

Hi, for versions 3.0.0 /3.0.1/3.0.2 I am referring to Arduino board manager esp8266 (see image example) ... With the modbusmaster library the problem does not occur with any version of the esp8266 board. I hope I have explained myself better, I use google translate and I apologize for this. :-)

How_to_add_boards_in_board_manager_0
mandulaj commented 2 years ago

Ahh, Ok, I see. Hmm, That is interesting indeed. I don't have time to investigate at the moment. But I will look into it later. Is it ok if you use the 3.0.0 version for your project now or the Modbusmaster library instead.

mandulaj commented 2 years ago

Could you also try compiling and uploading the sketch using Platform IO? I am curious because I haven't been able to reproduce your problem so far...