3

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'

Jace reading

Config

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
Pieter Hamman
  • 1,122
  • 13
  • 18
  • It could be so that the modbus slave on your remote site takes some time to update its registers , have you tried to read the same registers in a loop and with some sleep time in between ? My guess is you should see the values you are expecting atleast after some iterations. Pymodbus is just a facilitator and it does not store values , your logs shows that slave is returning registers `[0, 17335]` which is decoded in to 366. – Sanju Aug 18 '18 at 03:37
  • @Sanju I tried what you mentioned, after adding a delay, I'm still getting the same result (366) for both registers. I just checked our .Net integration and that is correctly retrieving both registers one as 366 and the other as 367. – Pieter Hamman Aug 20 '18 at 19:47
  • Your logs are missing the transactions for `40162` , could you add the logs for the same ? The current logs shows only the transactions to read 2 registers from offset `0xa0` (160) – Sanju Aug 21 '18 at 04:50
  • Also 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`. refer https://pymodbus.readthedocs.io/en/latest/source/library/pymodbus.html#pymodbus.register_read_message.ReadHoldingRegistersRequest – Sanju Aug 21 '18 at 05:04
  • @Sanju thanks for you help, turns out the offset ^ was part of the issue, and the `byteorder` and `wordorder` must the `Endian.Big`. After I've changed that the data retrieved is correct. Please add this as the answer. – Pieter Hamman Aug 22 '18 at 18:26
  • Glad it helped, I have added the answer, please accept. – Sanju Aug 23 '18 at 04:01

1 Answers1

1

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.

Sanju
  • 1,738
  • 1
  • 16
  • 30