0

[SOLVED] Could someone help on this? I use minimalmodbus on python 3.11.1 to query a USB<->RS485 transceiver (see picture below) that is connected to a PZEM-016 (for measuring AC power consumption: voltage, current, power, energy, frequency...). USB<->RS485 transceiver PZEM-016

When used with the Simply Modbus Master 8.1.2, I can read the data without any problem. Simply Modbus Master - Echo ON

With Python and minimalmodbus, I can only get up to 7 registers and of course the data is wrong (I can't pass the 7th register).

Here is the code

import minimalmodbus

#######################################
# CONSTANTS declaration
#######################################
# Communication constants
PORT_NAME       ='COM3'
BAUD_RATE       = 9600
BYTE_SIZE       = 8
STOP_BITS       = 1
TIMEOUT_VAL     = 0.7

SLAVE_ADDR      = 1
READ_MODE_NB    = 4 # minimalmodus default to 3 PZEM requires 4 or it issues an error
WRITE_FCT_NB    = 16 #HEX: 0x10
DEBUG_VAL       = True
ECHO_VAL        = False # Changed back from True to False after solution found

# Register constants
BASE_REG        = 0
VOLT_REG        = 0 #0x0000
CURR_LOW_REG    = 1 #0x0001
CURR_HIGH_REG   = 2 #0x0002
POWER_LOW_REG   = 3 #0x0003
POWER_HIGH_REG  = 4 #0x0004
ENGY_LOW_REG    = 5 #0x0005
ENGY_HIGH_REG   = 6 #0x0006
FREQCY_REG      = 7 #0x0007
POWER_FACTOR_REG = 8 #0x0008
ALARM_REG       = 9 #0x0009

#######################################
# Variables initialization
#######################################
instrument      = ''
response        = ''
voltage         = ''
current_high    = ''
current_low     = ''
power_high      = ''
power_low       = ''
energy_high     = ''
energy_low      = ''
frequency       = ''
power_factor    = ''
alarm_status    = ''

#######################################
# Set up the instrument
#######################################
instrument = minimalmodbus.Instrument(PORT_NAME, SLAVE_ADDR)

#######################################
# Explicit instrument settings
#######################################
instrument.serial.baudrate = BAUD_RATE # baud rate
instrument.serial.bytesize = BYTE_SIZE # data bits
instrument.serial.parity   = minimalmodbus.serial.PARITY_NONE # parity
instrument.serial.stopbits = STOP_BITS # stop bit
instrument.serial.timeout  = TIMEOUT_VAL # seconds
instrument.mode            = minimalmodbus.MODE_RTU # communication protocol
# instrument.address         = SLAVE_ADDR # slave address when only one slave in the bus
instrument.debug           = DEBUG_VAL
instrument.handle_local_echo = ECHO_VAL
instrument.close_port_after_each_call = True
instrument.clear_buffers_before_each_transaction = True

#######################################
# Typical request format of the master
# Slave address: x001
# Command type: 0x04
# First register address high = left bytes: 0x00
# First register address low = right bytes: 0x00 (=> 0x0000 = 0)
# Number of registers high = left bytes: 0x00
# Number of registers low = right bytes: 0x0A (=> 0x000A = 10)
# CRC check high = left bytes: 0x70
# CRC check low = right bytes: 0x0D (=> 0x700D)
# 01 04 00 00 00 0A 70 0D

# Typical reply format of the slave
# Slave address: 0x01
# Command type: 0x04
# Number of bytes: 0x14 (= 20 bytes = 2 bytes per requested register)
# Register 1: Voltage 8 bits
# Register 1 data high byte = left byte: 0x09
# Register 1 data low byte = right byte: 0x2E (=> 0x092E = 2350 = 235.0V)
# Register 2 + 3: Current 16 bits
# Register 2 data low byte = right bits: 0x01
# Register 2 data high byte = left bits: 0x78
# Register 3 data low byte = right bits: 0x00
# Register 3 data high byte = left bits: 0x00 (=> 0x00000178 = 376 = 0.376A)
# Register 4 + 5: Power 16 bits
# Register 4 data low byte = right bits: 0x01
# Register 4 data high byte = left bits: 0xE7
# Register 5 data low byte = right bits: 0x00
# Register 5 data high byte = left bits: 0x00 (=> 0x000001E7 = 487 = 48.7W)
# Register 6 + 7: Energy 16 bits
# Register 6 data low byte = right bits: 0x02
# Register 6 data high byte = left bits: 0xEA
# Register 7 data low byte = right bits: 0x00
# Register 7 data high byte = left bits: 0x00 (=> 0x000002EA = 746 = 746Wh)
# Register 8: Frequency 8 bits
# Register 8 data high byte = left byte: 0x01
# Register 8 data low byte = right byte: 0xF4 (=> 0x01F4 = 500 = 50.0Hz)
# Register 9: Power factor 8 bits
# Register 9 data high byte = left byte: 0x00
# Register 9 data low byte = right byte: 0x37 (=> 0x0037 = 55 = 0.55)
# Register 10: Alarm status 8 bits
# Register 10 data high byte = left byte: 0x00
# Register 10 data low byte = right byte: 0x00 (=> 0x0000 = 0 = No alarm)
# CRC check high = left bytes: 0xE5 (= 229)
# CRC check low = right bytes: 0xFD (= 253) (=> 0xE5FD)
# LED lights turned on
# 01 04 14 09 2E 01 78 00 00 01 E7 00 00 02 EA 00 00 01 F4 00 37 00 00 39 51
# LED lights turned off
# 01 04 14 09 34 00 00 00 00 00 05 00 00 02 E4 00 00 01 F3 00 64 00 00 E5 FD
#######################################

print('instrument: ', instrument)

#######################################
# Read and print registers' data
#######################################
response = instrument.read_registers(VOLT_REG, 10, READ_MODE_NB)
print('Response raw: ', response)

With the wrong read mode value, I get an error message with the read_registers() command

>>> ReadSerial.py
< MinimalModbus debug mode. Will write to instrument (expecting 25 bytes back): 01 03 00 00 00 0A C5 CD (8 bytes)
MinimalModbus debug mode. Clearing serial buffers for port COM3
MinimalModbus debug mode. No sleep required before write. Time since previous read: 167521078.00 ms, minimum silent period: 4.01 ms.
MinimalModbus debug mode. Closing port COM3
MinimalModbus debug mode. Response from instrument: 01 83 02 C0 F1 (5 bytes), roundtrip time: 0.8 ms. Timeout for reading: 700.0 ms.

< Traceback (most recent call last):
  File "...\ReadSerial.py", line 127, in <module>
    response = instrument.read_registers(VOLT_REG, 10, READ_MODE_NB)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\minimalmodbus.py", line 904, in read_registers
    returnvalue = self._generic_command(
                  ^^^^^^^^^^^^^^^^^^^^^^
  File "...\minimalmodbus.py", line 1245, in _generic_command
    payload_from_slave = self._perform_command(functioncode, payload_to_slave)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\minimalmodbus.py", line 1329, in _perform_command
    payload_from_slave = _extract_payload(
                         ^^^^^^^^^^^^^^^^^
  File "...\minimalmodbus.py", line 1880, in _extract_payload
    _check_response_slaveerrorcode(response)
  File "...\minimalmodbus.py", line 3538, in _check_response_slaveerrorcode
    raise error
minimalmodbus.IllegalRequestError: Slave reported illegal data address

I matched everything I could with the Simply Modbus Master example, except for the query mode, which value should be 4 (minimalmodbus default read mode is 3).

Update from above

I finally found the error in my code, which was very stupid, but errors are often stupid like a missing ";".

I still have to figure out why the read_register() command doesn't work (without the "s" for reading one register at a time). I can't pass the 7th register without errors and the returned data is wrong.

I leave the question and the answer for the community, in case someone uses the same PZEM-014 -> PZEM0-17 and looks for a python solution to retrieve the data

0 Answers0