Decode Sparkplug Encoded MQTT Messages with Node-RED

If you want a Sparkplug-compliant client to read in data two of the best options out there are Ignition Edge and MQTT.fx, but sometimes I need something that lies between these two applications — this is where Node-RED comes in.

Using Node-RED it’s possible to read in and decode Sparkplug messages right in your flow, then you can easily move that data into any other Node-RED endpoint, whether it’s groov View, a database, or even cloud analytic software.

Using this method Node-RED does not become a Sparkplug-compliant device, it is just a data consumer subscribed to an MQTT broker, but sometimes that’s all you need.
Here is the flow:

The blue node in the middle is a decoding node from the node-red-contrib-protobuf package that you can easily install through the pallet manager.

This node also requires the path to a *.proto file, which you can get for Sparkplug directly form the Eclipse Tahu project GitHub. The specific file you need is sparkplug_b.proto, which you can download from GitHub directly or just wget the raw file if you have SSH access.
If you do save it, make sure it’s a *.proto file and not a *.txt file.

For convenience, if you’re using groov EPIC you can just download the file attached to end of this forum post and move it into the unsecured file area through the groov Manage System menu.

Once the file is on your device make note of the full path to the file and put that into the decode node’s Proto File field, and set the protobuf type to “Payload”.

image

With that done you just need to set up your broker and put in the topic for some Sparkplug message, for example:

image

If you don’t know what your message topic looks like, download MQTT.fx, set up your broker, connect, change the “payload decoded by” to use the Sparkplug decoder, and then you can use the “topic collector” in the subscription window to find the topic you want.

Here I’m getting the “test_int32” publicly-readable numeric variable from the strategy running on my EPIC, as well as some useful meta data, all automatically split up into JSON:

image

Import the flow text below to try it out for yourself, and as always, happy coding!

sparkplug_b.proto.zip (2.3 KB)

[{"id":"199c087.0db14f8","type":"decode","z":"65e0d8c3.e0ef98","name":"sparkplug_b","protofile":"48ca2c90.fda3c4","protoType":"Payload","x":370,"y":360,"wires":[["f06989ae.5e6c68"]]},{"id":"f06989ae.5e6c68","type":"debug","z":"65e0d8c3.e0ef98","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":510,"y":360,"wires":[]},{"id":"ad848b2b.490b28","type":"mqtt in","z":"65e0d8c3.e0ef98","name":"groovEPIC_dev","topic":"spBv1.0/Opto22/+/groovEPIC_developer/#","qos":"2","broker":"fcec5047.03e36","x":200,"y":360,"wires":[["199c087.0db14f8"]]},{"id":"48ca2c90.fda3c4","type":"protobuf-file","z":"","protopath":"/home/dev/unsecured/sparkplug_b.proto"},{"id":"fcec5047.03e36","type":"mqtt-broker","z":"","name":"Chariot","broker":"chariot.server.com","port":"8883","tls":"","clientid":"","usetls":true,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

2 Likes

Hi Terry,

Have you used the encode part with protobuf to encode an outbound sparkplug_b message? I am using the encode function with the protobuf and the message I am getting from debug is: No protobuf type supplied!

I have a function that I built, and I am researching, but am not getting too far with encoding properly.
Not asking to fix, just any tips if you have.

My flow is enclosed.

[{"id":"8b8f8d4.5166a7","type":"inject","z":"f10784d0.015068","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":360,"y":720,"wires":[["af27afd3.3c2d2"]]},{"id":"29f44c8e.711f34","type":"mqtt out","z":"f10784d0.015068","name":"","topic":"","qos":"","retain":"","broker":"1008ff7a.33cd81","x":990,"y":720,"wires":[]},{"id":"af27afd3.3c2d2","type":"function","z":"f10784d0.015068","name":"","func":"var group_id = \"NetDNA\";       // Put Sparkplug group ID here\n\nvar edge_node_id = \"352656100597502\"; // Put Sparkplug edge node ID here\n\nmsg.qos = 1;\n\nmsg.retain = false;\n\nmsg.topic = \"spBv1.0/\" + group_id + \"/NCMD/\" + edge_node_id;\n\n       \n\nvar dout1_metric = {\n\nname: \"Dev1/DOUT1\",\n\ntimestamp: Date.now(),\n\ndatatype: 11,\n\nbooleanValue: true\n\n};\n\n \n\nvar payload = {\n\ntimestamp: Date.now(),\n\nseq: -1,\n\nmetrics: [dout1_metric]\n\n};\n\n \n\nmsg.payload = payload;\n\nreturn msg;","outputs":1,"noerr":0,"x":590,"y":720,"wires":[["5ac3043f.78453c","146daae5.d89fb5"]]},{"id":"5ac3043f.78453c","type":"debug","z":"f10784d0.015068","name":"send","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","x":890,"y":780,"wires":[]},{"id":"146daae5.d89fb5","type":"encode","z":"f10784d0.015068","name":"encode","protofile":"2693e25a.9357de","protoType":"","x":800,"y":720,"wires":[["29f44c8e.711f34","5ac3043f.78453c"]]},{"id":"1008ff7a.33cd81","type":"mqtt-broker","z":"","name":"Chariot","broker":"postman.cloudmqtt.com","port":"13650","tls":"","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"2693e25a.9357de","type":"protobuf-file","z":"","protopath":"/home/dev/unsecured/sparkplug_b.proto"}]

My mistake, I looked at the original code and realized i didn’t put Payload in the type field in protobuf node.

Thanks,
Dan

1 Like

2 posts were merged into an existing topic: Accessing where Palletes are stored on a AR1