I am trying to read String
(Usecase-1) & multiple type of data in one request
(Usecase-2) data from Modbus TCP device but, it failed to decode it correctly.
System Configuration:
Python 3.6.5
Pymodbus: 2.1.0
Platform: Windows 10 64-bit
Modbus TCP Server:
import logging
from pymodbus.constants import Endian
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.server.sync import StartTcpServer
class ModbusTCPServer(object):
# initialize your data store:
hrBuilder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big)
# Usecase-1
hrBuilder.add_string("abcdefghij")
#Uncomment below three lines for usecase-2
# hrBuilder.add_32bit_float(20.5)
# hrBuilder.add_32bit_int(45)
# hrBuilder.add_bits([1, 0, 0, 0, 0, 0, 0, 0])
hrBlock = ModbusSequentialDataBlock(0, hrBuilder.to_registers() * 100)
store = ModbusSlaveContext(hr=hrBlock, ir=hrBlock, di=hrBlock, co=hrBlock)
slaves = {
1: store,
}
context = ModbusServerContext(slaves=slaves, single=False)
# initialize the server information
identity = ModbusDeviceIdentification()
modbusDeviceAddress = "127.0.0.1"
modbusDevicePort = 501
# run the TCP server
# TCP:
print("Modbus TCP Server started.")
StartTcpServer(context, identity=identity, address=(modbusDeviceAddress, modbusDevicePort))
if __name__ == "__main__":
print("Reading application configurations...")
ModbusTCPServer();
Modbus TCP Client:
from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.constants import Endian
from pymodbus.compat import iteritems
if __name__ == '__main__':
client = ModbusClient('127.0.0.1', port=501)
client.connect()
result = client.read_holding_registers(0, 5, unit=1)
print("Result : ",result)
decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big, wordorder=Endian.Big)
# Usecase-1
decoded = {
'name': decoder.decode_string(10).decode(),
}
# Usecase-2
# decoded = {
# 'temp': decoder.decode_32bit_float(),
# 'rpm': decoder.decode_32bit_int(),
# 'status': decoder.decode_bits()
#}
for name, value in iteritems(decoded):
print ("%s\t" % name, value)
client.close()
Output Usecase-1:
Result : ReadRegisterResponse (5)
name cdefghijab
Modbus client should decode string as abcdefghij
but, it's decoding it as cdefghijab
.
Output Usecase-2:
Result : ReadRegisterResponse (5)
temp 0.0
rpm 2949376
status [True, False, False, False, False, False, True, False]
Look at the above output of reading multiple registers, the output values doesn't match with the one given in input to BinaryPayloadBuilder
.
I've tried all the combination of byteorder
& wordorder
but, it's not working with any case.
Please help me in understanding why data is decoded like this ? Did I missed something to add while encoding or decoding this data ?
FYI: This solution was working fine with Pymodbus 1.5.1
version. Recently I've upgraded version & it failed to work as expected.
Any help would be appreciated.
Tl;dr. Use zero_mode=True
in ModbusSlaveContext
.
If you want to map registers [0..n]
in your client reads to [0..n]
in the server. By default pymodbus server maps register reads for address [0..n]
to registers [1..n]
in its internal store. This is to adhere to the modbus specs . Quoting from the pymodbus source code.
#The slave context can also be initialized in zero_mode which means that a # request to address(0-7) will map to the address (0-7). The default is # False which is based on section 4.4 of the specification, so address(0-7) # will map to (1-8)::
So in your case you can either set the starting address of the ModbusSequentialDataBlock
to 1
or initialize ModbusSlaveContext
with zero_mode=True
.
hrBlock = ModbusSequentialDataBlock(1, hrBuilder.to_registers() * 100)
# Or
store = ModbusSlaveContext(hr=hrBlock, ir=hrBlock, di=hrBlock, co=hrBlock, zero_mode=True)