Industrial IoT

Sparkplug Birth Death Certificates: NBIRTH, NDEATH, DBIRTH and DDEATH

Birth and death certificates are how Sparkplug knows what is online and whether data is current. This guide explains NBIRTH, NDEATH, DBIRTH and DDEATH, sequence numbers and bdSeq, with a working Python simulator.

Timeline of Sparkplug birth death certificates showing NBIRTH, DBIRTH, data messages and NDEATH for an edge node
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

Sparkplug birth death certificates are the messages that tell every consumer when an edge node or device comes online, which metrics it has, and when it goes away. In this guide you will learn how NBIRTH, NDEATH, DBIRTH and DDEATH work together with the seq and bdSeq counters, and you will run a small Python edge node simulator that publishes the complete lifecycle to a live broker.

Why Sparkplug Needs Birth and Death Messages

Plain MQTT tells a subscriber that a value arrived. It does not tell it whether the sender is still alive, whether the last value is still valid, or what other values exist. In a plant that is a real problem: an operator screen that shows a pump running at 1,450 rpm is dangerous if the gateway went offline ten minutes ago.

Sparkplug solves this with a state model built on two MQTT features, the last will and testament and ordered delivery on a single connection. Every edge node announces itself with a birth certificate listing all of its metrics and their current values. When it disconnects, cleanly or not, a death certificate tells everyone that those values are now stale. Host applications can then mark tags as bad quality instead of showing old numbers as if they were live.

The Four Certificates

Message Published by When Contains
NBIRTH Edge node Right after connecting All node metrics, bdSeq, seq 0, Node Control metrics
NDEATH Broker via last will, or node When the node session ends bdSeq matching the NBIRTH
DBIRTH Edge node After NBIRTH, for each device All device metrics with current values
DDEATH Edge node When a device behind the node is lost Sequence number only

Topics follow the Sparkplug topic namespace: spBv1.0/{group}/NBIRTH/{node} for node messages and spBv1.0/{group}/DBIRTH/{node}/{device} for devices.

NBIRTH

The node birth is the first message after the MQTT connection succeeds. It must contain every node-level metric with its name, datatype and current value, plus a bdSeq metric and a Node Control/Rebirth boolean metric so a host can request a fresh birth. If metric aliases are used, the NBIRTH is where names and aliases are paired.

NDEATH

The node death is registered as the MQTT will message when the node connects, so the broker publishes it automatically if the TCP connection drops or keepalive expires. A node shutting down gracefully publishes it itself before disconnecting. It is not retained, and in Sparkplug 3.0 the will uses QoS 1.

DBIRTH and DDEATH

Devices do not have their own MQTT connection, so they cannot use a will. The edge node publishes a DBIRTH for each device after its own NBIRTH, and a DDEATH when it loses contact with a device, for example a Modbus timeout. When the node itself dies, all of its devices are implicitly dead too; there is no need for individual DDEATH messages.

Sequence Numbers: seq and bdSeq

Two counters keep host applications honest.

seq is a payload field from 0 to 255. The NBIRTH always has seq 0, and each following message from that node (DBIRTH, NDATA, DDATA, DDEATH) increments it by one, wrapping from 255 back to 0. A host that sees a jump knows it missed a message and its view may be wrong.

bdSeq (birth/death sequence) is a metric, not a payload field. It increments each time the node starts a new MQTT session. The node puts the same bdSeq value in the NDEATH will it registers at connect time and in the NBIRTH it publishes after connecting. Because a will can be delivered late, a host could otherwise receive an old NDEATH after a new NBIRTH and wrongly mark a live node offline. Matching bdSeq values let the host ignore a death that belongs to an earlier session.

The Lifecycle Step by Step

  1. The edge node increments bdSeq and builds an NDEATH payload containing it.
  2. It connects to the broker with that NDEATH as its will (QoS 1, not retained), using a clean session.
  3. It subscribes to its NCMD and DCMD topics so it can receive rebirth and write commands.
  4. If it is configured to follow a primary host application, it waits until that host's STATE message says online.
  5. It publishes NBIRTH with seq 0 and the same bdSeq.
  6. It publishes a DBIRTH for each device.
  7. It publishes NDATA and DDATA on change (report by exception), incrementing seq each time.
  8. On shutdown it publishes NDEATH; on a crash the broker publishes the will for it.

