python-3.xmodbus-tcppymodbuspymodbustcp

Pymodbus read and decode register value


I am new to modbus comunication, i have to read from an inverter a single value from one register using modbus protocol (i use python with pymodbus for this): From the inverter documentation i read about register documentation:

Register ADR: 31249 Description: Active power of system at PCC (W) CNT: 2 Type: S32 Format: FIX0 Access: RO

Well, i try my python script like this:

from pymodbus.client.sync import ModbusTcpClient
client = ModbusTcpClient("192.168.1.10", port=502, timeout=3)
client.connect()
read=client.read_holding_registers(address = 31249 ,count =2,unit=1)
read.registers

So i have this value:

[65535, 65535]

I guess that are not the decoded values of the register, so i try to decode for extract value:

read_encoded = read.encode()
read_encoded
b'\xff\xff\xff\xff'
read_encoded_value = int.from_bytes(read_encoded, byteorder="big")
read_encoded_value

So if i print my variable i get:

4294967295

it mean a little big as a value. Is the procedure correct for read and decode my modbus registers value?

How can i extract and read data starting from modbus registers doc of my inverter?

So many thanks in advance


Solution

  • As far as I can tell from your question you are trying to connect to an SMA inverter.

    Reading the first manual I could find it's not completely clear but it seems the register you are trying to read is not a holding register but an input register (for most Modbus devices if the register number is in the 3XXXX range it usually means input register and 4XXXX is for holding registers).

    The second trick is the offset: pyModbus does not account for the way registers are addressed in the protocol so you have to be aware that:

    -When you are trying to read or write an input register you need to subtract 30001. For your case, that is: 31249-30001=1248 (note that in some weird devices the offset is actually 30000 so you might want to try that too).

    -For holding registers the offset is obviously 40001 instead.

    If the manual I pointed to above is correct for your device then you should be addressing unit 2 instead of 1 as you are doing now. If that's correct you'd have to change this line:

    read=client.read_holding_registers(address = 31249 ,count=2,unit=1)
    

    to:

    read=client.read_holding_registers(address = 1248 ,count=2,unit=2)
    

    Once you figure out the right registers you will have to use BinaryPayloadDecoder from pymodbus.payload (see this example for more details).

    You will probably need to do something like the following (don't forget the imports above):

    decoder = BinaryPayloadDecoder.fromRegisters(read.registers, byteorder=Endian.Big, wordorder=Endian.Big)
    

    According to the manual I have, the format is Motorola big-endian for words s I'm guessing bytes are also big (you might have to try and see until you hit the right combination though).

    And finally, after building the decoder you need to decode the 32 bit signed int, like so:

    active_power_w = decoder.decode_32bit_int()
    

    That should give you some meaningful value for power when you print it.