Last Updated: October 2026
Sparkplug B 3.0 Protocol: The Complete Technical Guide for Unified Namespace
Plain MQTT will happily carry your factory data and tell you nothing about it. A subscriber sees bytes on a topic, with no way to know whether the publisher is alive, whether the last value is current, or what the other fifty tags on that machine are called. Sparkplug B closes exactly that gap. It is a specification layered on MQTT that fixes the topic structure, the payload encoding, and, most importantly, a session model with birth and death messages so that consumers always know what exists and whether it is trustworthy.
That matters now because Unified Namespace projects keep colliding with the same wall: brokers and dashboards work fine, then a gateway reboots and half the plant shows stale values that look live. Sparkplug 3.0 is the mechanism designed to prevent that failure, and it has been an ISO/IEC standard since 2023. This guide walks through the real protocol, corrects several widespread misconceptions, and ends with runnable Python and a design checklist.
What this covers: the topic namespace, the nine message types, birth and death certificates, the STATE topic and primary host application, sequence numbers and rebirth, the protobuf payload, working code, and how Sparkplug relates to a Unified Namespace.
Context and Background
Sparkplug began life at Cirrus Link as a way to make MQTT usable for SCADA, then moved into the Eclipse Foundation’s Sparkplug Working Group. Version 3.0.0 of the specification is dated 16 November 2022, and the same document was published as ISO/IEC 20237:2023 (edition 1, 123 pages, stage 60.60 “International Standard published”, October 2023) per the ISO catalogue entry. That makes Sparkplug one of very few IIoT messaging conventions with a formal international standard behind it, which matters in procurement conversations even when engineers do not care.
The specification is deliberately narrow. It does not define a plant information model, an ISA-95 hierarchy, or a security profile. It defines three things: how topics are laid out under the spBv1.0 namespace, how payloads are encoded using a Google Protocol Buffers schema, and how clients behave over time, meaning what they publish when they connect, what the broker publishes if they vanish, and what a consumer must do when it detects trouble. The third item is the real contribution.
It is worth separating Sparkplug from its neighbours. OPC UA offers a rich client-server and pub/sub information model but a heavy stack. Raw MQTT is lightweight but leaves everything to convention, as covered in our comparison of major IoT protocols and how to choose between them. Sparkplug sits between: a thin, opinionated contract on top of MQTT that standardises the boring parts so SCADA systems, historians, and edge gateways from different vendors interoperate without per-pair mapping work. For latency expectations of the underlying transports, see the IoT protocol latency benchmarks.
Three terms recur throughout. An Edge Node is the MQTT client that speaks Sparkplug at the edge, typically a gateway or a PLC with native support. A Device is something behind an Edge Node, such as a drive or a sensor cluster, that the node reports on. A Host Application is a consumer, such as a SCADA or IIoT platform, that subscribes to everything and may also send commands. One Host Application can be designated the Primary Host Application for an Edge Node, and that designation drives much of the state logic described below.
A note on currency. The Sparkplug repository on GitHub still publishes the 3.0.0 specification as its released document, and its contribution guide refers to a 4.0.0 specification and conformance kit being developed on a develop branch. I could not confirm a 4.0 release date, so everything below describes 3.0, which is what products certify against today.
The Core Architecture: Topics, Message Types and Session Semantics
Direct answer: Sparkplug B publishes on topics shaped spBv1.0/group_id/message_type/edge_node_id/[device_id], using nine message types: NBIRTH, NDEATH, DBIRTH, DDEATH, NDATA, DDATA, NCMD, DCMD and STATE. Birth messages declare every metric, data messages carry changes, death messages mark data stale, command messages write values, and STATE reports whether a host application is online.

