PZEM-016 single phase modbus energy meter

    print(' '.join(str(v) for v in pz.getData()+[time.time()]))
TypeError: unsupported operand type(s) for +: 'bool' and 'list'

Ok, Thats saying that the getData failed and returned False (boolean) so it cannot attach the list [time.time() to that bool as it expects a list.

you could try wrapping it in a test eg

data = pz.getData()
if data:
    print(' '.join(str(v) for v in data +[time.time()]))

But if that getData() returning false that means it is not responding to 5 retries, that doesn’t sound right at all, you could up the retry count to see if it helps, but mine always succeeds on the 1st retry if it fails first attempt.

I do not know if maybe it’s some cut and paste errors or not but it appears there might be some indentation errors in that code.

everything under getData() to the if __name__ line should be indented in one more level and everything below if __name__ to the bottom needs to be indented in 1 level too. Try sorting the indents first as that code looks fine to me, but I’m happy to dig deeper if it does need fixing.

Another possible “difference” is I’m still dev’ing on windows, I haven’t tried it on a Pi yet, and I am also using a different adapter, I haven’t tried the cheap usb adapters that came with the pzems yet.

Found it…

Here’s what I did:

        round((data[1]+data[2]*65536 * 0.001),3),    # Current(0.001A)
        round((data[3]+data[4]*65536 * 0.1),1),      # Power(0.1W)
        round((data[5]+data[6]*65536))]              # Energy(1Wh)

Here’s the output now:

247.7 28872.0 12394.6 33325.0 1532378288
248.1 28843.0 12406.6 33334.0 1532378293
247.9 28863.0 12420.6 33344.0 1532378298
248.0 28860.0 12413.6 33355.0 1532378303

Current and energy are good… power is still off…

You are right there is a opening for ambiguity there, I have now updated my own code to

                round((data[0]*0.1),1),                    # Voltage(0.1V)
                round((((data[1]*65536)+data[2])*0.001),3),    # Current(0.001A)
                round((((data[3]*65536)+data[4])*0.1),1),      # Power(0.1W)
                round((((data[5]*65536)+data[6])*1),0),        # Energy(1Wh)
                round((data[7]*0.1),1),                    # Frequency(0.1Hz)
                round((data[8]*0.01),2),                   # Power Factor(0.01)
                int(data[9]>0)]                            # Alarm(1)

lots more brackets to be sure things get done in the right order,

Although your code may be working, it still should have more brackets like so.

        round((data[1]+(data[2]*65536 * 0.001)),3),    # Current(0.001A)
        round((data[3]+(data[4]*65536 * 0.1)),1),      # Power(0.1W)
        round((data[5]+(data[6]*65536)))]              # Energy(1Wh)

it seems “BODMAS” might be getting interpreted differently on different machines/os’s

1 Like

Actually, I’ve only just noticed you have moved the “x65536” to the other value, I thought you jhad ust reversed the positioning at first glance. I need to think about that some more, there must be some crossed lines about lsb/msb and the array positions. I’ll re read the docs again.

It’s been decades since I did any Modbus work, and that was in C. I think the way I tackled it was to read the complete block of data into a character array that was mapped to the “useful” variables in a union.

A quick search by Mr G threw up this. I don’t understand Python but it looks as if it might circumvent some of the maths.

pzem-016_register_map

AHa!

Looking at your post #46 and the docs that came in the box with my device

and that latest table of registers you’ve just posted matches my docs but not your previous post, from what you are saying about the code, it seems the table posted earlier in the thread may be correct.

The next question is are there differences in the device models/revisions or just in the documentation?

That’s the Chinese for ya. My docs match post 46.

With this code:

        data[0]*0.1,                        # Voltage(0.1V)
        data[1]+(data[2]*65536),            # Current(0.001A)
        data[3]+(data[4]*65536),            # Power(0.1W)
        data[5]+(data[6]*65536)]            # Energy(1Wh)

I get this:

250.0 27742 69201 37636 1532380520
250.2 27758 69243 37645 1532380525
250.0 27781 69279 37655 1532380530
250.0 27791 69300 37665 1532380536
250.0 27794 69293 37675 1532380541

which are the correct numbers i.e 27.794 A, 6.9293 kW, and 37.675 kWh (last line of output)
Now they just need to be scaled.

