Skip to Content

Sparkplug B

Sparkplug B is MQTT — it does not replace the CMS MQTT integration, it is a specification that runs on top of it (MQTT 3.1.1), filling in what MQTT deliberately leaves open:

Plain MQTT (MQTT_TOPIC_SUBSCRIBER)Sparkplug B (SPARKPLUG_SUBSCRIBER)
Topicyou type it (plant/line1/oven/temp)fixed namespace spBv1.0/{group}/{type}/{edge}/{device}
Payloadwhatever the source sends (JSON, text, number)protobuf, with type and timestamp per metric
Tag discoverysomeone documents it and you register itarrives in the BIRTH, when the device connects
Device went downsilence, indistinguishable from “nothing changed”DEATH published by the broker, right away
Lost messageinvisiblegap in the sequence number (seq)
Writing backpublish to an agreed topicstandardized NCMD/DCMD

Use Sparkplug when the other side speaks Sparkplug — Ignition, HiveMQ Edge, Kepware, Cirrus Link, Opto 22. A sensor publishing JSON to a topic is still a plain MQTT case.

Connection

No new connection type: use the same MQTT External Connection of the broker (host, port, credentials). There is a single extra, optional field:

Primary Host ID — when filled in, the CMS announces itself as the namespace Host Application: it publishes spBv1.0/STATE/{hostId} as ONLINE on connect and registers OFFLINE as its Last Will. Many gateways use that signal to switch to store-and-forward when the host goes away.

Only fill in the Primary Host ID if no other host (an Ignition, for example) already plays that role with the same ID in your namespace. Left empty, the CMS is just another subscriber and does not interfere.

Collection — SPARKPLUG_SUBSCRIBER

The Collection points at a slice of the namespace, not at a topic:

FieldPurpose
Group IDgroups the edge nodes of a plant/area
Edge Node IDthe gateway
Device IDoptional — empty collects from every device of the node
Include edge node metricsalso brings NBIRTH/NDATA (gateway CPU, uptime)
Request Rebirthon connect, asks the node to re-announce its metrics

Each NDATA/DDATA becomes one message carrying the metrics that changed (report by exception, as the spec itself works):

{ "grupo": "Fabrica01", "edgeNode": "GW_L1", "device": "Forno_A", "tipo": "DDATA", "seq": 42, "timestamp": "2026-08-21T19:00:00.000Z", "recebidoEm": "2026-08-21T19:00:03.120Z", "metricas": { "Temperatura": 812.5, "Status": "RUN" } }

timestamp is the source stamp (when the device measured), not the arrival time — one of the reasons Sparkplug exists, and what lets you correctly order data delayed by the network.

Discover tags

The Discover tags button on the Collection screen lists the metric catalog of each device: name, data type and the last announced value. The catalog is stored, so the screen opens with it even when the device is offline; the button publishes a Rebirth and refreshes it.

Rebirth and ordering

After the BIRTH, metrics travel with the numeric alias only — without the catalog there is no way to know what alias 7 is. That is why the CMS requests a Rebirth on connect, when data arrives from a node with no known BIRTH, and when seq jumps (a sign of a lost message). Requests are throttled to one every 30s per node.

An edge node (NDEATH) or device (DDEATH) going down marks the collection as disconnected and raises the Sparkplug Device Offline alert — distinct from MQTT Connection Lost, which is the broker going down.

Float in Sparkplug is 32-bit: a value of 33.3 comes back as 33.29999923706055. For exact decimal precision use Double metrics on the gateway.

Delivery — SPARKPLUG_CMD

Writes metrics to the device by publishing DCMD (Device ID filled in) or NCMD (empty). The mapping mirrors the OPC UA Write Tags: each row links an alias expected in the payload to a device metric, with the data type used for encoding.

Row fieldPurpose
Aliaskey in the message payload
Metricname on the device (command target)
TypeBoolean, Int32, Int64, Float, Double, String or DateTime
Default valueused when the message does not carry the alias
Requiredwith no value (and no default), the delivery fails and nothing is published

Aliases are resolved against the already transformed payload — if there is a Transformer on the definition or on the forwarding, its output is what counts. (OPC UA/Modbus Write Tags do the opposite: they read the original payload.)

The stored return value is {"topico": "spBv1.0/.../DCMD/...", "metricas": N}.

Testing without a real gateway

The repository ships an edge node simulator that publishes a full lifecycle and answers commands:

docker compose -f docker-compose.test.yml up -d mosquitto # local broker (optional) node tools/sparkplug-simulador/edge-node.mjs --broker mqtt://localhost:18830 node tools/sparkplug-simulador/edge-node.mjs --morrer 30 # publishes NDEATH after 30s