Skip to content

Instantly share code, notes, and snippets.

@bandaangosta
Last active September 27, 2024 12:03
Show Gist options
  • Save bandaangosta/134c9d84ae9bd317297e96dcc0b9c860 to your computer and use it in GitHub Desktop.
Save bandaangosta/134c9d84ae9bd317297e96dcc0b9c860 to your computer and use it in GitHub Desktop.
Reading PZEM-004t power sensor (new version v3.0) through Modbus-RTU protocol over TTL UART
# Reading PZEM-004t power sensor (new version v3.0) through Modbus-RTU protocol over TTL UART
# Run as:
# python3 pzem_004t.py
# To install dependencies:
# pip install modbus-tk
# pip install pyserial
import serial
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu
# Connect to the sensor
sensor = serial.Serial(
port='/dev/PZEM_sensor',
baudrate=9600,
bytesize=8,
parity='N',
stopbits=1,
xonxoff=0
)
master = modbus_rtu.RtuMaster(sensor)
master.set_timeout(2.0)
master.set_verbose(True)
data = master.execute(1, cst.READ_INPUT_REGISTERS, 0, 10)
voltage = data[0] / 10.0 # [V]
current = (data[1] + (data[2] << 16)) / 1000.0 # [A]
power = (data[3] + (data[4] << 16)) / 10.0 # [W]
energy = data[5] + (data[6] << 16) # [Wh]
frequency = data[7] / 10.0 # [Hz]
powerFactor = data[8] / 100.0
alarm = data[9] # 0 = no alarm
print('Voltage [V]: ', voltage)
print('Current [A]: ', current)
print('Power [W]: ', power) # active power (V * I * power factor)
print('Energy [Wh]: ', energy)
print('Frequency [Hz]: ', frequency)
print('Power factor []: ', powerFactor)
print('Alarm : ', alarm)
# Changing power alarm value to 100 W
# master.execute(1, cst.WRITE_SINGLE_REGISTER, 1, output_value=100)
try:
master.close()
if sensor.is_open:
sensor.close()
except:
pass
@NA7KR
Copy link

NA7KR commented Jan 10, 2020

