-
-
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 | |
# 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 |
@bandaangosta
"Regarding the first one, yes, this will work on Windows too"
Check, the script wouldn't run because I missed the bit that the AC-side had to be connected.
I have since then rewritten the script a little, I added for example
import serial.tools.list_ports
ports = list(serial.tools.list_ports.comports())
for port in ports:
if port.serial_number == "A501JT6HA":
comport = port.device
to find the comport assigned to the FTDI-cable with a certain serial number.
Also, the
sensor = serial.Serial(
bit is in an if-statement and won't run if comport var is empty, in that same if-statement there\s a variable called Run that is set true, default is false, so if there's no FTDI-cable detected (comport var is empty), then Run = false which will cause the main loop (while Run) to not run.
Inside the main (while) loop I've got
for PZEM in PZEMs
data = master.execute(PZEM, cst.READ_INPUT_REGISTERS, 0, 10)
where
PZEMs = range(1, 6)
so it iterates/loops through the code accessing 5 modules, with try/except.
To do: Copy over some Home Assistant MQTT Auto Discovery code from my P1 DSMR smart meter script, I ended up spreading that one over several files and a whole bunch of functions.
Hi, @donemuhendislik. I'm happy to hear this code is useful. I remember reading (it's been a couple of years) that this version of the module does not allow for resetting the energy counter, although supposedly the function code 0x42 ("power zero clearing") does the trick. I have never tried it. In general, there is no need to clear the energy count. Just read the value at the beginning and end of a certain period. The difference is the energy consumption of the period.
Thank you so much @bandaangosta for your reply. In deed, i was meaning that 0x42 and i was afraid of trying that data to pzem-004t for may i could break it. So, your solution is best. Saving the initial energy value and saving last one and difference is consumption ππ» or calculating energy consumption with Wh formula that contains voltage and current. Again thank you so much for your helpful idea, i will use it ππ»
@bandaangosta Do you happen to have a TCP-version of this script? I'm now trying to access the data through a serial server running on an ESP-module. I can see from the log (of ESP Easy) that my script connects to the serial server, there's a single blink from a red LED on the PZEM-004t, but no data back :(
edit: Got as far as
import socket, sys
socket.setdefaulttimeout(5)
try:
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error:
print('Failed to create socket')
sys.exit()
print('Socket Created')
ESP_IP= "*.*.*.*"
ESP_PORT = *
client.connect((ESP_IP,ESP_PORT))
print('Socket Connected to ' + ESP_IP )
try:
print("Send command")
client.send(bytes("\x01\x04\x00\x00\x00\x0A\x70\x0D", 'ascii'))
print("Receive data")
data = client.recv(1024)
print(data)
except socket.error as e:
print(e)
The PZEM-module reacts to this (Rx LED goes on, followed by Tx-LED), but not receiving data back.
Hi, @donemuhendislik. I'm happy to hear this code is useful.
I remember reading (it's been a couple of years) that this version of the module does not allow for resetting the energy counter, although supposedly the function code 0x42 ("power zero clearing") does the trick. I have never tried it. In general, there is no need to clear the energy count. Just read the value at the beginning and end of a certain period. The difference is the energy consumption of the period.