Payload's number to two 16 bit register to be send via modbus

Hi!

I need help width, how to split/create two 16 bit from payload to be send via modbus leater.

We get payload value trough kplex and tcp in node, leater use switch node to take those rows we need.
in GPRMC function node we split it to messages, json node convert lat to number...
now after that we need to get rid of dot in number 59.3234917 to 593234917 then get it to 2 modbus registers (16bit) 4 byte array which we can feed modbus. Reason to get rid of dot is if we use like float latitude not going to be so exact coz float will round numbers.

19/10/2018, 10:59:09node: e8e2ab9e.6a8748
msg.payload : Object
object
lat: 59.3234917
lon: "18.1840927"

Feed from tcp in looks like

19/10/2018, 10:58:44node: cd9ba9b2.09dd28
msg.payload : string[66]
"$GPRMC,085844.00,A,5932.80963,N,018.60482,E,0.026,,191018,,,A*7A"

in attached flow is function to 2 UInt16 witch contain code I found both to read and create float registers

[{"id":"9b787397.f9a65","type":"tcp in","z":"df4f10d2.533e3","name":"","server":"client","host":"192.168.10.144","port":"10110","datamode":"stream","datatype":"utf8","newline":"\r\n","topic":"","base64":false,"x":157,"y":157,"wires":[["becb5746.5aea38"]]},{"id":"f8588990.06deb8","type":"debug","z":"df4f10d2.533e3","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":1157,"y":156,"wires":},{"id":"becb5746.5aea38","type":"switch","z":"df4f10d2.533e3","name":"","property":"payload","propertyType":"msg","rules":[{"t":"cont","v":"$GPRMC","vt":"str"},{"t":"cont","v":"$GPGGA","vt":"str"},{"t":"cont","v":"$SDDBT","vt":"str"},{"t":"cont","v":"$SDMTW","vt":"str"}],"checkall":"true","repair":false,"outputs":4,"x":395,"y":155,"wires":[["9e22f267.aca55","f8588990.06deb8"],["f8588990.06deb8"],["f8588990.06deb8"],["f8588990.06deb8"]]},{"id":"71b6fba0.c710b4","type":"function","z":"df4f10d2.533e3","name":"to 2 Uint16 ","func":"var rawData = new ArrayBuffer(4);\nvar intView = new Uint16Array(rawData);\nvar fltView = new Float32Array(rawData);\n\nintView[0] = msg.payload[1]; //low\nintView[1] = msg.payload[0]; //high\n\nmsg.payload = parseFloat(fltView[0].toFixed(1));\nmsg.topic = "to 2 Uint16";\n\nnode.status({fill:"blue",shape:"ring",text:msg.topic + ":" + msg.payload}); \n\nreturn msg;\n//The above code expects a 4 byte array \n//which is 2 Modbus registers\n//from node.js - Convert 32bit unsigned "Real" data type (splitted into two 16 bit signed words) to javascript - Stack Overflow uint16ToFloat32(low, high) {\n var buffer = new ArrayBuffer(4);\n var intView = new Uint16Array(buffer);\n var floatView = new Float32Array(buffer);\n\n intView[0] = low;\n intView[1] = high;\n return floatView[0];\n}\n\nfunction float32ToUint16(value) {\n var buffer = new ArrayBuffer(4);\n var intView = new Uint16Array(buffer);\n var floatView = new Float32Array(buffer);\n\n floatView[0] = value;\n return [intView[0], intView[1]];\n}\n\nconsole.log("Converted ints to", uint16ToFloat32(18584, 18081));\nconsole.log("Converted float to", float32ToUint16(20644.297));\n*/","outputs":1,"noerr":0,"x":936,"y":261,"wires":[["fc550a63.065c68"]]},{"id":"9e22f267.aca55","type":"function","z":"df4f10d2.533e3","name":"GPRMC","func":"//Example of data received:\n//"$GPRMC,071723.00,A,5914.81006,N,01816.64371,E,0.006,,191018,,,A*7D"\n\n\nmessage0 = {};\nmessage1 = {};\nmessage2 = {};\nmessage3 = {};\nmessage4 = {};\nmessage5 = {};\nmessage6 = {};\nmessage7 = {};\nmessage8 = {};\nmessage9 = {};\nmessage10 = {};\nmessage11 = {};\nvar symblat = "";\nvar symblong = "";\n\n//Split the 12 NMEA data into 12 messages\nmsg.string = msg.payload.split(",");\n\n//Find if the data gps fix is valid \nif (msg.string[2] == "A") {\n \n//Find if latitude is south and add minus\nif (msg.string[4] == "S") {\nsymblat = "-";\n} else {\n\n}\n\n//Find if longitude is west and add minus\nif (msg.string[6] == "W") {\nsymblong = "-";\n} else {\n\n}\n\nvar nvals = msg.payload.match(/,(\d+)(\d\d\.\d+),(N|S)/);\nvar evals = msg.payload.match(/,(\d+)(\d\d\.\d+),(E|W)/);\nmsg.payload = {\nlat: symblat + (+nvals[1] + nvals[2]/60).toFixed(7),\nlon: symblong + (+evals[1] + evals[2]/60).toFixed(7)\n\n}}\n\nreturn [msg];","outputs":1,"noerr":0,"x":644,"y":92,"wires":[["f8588990.06deb8","eaa9f38e.e839c"]]},{"id":"eaa9f38e.e839c","type":"json","z":"df4f10d2.533e3","name":"","property":"payload.lat","action":"","pretty":true,"x":923.5,"y":85,"wires":[["fc550a63.065c68","71b6fba0.c710b4"]]},{"id":"fc550a63.065c68","type":"debug","z":"df4f10d2.533e3","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":1273,"y":84,"wires":}]

/IQAPPS

Suppose the number was 59.320 would you convert that to 59320? If so how do you know where the point is supposed to be?

Be careful here... GPRMC format is DDMM.MMMMM for latitude and DDDMM.MMMMM for longitude... ie it's not just decimal - so yes you can remove the decimal point - BUT are you sure that's what you want ???

Yeah good question maybe is easier to use it like float even if it's roundet location gooing to be enough accurate...

Yes I know.... U are right guys.
My troughs was to doit easy in flow but its complete wrong...
We take like float....

you must absolutely NOT use it like a float... you will be a long way out. You need to parse GPRMC properly to split off the DD and DDD parts from the MM.MMMMM and then do the maths to then create a proper float.

In GPRMC function node we do allready that math to get like "Google maps format of latitude"
How we split that....split node? Or?
I do not understand how to take out eg lat from payload for next step!

19/10/2018, 10:59:09node: e8e2ab9e.6a8748
msg.payload : Object
object
lat: 59.3234917
lon: "18.1840927"

msg.payload.lat

It seems odd that you have converted the lat to a number but the lon is a string. I suspect that is not intentional.

I think it probably will be accurate enough. Floats in javascript (actually all numbers in js) are 64 bit floating point which I estimate should give you a maximum error of about 10^-14 degrees which is about .00001 mm. I doubt if your GPS is that accurate.

Yes agree gps is not that acurrate

That was only test to test conversions....

Of course if you just add a msg.name to the object you then feed it direct to a worldmap node to see/check its in the right place

May be you can use this method convert float to int16 value.

let base = new ArrayBuffer(4); //4 bytes
let int16_view = new Int16Array(base);
let float32_view = new Float32Array(base);

float32_view[0] = msg.payload;

let rValue = [];
rValue.push(int16_view[0]);
rValue.push(int16_view[1]);

msg.payload = rValue;

this is example for node-red

[{"id":"2a79d0f3.318d3","type":"function","z":"6dd3fb7d.264d44","name":"float32 -> int16","func":"let base = new ArrayBuffer(4); //4 bytes\nlet int16_view = new Int16Array(base);\nlet float32_view = new Float32Array(base);\n\nfloat32_view[0] = msg.payload;\n\nlet rValue = [];\nrValue.push(int16_view[0]);\nrValue.push(int16_view[1]);\n\nmsg.payload = rValue;\n\nreturn msg;","outputs":1,"noerr":0,"x":530,"y":380,"wires":[["46d2875c.9da9e8","4874a5f0.1de0dc"]]},{"id":"8f55454e.8ce888","type":"inject","z":"6dd3fb7d.264d44","name":"","topic":"","payload":"0.5","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":400,"wires":[["64b0d21e.d3996c"]]},{"id":"33a7dcdd.dff8f4","type":"inject","z":"6dd3fb7d.264d44","name":"","topic":"","payload":"1.0","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":340,"wires":[["64b0d21e.d3996c"]]},{"id":"53bca83.01fdb58","type":"inject","z":"6dd3fb7d.264d44","name":"","topic":"","payload":"0.25","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":460,"wires":[["64b0d21e.d3996c"]]},{"id":"6b0b8b9.0e44774","type":"inject","z":"6dd3fb7d.264d44","name":"","topic":"","payload":"12345.67891234567","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":200,"y":520,"wires":[["64b0d21e.d3996c"]]},{"id":"46d2875c.9da9e8","type":"debug","z":"6dd3fb7d.264d44","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":760,"y":380,"wires":[]},{"id":"64b0d21e.d3996c","type":"function","z":"6dd3fb7d.264d44","name":"link","func":"\nreturn msg;","outputs":1,"noerr":0,"x":330,"y":380,"wires":[["2a79d0f3.318d3"]]},{"id":"acabf386.47cdf","type":"debug","z":"6dd3fb7d.264d44","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":760,"y":440,"wires":[]},{"id":"4874a5f0.1de0dc","type":"function","z":"6dd3fb7d.264d44","name":"int16 -> float32","func":"let base = new ArrayBuffer(4); //4 bytes\nlet int16_view = new Int16Array(base);\nlet float32_view = new Float32Array(base);\n\nint16_view[0] = msg.payload[0];\nint16_view[1] = msg.payload[1];\n\nmsg.payload = float32_view[0];\n\nreturn msg;","outputs":1,"noerr":0,"x":530,"y":440,"wires":[["acabf386.47cdf"]]}]

Thanks
I used already similar code.... Gooing to post flow here soon...

I am trying to pass a value greater that 65535 via the 'modbus-write' node. Using the above, I have had mixed success with values in the range of 104000 - 106000 noticing that sometimes my array values are negative. For example running this code on 105902 I receive [-10496,18382]. I therefore receive an error from the Node that the Value is out of Range. Any suggestions? Is this code not intended for my application if I don't have a Floating Point number entering the 'Function' node?

Thanks

Post code tomorrow, and u can use some modbus app like poll modbus to check if u get right value, and value in modbus can be negative....

Hi !
U can use this code in function....after connect it to Modbus Flex Write

function float32ToUint16(value) {  
  var buffer = new ArrayBuffer(4);
  var intView = new Uint16Array(buffer);
  var floatView = new Float32Array(buffer);

  floatView[0] = value;
 

  return [intView[0], intView[1]];
}

//var incoming = JSON.parse(msg.payload);

//Compose ModBus message (Holding Registers) READ/WRITE
var modbusHR = {
 payload : {
 'value' : [
 float32ToUint16(msg.payload)[0], 
 float32ToUint16(msg.payload)[1],
 ],
 'fc' : 16,
 'unitid' : 1,
 'address' : 4,
 'quantity' : 2
 }
};

node.send([modbusHR]);
1 Like

This worked excellent. I was not as familiar with the Flex Writer so I did a little more separation with another function block. I am sure this could have been done in your function if I was more versed. This is my flow at this point:

[{"id":"57448280.59b23c","type":"inject","z":"2a594c0.9497fb4","name":"","topic":"","payload":"105902.25","payloadType":"num","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":440,"y":540,"wires":[["b69c1522.978948"]]},{"id":"b69c1522.978948","type":"function","z":"2a594c0.9497fb4","name":"To Modbus","func":"function float32ToUint16(value) {  \n  var buffer = new ArrayBuffer(4);\n  var intView = new Uint16Array(buffer);\n  var floatView = new Float32Array(buffer);\n\n  floatView[0] = value;\n \n\n  return [intView[0], intView[1]];\n}\n\n//var incoming = JSON.parse(msg.payload);\n\n//Compose ModBus message (Holding Registers) READ/WRITE\nvar modbusHR = {\n payload : {\n 'value' : [\n float32ToUint16(msg.payload)[0], \n float32ToUint16(msg.payload)[1],\n ],\n 'fc' : 16,\n 'unitid' : 1,\n 'address' : 4,\n 'quantity' : 2\n }\n};\n\n\n\n\nnode.send([modbusHR]);","outputs":1,"noerr":0,"x":670,"y":540,"wires":[["c8b3c4bb.93d7f8","ebd797bc.086fe8"]]},{"id":"dba49e67.7d15","type":"debug","z":"2a594c0.9497fb4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1070,"y":500,"wires":[]},{"id":"c8b3c4bb.93d7f8","type":"function","z":"2a594c0.9497fb4","name":"link","func":"var newMsg = msg.payload.value[0];\n\nmsg.payload = newMsg;\nreturn msg;\n\n\n","outputs":1,"noerr":0,"x":910,"y":500,"wires":[["dba49e67.7d15"]]},{"id":"ebd797bc.086fe8","type":"function","z":"2a594c0.9497fb4","name":"link","func":"var newMsg = msg.payload.value[1];\n\nmsg.payload = newMsg;\nreturn msg;\n\n\n","outputs":1,"noerr":0,"x":910,"y":580,"wires":[["d4f99d8b.07249"]]},{"id":"d4f99d8b.07249","type":"debug","z":"2a594c0.9497fb4","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":1070,"y":580,"wires":[]}]

Thank you for the help.

1 Like