IoT industrial

Certificados Birth e Death do Sparkplug: NBIRTH, NDEATH, DBIRTH e DDEATH

Os certificados de birth e death são a forma como o Sparkplug sabe o que está online e se os dados estão atualizados. Este guia explica NBIRTH, NDEATH, DBIRTH e DDEATH, números de sequência e bdSeq, com um simulador em Python funcionando.

Linha do tempo dos certificados birth e death do Sparkplug mostrando NBIRTH, DBIRTH, mensagens de dados e NDEATH de um edge node
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Os certificados birth e death do Sparkplug são as mensagens que informam a todos os consumidores quando um edge node ou device fica online, quais métricas ele tem e quando ele sai do ar. Neste guia você vai aprender como NBIRTH, NDEATH, DBIRTH e DDEATH funcionam em conjunto com os contadores seq e bdSeq, e vai rodar um pequeno simulador de edge node em Python que publica o ciclo de vida completo em um broker real.

Por que o Sparkplug precisa de mensagens de birth e death

O MQTT puro avisa ao subscriber que um valor chegou. Ele não diz se o remetente ainda está vivo, se o último valor continua válido ou que outros valores existem. Em uma planta isso é um problema real: uma tela de operador que mostra uma bomba girando a 1.450 rpm é perigosa se o gateway caiu há dez minutos.

O Sparkplug resolve isso com um modelo de estado construído sobre dois recursos do MQTT: o last will and testament e a entrega ordenada em uma única conexão. Todo edge node se anuncia com um certificado de birth que lista todas as suas métricas e os valores atuais. Quando ele se desconecta, de forma limpa ou não, um certificado de death avisa a todos que esses valores agora estão desatualizados. As aplicações host podem então marcar as tags com qualidade ruim em vez de exibir números antigos como se fossem ao vivo.

Os quatro certificados

Mensagem Publicada por Quando Contém
NBIRTH Edge node Logo após conectar Todas as métricas do node, bdSeq, seq 0, métricas Node Control
NDEATH Broker via last will, ou o node Quando a sessão do node termina bdSeq igual ao do NBIRTH
DBIRTH Edge node Depois do NBIRTH, para cada device Todas as métricas do device com valores atuais
DDEATH Edge node Quando um device atrás do node é perdido Apenas o número de sequência

Os topics seguem o namespace de topics do Sparkplug: spBv1.0/{group}/NBIRTH/{node} para mensagens de node e spBv1.0/{group}/DBIRTH/{node}/{device} para devices.

NBIRTH

O birth do node é a primeira mensagem depois que a conexão MQTT é estabelecida. Ele precisa conter todas as métricas em nível de node com nome, tipo de dado e valor atual, além de uma métrica bdSeq e de uma métrica booleana Node Control/Rebirth, para que um host possa solicitar um novo birth. Se forem usados aliases de métricas, é no NBIRTH que nomes e aliases são pareados.

NDEATH

O death do node é registrado como a mensagem will do MQTT quando o node se conecta, de modo que o broker o publica automaticamente se a conexão TCP cair ou o keepalive expirar. Um node que encerra de forma ordenada o publica ele mesmo antes de se desconectar. Ele não é retained e, no Sparkplug 3.0, o will usa QoS 1.

DBIRTH e DDEATH

Os devices não têm conexão MQTT própria, então não podem usar um will. O edge node publica um DBIRTH para cada device depois do seu próprio NBIRTH e um DDEATH quando perde contato com um device, por exemplo em um timeout Modbus. Quando o próprio node morre, todos os seus devices também ficam implicitamente mortos; não há necessidade de mensagens DDEATH individuais.

Números de sequência: seq e bdSeq

Dois contadores mantêm as aplicações host fiéis à realidade.

seq é um campo do payload que vai de 0 a 255. O NBIRTH sempre tem seq 0, e cada mensagem seguinte daquele node (DBIRTH, NDATA, DDATA, DDEATH) o incrementa em um, voltando de 255 para 0. Um host que vê um salto sabe que perdeu uma mensagem e que a sua visão pode estar errada.

bdSeq (sequência de birth/death) é uma métrica, não um campo do payload. Ele é incrementado a cada vez que o node inicia uma nova sessão MQTT. O node coloca o mesmo valor de bdSeq no will NDEATH que registra na conexão e no NBIRTH que publica depois de conectar. Como um will pode ser entregue com atraso, um host poderia, de outra forma, receber um NDEATH antigo depois de um NBIRTH novo e marcar erroneamente como offline um node que está vivo. Valores de bdSeq correspondentes permitem que o host ignore um death que pertence a uma sessão anterior.

O ciclo de vida passo a passo

  1. O edge node incrementa o bdSeq e monta um payload NDEATH que o contém.
  2. Ele se conecta ao broker com esse NDEATH como will (QoS 1, não retained), usando clean session.
  3. Ele assina os seus topics NCMD e DCMD para poder receber comandos de rebirth e de escrita.
  4. Se estiver configurado para seguir uma Primary Host Application, ele espera até que a mensagem STATE desse host indique online.
  5. Ele publica o NBIRTH com seq 0 e o mesmo bdSeq.
  6. Ele publica um DBIRTH para cada device.
  7. Ele publica NDATA e DDATA quando há mudança (report by exception), incrementando o seq a cada vez.
  8. No desligamento ele publica o NDEATH; em caso de falha o broker publica o will por ele.