Figure 1: The Sparkplug B topic structure and how message types split into node, device and host scopes.
The diagram shows that the message type sits in the middle of the topic, which lets a broker ACL or a subscription filter select behaviour (all births, all commands) independently of location (one plant, one node). The device segment is present only for device-scoped messages, and the STATE branch uses a different layout because it describes a host, not an edge asset.
The topic namespace
The namespace element is the constant spBv1.0, identifying the Sparkplug B payload format. The group_id is a logical grouping that most teams map to a site, area or line. The edge_node_id names the Edge Node and the optional device_id names a Device behind it. Per the specification, identifiers are UTF-8 strings that must not contain +, / or #, because those are MQTT topic characters, and a Device ID must be unique within an Edge Node but may repeat across nodes.
The device segment appears only in DBIRTH, DDEATH, DDATA and DCMD topics. NBIRTH, NDEATH, NDATA, NCMD and STATE omit it. A valid example from the spec is spBv1.0/Group1/NBIRTH/EdgeNode1, and a typical host subscription is spBv1.0/Group1/+/EdgeNode1/#, which uses the multi-level wildcard to capture both node and device traffic for one node. Topic names are case sensitive, so NBIRTH and nbirth are different topics.
This is the first concrete advantage over free-form MQTT. Because the grammar is fixed, a host can subscribe to spBv1.0/# once and discover every node and device in the system purely from topic names, then drill into payloads for metadata.
The nine message types
The types come in node-scoped and device-scoped pairs, plus one host message:
| Type | Direction | Scope | Purpose |
|---|---|---|---|
| NBIRTH | Edge to host | Node | Node comes online, declares all metrics |
| NDEATH | Broker to host | Node | Node gone, sent as MQTT Will |
| DBIRTH | Edge to host | Device | Device online, declares all metrics |
| DDEATH | Edge to host | Device | Device unavailable |
| NDATA | Edge to host | Node | Changed node metrics |
| DDATA | Edge to host | Device | Changed device metrics |
| NCMD | Host to edge | Node | Write node metrics, e.g. rebirth |
| DCMD | Host to edge | Device | Write device metrics |
| STATE | Host to all | Host | Host online or offline |
The specification fixes delivery semantics per type. Based on my reading of the 3.0.0 document, NBIRTH, NDATA, NCMD, DBIRTH, DDATA, DDEATH and DCMD are all published at QoS 0 with the retain flag false. STATE messages, both the birth and the death, use QoS 1 with retain true. NDEATH is not published by the client at all: it is registered as the MQTT Will message at CONNECT time, so the broker publishes it on the client’s behalf if the connection drops without a clean DISCONNECT.
The surprise for many engineers is QoS 0 for data. The design logic is that Sparkplug does not rely on the transport for reliability of state; it relies on sequence numbers and rebirth. If a data message is lost, the consumer detects the gap and requests a fresh snapshot, which is cheaper and more robust than pushing QoS 2 handshakes across thousands of tags.
Birth certificates: schema first, values second
A birth message is a complete declaration. Per the spec, NBIRTH must carry sequence number 0, a timestamp, every metric the node will ever report with its name, datatype and current value, and a bdSeq metric that matches the one in the Will payload. It also includes a boolean metric named Node Control/Rebirth set to false, which is the hook hosts use to request a new birth. Optional control metrics include Node Control/Reboot, Node Control/Next Server and Node Control/Scan Rate, and optional property metrics describe hardware make, model and operating system.
DBIRTH does the same for a device: every metric with name, datatype and value, plus optional Device Control/Rebirth, Device Control/Reboot and Device Control/Scan rate metrics and hardware properties. Because births list every metric, a host can build its tag database from births alone. That is the property that lets a new sensor appear in a historian without manual mapping, but note that it works because the edge node is the schema authority and the host trusts the birth. There is no separate registry.
Data, command and death behaviour
After birth, NDATA and DDATA carry only the metrics that changed since the last birth or data message. This is report-by-exception, and it is where the bandwidth savings come from. A command arrives as NCMD or DCMD with the metrics to write; the edge node applies the write and, if the value changes, reports it in a subsequent data message. There is no dedicated reply message type, which is a frequent source of confusion: the confirmation of a command is the changed value showing up in data traffic.
DDEATH is published by the Edge Node itself when a device becomes unavailable, for example when a serial link to a drive drops. Per the spec, the host then marks all that device’s metrics stale and records when it learned of the death. NDEATH plays the same role for the whole node, but it is emitted by the broker, so it also covers crashes and network partitions where the node could not say goodbye.
Deeper Analysis: State, Sequence Numbers and Rebirth
The birth and death messages only help if the surrounding state machine is airtight. The next sections go through the three mechanisms that make Sparkplug more than a naming convention: the host STATE topic, the dual sequence counters, and the rebirth request.

