Industrial IoT

Sparkplug Primary Host Application and STATE Messages

The primary host application is the Sparkplug consumer edge nodes depend on. This guide explains STATE messages in Sparkplug 3.0, how edge nodes react when the host goes offline, and how to test the flow with Python and mosquitto.

Sparkplug primary host application publishing an online STATE message that edge nodes watch before sending births
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

A Sparkplug primary host application is the main consumer of plant data, typically SCADA or an MES, that edge nodes watch before they publish anything. In this guide you will learn how the host announces itself with STATE messages, how edge nodes are expected to react when it goes offline, and how to test that behaviour end to end with a small Python host and a couple of command-line tools.

What a Host Application Does

Sparkplug defines two kinds of participants besides the broker. Edge nodes publish data. Host applications consume it, track the state of every node and device, and send commands such as rebirth requests or setpoint writes. A plant can have many host applications: SCADA, a historian, an analytics service, a Unified Namespace bridge.

A host application has several responsibilities:

  • Subscribe to spBv1.0/# (or the groups it manages) and maintain a live model of nodes, devices and metrics.
  • Use birth certificates to learn metric names, aliases and datatypes.
  • Mark data stale when an NDEATH or DDEATH arrives, using bdSeq to discard deaths from old sessions.
  • Watch sequence numbers and request a rebirth when a gap appears.
  • Publish its own STATE so edge nodes know whether it is listening.

What Makes a Host Primary

One host can be designated the primary host application for a set of edge nodes. This is configured on the edge node, not on the broker: the node is told the host ID it depends on. The idea is simple. If the system that operators rely on is not connected, data published now would be lost to it, so the edge node should stop publishing live data, store it locally if it supports store and forward, and wait.

Without a primary host configured, an edge node publishes as soon as it is connected to the broker, regardless of who is listening. That is fine for monitoring and dashboards, but it is weaker for SCADA where gaps in history matter.

STATE Messages in Sparkplug 3.0

The host announces itself on a dedicated topic outside the group structure:

spBv1.0/STATE/{host_id}
Property Birth (online) Death (offline)
How it is sent Published by the host after connecting Registered as MQTT will at connect
Payload {"online": true, "timestamp": T} {"online": false, "timestamp": T}
QoS 1 1
Retained Yes Yes
Timestamp Same T for this session Same T for this session

Retaining the message means an edge node that connects at any time immediately learns whether the host is online. The shared timestamp lets nodes ignore an old message: if a node has seen online with timestamp T2, an offline message with an older timestamp T1 is a leftover from a previous session and must not trigger a disconnect.

Older 2.2 systems use STATE/{host_id} with plain ONLINE and OFFLINE strings. The differences are summarised in our Sparkplug 3.0 comparison.

How Edge Nodes React

An edge node configured with a primary host follows a predictable pattern:

  1. Connect to the broker with its NDEATH registered as the will.
  2. Subscribe to spBv1.0/STATE/{primary_host_id} and to its own NCMD and DCMD topics.
  3. Wait for a STATE message with "online": true.
  4. Publish NBIRTH and DBIRTH, then data as usual.
  5. If STATE switches to "online": false with a timestamp at least as new as the last one seen, publish NDEATH, disconnect, and buffer data if supported.
  6. Reconnect (possibly to a different broker in a multi-server setup) and wait again for the host to be online.
  7. When the host returns, publish fresh births, then any buffered historical data flagged with is_historical.

Step 7 is why STATE matters: births give the host a clean model, and historical flags let it backfill without overwriting live values. The birth and death mechanics are covered in Sparkplug birth and death certificates.

Test the Flow with a Minimal Python Host

This script plays the role of a primary host: it registers its offline will, publishes its online state and listens to a group. It uses paho-mqtt 2.x and needs no Protobuf because STATE payloads are JSON.

import json
import time
import uuid

import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
PORT = 1883
HOST_ID = "demo-host-" + uuid.uuid4().hex[:6]
GROUP = "demo-" + HOST_ID[-6:]
STATE_TOPIC = f"spBv1.0/STATE/{HOST_ID}"
SESSION_TS = int(time.time() * 1000)