Run a Birth and Death Simulator in Python

The script below implements that lifecycle with paho-mqtt 2.x. It needs Python classes generated from the Eclipse Tahu sparkplug_b.proto file. Download the proto from the Tahu repository and generate them once:

pip install "paho-mqtt>=2.0" protobuf grpcio-tools
curl -O https://raw.githubusercontent.com/eclipse/tahu/master/sparkplug_b/sparkplug_b.proto
python -m grpc_tools.protoc -I. --python_out=. sparkplug_b.proto

That produces sparkplug_b_pb2.py next to the proto. Save the simulator as edge_sim.py in the same folder:

import random
import time
import uuid

import paho.mqtt.client as mqtt
import sparkplug_b_pb2 as spb

BROKER = "broker.connectmq.com"
PORT = 1883
GROUP = "demo-" + uuid.uuid4().hex[:6]   # unique group on the public broker
NODE = "line1"
DEVICE = "cnc1"

# Sparkplug datatype codes
INT64, DOUBLE, BOOLEAN = 4, 10, 11

seq = 0
bd_seq = 0


def now_ms():
    return int(time.time() * 1000)


def next_seq():
    global seq
    value = seq
    seq = (seq + 1) % 256
    return value


def add_metric(payload, name, datatype, value):
    m = payload.metrics.add()
    m.name = name
    m.timestamp = now_ms()
    m.datatype = datatype
    if datatype == INT64:
        m.long_value = value
    elif datatype == DOUBLE:
        m.double_value = value
    elif datatype == BOOLEAN:
        m.boolean_value = value


def topic(msg_type, device=None):
    base = f"spBv1.0/{GROUP}/{msg_type}/{NODE}"
    return f"{base}/{device}" if device else base


def ndeath_payload():
    p = spb.Payload()
    p.timestamp = now_ms()
    add_metric(p, "bdSeq", INT64, bd_seq)
    return p.SerializeToString()


def publish_births(client):
    global seq
    seq = 0
    nbirth = spb.Payload(timestamp=now_ms(), seq=next_seq())
    add_metric(nbirth, "bdSeq", INT64, bd_seq)
    add_metric(nbirth, "Node Control/Rebirth", BOOLEAN, False)
    add_metric(nbirth, "Properties/Uptime", INT64, 0)
    client.publish(topic("NBIRTH"), nbirth.SerializeToString(), qos=0)

    dbirth = spb.Payload(timestamp=now_ms(), seq=next_seq())
    add_metric(dbirth, "Spindle/Speed", DOUBLE, 0.0)
    add_metric(dbirth, "Spindle/Running", BOOLEAN, False)
    client.publish(topic("DBIRTH", DEVICE), dbirth.SerializeToString(), qos=0)
    print(f"births published for {GROUP}/{NODE}/{DEVICE} (bdSeq={bd_seq})")


def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print("connect failed:", reason_code)
        return
    client.subscribe(f"spBv1.0/{GROUP}/NCMD/{NODE}", qos=0)
    client.subscribe(f"spBv1.0/{GROUP}/DCMD/{NODE}/#", qos=0)
    publish_births(client)


def on_message(client, userdata, msg):
    cmd = spb.Payload()
    cmd.ParseFromString(msg.payload)
    for m in cmd.metrics:
        if m.name == "Node Control/Rebirth" and m.boolean_value:
            print("rebirth requested")
            publish_births(client)


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=f"edge-sim-{uuid.uuid4().hex[:8]}",
                     clean_session=True)
client.on_connect = on_connect
client.on_message = on_message
client.will_set(topic("NDEATH"), ndeath_payload(), qos=1, retain=False)
client.connect(BROKER, PORT, keepalive=30)
client.loop_start()