Figure 2: A complete Sparkplug session, from host STATE birth through edge node birth, data and a host-initiated rebirth.
The sequence shows that the host announces itself first and the edge node reacts to that announcement. That ordering is deliberate and is the basis of the primary host feature described next.
STATE and the primary host application
Hosts are first-class participants. Each Host Application has a Sparkplug Host ID and publishes on spBv1.0/STATE/sparkplug_host_id. In Sparkplug 3.0 the payload is UTF-8 JSON containing an online boolean and a timestamp in UTC milliseconds since the epoch. The birth sets online to true; the death sets it to false. Both use QoS 1 and are retained, which is what makes the model work: a late-joining edge node subscribes to the STATE topic and immediately receives the retained value.
The death is registered as the host’s MQTT Will, and the spec requires the death timestamp to match the timestamp in the Will payload so that stale and fresh messages can be told apart. On a clean disconnect the host publishes the death message itself before sending DISCONNECT. After connecting, a host must subscribe to its own STATE topic, which lets it detect and correct a case where another instance is announcing the wrong state.
An Edge Node may be configured with one Primary Host Application. If it is, the node delays its NBIRTH and DBIRTH until the retained STATE message shows that host online. While the host is offline, the node may buffer data and flush it once STATE returns to online. If the node has several MQTT servers available, it should prefer one where the primary host’s STATE says online, which is how Sparkplug supports redundant brokers without a custom failover protocol.
This corrects a common misdescription. The primary host is not elected by the edge node through a DBIRTH, and it does not publish NDEATH on a node’s behalf. NDEATH comes from the broker via the Will. The primary host is simply a named consumer whose liveness gates when the edge node starts talking.
Two counters: seq and bdSeq
Sparkplug uses two different counters, and mixing them up is the most common implementation bug.
The seq field in the payload orders messages inside a session. NBIRTH uses 0. Each subsequent NDATA, DBIRTH, DDATA and DDEATH from that node increments the value by one and wraps from 255 to 0. The counter belongs to the Edge Node, so device messages share the node’s sequence rather than keeping their own. A consumer that sees a jump has lost a message. NDEATH carries no seq.
The bdSeq metric, birth-death sequence, identifies the session itself. It starts at zero and increments by one for every new MQTT CONNECT. The same value appears in the Will (NDEATH) payload registered at connect time and in the NBIRTH published afterward. When a host receives an NDEATH, it compares the bdSeq to the one in the most recent NBIRTH. If they match, the death belongs to the current session and the host marks all node metrics stale. If they do not match, it is a delayed Will from an earlier session and must be ignored; otherwise a slow broker could mark a freshly reborn node as dead.
This pairing solves a race that is easy to underestimate. When a gateway power-cycles and reconnects quickly, the broker may deliver the old session’s Will after the new NBIRTH. Without bdSeq a host would flip a healthy node to offline and keep it there until the next birth, which might be hours away.
Rebirth: the repair path
When a consumer decides its picture is wrong, it publishes an NCMD to that node with the metric Node Control/Rebirth set to true. The Edge Node responds by publishing a fresh NBIRTH and DBIRTHs, restoring a consistent baseline. DBIRTH-level rebirth exists too, via Device Control/Rebirth.
When should a host ask? The specification text I retrieved defines the mechanism but does not by itself mandate every trigger, so these are the triggers I would treat as standard practice: a seq gap that does not resolve within a short reordering window, a data message for a metric or alias the host has never seen, a data message arriving for a node with no known birth, and a host restart where retained state has been lost. A host that implements all four never needs to guess whether its cache is trustworthy.
Rebirth is also the answer to a design tension. Because data messages are QoS 0 and carry only changes, a consumer that joins late would otherwise see nothing until a value changed. Sparkplug avoids retained data messages on purpose, since retained values would go stale silently, and uses rebirth to pull a full snapshot on demand.

