Modbus TCP conversion help from two 16 Bit Registers to a single float

6,401 views
Skip to first unread message

nairn.h...@gmail.com

unread,
Apr 18, 2017, 7:42:42 AM4/18/17
to Node-RED
Hi All,

slightly lacking on the programming side in trying to achieve what I need to do with this. Currently using Node-red to interface with various Modbus devices on a Modbus TCP network and getting payload back in the below format:


from the research I have done these are seemingly the contents of two 16 bit registers from the meters in decimal format (High and Low words). In order for it to make any sense to the logged values these two registers need to be combined
and then displayed as a single ieeefloat. After looking some more I located  spreadsheet that's acts as a validation tool to input these two 16bit registers in decimal format which in turn gives me the correct result of 245.48 as the final result.
The spreadsheet works correctly and actually looks to detail the stages of conversion but wondering if anyone had done anything similar to this in a node red function that could be directly used for conversion.

any help or guidance would be greatly appreciated and have attached the spreadsheet with the values already entered to show the example.
ieeefloats.xls
Auto Generated Inline Image 1

steve rickus

unread,
Apr 18, 2017, 9:05:17 AM4/18/17
to Node-RED
I was thinking there is a way to tell the modbus node to read the registers as floating point numbers -- however, it seems that level of control is not exposed in the actual node. And I don't see any pre-built node that will do that for you, but it would be nice to have buffer array conversions built in to something like the change node, maybe...

in the meantime, you can use this code inside a function node:

var rawData = new ArrayBuffer(4);
var intView = new Uint16Array(rawData);
var fltView = new Float32Array(rawData);


intView
[0] = msg.payload[1]; //low
intView
[1] = msg.payload[0]; //high


msg
.payload = fltView[0];
return msg;


I'm guessing you will have many places you would need to do this same conversion (different measured values?). I would assign a different topic to each incoming msg source (i.e. different sensors mapped to modbus addresses) and pass the them all through this one function. Then split out the messages based on the topic, if necessary, rather than copy this node everywhere you need it. Of course, your mileage will vary...
--
Steve

Dave C-J

unread,
Apr 18, 2017, 9:07:26 AM4/18/17
to node...@googlegroups.com
The node-red-contrib-binary node https://www.npmjs.com/package/node-red-contrib-binary can help ... but is a bit specialist in that you need to get to grips with the underlying syntax to specify what you want - but it's really good for parsing out packets of data into various fields.

Nairn Harrison

unread,
Apr 18, 2017, 9:43:45 AM4/18/17
to Node-RED
Steve,

perfect that does exactly what is required can't thank you enough and the speedy reply! is there a way to add a bit of code in that same to reduce to only two decimal places on the msg payload?

many thanks once again
 

steve rickus

unread,
Apr 18, 2017, 10:52:01 AM4/18/17
to Node-RED
It is usually best to do any rounding in the display of the value, not in the calculation/conversion. But not all UI elements have a nice way to specify the display format. In that case, you can use either the javascript Number.toFixed(...) or the Math.round(...) functions. Just be aware that msg.payload.toFixed(2) will convert the payload data type from a number to a string. Here is a good list of methods for manipulating numbers in javascript.

Dave, thanks for the pointer to node-red-contrib-binary node -- i think i searched for 'buffer', 'convert', 'ieee', etc but not 'binary'... *sigh*

So I tried to test it with an input array of two integers, and it seems to fail with that input. The (bare minimum) node description doesn't really give any examples of either inputs or patterns to use, but suggests that the input needs to be a buffer. Is a function node the only way to convert an array of ints to a buffer? If so, then I might as well do the conversion in the function, too. Would buffer support make sense to add as a new feature in the change node? Or do you think it's best to see if the author of the binary node can enhance it?

Dave C-J

unread,
Apr 18, 2017, 11:01:24 AM4/18/17
to node...@googlegroups.com
hmm - yes - being able to "parse" an array may make sense - so may be worth asking - but also as you said as it's only two bytes - not too troublesome to handle in a function.

Nairn Harrison

unread,
Apr 18, 2017, 12:35:16 PM4/18/17
to Node-RED
thanks for the help with this. I found another snippet of code which seemed to do the job, I tried:

var originalval = msg.payload;
var newval = "";
newval = parseFloat(originalval).toFixed(2);
msg.payload = newval;
return msg;

this did as you said and returned a string but when I added the - 0 it seemed to convert it back to a number so therefore rounded to 2 decimal places and it remained a number:

var originalval = msg.payload;
var newval = "";
newval = parseFloat(originalval).toFixed(2) - 0;
msg.payload = newval;
return msg;

again not an expert and don't understand why but it works this way. thanks again!

Colin Law

unread,
Apr 18, 2017, 3:52:31 PM4/18/17
to node...@googlegroups.com
What you have done here is round it to a 2 dec places string and then
convert it back to a floating point number again. This will mostly
work but not all decimal numbers like that can be stored exactly in a
floating point number. Therefore if, for example you start with
314.159 this may convert to string "314.16" but when you convert it
back it may end up as 314.16000001. That is why it is best to convert
when you display it, not earlier. Note I am not saying that that
particular number will behave as I have suggested, but some numbers
will do that.

Colin

>>
>>
> --
> http://nodered.org
>
> Join us on Slack to continue the conversation: http://nodered.org/slack
> ---
> You received this message because you are subscribed to the Google Groups
> "Node-RED" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to node-red+u...@googlegroups.com.
> To post to this group, send email to node...@googlegroups.com.
> Visit this group at https://groups.google.com/group/node-red.
> To view this discussion on the web, visit
> https://groups.google.com/d/msgid/node-red/f1212d87-843f-4814-b761-d9cc310a4bc0%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.

Nairn Harrison

unread,
Apr 19, 2017, 2:51:10 PM4/19/17
to Node-RED

sorry this will be the final request if possible the Modbus TCP node toiled when under pressure with the amount of variables I was pulling back from the server so switched to the alternative "flex getter" which brings the data back in a different format to the original node.

Lack of programming skills let me down badly but this is the return I get from this alternative node:



I would like to do exactly the same as before but with this output I need to apply the same function to the two numbers inside the values array where as before it came back simply as array with the older node. do I simply need to add the word "values:"

I couldn't get it to work.


Nairn 



steve rickus

unread,
Apr 19, 2017, 3:19:03 PM4/19/17
to Node-RED
Well, debug is showing the 'msg.payload' object --
the 'values' field contains the array of the two int16 values (the colon is just for display purposes, like the arrows) --
and from your previous example, the order of the values is  [ low, high ] (little-endian?) --
so you should be able to refer to the high16 as msg.payload.values[1] and the low16 as msg.payload.values[0]

DJ S

unread,
May 25, 2017, 12:29:03 AM5/25/17
to Node-RED
Hi Dave.

Sorry if this is a little of topic :( 
I'm also having some trouble with the above in a different way. Would you have a solution for the following problem.
I'm no programmer and only discovered node red 1 week ago. I thought it would be as simple as routing Modbus in then out. But it appears there has to be some form of function converting the message from one protocol to another. 

I'm reading 2 16bit uint, i have managed to get them into Node red and also sending via MQTT to my broker. Problem is the message is sending a couple of characters and values i don't need and prevent my UI trend graph from reading. for this example my value should be   91919191 as per the picture below.   I'm getting '{"0":91919191}'  this same message is being sent to the broker.
As per the picture below i have already implemented code you have left on other topics that have got me the 32 bit number. 
Could you advise me on how i get rid of the unwanted characters and that zero.



I would love you for 100 thousand years mate.

Dan from down under

Dave C-J

unread,
May 25, 2017, 2:46:14 AM5/25/17
to node...@googlegroups.com
If you attach a debug node to that function output as well. What does it show exactly? From what you describe the message seems to be an object. It may just need to change the function slightly so that
   msg2 = {payload: ui32[0]};

DJ S

unread,
May 25, 2017, 8:48:48 AM5/25/17
to Node-RED
HI Dave.

Just got back in mate.  Ill stick a debug and make changes and get back to you.

Thanks mate


Dan 

DJ S

unread,
May 25, 2017, 8:50:50 AM5/25/17
to Node-RED
David !

You absolute legend.  

Thank you thank you, 

One million loves for you this week pall :) 