Got it…

        data[0]*0.1,                        # Voltage(0.1V)
        (data[1]+(data[2]*65536)) * .001,   # Current(0.001A)
        (data[3]+(data[4]*65536)) * .1,     # Power(0.1W)
        data[5]+(data[6]*65536)]            # Energy(1Wh)

Yields:

248.8 28.433 7059.6 38348 1532380885
248.8 28.427 7057.1 38358 1532380890
248.9 28.42 7055.7 38367 1532380895

I just grabbed this table from the word doc in the pzem docs and utility zip, This would appear to be correct by your findings. However, if your printed docs match this too, then I do not yet know if my devices are the same as yours but supplied with incorrect printed docs or different devices (checked the other box and I have 2 printed docs that both contradict your findings)

Register address Description Resolution
0x0000 Voltage value 1LSB correspond to 0.1V
0x0001 Current value low 16 bits 1LSB correspond to 0.001A
0x0002 Current value high 16 bits
0x0003 Power value low 16 bits 1LSB correspond to 0.1W
0x0004 Power value high 16 bits
0x0005 Energy value low 16 bits 1LSB correspond to 1Wh
0x0006 Energy value high 16 bits
0x0007 Frequency value 1LSB correspond to 0.1Hz
0x0008 Power factor value 1LSB correspond to 0.01
0x0009 Alarm status 0xFFFF is alarm,0x0000is not alarm

That matches my docs verbatim.

Just hooked up a CT to my 016 and got the same issues you had, I have reversed the lsb and msb in the calcs and all is well!

I can’t test the 014 right now as I need a bulb holder or socket with wire tails to wire the monitor in series, I’ll try to check it over the next couple of days.

But with the current findings 2 out of 3 devices say my original code is wrong, so I will post amended versions once I confirm both 014 and 016’s are both the same otherwise I may end up retracting the revised code again to make it work either way depending on a user setting or make separate pzem014 and pzem016 classes, assuming that’s the deciding factor and not vintage or the wind direction on the day of production.

A little excerpt of output, device id 2 is connected to a LED bench magnifier looped 6x through CT. So the load looks like it’s 38w and ramped up a measly 5 watts since I connected it. The power factor is around 0.74.

1532382900.0 2 250.2 0.207 38.2 5.0 50.0 0.74 0
1532382900.0 3 250.3 0.0 0.0 0.0 50.0 0.0 0
1532382910.0 2 250.0 0.207 38.2 5.0 50.0 0.74 0
1532382910.0 3 250.2 0.0 0.0 0.0 50.0 0.0 0
1532382920.0 2 250.2 0.207 38.2 5.0 50.0 0.74 0
1532382920.0 3 250.3 0.0 0.0 0.0 50.0 0.0 0
1532382930.0 2 250.4 0.207 38.2 6.0 50.0 0.74 0
1532382930.0 3 250.5 0.0 0.0 0.0 50.0 0.0 0
1532382940.0 2 250.1 0.207 38.2 6.0 50.0 0.74 0
1532382940.0 3 250.2 0.0 0.0 0.0 50.0 0.0 0
1532382950.0 2 250.2 0.207 38.3 6.0 50.0 0.74 0
1532382950.0 3 250.3 0.0 0.0 0.0 50.0 0.0 0
1532382960.0 2 248.7 0.207 38.1 6.0 50.0 0.74 0
1532382960.0 3 248.8 0.0 0.0 0.0 50.0 0.0 0

Sounds good. At least we got part of it figured out. :wink:

Edit - Your output looks good too.

1 Like

At least something went right today. I had a crown detach from a molar yesterday.
Went to the dentist to have it re-cemented (not the dentist who originally put it in. This was the 2nd time it became detached) and walked away with a couple of posts in the tooth (endo, so no feeling in the tooth) and a reattached crown. All to the tune of 3 dollars shy of 500 bucks. “Ouch.” said my wallet!

The read_long() funtion in the minimalmodbus module cannot be used to return just one value then.

Instead we will need to use (for example)

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

or something similar.

Ouch indeed!

No more reading all of the registers into a single list?

That’s still my preferred way, but I intended to make pzem.py a command line utility as well as a library for bespoke scripts. Plus (for example) it might be nice to check that the energy registers are actually zero immediately after issuing a reset command.

Good ideas, all.