Figure 3: Edge node lifecycle, including holding births until the primary host is online and returning to birth on a rebirth command.
The state machine shows two loops that are easy to miss in prose: the wait loop while the primary host is offline, and the reconnect loop that regenerates the Will with a new bdSeq.
The Protobuf Payload and Working Code
Sparkplug B payloads are Protocol Buffers, not JSON. The schema, shipped by Eclipse Tahu as sparkplug_b.proto, defines one top-level Payload message with a timestamp (field 1), repeated Metric entries (field 2), a seq (field 3), an optional uuid (field 4) and an optional body (field 5) for custom encodings. The top-level message is called Payload, not SparkplugPayload, a naming slip that appears in many tutorials.
Anatomy of a metric
Each Metric carries an optional name, an optional numeric alias, a timestamp, a datatype, flags and one value from a oneof. The flags matter in practice: is_historical marks a back-filled value that must not overwrite the live tag, is_transient tells consumers not to persist the metric as a tag, and is_null states explicitly that the value is null instead of encoding a sentinel such as -1. Optional properties hold engineering metadata like units and ranges, and metadata supports file and multi-part content.
The datatype field uses an enumeration. The values worth memorising are Int8 through Int64 as 1 to 4, UInt8 through UInt64 as 5 to 8, Float as 9, Double as 10, Boolean as 11, String as 12, DateTime as 13, DataSet as 16, Template as 19, plus array variants from 22 upward (Int8Array is 22, DateTimeArray is 34). The enumeration is the contract: a host that reads a float_value while the datatype says Double will decode garbage without any protobuf error.
Aliases, and what they really save
The schema comment on alias says it is tied to the name on birth and included in all later data messages. So the intended pattern is: births carry both name and alias, data messages carry only the alias, and the host keeps a name-to-alias table per node, rebuilt on every birth. Aliases are not a 3.0 invention, as an older description of this protocol claimed; they are part of the protobuf schema Tahu publishes, and whether a node uses them is its choice.
How much do they save? I measured it with the Python in this section. A DDATA payload with one Float metric, a metric timestamp, a message timestamp and a seq, using the 24-character name Line1/Motor3/Temperature, serialises to 51 bytes. The same message using alias 1 and no name is 27 bytes. An equivalent hand-written JSON document with the same fields is 153 bytes. Those numbers are for this single illustrative metric only; real savings scale with name length and with how many metrics change together, and they say nothing about broker overhead, because the MQTT header and topic string add a fixed cost to every message regardless.
Templates and DataSets
Two datatypes deserve attention for digital twin work. A Template (type 19) is a reusable structure: a definition lists a set of metrics and parameters under a name and version, and instances reference it through template_ref. A “Motor” definition with speed, current and temperature can then be instantiated per motor, and the birth must include every template definition that will be used. A DataSet (type 16) is a table of typed columns and rows, suited to recipes, batch records or small waveforms. Templates are the closest Sparkplug gets to a data model, but they give structure, not semantics: nothing in the protocol says what “speed” means or relates it to an equipment hierarchy.
A runnable edge node and host
The following code builds the payloads and runs the host-side sequence check without needing a broker, so it can be tested anywhere. Generate the bindings first with python -m grpc_tools.protoc -I. --python_out=. sparkplug_b.proto using the proto from the Tahu repository. I ran this file against protobuf and the generated module; the output follows the listing.
import time
import sparkplug_b_pb2 as sp
DT = sp.DataType
class EdgeNode:
"""Minimal Sparkplug B payload builder (no network)."""
def __init__(self):
self.seq = 0
self.bd_seq = 0
def _next_seq(self):
s = self.seq
self.seq = (self.seq + 1) % 256 # wraps 255 -> 0
return s
def will_payload(self):
p = sp.Payload()
m = p.metrics.add()
m.name = "bdSeq"; m.datatype = DT.Int64; m.long_value = self.bd_seq
return p.SerializeToString() # no seq in NDEATH
def nbirth(self):
self.seq = 0 # NBIRTH always uses seq 0
p = sp.Payload()
p.timestamp = int(time.time() * 1000)
p.seq = self._next_seq()
m = p.metrics.add(); m.name = "bdSeq"; m.datatype = DT.Int64
m.long_value = self.bd_seq; m.timestamp = p.timestamp
m = p.metrics.add(); m.name = "Node Control/Rebirth"
m.datatype = DT.Boolean; m.boolean_value = False; m.timestamp = p.timestamp
return p.SerializeToString()
def dbirth(self):
p = sp.Payload()
p.timestamp = int(time.time() * 1000)
p.seq = self._next_seq()
m = p.metrics.add(); m.name = "Temperature"; m.alias = 1
m.datatype = DT.Float; m.float_value = 21.5; m.timestamp = p.timestamp
return p.SerializeToString()
def ddata(self, temp):
p = sp.Payload()
p.timestamp = int(time.time() * 1000)
p.seq = self._next_seq()
m = p.metrics.add(); m.alias = 1 # alias only, no name
m.datatype = DT.Float; m.float_value = temp; m.timestamp = p.timestamp
return p.SerializeToString()
class Host:
def __init__(self):
self.last_seq = None; self.aliases = {}; self.rebirth_needed = False
def on_message(self, kind, raw):
p = sp.Payload(); p.ParseFromString(raw)
if kind in ("NBIRTH", "DBIRTH"):
for m in p.metrics:
if m.HasField("alias"): self.aliases[m.alias] = m.name
if kind == "NBIRTH":
self.last_seq = p.seq; self.rebirth_needed = False
return p
if self.last_seq is None or p.seq != (self.last_seq + 1) % 256:
self.rebirth_needed = True # gap: publish NCMD Node Control/Rebirth
self.last_seq = p.seq
return p
n, h = EdgeNode(), Host()
h.on_message("NBIRTH", n.nbirth()); h.on_message("DBIRTH", n.dbirth())
p = h.on_message("DDATA", n.ddata(21.6))
print(h.aliases[p.metrics[0].alias], round(p.metrics[0].float_value, 1), "seq", p.seq)
n.ddata(21.8) # simulate a lost message
h.on_message("DDATA", n.ddata(21.9))
print("rebirth_needed", h.rebirth_needed)
Running it prints Temperature 21.6 seq 2 and rebirth_needed True. The first line shows the host resolving an alias-only data message back to a name, and the second shows a single dropped message being detected by the sequence check. Note the modulo-256 comparison; comparing with a plain + 1 would raise a false alarm at every wraparound.
Connecting with the Will and the host STATE
The network side adds the Will and the retained STATE message. The snippet below registers NDEATH as the Will with QoS 1 and retain false, which is how I read the 3.0 connection requirements, and builds a host death payload. Edge Nodes and hosts may use MQTT 3.1.1 or 5.0; with 3.1.1 the clean session flag must be true, and with 5.0 clean start must be true and the session expiry interval zero, so no broker-side session state survives a reconnect.
import json, time
import paho.mqtt.client as mqtt
GROUP, NODE = "Plant1", "Gateway7"
def make_client(will_payload: bytes) -> mqtt.Client:
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=NODE,
protocol=mqtt.MQTTv311, clean_session=True)
# NDEATH is the Will; the client never publishes it itself.
c.will_set(f"spBv1.0/{GROUP}/NDEATH/{NODE}", will_payload,
qos=1, retain=False)
return c
def host_death():
ts = int(time.time() * 1000) # must equal the Will timestamp
return json.dumps({"online": False, "timestamp": ts})
The pattern for each reconnect is to increment bd_seq, rebuild the Will payload, call will_set before connect, and only then publish NBIRTH with the same bdSeq. The Will must be configured before the CONNECT packet leaves, which is why a Sparkplug client library wraps the connect call instead of letting application code do it.
Sparkplug and the Unified Namespace
The phrase Unified Namespace usually refers to the architecture popularised by Walker Reynolds: one broker-centred hub where every system publishes current state under a hierarchical, human-meaningful topic path, typically following ISA-95 levels such as enterprise, site, area, line and cell. Our primer on the Unified Namespace as an industrial data fabric covers the pattern itself. The question here is how Sparkplug fits.