def state(online):
    return json.dumps({"online": online, "timestamp": SESSION_TS})


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}/#", qos=1)
    client.subscribe(STATE_TOPIC, qos=1)
    client.publish(STATE_TOPIC, state(True), qos=1, retain=True)
    print(f"host {HOST_ID} online, watching group {GROUP}")


def on_message(client, userdata, msg):
    if msg.topic == STATE_TOPIC:
        print("STATE:", msg.payload.decode())
    else:
        print(f"{msg.topic} ({len(msg.payload)} bytes)")


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=f"host-{uuid.uuid4().hex[:8]}",
                     clean_session=True)
client.on_connect = on_connect
client.on_message = on_message
client.will_set(STATE_TOPIC, state(False), qos=1, retain=True)
client.connect(BROKER, PORT, keepalive=15)

try:
    client.loop_forever()
except KeyboardInterrupt:
    client.publish(STATE_TOPIC, state(False), qos=1, retain=True).wait_for_publish()
    client.disconnect()

Run it, note the host ID it prints, and in a second terminal play the edge node watching that host:

mosquitto_sub -h broker.connectmq.com -p 1883 -i node-watch-91aa -q 1 -v \
  -t "spBv1.0/STATE/demo-host-xxxxxx"

You will immediately receive the retained online message. Now kill the Python process without Ctrl+C (close the terminal or end the task). After the keepalive window passes, the broker publishes the will and the watcher prints "online": false with the same timestamp. Restart the host and you see a new online message with a newer timestamp. That is exactly the signal an edge node uses to decide when to publish births.

When you are done, clear the retained state so the shared public MQTT test broker does not keep it:

mosquitto_pub -h broker.connectmq.com -p 1883 -i cleanup-91aa -r -n -t "spBv1.0/STATE/demo-host-xxxxxx"

Watching Host State in a Topic Explorer

During commissioning you want to see host state and node births on one screen. ConnectMQ shows retained STATE messages in its portal topic explorer next to decoded Sparkplug births and data, and exposes client connection state at $SYS/broker/connection/{clientId}/state. Subscribing to that topic for your host's client ID is a quick cross-check: if the connection state says disconnected but STATE still says online, the will has not fired yet because the keepalive window has not expired.

Common Problems

Symptom Likely cause Fix
Edge nodes never publish births Host ID mismatch between node config and STATE topic Copy the exact host ID, case-sensitive
Nodes disconnect right after host restarts Old offline will processed after new online Nodes must compare timestamps; check 3.0 support
STATE missing for new nodes STATE published without retain Publish online and will with retain true
Long delay before nodes notice host loss Large keepalive on host Use a short keepalive such as 15 to 30 seconds
Nodes and host ignore each other One side uses 2.2 STATE/ topic Align versions or enable compatibility mode

Designing for Host Availability

Because edge nodes stop publishing when the primary host is offline, the host and its broker connection become critical. Run the host on reliable infrastructure, use a short keepalive so failures are noticed quickly, and make sure the broker itself is not the weak link. A fully managed MQTT service or a cloud MQTT broker removes the burden of patching and monitoring the broker. If a node loses its connection, the Sparkplug rebirth process is how the host gets back to a consistent view. For the full IIoT picture, see Industrial IoT with MQTT.

Get Started with ConnectMQ

Create a free Serverless broker at app.connectmq.com, no credit card required. Run the host script with your generated credentials over TLS on port 8883, connect an edge node configured with that host ID, and watch STATE, births and data in the ConnectMQ portal.

Frequently Asked Questions

Can there be more than one primary host application?

Each edge node is configured with one primary host ID. Different groups of nodes can depend on different hosts, and any number of non-primary host applications can consume data at the same time.

Is the STATE message retained?

Yes. Both the online message and the offline will are published retained at QoS 1, so any edge node that connects later learns the current host state immediately.

Does the broker need special Sparkplug support for STATE?

No. STATE relies on standard MQTT features: retained messages, QoS 1 and the last will. Any compliant broker can carry it.

What happens to data while the primary host is offline?

Edge nodes stop publishing live data. Nodes that support store and forward buffer it and send it later as historical metrics after new births.

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.