Dan 

Dave C-J

unread,
May 25, 2017, 10:46:49 AM5/25/17
to node...@googlegroups.com
No probs. Have a good weekend.

sent from phone

DJ S

unread,
May 30, 2017, 3:51:19 AM5/30/17
to Node-RED
Hi Dave.

I have another problem if you could be so kind to assist ? it is another Modbus issue, as always :( . So have changed MQTT Brokering to Cayenne. one stop shop for Broker and UI control. 
My PLC or should i say the Node does not like the messages it publishes back. The cayenne MQTT node includes a data type layer in the node, i'm not sure if this is altering the payload? I have done what you said in the past and put a Debug and the payload number looks OK.
I have managed find a work around for everything apart from being able to write a value on the MQTT UI and publish it to node red, thus changing the PLC address. Working with 16 Int.
If i bypass the Function node the modbus response quickly flashes with Function code not recognized. If i plug this directly into my other MQTT brother it works fine and received the messages OK.

Here is a picture of the flow. I have opened up the Convert so you can see the code.




Would totally appreciate your tutoring on this matter mate. 
I'm slowly getting an understanding, however my fault finding skills are eons away..

Thanks again

Kind regards

Daniel 

DJ S

unread,
May 30, 2017, 3:57:06 AM5/30/17
to Node-RED
Hi Mate. 

Always the case. I figured it out. I went through each data type in the MQTT node and one worked.

Not really the fault finding methods i want to be a custom to, id love to know what i was doing wrong !.

Cheers again MR.

Dan

Mark Setrem

unread,
May 30, 2017, 4:32:25 AM5/30/17
to Node-RED
According to the screenshot in the debug panel
msg.payload was a number

But your function node was set convert msg.payload.data to a string.

As msg.payload.data didnt exist you got the error message.

DJ S

unread,
Jun 1, 2017, 1:11:14 AM6/1/17
to Node-RED
Thanks Mark. 

I actually removed the Convert block and it worked!   

Cheers for your feedback.

BTW, my comment "Always the case i have figured it out" wasn't a self praise on my behalf. it meant to read, as always you ask for help then ten minutes later the problem was solved.!....Typical!.. 

Thanks again for your help.

Dan 

Nairn Harrison

unread,
Jun 1, 2017, 8:52:13 AM6/1/17
to Node-RED

Hi All, with the electric meters well and truly cracked through previous and much appreciated help! I am now adding heat meters to the project. these are coming in to the system in a slightly different way. After getting the various data sheets from the supplier these registers are listed as example:



Modbus Memory = 56
Size in bytes = 4
contents = Values in integer
Data type = Double word 32 Bit

this is the result from the debug output from the device:


would anyone be able to advise me of how to alter this typical function but to work with the now incoming message:

var rawData = new ArrayBuffer(4);
var intView = new Uint16Array(rawData);
var fltView = new Float32Array(rawData);
intView[0] = msg.payload[1]; //low
intView[1] = msg.payload[0]; //high
msg.payload = parseFloat(fltView[0].toFixed(1));
msg.topic = "TempT1";
node.status({fill:"blue",shape:"ring",text:msg.topic + ":" + msg.payload});   
return msg;


I'm assuming that because I now have 4 objects in the array the above would not work as the electric meters did where only two objects came back.

any assistance would be appreciated. Should have taken Java programming course when I had the chance!

Nairn



 
Reply all
Reply to author
Forward
0 new messages