IoT industrial

Certificados birth y death de Sparkplug: NBIRTH, NDEATH, DBIRTH y DDEATH

Los certificados birth y death son la forma en que Sparkplug sabe qué está online y si los datos están vigentes. Esta guía explica NBIRTH, NDEATH, DBIRTH y DDEATH, los números de secuencia y bdSeq, con un simulador funcional en Python.

Línea temporal de los certificados birth y death de Sparkplug con NBIRTH, DBIRTH, mensajes de datos y NDEATH de un edge node
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

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

  1. El edge node incrementa bdSeq y construye un payload NDEATH que lo contiene.
  2. Se conecta al broker con ese NDEATH como will (QoS 1, no retained), usando una clean session.
  3. Se suscribe a sus topics NCMD y DCMD para poder recibir comandos de rebirth y de escritura.
  4. Si está configurado para seguir a una aplicación host primaria, espera hasta que el mensaje STATE de ese host indique online.
  5. Publica el NBIRTH con seq 0 y el mismo bdSeq.
  6. Publica un DBIRTH para cada dispositivo.
  7. Publica NDATA y DDATA cuando hay cambios (report by exception), incrementando seq cada vez.
  8. 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.

Artículos relacionados

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.