Skip to main content
Version: 1.0.8

MQTT & Sparkplug B

One connection type covers both classic MQTT (you choose the topics and payload shape) and Sparkplug B (a self-describing convention with birth/death certificates and metric metadata). The payload format switch decides which.

Configuration​

FieldDescription
NameConnection identifier.
BrokerHostname or IP.
Port1883 plain, 8883 TLS, 9001/443 for WebSocket transports.
Protocoltcp, ssl/tls, ws, wss.
Client IDOptional. Leave blank for a generated one. Must be unique on the broker.
Username / PasswordOptional broker credentials.
Clean sessionOn: no persistent broker-side subscription state. Off: the broker queues messages for this client ID while it is away.
Keep aliveSeconds between pings.
QoS0 at most once, 1 at least once, 2 exactly once.
Payload formatmqtt (classic) or sparkplug_b.

Sparkplug-only fields​

FieldDescription
Group filterSubscribe to spBv1.0/{group}/# instead of everything. Essential on a busy shared broker.
Primary host IDIf set, QUBIQ advertises spBv1.0/STATE/{id}, so edge nodes know a primary application is online.
Rebirth on sequence gapauto (detect the edge node's scheme — default), on (always request rebirth), off (never).

Classic MQTT​

In classic mode, a tag binding names the topic and, for structured payloads, a path into it. The tag is subscribed to the topic and updates whenever the device publishes.

FieldNotes
AddressThe topic, e.g. plant/line1/motor1/speed.
Quality of Service (QoS)Per-binding override of the connection default. 1 is the usual choice.
JSON PathOptional. A dot path to the value inside a JSON payload, e.g. data.speed for {"data":{"speed":1480}}. No leading $.. Leave it empty to take the whole payload.
Retain MessageFor writes: whether a value written to the tag is published retained.
Device time, quality and statusOptional. See below. 1.0.8

A JSON path that does not resolve, a payload that is not JSON when a path is set, a null, or a value that does not fit the tag's data type all read Bad with the reason. The whole payload is never substituted for a field that is missing.

Wildcards (+, #) are for subscription scope, not for tag addresses — a tag must resolve to one value.

Several tags on one topic Changed in 1.0.8​

Any number of tags can bind to the same topic — typically one per JSON field of a device's payload. Each receives its own field from every message, and each value is delivered once.

Fixed in 1.0.8: tags sharing a topic

Before 1.0.8 the client kept one handler per topic, so of several tags on one topic only the one subscribed last received data; the others stayed silent. Deleting that tag left its handler in place, still publishing for a tag that no longer existed while the rest stayed dark. If you worked around this with one topic per tag, it is no longer necessary.

The device's own time and quality New in 1.0.8​

MQTT carries no timestamp and no quality, so by default a message is stamped when it arrives and is Good if it decodes. Many devices put both into their payload. The binding can read them:

FieldWhat it does
Timestamp PathDot path to the time the device measured the value, e.g. ts or meta.time. Empty: the receive time is used.
Timestamp formatAuto-detect, ISO 8601 text, Unix seconds, Unix milliseconds, Unix microseconds, Unix nanoseconds.
Timezone for text without an offsetFor ISO text with no zone. UTC by default.
Quality PathDot path to the device's own quality for the reading, e.g. q or status.quality.
Device quality codesOptional, one code = Good, code = Uncertain or code = Bad per line. Consulted before the automatic reading. A line that cannot be read is flagged and blocks the save.

The device's time becomes the event time. A readable, plausible device time is what the historian stores the value at, so a device that buffers or batches is no longer historised at the moment its batch arrived. Plausible means not before the year 2000 and not more than ten minutes ahead of the gateway's clock. A time that is missing, unreadable or implausible falls back to the receive time; the value keeps its quality and the status says why, for example timestamp "garbage" cannot be read (format epoch_ms); using receive time. → Event time in history

The device's quality grades the reading. Without codes of your own it is read automatically:

Device reportsRead as
good, ok, valid, normalGood
uncertain, questionable, stale, suspectUncertain
bad, error, invalid, faultBad
true / falseGood / Bad
A number 0–255 — an OPC DA quality byte192 and above Good, 64–191 Uncertain, below 64 Bad
A number above 255 — an OPC UA status codeBy its severity bits
Anything elseUncertain, with the code in the status

The tag's quality is the worse of the payload check and the device's report. A reading the device marks Bad keeps its value, stored with that quality. A configured quality field that is missing from a message reads Uncertain, never Good.

0 means two things

0 is the worst OPC DA quality byte and OPC UA's Good status code. It is read as the byte. A device that sends OPC UA status codes needs the line 0 = Good.

These settings are part of the binding in a UDT member too, overridable per instance. → UDT overrides

Retained messages Changed in 1.0.8​

On every subscribe and resubscribe the broker replays a topic's retained message, which may be hours old. It used to be stamped now, Good, and stored as a fresh reading.

  • With a device timestamp, a retained message is an ordinary reading at its true time. A repeat of a value already stored is absorbed by the historian.
  • Without one, it is shown live as Uncertain — retained message; original time unknown — and not historised, because there is no true time to store it at.

The device's status topic New in 1.0.8​

A plain MQTT device that drops off the network used to leave its tags Good on their last value. Most devices announce themselves on a status topic and leave a Last Will there, which the broker publishes when they disconnect. A binding can listen to it:

FieldNotes
Device status topicThe device's status topic, e.g. plant/pump1/status. Wildcards are refused: one device's status cannot come from a pattern.
Online payload / Offline payloadonline and offline by default.
JSON PathOptional, for a JSON status payload.

When the status topic says offline, every tag naming it turns Bad at once — one row per outage, Device offline (plant/pump1/status). When it says online, nothing is written: the tags stay Bad until the device sends a fresh value, because the value may have changed while it was away. A retained data message replayed while the device is offline does not clear the Bad; a live one does. Any other payload on the status topic is ignored.

This is the device's Last Will. It is not the connection's own Last Will, which is QUBIQ's. In a UDT, set it once on the type as {Device}/status and every instance listens to its own.

Subscribing at scale Changed in 1.0.8​

At start-up, after a restart and after a reconnect, MQTT bindings are subscribed in batches — each distinct topic once, however many tags read it — rather than one tag at a time. A 10,000-tag plant used to take about fifty seconds to go live, and a 50,000-tag plant was still not fully subscribed after ten minutes while the broker held millions of messages for topics nobody had subscribed yet. Incoming messages are also decoded once per message rather than once per tag, and spread over several workers with each topic kept in order.

A broker reconnect no longer reloads live tags: the client re-subscribes its own topics. Reloading them used to drop values until every tag was subscribed again — about a quarter of a 10,000-tag plant's values after a single spurious disconnect.

A disabled MQTT connection registers no tags. It used to register them without subscribing them.

Sparkplug B​

Sparkplug adds structure that QUBIQ uses automatically:

  • Birth certificates (NBIRTH/DBIRTH) declare every metric with its name, data type, engineering unit and properties. QUBIQ reads them, so discovered metrics arrive already typed.
  • Death certificates (NDEATH/DDEATH) signal that an edge node or device has gone away. Bound tags go to Bad quality immediately, instead of silently holding a stale value.
  • Sequence numbers detect lost or out-of-order messages. On a gap, QUBIQ requests a rebirth so state is resynchronised rather than drifting.
  • State topic. With a primary host ID configured, QUBIQ publishes its own STATE, which is what lets compliant edge nodes decide whether a primary application is listening.

Discovery​

Sparkplug discovery in the connection's editor (needs View Connections) returns the groups, edge nodes, devices and metrics observed from birth certificates. Discovery is how you populate the namespace from a Sparkplug plant without typing a single topic.

A Sparkplug tag address follows the hierarchy:

{group}/{edge_node}/{device}/{metric}

The device segment is left out for a metric of the edge node itself. When binding a single tag, the browse button beside the address shows the same metric tree and writes this address for you. 1.0.8 → Address formats

Writes​

Writing a tag bound to a Sparkplug metric sends a command to the edge node — an NCMD for a node metric, a DCMD for a device metric — typed with the data type the metric's birth declared.

The value is converted to that type correctly now. 1.0.7 Two conversions were wrong without anything saying so:

  • A write of an integer to a Boolean metric — 1, from a numeric input or a script — went out as false. Non-zero is now true.
  • A write of a text timestamp (2026-09-23T08:00:00Z) to a DateTime metric went out as 1 January 1970. It now carries the time it names.

JSON and byte values also go out as JSON text and as the text the bytes hold, rather than in an internal notation no edge node can read.

This page is the inbound half. To publish QUBIQ's own tags to a broker, as a Sparkplug edge node, see MQTT publishing.

Choosing between them​

Use classic MQTT whenUse Sparkplug B when
The publisher is not Sparkplug-awareEdge devices already speak it
Payloads are your own JSON or scalarsYou want typed, self-describing metrics
Topic structure is fixed and smallYou want automatic discovery of a large plant
You do not need liveness semanticsYou need death certificates to invalidate stale values

If you control both ends and the deployment is more than a handful of tags, Sparkplug pays for itself in the metadata alone.

TLS​

Set the protocol to ssl/tls (or wss) and use the TLS port. QUBIQ validates the broker's certificate chain. For a private CA, ensure the CA is trusted by the host — QUBIQ uses the system trust store.

Quality and liveness​

ConditionTag quality
Message received and parsedGood, or the device's own quality if a Quality Path is set
Broker connection lostBad after the connection is marked down
Sparkplug DDEATH/NDEATH for the deviceBad
The device's status topic says offline 1.0.8Bad until a fresh value
Payload could not be parsed, or the JSON path did not resolveBad
Retained message replayed with no device time 1.0.8Uncertain, not historised
No message yet since subscribeUnknown — the device has not reported a value yet

When the connection drops, the value a tag keeps showing is its last Good one. 1.0.8 A Sparkplug null or a Bad metric used to replace it, so the display went blank at the moment comms failed. A Bad Sparkplug metric also no longer counts as proof that a disconnected connection is back; only a Good one does.

Because MQTT is push-only, a device that publishes on change sends nothing while its value holds steady. A device that publishes on a cycle can use stale after to make silence visible; a device with a Last Will is better served by its status topic.

Troubleshooting​

SymptomCheck
Connects then immediately drops, repeatedlyDuplicate client ID — another client is stealing the session. Set a unique one.
Subscribed but no dataTopic mismatch (MQTT topics are case-sensitive), or the publisher only sends on change and nothing has changed.
Sparkplug metrics never appearNo birth certificate received. Restart the edge node, or force a rebirth.
Sequence gaps logged repeatedlyNetwork loss or an edge node restarting. Leave rebirth on auto.
Values stop after a broker restartClean session on, and the publisher used retained messages that were lost.
TLS handshake failurePrivate CA not in the host trust store, or hostname mismatch.

Next​

→ Modbus · Binding tags