try:
    while True:
        time.sleep(2)
        ddata = spb.Payload(timestamp=now_ms(), seq=next_seq())
        add_metric(ddata, "Spindle/Speed", DOUBLE, round(random.uniform(1400, 1500), 1))
        add_metric(ddata, "Spindle/Running", BOOLEAN, True)
        client.publish(topic("DDATA", DEVICE), ddata.SerializeToString(), qos=0)
except KeyboardInterrupt:
    ddeath = spb.Payload(timestamp=now_ms(), seq=next_seq())
    client.publish(topic("DDEATH", DEVICE), ddeath.SerializeToString(), qos=0)
    client.publish(topic("NDEATH"), ndeath_payload(), qos=1).wait_for_publish()
    client.disconnect()
    client.loop_stop()

Run it with python edge_sim.py. Watch the group with mosquitto_sub -h broker.connectmq.com -p 1883 -i watcher-1234 -t "spBv1.0/demo-xxxxxx/#" -v, replacing the group with the one the script printed. To see the will in action, kill the process instead of pressing Ctrl+C: after the keepalive expires the broker publishes the NDEATH on the node's behalf.

The public MQTT test broker is readable by everyone and allows 10 messages per second per client, so keep the publish interval relaxed and never send real plant data. For a real deployment, swap in port 8883 with TLS and your YOUR_USERNAME, YOUR_PASSWORD and YOUR_CLIENT_ID from the portal.

Seeing Births and Deaths Decoded

The raw output from mosquitto_sub will look partly binary because the payloads are Protobuf. In ConnectMQ, the topic explorer decodes Sparkplug B, so you can open the NBIRTH and see bdSeq, Node Control/Rebirth and each device metric by name. That is the quickest way to confirm that the bdSeq in the NDEATH matches the NBIRTH, and that seq increments without gaps. If you want to build your own decoder, follow decode Sparkplug B in Python.

Common Mistakes

Mistake Effect Fix
Will set after connect No NDEATH on crash Call will_set before connect
bdSeq differs between NBIRTH and NDEATH Host ignores or misapplies deaths Use the same value for one session
DDATA before DBIRTH Host has no metric definitions Always publish births first
seq not reset on rebirth Host detects false gaps NBIRTH always uses seq 0
Retained births Stale births replayed to new hosts Births and deaths are never retained
Persistent session Old subscriptions and queued commands Use a clean session

Where Birth and Death Fit in Your Architecture

Birth and death certificates are what turn a pile of MQTT topics into a trustworthy, state-aware data layer, which is why Sparkplug is common in Unified Namespace designs and SCADA. For the wider picture, see the Industrial IoT with MQTT guide. In production, the reliability of the will mechanism also depends on the broker, so most teams run Sparkplug on a managed MQTT broker rather than a box under a desk.

Get Started with ConnectMQ

Create a free Serverless broker at app.connectmq.com, no credit card required. Point the simulator at your broker with the generated credentials, then watch NBIRTH, DBIRTH, DDATA and NDEATH decoded live in the ConnectMQ portal.

Frequently Asked Questions

Are Sparkplug birth certificates retained?

No. Births and deaths are published without the retain flag. A host that starts later sends a rebirth command to get current births instead of relying on retained messages.

Who publishes the NDEATH message?

Usually the broker, because the NDEATH is registered as the edge node's MQTT will. A node that shuts down gracefully can publish it itself before disconnecting.

Does every device need a DDEATH when the node dies?

No. When the NDEATH arrives, the host treats all devices under that node as offline. DDEATH is only needed when a single device is lost while the node stays connected.

What happens when seq reaches 255?

It wraps to 0 and continues. Only an NBIRTH resets it deliberately; any other unexpected jump indicates a missed message.

Related articles

IIoT Edge Gateway: What It Does and How to Choose One

An IIoT edge gateway connects machines that speak Modbus, OPC UA or proprietary protocols to MQTT and the cloud. Learn its core roles, common architectures and how to choose the right one.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.