Figure 4: Sparkplug B at the edge, a decoding bridge, and an ISA-95 shaped UNS above it feeding historian, analytics and MES.
Where they agree and where they conflict
Sparkplug and UNS share the broker-centred, publish-subscribe, report-by-exception philosophy. They disagree on topic design. A Sparkplug topic encodes the producer (group_id, edge_node_id, device_id) and the message role, while a UNS topic encodes the business context (Site/Area/Line/Cell). A tag named Temperature on spBv1.0/Plant1/DDATA/Gateway7/Oven2 says nothing about which line it belongs to unless the group and device IDs were chosen to say so.
Teams resolve this in three ways. The first is to run Sparkplug end to end and use group, node and device IDs plus metric name paths (for example Line1/Oven2/Temperature as the metric name) to carry the hierarchy. This works but couples the plant model to edge-node deployment. The second is to keep Sparkplug as the edge-to-platform protocol and use a bridge that decodes births and data, applies a mapping to the ISA-95 path, and republishes plain JSON into the UNS tree, as in Figure 4. The third is to let a Sparkplug-aware platform sit in the middle and expose both views. All three are used in production; the second is the most common in new designs because it keeps the UNS readable by tools that cannot decode protobuf.
What the bridge must preserve
A bridge is where Sparkplug’s guarantees quietly disappear, so it deserves explicit requirements. It must honour NDEATH and DDEATH by republishing a stale quality or clearing the UNS value, otherwise the UNS shows frozen numbers that look live. It must respect is_historical and avoid overwriting current values with back-filled data. It should carry the metric timestamp, not the arrival time, into the UNS payload. And it should be a Sparkplug host in its own right, with its own Host ID and STATE, so that edge nodes configured with it as primary hold their data when the bridge is down.
Ignoring this is the root cause of the most common UNS complaint, namely that dashboards show plausible but dead values. Our comparison of MindSphere, AWS IoT SiteWise and Azure IoT Hub shows how cloud platforms handle the same quality problem differently; the lesson transfers, since quality and staleness must be modelled explicitly at every hop.
Sparkplug versus plain MQTT, honestly
Plain MQTT remains the right answer in several cases: a small number of devices you control, consumers that are all custom code, or payloads that must be human-readable for ad hoc tools. Sparkplug wins when you have heterogeneous vendors, many consumers that need discovery, and a requirement that stale data be unmistakable. It costs you protobuf tooling, a stricter client implementation, and discipline about births. The break-even is usually around the point where you would otherwise write a second mapping document between two vendors.
Trade-offs, Gotchas, and What Goes Wrong
Sparkplug fixes a real problem and introduces its own.
Births are a scaling event. Every NBIRTH and DBIRTH lists every metric. When a broker restarts, thousands of nodes reconnect and rebirth simultaneously, and the resulting burst can be many times steady-state traffic. Plan broker capacity for the rebirth storm, not the average, and stagger reconnects with jittered backoff in the edge client.
Primary host outages block the plant. An Edge Node that names a primary host holds its birth until that host’s STATE is online. If the host crashes and its Will does not fire correctly, or if a retained STATE is left stale, nodes may sit silent. Test host failure and broker failover as explicit scenarios, and consider a standby host that takes over the same Host ID rather than a different one.
QoS 0 means silent loss. The whole design assumes you detect loss by sequence gaps. A host that ignores seq is not Sparkplug-compliant in spirit, and will drift. Equally, a gap detector must treat the 255-to-0 wrap as normal. At 10 messages per second a single node wraps roughly every 25.6 seconds, so wraparound is routine, not exceptional.
Alias drift. Aliases are bound at birth. If an edge node reorders metrics or changes aliases without a new birth, hosts will decode data against the wrong names, and the protobuf will parse without complaint. Treat any metric change as a rebirth trigger on the node side.
Security is not in the spec. Sparkplug assumes TLS and broker authentication are handled elsewhere. A client with write access to spBv1.0/+/NCMD/# can command anything. Enforce per-node ACLs that restrict an Edge Node to publishing under its own group_id and edge_node_id, and restrict command topics to authorised hosts.
Not every metric belongs. High-rate waveforms (vibration at kilohertz rates) do not fit report-by-exception semantics and often overwhelm the broker. Buffer and batch them as DataSets, or send them through a different path.
Evolving specs. Tooling support varies by version and by vendor. Verify that your host and edge products implement 3.0 behaviours such as the JSON STATE payload and bdSeq matching, rather than assuming parity with older releases. Mixed-version estates are common and produce subtle failures, such as hosts that ignore bdSeq and flip healthy nodes to offline.
Practical Recommendations
Start by deciding the role of Sparkplug in your architecture before choosing products. If Sparkplug is the edge protocol and a bridge feeds the UNS, design the bridge as a Sparkplug host with a proper Host ID, and make it the primary host for edge nodes that must not lose data. If Sparkplug is the whole stack, invest in naming: put the plant hierarchy into group and node IDs deliberately, because those cannot be changed cheaply once ACLs and dashboards depend on them.
Next, harden the failure paths that the happy-path demos skip. Implement the bdSeq comparison on every host. Implement the seq gap detector with a short reordering window and a rebirth request. Set MQTT keep-alive low enough that a dead gateway is noticed within an acceptable time, and test the Will by pulling the network cable, not by calling a clean disconnect. Remember that a clean disconnect suppresses the Will, so a test that only shuts the client down proves nothing.
Finally, treat conformance as a procurement criterion. The Eclipse project maintains a technology compatibility kit, and the ISO standard gives a stable reference. Ask vendors which Sparkplug version they implement and whether they support primary host and rebirth, not just the topic format.
Checklist:
- Choose Sparkplug versus plain MQTT per interface, with the discovery and staleness requirement written down.
- Fix group, node and device ID conventions before the first deployment.
- Enforce per-node publish ACLs and restrict NCMD and DCMD to named hosts.
- Implement bdSeq matching, seq gap detection and rebirth on every consumer.
- Give every host and bridge a unique Host ID with retained STATE and a matching Will.
- Load-test the rebirth storm at your real tag count.
- Map death messages to stale quality in the UNS and in dashboards.
- Test failures by cutting power and network, not by clean shutdown.
Frequently Asked Questions
What is Sparkplug B?
Sparkplug B is an open specification on top of MQTT that defines a fixed topic namespace (spBv1.0), a Protocol Buffers payload format, and a session model with birth, data, command and death messages. It lets industrial applications discover devices and metrics automatically and know when data is stale. Version 3.0 is published by the Eclipse Foundation and also as ISO/IEC 20237:2023.
Is Sparkplug the same as a Unified Namespace?
No. A Unified Namespace is an architecture pattern with a human-readable, ISA-95 style topic tree as the single source of current state. Sparkplug is a protocol with a producer-centred topic layout and binary payloads. They are often combined, with Sparkplug at the edge and a bridge publishing business-context topics into the UNS, but neither requires the other.
How does Sparkplug detect a dead edge node?
The node registers an NDEATH message as its MQTT Will at connection time, carrying a bdSeq metric. If the connection drops without a clean disconnect, the broker publishes it. The host compares bdSeq against the node’s latest NBIRTH and, if they match, marks all the node’s metrics stale. A non-matching value belongs to an older session and is ignored.
What is a rebirth in Sparkplug?
A rebirth is a request for an Edge Node to republish its birth certificates. A host publishes an NCMD with the metric Node Control/Rebirth set to true, and the node responds with a new NBIRTH and DBIRTHs containing every metric and current values. Hosts use it after sequence gaps, unknown metrics, or a restart that lost cached state.
Does Sparkplug use QoS 2 or retained messages?
Mostly neither. In the 3.0 specification, birth, data and command messages use QoS 0 with retain false, because reliability comes from sequence numbers and rebirth. The exceptions are the host STATE messages, which use QoS 1 with retain true, and the NDEATH Will, whose delivery the broker handles. The retained STATE is what lets late-joining nodes see the host status.
Which version of Sparkplug should I use in 2026?
Use 3.0, which is the released specification and the ISO/IEC 20237:2023 standard. The project repository references a 4.0.0 specification under development, but I could not confirm a release. Check that your products implement 3.0 behaviours, notably the JSON STATE topic and bdSeq matching, and plan for compatibility testing if you mix vendors or older firmware.
Further Reading
- Unified Namespace for industrial data fabric: the architecture pattern Sparkplug is usually paired with.
- Major IoT protocols and how to choose: where MQTT and Sparkplug sit among the alternatives.
- IoT protocol latency benchmarks: transport-level numbers for MQTT, CoAP, AMQP and HTTP/3.
- Modbus RTU and TCP guide: the field protocol most Sparkplug edge nodes read from.
- Industrial IoT platform comparison: MindSphere, AWS IoT SiteWise and Azure IoT Hub side by side.
- External: the Eclipse Sparkplug 3.0.0 specification, the ISO/IEC 20237:2023 catalogue entry and the Eclipse Tahu reference libraries and proto file.
By Riju — about
