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

Issue with multiple pzems - solved #35

Closed ppd13 closed 3 years ago

ppd13 commented 3 years ago

Hi there, i worked on a project to monitor multiple breakers using this library + wemos d1 mini, but came into some issues. First it was powering them via internal voltage regulator which was solved by using max 6 pzem on one gpio pair, so only 6 pzems having load at the same time. Other issue was memory limit, with more than 16 devices i get into system crash - heap was full. This was solved by great help of a guy @mzero on adafruit discord channel. You need to modify

PZEM004Tv30.h ####################################

if defined(PZEM004_SOFTSERIAL)

PZEM004Tv30(uint8_t receivePin, uint8_t transmitPin, uint8_t addr=PZEM_DEFAULT_ADDR);
PZEM004Tv30(SoftwareSerial &port, uint8_t addr=PZEM_DEFAULT_ADDR);

endif

####################################

PZEM004T.cpp ####################################

if defined(PZEM004_SOFTSERIAL)

PZEM004Tv30::PZEM004Tv30(uint8_t receivePin, uint8_t transmitPin, uint8_t addr) { SoftwareSerial *port = new SoftwareSerial(receivePin, transmitPin); port->begin(PZEM_BAUD_RATE); this->_serial = port; this->_isSoft = true; init(addr); } PZEM004Tv30::PZEM004Tv30(SoftwareSerial &port, uint8_t addr) { port.begin(PZEM_BAUD_RATE); this->_serial = &port; this->_isSoft = false; init(addr); } ####################################

then you can use this example: #################################### SoftwareSerial serial56(D5, D6); SoftwareSerial serial34(D3, D4); SoftwareSerial serial12(D1, D2);

PZEM004Tv30 pzem1(serial56, 0x01); PZEM004Tv30 pzem2(serial56, 0x02); PZEM004Tv30 pzem3(serial34, 0x03); PZEM004Tv30 pzem4(serial34, 0x04); PZEM004Tv30 pzem5(serial12, 0x05); PZEM004Tv30 pzem6(serial12, 0x06); PZEM004Tv30 pzem7(serial56, 0x07); PZEM004Tv30 pzem8(serial56, 0x08); PZEM004Tv30 pzem9(serial34, 0x09); PZEM004Tv30 pzem10(serial34, 0x0a); PZEM004Tv30 pzem11(serial12, 0x0b); PZEM004Tv30 pzem12(serial12, 0x0c); PZEM004Tv30 pzem13(serial56, 0x0d); PZEM004Tv30 pzem14(serial56, 0x0e); PZEM004Tv30 pzem15(serial34, 0x0f); PZEM004Tv30 pzem16(serial34, 0x10); PZEM004Tv30 pzem17(serial12, 0x11); PZEM004Tv30 pzem18(serial12, 0x12); ####################################

and memory stays low because heap isnt being used at all because nothing is being dynamically allocated. Once again, big thanx to mzero.

morganflint commented 3 years ago

I've read the Adafruit Discord channel thread about this issue... I wasn't conscious at all of the memory problem (which isn't strange as I'm a complete beginner to C++...). Although I'm not planning to use as many sensors, I'd also do it like this on my sketch. Thanks to @mzero!

Now, you could try to combine this approach with the array's one (at the end of this post), I read @mzero said it didn't solve the memory issue by itself, but combined with your mod it'll probably work.

The code could be like this (using the modified library, of course):

####################################
SoftwareSerial serial56(D5, D6);
SoftwareSerial serial34(D3, D4);
SoftwareSerial serial12(D1, D2);

PZEM004Tv30 pzem[18] = {
  PZEM004Tv30(serial56, 0x01),
  PZEM004Tv30(serial56, 0x02),
  PZEM004Tv30(serial34, 0x03),
  PZEM004Tv30(serial34, 0x04),
  PZEM004Tv30(serial12, 0x05),
  PZEM004Tv30(serial12, 0x06),
  PZEM004Tv30(serial56, 0x07),
  PZEM004Tv30(serial56, 0x08),
  PZEM004Tv30(serial34, 0x09),
  PZEM004Tv30(serial34, 0x0a),
  PZEM004Tv30(serial12, 0x0b),
  PZEM004Tv30(serial12, 0x0c),
  PZEM004Tv30(serial56, 0x0d),
  PZEM004Tv30(serial56, 0x0e),
  PZEM004Tv30(serial34, 0x0f),
  PZEM004Tv30(serial34, 0x10),
  PZEM004Tv30(serial12, 0x11),
  PZEM004Tv30(serial12, 0x12)
};
####################################

The memory problem is avoided as only 3 instances of the soft serial port are created, instead of 18.

And when you read the sensors, instead of using (example for sensor nº5):

float voltage = pzem5.voltage();

You should use:

float voltage = pzem[4].voltage();

With the great advantage that you can use a variable instead of the fixed number, so you can include that into a loop, for example. (Note I'm using 4 instead of 5 in the second case because the array starts at 0, so the pzem's are numbered 0...17 instead of 1...18).

It would be very good if mandulaj included this simple mod in the main branch. Apparently, is fully backward compatible with the present version and best suited for multiple sensors (a use that, probably, was unforeseen when designing the library, as @mzero said).

morganflint commented 3 years ago

Another question: You don't really need to use different serial ports for each 6 PZEM's if you use a buffer between microcontroller's TX and PZEM's RX. This can be as simple as a single PNP transistor. This way, you can free 2 GPIOs for other uses (and save two soft serial port instances). Also, it's better to connect the PZEMs to +5V instead of 3.3V to avoid saturating the WemosD1 weak regulator.

In the previous posts to that one, you can also see considerations about the current used by each PZEM and also a mod I made to the library that allows passing the address of the PZEM as a parameter (I'll try to combine it with the one proposed here).

ppd13 commented 3 years ago
  1. @morganflint thats a good point with the array & loops, i already tried but i wasnt able to manage to work the correct syntax for client publish. There the topic must be inside quotes and dont know how to make it variable

example:

for (int i=0; i<18; i++){
Serial.print("pzem "); Serial.println(i+1); float voltage = pzem[i].voltage(); client.publish("/topic/pzem[i]",String(voltage).c_str()); }

this is the issue:"/topic/pzem[i]" in mqtt explorer i can see it created new topic /topic/pzem[i] instead of pzem1..pzem18... any idea ? thanx

  1. i am powering via 5V, 3.3v didnt work good.
  2. i have no use for additional gpio so adding pnp transistor might be good idea but not for my setup and will it leave this way
morganflint commented 3 years ago

I haven't worked with mqtt so I'm not sure if this will help but I'm afraid _client.publish("/topic/pzem[i]",String(voltage).cstr()) is not working because the variable i is inside the quotation marks, so it's considering it part of the string and not as a variable.

I'd try to define an array also for mqtt topics, something like:

String topic[18] = {
"/topic/pzem1",
"/topic/pzem2",
...
"/topic/pzem18"
};

And then:

float voltage = pzem[i].voltage(); client.publish(topic[i],String(voltage).c_str());

Maybe you can get better ideas from here.

mandulaj commented 3 years ago

It looks like, this issue was resolved. I am closing it now.