I've started using pymodbus
to read values from modbus to store in a database off site. I've been struggling with an issue that the value received in the response is not the same as the value I can see on the Jace.
I've tried modbus-tk
as well and I'm getting the same incorrect response, so it must be something in my Python code that is causing this issue. The readings retrieved from our legacy system (VB.Net) are the same as the outputs I see on the Jace.
This is the simple function retrieving the data from modbus. We have 2 registers at 40160
and 40162
the first one is reading 366
which is correct and the 2nd one is reading 367
(This is the one I'm having the problem with). I've also seen the same issue with other registers where the reading does not update even though I can see on the Jace that the value has increased.
# -*- coding: utf-8 -*-
from __future__ import division, print_function, unicode_literals
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
def get_modbus_register_data(ip_address, register, device, count=2):
"""
Retrieve modbus data.
"""
client = ModbusTcpClient(ip_address, timeout=10)
client.connect()
# Read registers
response = client.read_holding_registers(
address=register, # 40162
count=count, # 2
unit=device) # 4
decoder = BinaryPayloadDecoder.fromRegisters(
registers=response.registers,
byteorder=Endian.Big,
wordorder=Endian.Little)
value = decoder.decode_32bit_float()
client.close()
return value # 366 and it should be 367
Pymodbus debug logs
DEBUG:pymodbus.transaction:Current transaction state - IDLE
DEBUG:pymodbus.transaction:Running transaction 1
DEBUG:pymodbus.transaction:SEND: 0x0 0x1 0x0 0x0 0x0 0x6 0x4 0x3 0x0 0xa0 0x0 0x2
DEBUG:pymodbus.client.sync:New Transaction state 'SENDING'
DEBUG:pymodbus.transaction:Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
DEBUG:pymodbus.transaction:Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
DEBUG:pymodbus.transaction:RECV: 0x0 0x1 0x0 0x0 0x0 0x7 0x4 0x3 0x4 0x0 0x0 0x43 0xb7
DEBUG:pymodbus.framer.socket_framer:Processing: 0x0 0x1 0x0 0x0 0x0 0x7 0x4 0x3 0x4 0x0 0x0 0x43 0xb7
DEBUG:pymodbus.factory:Factory Response[ReadHoldingRegistersResponse: 3]
DEBUG:pymodbus.transaction:Adding transaction 1
DEBUG:pymodbus.transaction:Getting transaction 1
DEBUG:pymodbus.transaction:Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
Update
With the help of Sanju, it was pointed out to me that the offset I'm using might be incorrect. This was indeed the case, by changing the offset by 1 (40162 - 40001 = 161) I was able to retrieve the correct values from the register, the wordorder
needed to change to Endian.Big
.
Updated code
def get_modbus_register_data(ip_address, register, device, count=2):
"""
Retrieve modbus data.
"""
client = ModbusTcpClient(ip_address, timeout=10)
client.connect()
# Read registers
response = client.read_holding_registers(
address=register, # 40161
count=count, # 2
unit=device) # 4
decoder = BinaryPayloadDecoder.fromRegisters(
registers=response.registers,
byteorder=Endian.Big,
wordorder=Endian.Big)
value = decoder.decode_32bit_float()
client.close()
return value # 367
With pymodbus you will have to pay attention to how pymodbus deals with offsets, an offset 0 maps to register 40001 so the offset for 40162 would be 40162-40001
which is 0xa1
and similarly for 40160
the offset would be 0x9f
.
For more info refer https://pymodbus.readthedocs.io/en/latest/source/library/pymodbus.html#pymodbus.register_read_message.ReadHoldingRegistersRequest
Also note, the default Endianness
assumed by BinaryPayloadDecoder
is Endian.Little
for byteorder
and Endian.Big
for wordorder
. You will end up with wrong decoded values if these orders
are not correct.