Rode um simulador de birth e death em Python

O script abaixo implementa esse ciclo de vida com paho-mqtt 2.x. Ele precisa de classes Python geradas a partir do arquivo sparkplug_b.proto do Eclipse Tahu. Baixe o proto do repositório do Tahu e gere as classes uma 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

Isso gera o sparkplug_b_pb2.py ao lado do proto. Salve o simulador como edge_sim.py na mesma pasta:

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]   # group exclusivo no broker público
NODE = "line1"
DEVICE = "cnc1"

# Códigos de tipo de dado do Sparkplug
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()

Execute com python edge_sim.py. Acompanhe o group com mosquitto_sub -h broker.connectmq.com -p 1883 -i watcher-1234 -t "spBv1.0/demo-xxxxxx/#" -v, substituindo o group pelo que o script imprimiu. Para ver o will em ação, mate o processo em vez de pressionar Ctrl+C: depois que o keepalive expira, o broker publica o NDEATH em nome do node.

O servidor MQTT público, voltado a testes e protótipos, pode ser lido por qualquer pessoa e permite 10 mensagens por segundo por cliente, então mantenha um intervalo de publicação folgado e nunca envie dados reais da planta. Em uma implantação real, troque para a porta 8883 com TLS e use os seus YOUR_USERNAME, YOUR_PASSWORD e YOUR_CLIENT_ID do portal.

Vendo births e deaths decodificados

A saída bruta do mosquitto_sub vai parecer parcialmente binária porque os payloads são Protobuf. No ConnectMQ, o topic explorer decodifica Sparkplug B, então você pode abrir o NBIRTH e ver bdSeq, Node Control/Rebirth e cada métrica do device pelo nome. Essa é a forma mais rápida de confirmar que o bdSeq do NDEATH corresponde ao do NBIRTH e que o seq é incrementado sem lacunas. Se quiser construir o seu próprio decodificador, siga decodificar Sparkplug B em Python.

Erros comuns

Erro Efeito Solução
Will definido depois do connect Nenhum NDEATH em caso de falha Chame will_set antes de connect
bdSeq diferente entre NBIRTH e NDEATH O host ignora ou aplica deaths de forma errada Use o mesmo valor durante uma sessão
DDATA antes do DBIRTH O host não tem as definições das métricas Sempre publique os births primeiro
seq não zerado no rebirth O host detecta lacunas falsas O NBIRTH sempre usa seq 0
Births retained Births antigos reproduzidos para novos hosts Births e deaths nunca são retained
Sessão persistente Assinaturas antigas e comandos enfileirados Use clean session

Onde birth e death se encaixam na sua arquitetura

Os certificados de birth e death são o que transforma um amontoado de topics MQTT em uma camada de dados confiável e consciente de estado, e é por isso que o Sparkplug é comum em projetos de Unified Namespace, SCADA e gêmeo digital, de uma mineradora a um frigorífico: o gêmeo digital só reflete a planta com fidelidade se souber quais nodes estão vivos. Para o panorama mais amplo, veja o guia de IoT Industrial com MQTT. Em produção, a confiabilidade do mecanismo de will também depende do broker, por isso a maioria das equipes roda Sparkplug em um broker MQTT gerenciado em vez de uma máquina embaixo da mesa.

Comece com o ConnectMQ

Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito. Aponte o simulador para o seu broker com as credenciais geradas e acompanhe NBIRTH, DBIRTH, DDATA e NDEATH decodificados ao vivo no portal do ConnectMQ.

Perguntas frequentes

Os certificados de birth do Sparkplug são retained?

Não. Births e deaths são publicados sem a flag retain. Um host que inicia depois envia um comando de rebirth para obter os births atuais, em vez de depender de mensagens retained.

Quem publica a mensagem NDEATH?

Normalmente o broker, porque o NDEATH é registrado como o will MQTT do edge node. Um node que encerra de forma ordenada pode publicá-lo ele mesmo antes de se desconectar.

Todo device precisa de um DDEATH quando o node morre?

Não. Quando o NDEATH chega, o host trata todos os devices sob aquele node como offline. O DDEATH só é necessário quando um único device é perdido enquanto o node continua conectado.

O que acontece quando o seq chega a 255?

Ele volta para 0 e continua. Apenas um NBIRTH o zera deliberadamente; qualquer outro salto inesperado indica uma mensagem perdida.

Artigos relacionados

Cliente MQTT no CODESYS: Publicando Variáveis do CLP em um Broker

Configure a biblioteca IIoT MQTT Client do CODESYS para publicar variáveis do CLP em JSON, verifique-as no servidor MQTT público broker.connectmq.com e depois migre para um broker privado com credenciais e TLS.

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.