How to convert value hex to float32 big endian in PyModbus

1.6k views Asked by At

I'm currently coding modbus rtu in python using package pymodbus. In this case, I will read the data from the TDS sensor using func code 4 / Read Input Registers (refer to the Aquas SMR-08 datasheet). The data was successfully obtained and I convert it to hex , but the data must be converted again to the correct value. I try convert the hex value with scadacore (online hex converter) and I see the correct data on Float Big Endian (ABCD)

The Hex Value is 41FB:

The Hex Value is 41FB

conversion result is 31.375 (this is Temperature value):

conversion result is 31.375 (this is Temperature value)

So, how algorithm or what code pymodbus to convert like scadacore (online hexa conversion) ?

can anyone help ? Thanks In Advance, and sorry for my bad english hihihi

this is my code

import time
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from pymodbus.constants import Defaults
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
Defaults.RetryOnEmpty = True
Defaults.Timeout = 5
Defaults.Retries = 5

client = ModbusClient(method='rtu', port='/dev/ttyUSB0', timeout=2, stopbits=1, bytesize=8, 
parity='N', baudrate=19200)
client.connect()


while True:

tds1 = client.read_input_registers(address=0, count=2, unit=1)

bacatds1 = tds1.registers[0]
bacatds2 = tds1.registers[1]

tds1hex = hex(bacatds1)
tds2hex = hex(bacatds2)

print(tds1hex)
print(tds2hex)

decoder1 = BinaryPayloadDecoder.fromRegisters(tds1.registers, Endian.Little)
hasil1 = decoder1.decode_32bit_float()

print(hasil1)
print('==================================')
time.sleep(1)

# ----------------------------------------------------------------
tds2 = client.read_input_registers(address=1, count=2, unit=1)

bacatds3 = tds2.registers[0]
bacatds4 = tds2.registers[1]

tds3hex = hex(bacatds3)
tds4hex = hex(bacatds4)

print(tds3hex)
print(tds4hex)

decoder2 = BinaryPayloadDecoder.fromRegisters(tds2.registers, Endian.Big)
hasil2 = decoder2.decode_32bit_float()
print(hasil2)
print('===================================')
time.sleep(1)
2

There are 2 answers

0
Kumar Anil Chaurasiya On

I don't have much knowledge about the python, But I have seen similar question in java.

I think you are not using the hex base. I'm giving you the steps to achieve your requirement.

  1. Consider your hex as an string.
  2. convert string to float with the base 16.

After following these two steps you will get your output.

I'm providing you the link below for an example.

Write hexadecimal values into register with leading zeros

Convert hex to float

I hope, it would help you.

0
Vaebhav On

I went through a similar problem in converting the values fetched to a float value

Upon checking the internal code base for pymodbus

I came up with the below function

import struct

def decodeRegisters_float(in_registers,bit_size=32,step=2):

    n = len(in_registers)

    float_res = []
    for i in range(0,n,step):
        if bit_size == 32:
            pack_string = '!2H'
            unpack_string = '!f'
            raw = struct.pack(pack_string, in_registers[i] ,in_registers[i+1])
        elif bit_size == 64:
            pack_string = '!4H'
            unpack_string = '!d'
            raw = struct.pack(pack_string, in_registers[i] ,in_registers[i+1],in_registers[i+2],in_registers[i+3])
        value = struct.unpack(unpack_string, raw)[0]
        float_res += [value]

    return float_res

The arguments to the function are as below

in_registers - Register values read from holding/input resistors

bit_size - 32,64

step - 2 for 32 and 4 for 64

The above function can be considered as a wrapper once you have read the values and implicitly, need to convert them to the respective float values

Below is the example usage -

def _read_register_dtype_float(client,unit,nreg,registers=2,type='holding',bit_size=32,step=2):

        assert bit_size in [32,64],"Only 32 and 64 bit size is allowed to be read"

        if bit_size == 32:
            registers = nreg * registers
        elif bit_size == 64:
            registers = nreg * registers * 2
            step = 4

        if type == 'holding':
            res = client.read_holding_registers(0,registers,unit=unit)

        if type == 'input':
            res = client.read_input_registers(0,registers,unit=unit)

        return decodeRegisters_float(res.registers,bit_size=bit_size,step=step)