Please, can you help? (new to RS485 )
Traceback (most recent call last):
File "test4.py", line 28, in
data = master.execute(1, cst.READ_INPUT_REGISTERS, 0, 10)
File "/usr/local/lib/python3.7/dist-packages/modbus_tk/utils.py", line 39, in new
raise excpt
File "/usr/local/lib/python3.7/dist-packages/modbus_tk/utils.py", line 37, in new
ret = fcn(*args, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/modbus_tk/modbus.py", line 306, in execute
response_pdu = query.parse_response(response)
File "/usr/local/lib/python3.7/dist-packages/modbus_tk/modbus_rtu.py", line 46, in parse_response
raise ModbusInvalidResponseError("Response length is invalid {0}".format(len(response)))
modbus_tk.exceptions.ModbusInvalidResponseError: Response length is invalid 0

@connermacleod69
Copy link

Hi,
works like a charm, thanks for sharing.
Regards.

@trytomo
Copy link

trytomo commented May 26, 2020

Please, can you help? (new to RS485 )
Traceback (most recent call last):
File "test4.py", line 28, in
data = master.execute(1, cst.READ_INPUT_REGISTERS, 0, 10)
File "/usr/local/lib/python3.7/dist-packages/modbus_tk/utils.py", line 39, in new
raise excpt
File "/usr/local/lib/python3.7/dist-packages/modbus_tk/utils.py", line 37, in new
ret = fcn(*args, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/modbus_tk/modbus.py", line 306, in execute
response_pdu = query.parse_response(response)
File "/usr/local/lib/python3.7/dist-packages/modbus_tk/modbus_rtu.py", line 46, in parse_response
raise ModbusInvalidResponseError("Response length is invalid {0}".format(len(response)))
modbus_tk.exceptions.ModbusInvalidResponseError: Response length is invalid 0

Check the rx tx pin & try to cross the cable in your USB to TTL dongle

@deejayves
Copy link

This works like a charm. Thx for sharing. I'm using this in a statistics project for my outdoor spa.

Regards !

@cmesoar
Copy link

cmesoar commented Mar 2, 2021

Thank you for this effort! Worked like a charm on my version 3 board under Linux. Great to test the board before moving it to Raspberry Pi monitoring system (Domoticz) to be certain it was working.
Cheers!!

@probrancor
Copy link

hello to all, i'm trying to conect my pzem004t v3 to HA to get the readings, but i don't understand how and where to put your file?
Can you explain to me, please?

@FelixRG161100
Copy link

Hello, first of all, thank you for your work. I used your code and it works perfect. In this moment, I want to expand my current work I want to use three PZEM sensor. I looked into the modbus_tk library documentation but it is not clear to me a way to read data from three sensors in the same channel. I would be grateful if you could support me in this project. Thank you very much.

@bandaangosta
Copy link
Author

bandaangosta commented Jul 5, 2022

Hello, first of all, thank you for your work. I used your code and it works perfect. In this moment, I want to expand my current work I want to use three PZEM sensor. I looked into the modbus_tk library documentation but it is not clear to me a way to read data from three sensors in the same channel. I would be grateful if you could support me in this project. Thank you very much.

Hi, Felix. I'm glad you found this useful.
If you have 3 sensors communicating over UART (let's say using USB to UART adapters on a PC/Raspberry Pi), then you need 3 separate UART channels (3 USB to UART adapters). Then you will have three sensors defined like this:


sensor1 = serial.Serial(
    port='/dev/ttyUSB0',
    baudrate=9600,
    bytesize=8,
    parity='N',
    stopbits=1,
    xonxoff=0
)
sensor2 = serial.Serial(
    port='/dev/ttyUSB1',
    baudrate=9600,
    bytesize=8,
    parity='N',
    stopbits=1,
    xonxoff=0
)
sensor3 = serial.Serial(
    port='/dev/ttyUSB2',
    baudrate=9600,
    bytesize=8,
    parity='N',
    stopbits=1,
    xonxoff=0
)

The /dev/ttyUSBX definition for the assigned ports is just an example. This may vary on your system.

Then apply modbus initialization and commands on each sensorX object separately.
Cheers.

@FelixRG161100
Copy link

FelixRG161100 commented Jul 5, 2022 via email

@connermacleod69
Copy link

connermacleod69 commented Jul 5, 2022 via email

@bandaangosta
Copy link
Author

Wow, thanks connermacleod69!! That's great info to have at hand. Would you mind sharing what model(s) from the catalog you have used?

@connermacleod69
Copy link

connermacleod69 commented Jul 5, 2022 via email

@PurcellClaudio
Copy link

Merci @bandaangosta !

@FujiwaraKengo
Copy link

FujiwaraKengo commented Nov 7, 2023

this only print the data once right and not a loop? what does the .close() method do?

@bandaangosta
Copy link
Author

this only print the data once right and not a loop?

Hi, @FujiwaraKengo . That is correct, data is printed out only once and then the scripts ends, closing the communication channel. You could put the reading part in a loop (lines 27 to 43), allowing for a reasonable waiting time between iterations, or call the full script periodically.

@bartgrefte
Copy link

bartgrefte commented Sep 27, 2024

Can this script be modified for usage with Windows? Changing "/dev/PZEM_sensor" to the comport of the FTDI-cable results in

Traceback (most recent call last):
  File "g:\PZEM-004T\pzem_004t.py", line 27, in <module>
    data = master.execute(1, cst.READ_INPUT_REGISTERS, 0, 10)
  File "c:\Users\Bart\AppData\Local\Programs\Python\Python310\lib\site-packages\modbus_tk\utils.py", line 39, in new
    raise excpt
  File "c:\Users\Bart\AppData\Local\Programs\Python\Python310\lib\site-packages\modbus_tk\utils.py", line 37, in new
    ret = fcn(*args, **kwargs)
  File "c:\Users\Bart\AppData\Local\Programs\Python\Python310\lib\site-packages\modbus_tk\modbus.py", line 356, in execute
    response_pdu = query.parse_response(response)
  File "c:\Users\Bart\AppData\Local\Programs\Python\Python310\lib\site-packages\modbus_tk\modbus_rtu.py", line 46, in parse_response
    raise ModbusInvalidResponseError("Response length is invalid {0}".format(len(response)))
modbus_tk.exceptions.ModbusInvalidResponseError: Response length is invalid 0

Yes, I checked the Tx-/Rx-connections.

edit: Hmm, seems it does this when there are no readings (didn't connect AC-side), I am getting output when AC-side is connected. So I added an try/except for modbus_rtu.ModbusInvalidResponseError and an if-statement to check if data contains something.

Now to figure out how to change the address from Python.
edit: data = master.execute(1, cst.WRITE_SINGLE_REGISTER, 2, output_value=2)
seems to do that :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment