I just wanted to share a simple way to parse and decode a binary stream of data using the npm module binary-parser-encoder. This is an alternative to the node-red-contrib-binary node
1- Install the module , in my case in Windows by typing:
npm install binary-parser
2- Amend settings.js config file by adding the binary_parser
line as shown below
functionGlobalContext: {
jsonata:require('jsonata'),
moment:require('moment'),
chance:require('chance'),
binary_parser:require('binary-parser').Parser,
Note that the module name uses a, hyphen
in the name whereas the property name in functionGlobalContext
will use underscore
3- Start (or restart) Node-RED. Hopefully there will be no errors, otherwise the initialization will not proceed( in which case you need to fallback and analyse).
Using the module.
As usual in Node-RED you need to use global.get
to require the module. See below how the function node will looks like. This will be the core of the flow. I am using it here to decode a proprietary packet format for a Lidar scanner (but using random test data).
var Packet = global.get('binary_parser');
var buf = msg.payload;
var typea = new Packet()
.endianess("little")
.uint16("magic")
.uint16("packet_type")
.uint32("packet_size")
.uint16("header_size")
.uint16("scan_number")
.uint16("packet_number")
.double("timestamp_raw")
.double("timestamp_syncw")
.uint32("status_flags")
.uint32("scan_frequencye")
.uint16("num_points_scan")
.uint16("num_points_packet")
.uint16("first_index")
.uint32("first_angle")
.uint32("angular_increment")
.uint32("iq_input")
.uint32("iq_overload")
.double("iq_timestamp_raw")
.double("iq_timestamp_sync")
.uint8("header_padding");
msg.payload = typea.parse(buf);
return msg;
Flow:
[{"id":"16dd9094.027e5f","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"c899e7c.87f7618","type":"inject","z":"16dd9094.027e5f","name":"Go","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":180,"wires":[["1ccf13aa.f5370c"]]},{"id":"1ccf13aa.f5370c","type":"function","z":"16dd9094.027e5f","name":"Dataset Buffer","func":"msg.payload = Buffer.from([0xa2, 0x5c, 0x01, 0x80, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x10, 0x02, 0x10, 0x03, 0x80, 0x02, 0x10, 0x02, 0x10, 0x02, 0x15, 0x95, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x10, 0x02, 0x10, 0x03, 0x01, 0x80, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x10, 0x02, 0x10, 0x03, 0x80, 0x02, 0x10, 0x02, 0x10, 0x02, 0x15, 0x95, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x10, 0x02, 0x10, 0x03, 0x03, 0x80, 0x02, 0x10, 0x02, 0x10, 0x02, 0x15, 0x95, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x10, 0x02, 0x10, 0x03, 0x01, 0x80, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x10, 0x02, 0x10, 0x03, 0x80, 0x02, 0x10, 0x02, 0x10, 0x02, 0x15, 0x95, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x10, 0x02, 0x10, 0x03]);\nreturn msg;","outputs":1,"noerr":0,"x":320,"y":180,"wires":[["7df2c612.8102a8"]]},{"id":"7df2c612.8102a8","type":"function","z":"16dd9094.027e5f","name":"binary_parser","func":"var Packet = global.get('binary_parser');\nvar buf = msg.payload;\n\nvar typea = new Packet()\n .endianess(\"little\")\n .uint16(\"magic\")\n .uint16(\"packet_type\")\n .uint32(\"packet_size\")\n .uint16(\"header_size\")\n .uint16(\"scan_number\")\n .uint16(\"packet_number\")\n .double(\"timestamp_raw\")\n .double(\"timestamp_syncw\")\n .uint32(\"status_flags\")\n .uint32(\"scan_frequencye\")\n .uint16(\"num_points_scan\")\n .uint16(\"num_points_packet\")\n .uint16(\"first_index\")\n .uint32(\"first_angle\")\n .uint32(\"angular_increment\")\n .uint32(\"iq_input\")\n .uint32(\"iq_overload\")\n .double(\"iq_timestamp_raw\")\n .double(\"iq_timestamp_sync\")\n .uint8(\"header_padding\");\n \nmsg.payload = typea.parse(buf);\nreturn msg;\n ","outputs":1,"noerr":0,"x":520,"y":180,"wires":[["92b3fe6d.84a35"]]},{"id":"92b3fe6d.84a35","type":"debug","z":"16dd9094.027e5f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":690,"y":180,"wires":[]}]
As the binary data structure is fixed the flow will be quite simple.