Los certificados birth y death de Sparkplug son los mensajes que indican a todos los consumidores cuándo un edge node o dispositivo se conecta, qué métricas tiene y cuándo desaparece. En esta guía aprenderás cómo NBIRTH, NDEATH, DBIRTH y DDEATH trabajan junto con los contadores seq y bdSeq, y ejecutarás un pequeño simulador de edge node en Python que publica el ciclo de vida completo en un broker real.
Por qué Sparkplug necesita mensajes de birth y death
MQTT puro le dice a un suscriptor que ha llegado un valor. No le dice si el emisor sigue vivo, si el último valor sigue siendo válido ni qué otros valores existen. En una planta, sea una embotelladora en Querétaro o una acería en Bilbao, eso es un problema real: una pantalla de operador que muestra una bomba girando a 1.450 rpm es peligrosa si el gateway se desconectó hace diez minutos.
Sparkplug lo resuelve con un modelo de estado construido sobre dos funciones de MQTT: el last will and testament y la entrega ordenada en una única conexión. Cada edge node se anuncia con un certificado de birth que enumera todas sus métricas y sus valores actuales. Cuando se desconecta, de forma limpia o no, un certificado de death avisa a todos de que esos valores ya están obsoletos. Las aplicaciones host pueden entonces marcar los tags con calidad mala en lugar de mostrar números antiguos como si fueran en vivo.
Los cuatro certificados
| Mensaje | Publicado por | Cuándo | Contiene |
|---|---|---|---|
| NBIRTH | Edge node | Justo después de conectarse | Todas las métricas del nodo, bdSeq, seq 0, métricas Node Control |
| NDEATH | El broker mediante el last will, o el nodo | Cuando termina la sesión del nodo | bdSeq coincidente con el del NBIRTH |
| DBIRTH | Edge node | Después del NBIRTH, para cada dispositivo | Todas las métricas del dispositivo con sus valores actuales |
| DDEATH | Edge node | Cuando se pierde un dispositivo detrás del nodo | Solo el número de secuencia |
Los topics siguen el namespace de topics de Sparkplug: spBv1.0/{group}/NBIRTH/{node} para los mensajes de nodo y spBv1.0/{group}/DBIRTH/{node}/{device} para los dispositivos.
NBIRTH
El birth del nodo es el primer mensaje tras establecerse la conexión MQTT. Debe contener todas las métricas de nivel de nodo con su nombre, tipo de datos y valor actual, además de una métrica bdSeq y una métrica booleana Node Control/Rebirth para que un host pueda solicitar un birth nuevo. Si se usan alias de métricas, el NBIRTH es donde se emparejan nombres y alias.
NDEATH
El death del nodo se registra como mensaje will de MQTT cuando el nodo se conecta, de modo que el broker lo publica automáticamente si se cae la conexión TCP o expira el keepalive. Un nodo que se apaga de forma ordenada lo publica él mismo antes de desconectarse. No es retained y, en Sparkplug 3.0, el will usa QoS 1.
DBIRTH y DDEATH
Los dispositivos no tienen conexión MQTT propia, así que no pueden usar un will. El edge node publica un DBIRTH para cada dispositivo después de su propio NBIRTH, y un DDEATH cuando pierde el contacto con un dispositivo, por ejemplo por un timeout de Modbus. Cuando muere el propio nodo, todos sus dispositivos se consideran implícitamente muertos; no hacen falta mensajes DDEATH individuales.
Números de secuencia: seq y bdSeq
Dos contadores mantienen a las aplicaciones host bien informadas.
seq es un campo del payload que va de 0 a 255. El NBIRTH siempre tiene seq 0, y cada mensaje posterior de ese nodo (DBIRTH, NDATA, DDATA, DDEATH) lo incrementa en uno, volviendo de 255 a 0. Un host que detecta un salto sabe que se ha perdido un mensaje y que su visión puede ser incorrecta.
bdSeq (secuencia de birth/death) es una métrica, no un campo del payload. Se incrementa cada vez que el nodo inicia una nueva sesión MQTT. El nodo pone el mismo valor de bdSeq en el will NDEATH que registra al conectarse y en el NBIRTH que publica tras conectarse. Como un will puede entregarse con retraso, sin esto un host podría recibir un NDEATH antiguo después de un NBIRTH nuevo y marcar por error como offline un nodo que está vivo. Comparar los valores de bdSeq permite al host ignorar un death que pertenece a una sesión anterior.
El ciclo de vida paso a paso
- El edge node incrementa bdSeq y construye un payload NDEATH que lo contiene.
- Se conecta al broker con ese NDEATH como will (QoS 1, no retained), usando una clean session.
- Se suscribe a sus topics NCMD y DCMD para poder recibir comandos de rebirth y de escritura.
- Si está configurado para seguir a una aplicación host primaria, espera hasta que el mensaje STATE de ese host indique online.
- Publica el NBIRTH con seq 0 y el mismo bdSeq.
- Publica un DBIRTH para cada dispositivo.
- Publica NDATA y DDATA cuando hay cambios (report by exception), incrementando seq cada vez.
- Al apagarse publica el NDEATH; si se bloquea, el broker publica el will en su nombre.
Ejecuta un simulador de birth y death en Python
El script siguiente implementa ese ciclo de vida con paho-mqtt 2.x. Necesita las clases Python generadas a partir del archivo sparkplug_b.proto de Eclipse Tahu. Descarga el proto del repositorio de Tahu y genéralas una vez:
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
Esto genera sparkplug_b_pb2.py junto al proto. Guarda el simulador como edge_sim.py en la misma carpeta:
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()
Ejecútalo con python edge_sim.py. Observa el grupo con mosquitto_sub -h broker.connectmq.com -p 1883 -i watcher-1234 -t "spBv1.0/demo-xxxxxx/#" -v, sustituyendo el grupo por el que haya mostrado el script. Para ver el will en acción, mata el proceso en lugar de pulsar Ctrl+C: cuando expire el keepalive, el broker publicará el NDEATH en nombre del nodo.
El servidor MQTT público gratuito es legible por todo el mundo y está pensado solo para pruebas y prototipos; además y permite 10 mensajes por segundo por cliente, así que mantén un intervalo de publicación holgado y no envíes nunca datos reales de planta. Para un despliegue real, cambia al puerto 8883 con TLS y usa tus YOUR_USERNAME, YOUR_PASSWORD y YOUR_CLIENT_ID del portal.
Ver births y deaths decodificados
La salida en bruto de mosquitto_sub se verá parcialmente binaria porque los payloads son Protobuf. En ConnectMQ, el explorador de topics decodifica Sparkplug B, de modo que puedes abrir el NBIRTH y ver bdSeq, Node Control/Rebirth y cada métrica de dispositivo por su nombre. Es la forma más rápida de confirmar que el bdSeq del NDEATH coincide con el del NBIRTH y que seq se incrementa sin saltos. Si quieres construir tu propio decodificador, sigue la guía para decodificar Sparkplug B en Python.
Errores habituales
| Error | Efecto | Solución |
|---|---|---|
| Will configurado después de conectar | No hay NDEATH cuando el nodo falla | Llama a will_set antes de connect |
| bdSeq distinto entre NBIRTH y NDEATH | El host ignora o aplica mal los deaths | Usa el mismo valor durante una sesión |
| DDATA antes del DBIRTH | El host no tiene definiciones de métricas | Publica siempre primero los births |
| seq no se reinicia en el rebirth | El host detecta saltos falsos | El NBIRTH siempre usa seq 0 |
| Births retained | Births obsoletos que se reenvían a hosts nuevos | Births y deaths nunca son retained |
| Sesión persistente | Suscripciones antiguas y comandos en cola | Usa una clean session |
Dónde encajan birth y death en tu arquitectura
Los certificados birth y death son lo que convierte un montón de topics MQTT en una capa de datos fiable y consciente del estado, y por eso Sparkplug es habitual en diseños de Unified Namespace y SCADA. Esos mismos births decodificados pueden alimentar un gemelo digital 3D de la planta que atenúe los equipos cuyo nodo haya publicado un NDEATH. Para una visión más amplia, consulta la guía de IoT industrial con MQTT. En producción, la fiabilidad del mecanismo de will también depende del broker, por lo que la mayoría de los equipos ejecutan Sparkplug en un broker MQTT gestionado en lugar de en un equipo debajo de una mesa.
Empieza con ConnectMQ
Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito. Apunta el simulador a tu broker con las credenciales generadas y observa NBIRTH, DBIRTH, DDATA y NDEATH decodificados en vivo en el portal de ConnectMQ.
Preguntas frecuentes
¿Los certificados de birth de Sparkplug son retained?
No. Births y deaths se publican sin el flag retain. Un host que arranca más tarde envía un comando de rebirth para obtener los births actuales en lugar de depender de retained messages.
¿Quién publica el mensaje NDEATH?
Normalmente el broker, porque el NDEATH se registra como el will MQTT del edge node. Un nodo que se apaga de forma ordenada puede publicarlo él mismo antes de desconectarse.
¿Cada dispositivo necesita un DDEATH cuando muere el nodo?
No. Cuando llega el NDEATH, el host considera offline todos los dispositivos de ese nodo. El DDEATH solo es necesario cuando se pierde un único dispositivo mientras el nodo sigue conectado.
¿Qué ocurre cuando seq llega a 255?
Vuelve a 0 y continúa. Solo un NBIRTH lo reinicia deliberadamente; cualquier otro salto inesperado indica un mensaje perdido.



