Sparkplug B (plus) with Node-RED

Context:

In the past I’ve written up several posts about working with Sparkplug B messages in Node-RED. Even though they’re easy to read in with the built-in MQTT core node, decoding them is the tricky part (see this thread: Decode Sparkplug Encoded MQTT Messages with Node-RED).
When it comes to sending messages, that gets a bit harder, but is doable – see this thread for that side of things: Sending MQTT Sparkplug B control messages from Node-RED

But Sparkplug B compliance involves much more than just reading and writing protobuf encoded MQTT messages, you can read the full specifications, but the important things are state management, message types, and payload format.

There has been a node-red-contrib-sparkplug package for Node-RED, but it’s always been “rather difficult” to set up and use. Yes, that’s a figure-8 going out and back into the Sparkplug node…

But thankfully there’s a “new” node package out that makes the whole process much easier, and within just a few minutes you can turn any Node-RED instance into a Sparkplug B compliant device with node-red-contrib-mqtt-sparkplug-plus.

The important stuff:

After installing the sparkplug-plus node package it’s super trivial to get started, and everything you need to know is in the help tab for the node. Just drop an instance of the “mqtt sparkplug device” node in, add your broker to the configuration, then head over to the “help” tab with the node highlighted.

image

You have two options for setting your metrics, you can either hardcode whatever you want to send in the Node configuration yourself, or you can dynamically set them with a msg.definition property any time. The birth, command, and death messages are all handled by the node, and the msg.payload “metrics” array is what triggers your data to be published.

image

You can create subtopics by using a / in the topic, just like with non-Sparkplug MQTT messages, just follow the same payload format they describe:

msg.payload = {
    "metrics": [
        {
            "name": "testing/test1",
            // Timestamp  is optional.
            "timestamp" : new Date(),
            "value": 11
        },
        {
            "name": "testing/test2",
            "value": 12
        }
    ]
};

And… that’s it! Using this node I was able to get some data up to my HiveMQ broker (although any broker would work) in just a few minutes. To close the loop and see my data, I decided to bring it open in my Ignition tag browser along side the data coming from my groov EPIC via the built-in Sparkplug B client.

The tags are complete with health and other useful metadata:

image

This is just a quick “getting started” and PSA about the existence of this package, but also be aware that you can easily use it to read in and decode Sparkplug B messages without needing to go out and download the protobuf file. Here’s some data from my RIO in the debug pane in Node-RED, via the Sparkplug B client built right into groov Manage (note that I have to provide user credentials that are on the ACL (access control list) for this topic namespace):

image


If you end up using this in your application, let us know in the thread below! And as always, happy flowing.

3 Likes

Yes, this node is very easy. We have been using this node for a while now to send data to our Mosquitto broker to forward to our Canary historian.

1 Like

That’s awesome! Thankyou for sharing a screenshot. This node package is definitely the way to go with Sparkplug on Node-RED.

1 Like

Hey All, I’ve been testing out using this block but the block seems to be simultaneously sending out NDEATH,NBIRTH,DBIRTH, and DDEATH commands when I deploy my flow. If I sent data directly to an MQTT Sparkplug Out node I see the data going through the broker but I can’t read it in Ignition because it can’t find the active birth certificate.

Anyone else seen this happen before?

Thanks!

Hello,

I was trying to install this node but I got this error message:
npm ERR! code ENOTSUP
npm ERR! notsup Unsupported engine for mqtt@5.3.6: wanted: {“node”:“>=16.0.0”} (current: {“node”:“14.20.0”,“npm”:“6.14.17”})
npm ERR! notsup Not compatible with your version of node/npm: mqtt@5.3.6
npm ERR! notsup Not compatible with your version of node/npm: mqtt@5.3.6
npm ERR! notsup Required: {“node”:“>=16.0.0”}
npm ERR! notsup Actual: {“npm”:“6.14.17”,“node”:“14.20.0”}

npm ERR! A complete log of this run can be found in:
npm ERR! /home/dev/.npm/_logs/2024-02-29T12_24_33_714Z-debug.log

