2

I am attempting to decode some data from a Shark 100 Power Meter via TCP modbus. I have successfully pulled down the registers that I need, and am left with two raw values from the registers like so:

[17138, 59381]

From the manual, I know that I need to convert these two numbers into a 32bit IEEE floating-point number. I also know from the manual that "The lower-addressed register is the high order half (i.e., contains the exponent)." The first number in the list shown above is the lower-addressed register.

Using Python (any library will do if needed), how would I take these two values and make them into a 32 bit IEEE floating point value.

I have tried to use various online converters and calculators to figure out a non-programmatic way to do this, however, anything I have tried gets me a result that is way out of bounds (I am reading volts in this case so the end result should be around 120-122 from the supplied values above).

3 Answers3

3

Update for Python 3.6+ (f-strings).

I am not sure why the fill in @B.Go's answer was only 2. Also, since the byte order was big-endian, I hardcoded it as such.

import struct
a = 17138
b = 59381
struct.unpack('>f', bytes.fromhex(f"{a:0>4x}" + f"{b:0>4x}"))[0]

Output: 121.45304107666016

Intrastellar Explorer
  • 3,005
  • 9
  • 52
  • 119
2

The following code works:

import struct
a=17138
b=59381
struct.unpack('!f', bytes.fromhex('{0:02x}'.format(a) + '{0:02x}'.format(b)))

It gives

(121.45304107666016,)

Adapted from Convert hex to float and Integer to Hexadecimal Conversion in Python

B. Go
  • 1,436
  • 4
  • 15
  • 22
  • 1
    That's the ticket! Thanks a million and thank you for publicly contributing, so that others may have the answer. – Andrew Sclafani Jan 23 '20 at 17:38
  • I believe this should be `struct.unpack('!f', bytes.fromhex('{0:04x}'.format(a) + '{0:04x}'.format(b)))` . For instance, if a=17138 and b=0, `'{0:02x}'.format(b)` would return '00' instead of '0000' and you would get `struct.error: unpack requires a buffer of 4 bytes` – Zaggo Nov 30 '20 at 23:13
2

I read in the comments, and @Sanju had posted this link: https://github.com/riptideio/pymodbus/blob/master/examples/common/modbus_payload.py

For anyone using pymodbus, the BinaryPayloadDecoder is useful as it's built in. It's very easy to pass a result.registers, as shown in the example. Also, it has a logging integrated, so you can help debug why a conversion isn't working (ex: wrong endianness).

As such, I made a working example for this question (using pymodbus==2.3.0):

from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder


a = 17138
b = 59381
registers = [a, b]
decoder = BinaryPayloadDecoder.fromRegisters(registers, byteorder=Endian.Big)
decoder.decode_32bit_float()  # type: float

Output: 121.45304107666016

Intrastellar Explorer
  • 3,005
  • 9
  • 52
  • 119