Search code examples
pythonmodbuspymodbus

How should negative numbers be represented in the pymodbus input register?


I want to assign negative numbers to the input register in a pymodbus asynchronous server. I have a 5 element array named PQV that contains numbers with magnitude ranging from 0 to 300, but some of the elements are negative

PQV=[145, -210, 54, 187, -10]

I use the code below to assign PQV to the Input Register (register 4) starting at address 0. I tried adding 65536 to all the negative numbers, but that didn't work.

How do I condition the negative elements of array PQV to be acceptable for pymodbus?

context[slave_id].setValues(4, 0, PQV)

Solution

  • The floats are to be represented in IEEE-754 hex format before writing in to the datastore. You can do something like this to achieve it.

    # Import BinaryPayloadBuilder and Endian
    from pymodbus.payload import BinaryPayloadBuilder, Endian
    # Create the builder, Use the correct endians for word and byte
    builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big)
    

    In your updating function

    busvoltages = [120.0, 501.3, -65.2, 140.3, -202.4]
    builder.reset() # Reset Old entries
    for vol in busvoltages:
        builder.add_32bit_float(vol)
    payload = builder.to_registers()   # Convert to int values
    # payload will have these values [17136, 0, 17402, 42598, 49794, 26214, 17164, 19661, 49994, 26214]
    context[slave_id].setValues(2, 0, payload)  # write to datablock
    

    When you read the values back , you will get the raw int values back. You will have to convert them back to floats using BinaryPayloadDecoder

    >>> from pymodbus.payload import BinaryPayloadDecoder, Endian
    >>> r = client.read_input_registers(0, 10, unit=1)
    # Use the same byte and wordorders
    >>> d = BinaryPayloadDecoder.fromRegisters(r.registers, byteorder=Endian.Big, wordorder=Endian.Big)
    >>> d.decode_32bit_float()
    120.0
    >>> d.decode_32bit_float()
    501.29998779296875
    >>> d.decode_32bit_float()
    -65.19999694824219
    >>> d.decode_32bit_float()
    140.3000030517578
    >>> d.decode_32bit_float()
    -202.39999389648438
    >>> # Further reads after the registers are exhausted will throw struct error
    >>> d.decode_32bit_float()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/Users/sanjay/.virtualenvs/be3/lib/python3.6/site-packages/pymodbus/payload.py", line 440, in decode_32bit_float
        handle = self._unpack_words(fstring, handle)
      File "/Users/sanjay/.virtualenvs/be3/lib/python3.6/site-packages/pymodbus/payload.py", line 336, in _unpack_words
        handle = unpack(up, handle)
    struct.error: unpack requires a buffer of 4 bytes
    >>>
    

    Hope this helps.