Trying to convert Hexadecimal to decimal or float from PLC Modbus data

Hello, I am trying to convert a hexadecimal number from my PLC and having trouble doing it. The numbers are for a temperature sensor. The number in the PLC is 775 which means 77.5 degrees. When send over Modbus to Home assistant the number reads as 1,909. This is what I have for code so far. Any help would be great, thanks.

modbus: 
  - name: hub1
    type: tcp
    host: 192.168.1.243   
    port: 502

    sensors:
      - name: Temp1
        address: 1024      
        data_type: int 
        unit_of_measurement: "°F"
        

I believe you need to set the sensor’s scale option to 0.1 so that the 775 is understood to be 77.5.

However, there is the other issue of correctly representing the value’s units. Is your Home Assistant configured for metric or imperial units? (See Configuration > General > Unit System)

I think I would need to convert it before. I don’t get the 775 number in HA only the 1,909.

Are you saying you tried adding scale: 0.1 to the sensor’s configuration and it still reports 775 as 1,909?

No, what I am saying is the PLC code displays 775 in the PLC software. When HA gets the data it thinks it is 1909? Would scaling it get it to 77.5?

I think there’s also a conversion taking place as well (although not quite Celsius to Fahrenheit because 775 C is 1427 F, not 1909 F). That’s why I had asked what is the default unit system metric or imperial.

Anyway, you don’t appear to be interested in my suggestion to experiment with scale, or answer my question, so I’ll step aside. Hopefully someone else will assist you.

Good luck.

I stated the PLC data appeared to be in hexadecimal and the conversion from hex to decimal was the right number. Scaling would not work in that case I would assume. Maybe to move the decimal. I am interested but your not giving me much to work with here. I am not a YAML coder. Default imperial.

Thanks

I’m a volunteer like everyone else here who offers assistance. In fact, I don’t even use the Modbus integration but read its documentation in order to help you. However, I do agree that using scale alone probably won’t produce the result you want. That’s why I am leaving the discussion and hopefully someone with far more Modbus experience will help you.

Blockquote
I’m a volunteer like everyone else here who offers assistance.

I understand. There are also people that don’t know where to look for the answers to the questions you are asking, like myself. I had to dig around.
Thanks for trying anyway.

I posted where to look:

I also supplied a link to the scale option.

FWIW, you might want to investigate the structure option. Unfortunately, there’s only one example of its usage in the documentation but it seems to suggest that you can indicate the data’s type. Sorry I can’t be of more help.

I didn’t know it was in the HA GUI front end. I was looking at YAML file names or coding entrys. I found it after a few post later.

I’ll take a look at structure.

I searched the forum for “Modbus structure” and found this example:

        data_type: custom
        structure: ">1f"

However, you might also need to use the precision option to specify the number of decimal places. Here’s the source of the information (maybe its author can help you):

Not clue about the modbus hex/dec mismatch, but here is a crude way to go back from 1909 (i.e. 0x775) to 775:
Tested in template debugger.

{% set hex = 1909 %}


{% set m = hex // 4096 %}
{% set hex = hex - m *4096 %}
{% set c = hex // 256 %}
{% set hex = hex - c *256 %}
{% set d = hex // 16 %}
{% set u = hex - d *16 %}
{{ m*1000 + c*100 + d*10 + u }}

Blockquote
here is a crude way to go back from 1909 (i.e. 0x775) to 775:

Cool. How do I add that code to be able to display it in the front end?

modbus: 
  - name: hub1
    type: tcp
    host: 192.168.1.243   
    port: 502

    sensors:
      - name: Temp1
        address: 1024      
        data_type: int 
        unit_of_measurement: "°F"

{% set hex = 1909 %}


{% set m = hex // 4096 %}
{% set hex = hex - m *4096 %}
{% set c = hex // 256 %}
{% set hex = hex - c *256 %}
{% set d = hex // 16 %}
{% set u = hex - d *16 %}
{{ m*1000 + c*100 + d*10 + u }}

If you prepend 0x to an integer, the Jinja2 interpreter understands how to convert it to decimal.

Screenshot 2021-07-19 092943

You can’t add it to the Modbus configuration. You would have to create a Template Sensor to convert the value of sensor.temp1 to display the desired value. However, none of that is needed if you can get data_type and structure configured correctly.

I don’t think it’s a matter of format. An int is an int, ie.a bunch of bits at the end of the day. You can display it in base 2, 10 or 16, it’s still the same value.

There is likely a logic error somewhere, maybe in the PLC, that did a wrong conversion.

My reply was in reference to the example you posted containing several lines of calculations in Jinja2. It isn’t necessary to use all of that to make the Jinja2 interpreter convert a hex value into decimal (it just needs a hint that the value is hexadecimal).

As for the Modbus sensor, based on reading related posts, it’s not uncommon to receive hex values that must be converted to decimal. The solution lies in the correct use of the available options (notably data_type and structure) but I can’t experiment with them because I don’t have anything based on Modbus. It should not be necessary to employ a Template Sensor to perform the conversion.

Not sure I follow. How would you convert a decimal 1909 to decimal 775 more simply?

If the sensor reports 775, this prepends 0x to ensure it’s understood to be a hex value and then converted to an integer but informing the int function that it’s dealing with a base 16 (hex) value.

{{ ('0x' ~ states('sensor.temp1')) | int(base=16) }}

In this case, the resulting value needs to be divided by 10 because the original hex value has 1 decimal place.

{{ ('0x' ~ states('sensor.temp1')) | int(base=16) / 10 }}

Further experimentation revealed that I didn’t even need to prepend the 0x because the base=16 in the int function was sufficient.

{{ states('sensor.temp1') | int(base=16) }}

1 Like