PZEM-016 single phase modbus energy meter

The only thing I need to do now is format the output like this:

IMPT value=19.48 1532385540 LF (Line Feed)
i.e measurement_name value=measurement_value time LF

Got a quick pointer on how I can do that to the pz.getData output?

Now that I think about it, if I use your

reply = pz.read_registers(5,2,4)
print(round((reply[0]+(reply[1]*65536)),0))

code, I can do it the same way I did it with my WattsOns.

All Iā€™d have to do is change reply to my measurement name.

But I like your idea of reading all the variables in one shot. More robust that way.

something like

names = [ "VOLT", "AMPS", "WATT", "WHRS", "FREQ", "PWRF" ]
timestamp = int(time.time())
data = pz.getData()
if data:
    for i in range (len(names)):
        print(names[i] + " value=" + str(data[i]) + " " + str(timestamp))

(untested)

The size of the array of ā€œnamesā€ defines what gets printed (sent?) so by just not including the alarm name, it will not be in the sent data

Hereā€™s the code I use to read the WattsOn:

meas = {'GENW':GENW, 'TOTL':TOTL, 'VOLT':VOLT, 'CONS':CONS, 'TECO':TECO, 'NETC':NETC, 'NETT':NETT, 'IMPT':IMPT, 'EXPT':EXPT,
        'L1PF':L1PF, 'L2PF':L2PF, 'PVPF':PVPF, 'LEG1':LEG1, 'LEG2':LEG2, 'WHTR':WHTR, 'WHWH':WHWH}

tmpData = []
for key, val in meas.items():
    tmpData.append('{} value={} {}'.format(key, val, int(time.time())))
    Data = '\n'.join(tmpData)
requests.post('http://192.168.1.61:8086/write?db=energy&precision=s', data=Data)

Came about as an excercise in minimizing the total number of concatenations. :wink:

How about this then