Do you have any idea to get this fixed?

The latest version of the node-red-contrib-mqtt-sparkplug-plus (V2.x) can’t be installed on RIO or EPIC, because it needs a newer version of Node.js. groov systems are running on Node.js Version 14.2 and the latest Sparkplug Plus Node needs a more current version.

You can install an earlier version 1.4.1 that will install proper.

To get it installed, go to the manage “install nodes” interface from groov Manage Home → Node-RED → Advanced: Node Management, it looks like this…

Once you’re there put in the module name node-red-contrib-mqtt-sparkplug-plus and put in just 1.4.1 for the version number, since 1.4.1 is the lowest version available for this package. For me this installed the module without any errors.

1 Like

Thank you! It worked.

1 Like

Hi,

How would the function node be if I want to send data from the IO board (Analog Input, Discrete Input/Output) from a RIO device to this MQTT Sparkplug device? I need to send data to our broker to forward to our InfluxDB historian no matter if they have changed their state or not.

Is the I/O data from the groov RIO I/O channels? Then you can use the integrated MQTT Sparkplug B client.

This video, starting from minute 28:05 is showing step by step setup of the native MQTT client to publish to a HiveMQ MQTT broker and from the to y Grafana Dashboard via a InfluxDB database.

MQTT publishes on change. Not sure how to get it publishing in a time interval.

The current status on the broker from a value is the real time status. So the subscriber is getting the latest state, no matter when it was published. For analog values you can set a deadband, which is a value change threshoöd for an MQTT publication.

I have tried the native MQTT Client. It works great but I need the time interval publishing. I have this sensor that does not change it status even in 0.0 deadband (I also tried 0.000000) and a couple of buttons (leds or on/off switches) that I need to monitor them even if they remain activated (or not). For the temperature sensor I have installed works fine because it always change so the data is published (almost every 2 seconds).

What would you recommend to have the data “polled” every “X” seconds/minutes?

@pbg One of the most powerful features of Sparkplug is to maintain state.
There is a tiny (few bytes at most) heartbeat packet sent from the RIO (using its native SparkplugB data service - this might not be the case for Node-RED (@torchard lets setup Wireshark and look for it to confirm), but I know for a fact that the RIO & EPIC native data server SparkplugB implementation (which are one of the very few PLC’s to have SPB3 certification) do send that heart beat packet.
My point there is that if a digital point does not change, you still know its good and the state of the point is what InfuxDB is showing.

So, with that said, it sounds like you are wanting to publish on a time period to force InfluxDB to put periodic time stamps on the data?
If this is the case, you can dig into the InfluxDB manual as I believe there is a way to force that ‘heart beat’ timestamp on unchanging data from within Influx itself.

2 Likes

Thank you for your help. I will do as you recommend and check if I can force the timestamp from Influx.

Hello,

I think I almost have it. I still don’t know how to write the function block. How would the code look like for this example?

@torchard is just getting some sample code together for you, but 2 posts back you were using the native Sparkplug Data Service in groov Manage which is the correct thing to do.

Why move back to Node-RED?

Have you tested the format that they provide in the node documentation?

The timestamp block in Node-RED allows to send data even if it hasn’t changed its status or value. I am using a lot of booleans and sensors (like water level or piston positions) that don’t change their values very often but still need to be monitored.

The function block is the one that converts the input data into Sparkplug data but I am still trying to write the object/array format for the metrics correctly.

That is the format I am testing but I don’t know how to change the “test” for my metric that is comming from an MMP address (AI channel or Scratchpad). For example I have “pistonposition1” from the AI Channel 3 as a float so I am using this IO groove read block. How would be the function block properly written?

Regarding the sending of data that hasn’t changed it’s value, that is going to create redundant network traffic for no actual gain in data, which MQTT and Sparkplug were created specifically to avoid.
Did you look into the InfluxDB heartbeat timestamp options that @Beno mentioned?


To answer your specific question for this flow, simply use the property sent out of the groov read inside the metrics object. By default groov read sends out the value on msg.payload

        {
            "name": "pistonposition1",
            "value": msg.payload
        }