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 - Multiple PZEM004Tv3 address assigned devices on same pins - Software Serial #59

Closed ejbrown1 closed 2 years ago

ejbrown1 commented 3 years ago

HI, Initially, many thanks to OLEH and MANDULAJ for their efforts. I am new to this site, and as I have setup, and painfully tested over the last couple of days, various methods of connecting and utilizing the library. (And I did some debugging lines in the library to learn it a bit better.) I'm not sure if what I will speak about is due to an unintentional bug, or what in the end, but what I will explain below, opened up a pretty nice option for me.

For me, I wanted to use multiple addressed PZEM devices on the same two sets of pins, as intend to have at least 7 of them connected to one board. Setting the address up using the specific sketch for this requirement was easy, but initially I did not realize you do need the AC 120/240 Supply connected, for this to be successful. Note:, if you do this after the sketch is running, you can hit the reset, on the ESP8266 board to rerun the "setAddress" command.

Once all the PZEM boards, are setup with unique Addresses, I started working on my project to read them all. I didn't start small, as I added in all my Gmail, Blynk, NTPTime, etc code and wrapped this code up, as If I knew from the discussions how this board worked. I also appreciated the request by users wanting to use arrays for loops, and simplified coding. I tried many examples, using the array/For loop possibilities, but was having issues with the returned data. I had to scale back to a basic sketch to try and figure out what the results were not as expected, such as like one user on this site, having only the last PZEM instance return data results when only the first address was setup yet. I did not see the answer to that user, but that Issue was closed. I was having similar issues. In the end I found a solution that fixed what that user experienced, as well as what other users wanted, as in an array, or more simply, a simple for loop to read all PZEM's in some simple fashion. @morganflint on #19 I suspect my issues were in the fact I was trying to use separate instances of the PZEM library on the same pins, with a different address, defined as follows:

PZEM004Tv30 pzem1(D5, D6, addr1); PZEM004Tv30 pzem2(D5, D6, addr2); PZEM004Tv30 pzem3(D5, D6, addr3);

With only one PZEM initially connected with address 1. The problem from this, was my reading pzem1 failed, and my reading pzem3 gave the results of addr1. Not consistent with expectations.

I also tried created instances of the PZEM like this:

PZEM004Tv30 pzemObj[] = {PZEM004Tv30(D5, D6, 0x01), PZEM004Tv30(D5, D6, 0x02), PZEM004Tv30(D5, D6, 0x03), PZEM004Tv30(D5, D6, 0x04), PZEM004Tv30(D5, D6, 0x05), PZEM004Tv30(D5, D6, 0x06), PZEM004Tv30(D5, D6, 0x07)}; AND Like this

PZEM004Tv30 pzemObj[] = {PZEM004Tv30 pzem1(D5, D6, 0x01), PZEM004Tv30 pzem2(D5, D6, 0x02), PZEM004Tv30 pzem3(D5, D6, 0x03), PZEM004Tv30 pzem4(D5, D6, 0x04), PZEM004Tv30 pzem5(D5, D6, 0x05), PZEM004Tv30 pzem6(D5, D6, 0x06), PZEM004Tv30 pzem7(D5, D6, 0x07)};

AND some other configurations using the NEW constructor. @efakostya second post on #32 for example. But I had to delete and recreate before reading in each loop. What I found did work to match the right address to the object was using the New Constructor before reading the device, and also deleting the constructor and setting to NULL as some code i found suggested to do, but as also warned in some posts, this could be a memory issue . It was, it gave great results for an hour of requests that were done every second, then failed wdt exception 29.

SO i started looking closer at the library code using the newly installed 'Visual Studio Code" software. For understanding, as well as to insert some Serial.print statements in, so I could see what was happening a bit better, and why my sketch was not relating to the right address/device. IE. Usually the last defined device received the results of a different board if you recall, so the address field seems to get mixed up in the library instance(s). I think this possibly is due to the expectation that all defined boards will be connected and powered up. (maybe). But if you code is setup to read 7 boards, on the same set of pins, and not all boards are available when the library is initialized,....issues?

Anyway, lets get to what worked for me, and worked well so far, but has not been discussed yet. Maybe i was be scolded for inappropriate use of code rules, as I am not at the level that of understanding that many here are. But here goes.

Step1. In the library header, i moved the "init" declaration from the Private, to the Public declartions.

Step 2 Sketch declare your PZEM library instance like this.

PZEM004Tv30 pzem = PZEM004Tv30(rxPin, txPin, addr); // This works, just have to call pzem.init(addr) before reading Addressed device, to change address calls

Do this only once. as in do not do it for your other PZEM devices, even though they have other addresses. Good new is , this method only requires one instance of the library to read all 247 other possible addressed PZEM devices.