names = [ "VOLT", "AMPS", "WATT", "WHRS", "FREQ", "PWRF" ]
timestamp = int(time.time())
data = pz.getData()
if data:
    for i in range (len(names)):
        print('{} value={} {}'.format(names[i], str(data[i]), str(timestamp))

Iā€™m less familiar with this method so prepared for bugs.

Not sure you need the str() casting, but I put it in for belt and braces :slight_smile:

1 Like

Iā€™ll give it a shotā€¦

Cool! Anyway, thatā€™s all from me folks! Time to hit the hay I think.

You da man PB!

Works like a big dog!

245.3 14.144 3417.8 47633 1532387278
VOLT value=245.3 1532387283
AMPS value=14.137 1532387283
WATT value=3414.5 1532387283
WHRS value=47638 1532387283

Still getting dropoutsā€¦

Edit - Caused by using the sleep() command to run the loop.
After I switched to APS, which gives me a true 5 second cycle time, the dropouts disappeared.

I donā€™t know how you have things set up behind grafana, but It would seem the issue is much like that seen with PHPfina based graphing. The fixed intervals do not work well with anything but data input at the correct intervals.

The use of ā€œsleep(5)ā€ will have made sure your loops were definitely not 5 secs due to the additional ā€œruntimeā€ of the loop.

I had included ā€œtimestamp.pyā€ and it was imported into the example script I showed you. Using that is extremely lightweight but it not only ensures that the time taken by each loop iteration is exactly 5s (for example), but also that each of the 5s loops are starting at a syncronised time.

The phpfina feedā€™s ā€œtimeslotsā€ all relate directly to the feeds unix start time, each ā€œslotā€ is anchored to an exact point in time, which is why there are so many posting and querying irregularities.

What a phpfina timestamp really says is that this value arrived at some point between the declared timestamp and (timestamp + fixed interval + 0.999), thatā€™s a 120% margin of error on a 5s interval.

Using the method I use, all data is always supplied in total sync, when I say total, I mean in all aspects with a acceptable level of flex, not that the loops are exactly timed to a atomic level.

If the device is off line (eg power cut, firmware update etc) when it resumes the loops will still be in sync with the data prior to the outage, as if the outage( and restartup) itself was an exact multiple of the loop iteration. And, because of the way itā€™s done it will also match the loops of other devices.

The one function ā€œif ready():ā€ does both the idle loop speed by implementing a sleep (as a percentage of the desired loop time) and the check to allow the timed code (talk to pzems here) to run.

Hereā€™s a couple of runs of a test script

The first one uses a slower idle loop time set to 10% of the set interval equal to a 0.5 second sleep each (5sec) loop, this decreases processor use but increases the potential time inaccuracy to upto 0.5s

[edit - see note at end of post 1_looptime10%.txt (36.3 KB)]

. . .  #see attached file 1_looptime10%.txt
Timestamped:1532513650.0        Actual:1532513650.07    Diff:69.0mS     (min:1.0,max:500.0,avg:251.1)
Timestamped:1532513655.0        Actual:1532513655.07    Diff:72.0mS     (min:1.0,max:500.0,avg:250.6)
Timestamped:1532513660.0        Actual:1532513660.08    Diff:77.0mS     (min:1.0,max:500.0,avg:250.1)
Timestamped:1532513665.0        Actual:1532513665.08    Diff:82.0mS     (min:1.0,max:500.0,avg:249.6)
Timestamped:1532513670.0        Actual:1532513670.09    Diff:87.0mS     (min:1.0,max:500.0,avg:249.1)

over half an hour it averages 0.25 secs ā€œaccuracyā€ of reading the registers, but note the timestamp submitted is always an exact multiple of 5.

This second run is at a idle loop time of 1% (default setting)

[edit - see note at end of post 2_looptime1%.txt (35.6 KB)]

. . .  #see attached file 2_looptime1%.txt
Timestamped:1532517305.0        Actual:1532517305.0     Diff:0.0mS      (min:0.0,max:59.0,avg:23.2)
Timestamped:1532517310.0        Actual:1532517310.05    Diff:47.0mS     (min:0.0,max:59.0,avg:23.3)
Timestamped:1532517315.0        Actual:1532517315.05    Diff:49.0mS     (min:0.0,max:59.0,avg:23.4)
Timestamped:1532517320.0        Actual:1532517320.04    Diff:42.0mS     (min:0.0,max:59.0,avg:23.5)
Timestamped:1532517325.0        Actual:1532517325.04    Diff:37.0mS     (min:0.0,max:59.0,avg:23.5)

I would say that an average accuracy of 0.0235 seconds is possibly far more accurate than we need (especially as there maybe minor timeclock differences between machines), but the important thing here is that the timestamps are always a multiple of the interval and that the readings are for that exact time (within the allowed accuracy) rather than allowing the loop timestamps to ā€œfreewheelā€ and then fudging them afterwards ie rounding the timestamp float to an int at the emoncms input and then depositing it into a phpfina feed at the nearest fixed interval (anchored to the feeds start time) below the supplied timestamp, as the data will still belong to a different point in time.

This method essentially changes the timestamp supplied from an approximate request to an exact instruction.

[edit - Darn, the post is too big ā€œBody is limited to 32000 characters; you entered 75853.ā€. I have removed all but the last 5 entries of each of the outputs and attached them in text files instead.]

1 Like

Spot on.

I use APS because it has the capability to do cron-type scheduling - with a resolution of one second - which enables me to schedule additional events. e.g. resetting my WattsOns at midnight, writing the daily production/export/import figures to a text file and sending data to pvoutput.org. All within the confines of a single Python script. (I figured Iā€™m already running a Python script to read the meter. Itā€™d be a cleaner solution if I could do those tasks from the same script)

APS schedules the events to occur at 5, 10, 15ā€¦ seconds past the top of the minute the same way cron works with a */5 entry in the minutes column of a crontab. You can also tell it the exact second you want the task to run.

Hereā€™s what that looks like:

def main():
    sched.add_cron_job(read_meter, second='*/5')
    sched.add_cron_job(send_to_pvo, minute='*/5', second='2')
    sched.add_cron_job(wrt_daily_data, hour='23', minute='59', second='52')
    sched.add_cron_job(wrt_monthly_separator, day='1', hour='0', minute='0', second='1')

    while True:
       sleep(1)

if __name__ == "__main__":
    main()

It survives Power and network outages with no loss of sync. Havenā€™t had any problems of that type since I started using it about 3 years ago.

I use InfluxDB, and up until a year ago, I let the Influx sever assign the timestamp vice sending the time along with the measurement. It doesnā€™t seem to make any difference, as both ways have worked equally well.

In order to keep a local source of reasonably accurate time, I put a DS3231 RTC on one of my RPis and I use an NTP app called Chrony to keep its clock disciplined to NTP time. The 3231 has a TCXO, so the drift is minimal. (Ā±2ppm from 0Ā° to 40Ā° C) I sync all the machines on my LAN to the one Pi and it ā€œkeeps everyone on the same sheet of musicā€ so-to-speak.

The PZEM-016 is priced at Ā£6.34 ($7.99) till 10 Sept at Banggood.com. Free shipping.

Š”Š¾Š±Ń€Ń‹Š¹ Š“ŠµŠ½ŃŒ! ŠŸŠ¾Š“сŠŗŠ°Š¶ŠøтŠµ уŠ²Š°Š¶Š°ŠµŠ¼Ń‹Šµ ŠŗŠ°Šŗ ŠæŠ¾Š¼ŠµŠ½ŃŃ‚ŃŒ Š°Š“рŠµŃ Š² устрŠ¾Š¹ŃŃ‚Š²Šµ?

Google translates this to:
Good afternoon! Tell me how to change the address in the device

Moderator - BT

That wouldnā€™t change anything, you just canā€™t measure current flow direction with a current transformer (CT).
If you need to do that you would need a shunt (high power resistor with very little resistance and high precision).
Peacefair has the (new?) PZEM-017 that comes with a 200A shunt. I donā€™t know if it is direction aware, but it at least have the hardware to be able to.
https://www.aliexpress.com/item/PZEM-017-DC-0-300-V-RS485-Modbus/32913581017.html

UPS! Didnā€™t notice it was for DC.
Bummer!

But you can determine the direction of power flow, if you compare the current and voltage waveforms, as we do in the emonTx and emonPi. There is an explanation here: Learnā†’Electricity Monitoringā†’AC Power Theoryā†’Introductionā†’An Introduction to AC Power

I always strongly advise against using shunts for both a.c. and d.c., because of the difficulties of providing adequate isolation between the high voltage mains and the low voltage monitoring circuits. I advocate the use of a Hall effect transducer for d.c., and we always advise a current transformer for a.c. as it is non-invasive, and it is often necessary to monitor at a point where tampering with the supply cables is illegal.

I stand corrected! :face_with_hand_over_mouth:
Didnā€™t though about the fact the phases will be inverted. Very clever.

I very much agree. I just didnā€™t thought it could be done without a shunt.

BTW: do any of you clever guys know of a small open end CT? I was planning on installing the one that comes with the PZEM-016 (the open types), but as I live in a 3-phase country, I need 3 CTā€™s, and those are just way to big to fit in my powerpanel :disappointed:

Oh dear, thatā€™s like asking ā€œHow long is a piece of string?ā€
What is the maximum current that you want to measure, and what is it feeding into, i.e., do you want a current or a voltage, and what is the maximum peak-peak swing that you can use?
Can you use a ring-core type, or must it be a split-core?

(e.g., I want to measure 100 A, Iā€™ve got a 3.3 V Atmel 328P, therefore I need less than 3.3 V peak-peak, or about 1.1 V rms, from my c.t. and burden resistor combination.)

I take a long time to find this software to calibration for PZEM sensor, so could you tell me password for calibrate? thanks so much

Iā€™ve never attempted to calibrate one of these as they come pre-calibrated and the instructions are unclear, especially about what the spec of the calibration load is, however the password is included in the ā€œthe user manual of the software.txtā€ file in the same zip as the software attached earlier in this thread.

5.Please be with caution to use the calibration function in the software!
The module has been calibrated at least twice with the professional equipment before delivery, so the data will
has problem in general.
If it is must be calibrated because of the defective product,the calibrationg password is: PZEM014/6
Calibration condition:
PZEM-014: VOLTAGE:220v,CURRENT:1A
PZEM-014: VOLTAGE:220v,CURRENT:10A

As far as I can make out it can be done via a simple modbus command too

2.6 Calibration
The command format of the master to calibrate the slave is (total 6 bytes):
0xF8 + 0x41 + 0x37 + 0x21 + CRC check high byte + CRC check low byte.
Correct reply: 0xF8 + 0x41 + 0x37 + 0x21 + CRC check high byte + CRC check low byte.
Error Reply: 0xF8 + 0xC1 + Abnormal code + CRC check high byte + CRC check low byte.
It should be noted that the calibration takes 3 to 4 seconds, after the master sends the command, if the calibration is successful, it will take 3 ~ 4 seconds to receive the response from the slave.

I suspect this operation must be done one to one via modbus, not on a shared bus.