Newbie: convert modbus float [array] register to float value

I have problems to convert float and 32b (signed/unsigned) registers to obtain a result able to be sent to MQTT broker. The modbus device is a semaphore/Servelec Tbox unit. I use node-red modbustcp

Example: I have a float register with a value of 22.362 in it. I want to pass that value to my MQTT broker.
When I debug in node-red, I obtain an array: [16818,58720] but I'm stuck to convert this to a readable precise value of 22.362 (22.36 will be ok)

I have no probs with int16 registers (because they are 16bits obviously but need to find a function to convert negative values to readable ones)
I found here a function but the result seems weird (imprecise).

*Edit: I made my test simple. That's the reason I set the value of 22.362 into register 22362 ! Easier to remember

See this thread (read it all) there are some pointers in there that might help.

Hi Steve and thank you for the link.
It seems that for me the code is not that difficult (...) because values on the array are not hex but plain decimal.
With my first test, I put readFloatBE() and I obtained a usable value.
However, very strange to me, the result varies with every poll (even the data in the register is fixed for my test). Stranger, with 2 polls with the same code, I obtain 2 sets of variable values... The value set in my modbus register is 35.174 and I have the right value (35.17) only once in a while.

*Edit: here is the code I put. Ok, I'm a real newbie and I added what I found around here. I really don't know why those 2 lines are needed, but without: no results.

const buf = Buffer.allocUnsafe(4);
buf.writeUInt16BE(pay[0]); // high byte

*Edit2: it seems, for unknown reasons, that LE displays now "correct" values (aka 0 because wrong type) instead of a replicated one. So Big Endian seems +/- ok, except the variability.

How is it possible ?
Thank you++

I suspect when you update msg.payload in function 1 you are affecting the object before it's processed in the 2nd function.

It looks looks like the payload is an array/object so I world probably try returning a new object instead of updating msg.payload e.g...

var msg2 = {
  payload : buf.readFloatBE(0)
return msg2;

Try to remember the asynchronous nature of node-red and how object references work in JavaScript.

Can't really help more unless you post your flow and sample data.

Assuming this is in a recent version of node-red (@PPz which version is it?) then I don't think that should happen as node-red should automatically clone the message for the second wire out of the node.
@PPz in the functions use node.warn() to show the contents of pay after you have picked it up.

thank you both of you (@Steve-Mcl) for your kind help.
My version is 1.0.3

the code of the function is :

let pay = msg.payload;
const buf = Buffer.allocUnsafe(4); // (4) is ok
buf.writeUInt16BE(pay[0]); // high byte
msg.payload = buf.readFloatBE(0);
// 2 numbers after comma (string)
// msg.payload = buf.readFloatBE(0).toFixed(2);
return msg;

the float value into my plc is 22.362
the raw received data is an array [16818,58720]
the result (after function) varies from 22.25 to 22.37

about node.warn(), I'm affraid that I'm a real newbie about node-red

Have you tried searching for it?

Yes, but when I inject
I receive exactly the same array value [16818,58720] as the raw debug message

Update: when I inject node.warn(buf), I obtain a splitted array like this one: [65,178,98,5], or [65,178,125,3], or [65,178,0,0] (variable). Ok, 16818 = (256*65)+178

Update 2: for what I understand here, the 2 first values of the array [65,178] seems consistent and represent probably/certainly the value that doesn't change (22). Why the others are changing ? A real mystery for me.

Update 3: maybe it will help you, but the weird thing about my plc is that the right value is in the array when dealing with 32b registers. If I poll a 32b register with a value of 22358, the node-red poll send me an array with [0,22358]. So it seems that's even not a hex value, just the plain decimal value...

You are allocating a buffer of length 4 bytes. Then you use writeUInt16BE to write to the first two bytes. You have not written anything to the second two bytes so they are left with whatever happened to be lying around in that bit of memory. That is why they contain variable data. You then use readFloat to read all four bytes to give you the (veriable) result.

tagging @Iqapps

Thank you. I need to dig more about this and I will probably come come back later with this. For you, it's probably a piece of cake. Not that obvious for me (must read some doc. I usually learn while have a problem to solve).

For the moment, I simplified my problem with my PLC (that I know far better): I created a bunch of 16b variables and made a small pgm (into my plc) to convert some floats registers into int16 (float*100) and I transform them into floats (int16/100) to obtain a nice result with node-red and passing the values to mqtt and hubitat [shame on me).
Because my main problem was to harvest modbus data and send them to Hubitat. Learn Hubitat, MQTT apps, mosquitto and Node-red at the same time is a little bit too much for me (I'm an old guy).

Thank you again for your answers.


Here is little more information about how modbus handle 32 bit....

1 Like

tagging @Colin

thanks for the doc and the help.
I discovered more and more and simplified a lot.
Build on my plc a set of int16 registers.
at first, I defined different polls, but the target plc was not happy of that (poll errors)
Like in modbus devices, I set now 1 poll with successive addresses :slight_smile:
Far better !
But now, with that method, I loose my different topic names for my MQTT broker (before: 1 topic per poll, now: 1 topic only because 1 poll)

Is it a way to define an array of topics when polling once and/or functions to build a specific topic for each extracted value ?



In the Change nodes where you extract the individual parts add another row that Sets msg.topic To the/topic/for/this/value

1 Like

Mucho Perfecto ! And much faster too...