Step 3: Before reading, call the

pzem.init(addr); statement to assign the wanted Device address.

Then to read/display the other devices you can do it very simply like this: (Full sketch example.)

`#include

include

include

include

include

include

include

uint8_t rxPin = 14; //D5 type to match library uint8_t txPin = 12; //D6 type to match library uint8_t addr = 0x0001; uint8_t addrA[7] = {0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007};

float v[7]; float p[7]; float pm[7]; float hz[7]; float a[7]; float pf[7];

PZEM004Tv30 pzem = PZEM004Tv30(rxPin, txPin, addr); // This works, just have to call pzem.init(addr) before reading Addressed device, to change address calls

// **** SETUP SETUP SETUP SETUP SETUP SETUP ***** void setup() {

Serial.begin(115200);

} //**** LOOP LOOP LOOP LOOP ***** void loop() {

delay(600);

loadData(); // The PZEM reading/writing subroutine

} //Loop // **** LOOP ENDS LOOP ENDS LOOP ENDS LOOP ENDS *****

void loadData() {

for (int x = 0; x < 4 ; x++) {

pzem.init(addrA[x]); //  **** This will make sure the wanted address is accessed, and 
                    //resets the access timer in the librray, 
                     //so try not to exceed reading more often than 1 second between same devive reads(Hardwrae datasheet limit?). (But one command loads all parameters for the device.)

delay(10);

v[x] = pzem.voltage();

Serial.print(" x : " + String(x) + " ");
Serial.print(" V : " + String(v[x])+"   ");

// If Voltage was nan, just expect the rest of the PZEM device to be nan and dont waste time reading the device, 
// as it does read the same again, based on last successful read time.  If it was a failed read, it will keep trying, but that is a wasted effort and time.AND doesnt forced 1 second between values in this case.

if( !isnan(v[x]) ){ a[x] = pzem.current(); p[x] = pzem.power(); pm[x] = pzem.energy(); hz[x] = pzem.frequency(); pf[x] = pzem.pf();

  Serial.print(" A : " + String(a[x])+"   ");
  Serial.print(" p : " + String(p[x])+"   ");
  Serial.print(" pm: " + String(pm[x])+"   ");
  Serial.print(" Hz: " + String(hz[x])+"   ");
  Serial.print(" PF: " + String(pf[x])+"   ");
}//End If Device values available

Serial.println(" Address: " + String(pzem.getAddress())+" ");

} // end for x (Device Addr) Serial.println();

} `

So you can see I am changing the address of the pzem instance by calling the init subroutine with the desired address, before reading the values again. The extra benefit of calling the init subroutine, is that it resets teh last know device read time, so that you can read the device with the new address right away. Intent of the library was to delay reads of the device, as the PZEM hardware is only supposed to be read every (second?), but we are chnaging the device being read via the address call change. As you can also seem this simplifies the reading of the device values. This type of method was requested by one of the users in the posts as well. So, hopefully I will not recieve a roast for my hack methods, but so far this works well for me. No memory issues. simple code structure. It can handle a board dropping from an available state..

I hope this helps someone else, achieve data access from the PZEM in the same manner as I had wanted. IE 2 pins to read many addressed PZEM devices in an efficient arrayish manner. Utilizing an ESP8266 NodeMCU in this case. And hopefully there is not some reason, such as memory, or bad program structure that this is not an ok method.

So not sure if this is great idea, bad idea, bug, help or new wiki help? Please be kind. Thanks! Earl

vortigont commented 3 years ago

For me it works pretty fine with different object instances - one per PZEM device. There is no need to init() it every time. Only set individual addresses once.

Something like

PZEM004Tv30 pzemb(&PzemSerial, 22, 17, 0xb); // rx 22, tx 17
PZEM004Tv30 pzemc(&PzemSerial, 22, 17, 0xc); // rx 22, tx 17

Serial.print("Voltage b: "); Serial.print(voltage); Serial.println("V");
Serial.print("Voltage c: "); Serial.print(voltage); Serial.println("V");
deri257 commented 2 years ago

Hello, Can you share your wiring for 3pzem004t with esp32 ?

Thanks.

morganflint commented 2 years ago

I described it here.

I'm attaching two pictures illustrating it. The second one shows the calculations without a buffer, if I didn't have it wrong back then, up to 5 units can be paralleled without buffer, but I recommend installing it (just a PNP transistor).

I'm also attaching two photos of the setup. WhatsApp Image 2022-06-06 at 10 11 51 AM WhatsApp Image 2022-06-06 at 10 13 10 AM WhatsApp Image 2022-06-06 at 10 21 44 AM WhatsApp Image 2022-06-06 at 10 21 56 AM