MQTT

Devices that publish through an external MQTT broker, in plain MQTT or Sparkplug B, with the broker modeled as the node every device under it shares, and the station's own data published to a broker.

View as Markdown

The MQTT driver reads and writes the tags of equipment that publishes through an external MQTT broker: a gateway, a wireless sensor, a Node-RED flow, an edge node that speaks Sparkplug B. The station is a client of that broker: it subscribes to the topics its tags read and publishes the writes you make, and each broker can publish the station's own data, the tags, Logic variables and alarms listed on its Publish card, as JSON or in Sparkplug B. It is a client and not a broker, so the equipment and the station both connect to a broker: when you have none, the station's own MQTT broker can be that broker, and turning it on adds the broker This station to this driver; Installing a broker shows how to set up a separate one.

Every device behind a broker rides the same connection, so the topology is Driver → Broker → Device → Tag: the broker owns where it answers and how the station signs in, typed once; each device under it owns only how it publishes, Plain MQTT or Sparkplug B, and its tags. Both protocols live side by side under one broker. Values arrive by subscription, not by polling, so no MQTT device or tag has a poll interval.

The driver's page

The MQTT driver's page lists brokers instead of devices. The line under its header reads "Each broker is one connection to an external MQTT broker. Configure where it answers and how the station signs in, then add the devices that publish through it." The MQTT brokers table carries Add broker at the right of its title and one row per broker: Name, Address (mqtt://host:port, or mqtts://host:port over TLS, empty while the broker has no host), Devices, State (a dot and the broker's state with what its devices are doing, such as "Online · 2/2 devices online"), then Open and, for Configure connector, Enable, Disable and Delete. The empty table reads "No brokers yet." A broker that refused the station carries the tree's red marker after its name, and the page names under its header the brokers with an error ("1 broker with an error: Plant").

In the tree the driver row counts both levels ("1 broker · 2 devices"), and the page header shows the broker count in parentheses. In the column's New… menu the MQTT entry offers New broker, then each broker with its own New device and its devices, and right-clicking the driver row offers Add broker. The driver's bar has no Discover: a scan listens on one broker, so it lives on the broker's page. Once added, the driver stands under the Drivers folder of the tree, with or without a broker.

The broker

Add broker, New broker in the New… menu and the driver row's right-click menu create a broker named "New broker" (then "New broker 2") and disabled, so its connection is filled in before the station connects. The feed says so: "Added disabled broker 'New broker'. Enter where it answers, then enable it." The broker is not opened: it is marked in the MQTT brokers table of the driver's page, where the screen goes from New… or the right-click. Its page, reached by Open on its row or its row in the tree, carries the line "One MQTT broker: where it answers and how the station signs in to it. Every device under it shares this connection." and, in the header, the state, its dot and the address. Under the header, the broker's devices that have an error of their own are named ("1 device with an error: Pump"); while the broker itself has an error, its devices point to it instead: a device's page says "This device goes through broker Plant, which has an error. The broker's page says why."

The Broker connection card

Field Meaning Default
Name The friendly name of the broker in the tree and on every device under it. It never enters an address. Blank is refused: "Broker name is required." "New broker"
Host The broker's host name or IP address. A whole address can be pasted: mqtt:// or tcp:// in front sets the transport to the clear, mqtts://, ssl:// or tls:// to TLS, a port after the host goes to Port, and the host is kept bare. Another scheme, a path or a sign-in written into the address is refused under the box, every field keeping what it held: "'http://192.168.0.20' is not a broker address. Write the broker's host name or IP address, optionally as mqtt://host:port or mqtts://host:port." empty
Port The TCP port the broker answers on, 1 to 65535. 1883
Transport The checkbox Use TLS. On, the connection is encrypted and the broker's certificate is checked against the Windows certificate store, or against the certificates approved under Equipment access: "Encrypted: the broker's certificate has to be trusted by Windows or approved under Equipment access on the MQTT driver's page." Off, the note warns "Unencrypted: values and the password travel in the clear." Switching it moves a port left at 1883 to 8883, and back; any other port stays as it is. off
Username, Password The account the station signs in to the broker with. An empty username signs in anonymously. The password is protected at rest like a device's credentials and never appears in an address, the journal or an agent's answer. empty
Client ID The name the station's session carries on the broker, 1 to 128 characters with no control characters. A broker keeps one session per client ID and drops the older of two, so a client ID belongs to one broker of this station: "Client id 'GanterLab01' already belongs to broker 'Plant'. Two sessions with one client id push each other off the broker: choose another." When two stations share a broker, each keeps its own. GanterLab and 14 hexadecimal digits, generated
Keep-alive (s) How often the session proves it is alive when nothing else travels: "Keep-alive must be between 5 and 3600 seconds." 60

Under the fields, a line says what the broker and its devices are doing, what the broker still lacks, and "Every device under this broker and what the station publishes to it share this connection: the session opens when the first enabled device or published row needs it." Every field stays editable while the broker is enabled. A saved change of where it answers, the transport, the sign-in, the client ID or the keep-alive reconnects the one session every device under the broker shares.

The command bar

Command What it does Greyed when (situation) Not drawn when (role)
Enable Lets the devices under the broker use its session. A broker with no host is refused: "Enter the broker's host name or IP address before the broker is enabled." Already enabled, the connection is not complete ("This broker's connection is not complete yet."), a scan is running, or the station refuses mutations. Without Configure connector.
Disable Closes the session for every device under it; each device keeps its own Enabled choice and comes back as it was when the broker is enabled again. Already disabled, a scan is running, or the station refuses mutations. Without Configure connector.
Delete broker (danger ink, on the right) Asks first. An empty broker: "This broker has no devices."; one with devices: "This also deletes the broker's 2 devices and all of their tags." Then the MQTT driver is selected and the feed reads "Deleted broker 'Plant' and its 2 devices." A scan is running ("A discovery scan is using this broker. Wait for it to finish."), or the station refuses mutations. Without Configure connector.

Right-clicking a broker row offers Add device, Enable, Disable and Delete.

Broker states

The broker reports its session apart from its devices, in the tree row, in the driver's brokers table and in its own header:

State Dot Meaning
Off grey The broker is disabled.
No session grey Enabled, but no enabled device rides it and its Publish card publishes nothing, so no session is opened.
Connecting… amber A device is enabled, or the broker publishes, and the session is connecting, or reconnecting after a loss.
Online green The session is connected.
Unreachable red The broker could not be reached or refused the station. The cause is printed in red under the header in the station's language and, in the driver's words, as the row's error tooltip, in the terms of what to correct: nothing answers on that port, the name is unknown on this network, the user name or the password was refused, the certificate is not trusted or waits for approval under Equipment access, another client took the session with the same client ID. A broker that refuses the port or a client without a user name is said with its setting instead (see Prepare the broker).

The tree row reads the state and the device count ("Online · 2 devices"), and the summary under the card adds what the devices are doing: "Online · 2/2 devices online", "Online · 1 online · 1 with issues", "No session · no enabled devices". A Sparkplug B device that announced its death takes only itself off the air: the broker stays Online. The session speaks MQTT 5 and falls back to MQTT 3.1.1 for a broker that does not, starts clean, and reconnects by itself, waiting longer between attempts while the broker stays away.

The Discover devices card

Drawn only for Configure connector, under the line "Listens on this broker for a while: the Sparkplug B edge nodes and devices that announce themselves, and the topics under a filter."

Field Meaning Default
Listen (s) How long the scan listens, 3 to 60 seconds. 10
Topic filter The plain topics to list, as tags of one device. The wildcards + (one level) and # (this level and everything under it, last only) are allowed, so "e.g. plant/#". Empty lists Sparkplug B devices only. empty

Discover starts the scan and turns into Cancel while it runs. The line under the fields says what it will do ("Listens to 192.168.0.20:1883 for 10 s."), and the progress line counts the window ("Listening to 192.168.0.20:1883 · 4 of 10 s…"). The scan needs a host stored on the broker: until then the card says "Enter the broker's host and let the edit save before discovery." A pending edit on the broker is saved first; when it cannot be, nothing starts: "Discovery was not started because the broker edit could not be saved."

What a scan does on the broker:

  • It connects under a client ID of its own, so it never pushes the devices' session off the broker, and it may run while the broker is enabled and in use.
  • It subscribes to every Sparkplug B topic (spBv1.0/#) and to the topic filter. Sparkplug B birth certificates are not retained, so an edge node heard without one is asked to announce itself again (the Node Control/Rebirth command, the only message the scan publishes); an edge node that is running answers inside the window.
  • Each edge node and each device behind it that announces itself (NBIRTH, DBIRTH) becomes a candidate with its metrics as tags, typed by what the birth declares. The edge node's own plumbing (bdSeq and the Node Control/… and Device Control/… metrics) is left out, and so are the metrics no tag reads (arrays, data sets, templates, property sets, bytes, files), counted under their reason. An edge node heard but not born within the window is offered with no tags.
  • With a topic filter, the plain topics heard under it become one candidate of their own: each topic is a tag, or, for a JSON payload, each value inside it is one by its value path (up to 32 per topic, four levels deep, the first 8 elements of an array). A number is offered as Double, true or false as Boolean, anything else as String. The list stops at 500 topics. A topic whose publisher retains its last message is heard at once; any other only if it publishes during the window.
  • Nothing is saved until you add a candidate. Cancel ends the window at once.

The results card, Discovered devices, lists Publishes as (group/edge node, with /device behind an edge node, or "Plain topics"), Device (the name to add it under, editable: the device or edge node ID, or the levels of the filter before its first wildcard), Result ("12 of 12 tags selected") and Add device, which creates the device under this broker with those tags. A Sparkplug B identity already configured on the broker reads "Already configured" and offers Open instead. A scan that heard nothing says "Nothing announced itself on this broker while the scan listened."

A device you already have finds what it does not read yet from its own page: Discover tags, on the head of its Tags card, listens on its broker for 10 s. A Sparkplug B device hears its own edge node alone, which is asked for its birth while no other edge node is asked anything; a plain device hears the topics under the levels its tags share (plant/oven/# for plant/oven/temperature and plant/oven/count), and every topic on the broker when they share none. Only the values it has no tag for yet are offered (see Discovering a device's tags).

The Devices card

"Each device reads plain topics, or one Sparkplug B edge node or device. The connection is inherited from the broker and cannot be changed here." Add device creates "New device" under this broker, enabled, in Plain MQTT, marked in this table and scrolled into view: "Added device 'New device' on 'Plant'." The table lists Device, Publishes as, Tags and Health, with Open, Enable, Disable and Delete on each row; empty, it reads "No devices configured on this broker." A device with an error of its own carries the tree's red marker after its name.

The Publish card

The last card publishes the station's own data to this broker, under the line "The station publishes each row to this broker as JSON on its topic whenever the value or its quality changes, and every row again each time the connection opens. The broker keeps the last message of each topic for whoever subscribes later. The station subscribes only to the write topic of each row set to Read and write." Viewing the Connector reads the card; configuring it changes it. The broker This station of the station's own MQTT broker carries the same card.

Field Meaning Default
Publication format How the rows go out: JSON on each row's topic (Publishing), or Sparkplug B, the station an edge node of the broker (Publishing in Sparkplug B). Its tooltip: "How the rows go out: JSON on each row's own topic, or Sparkplug B, with this station as an edge node that Ignition and other Sparkplug B consumers find on their own." JSON
Topic prefix JSON only. The level the topic of each new row is suggested under. Empty, the field shows the default and uses it. "The topic prefix cannot hold + or #, start with $ or hold a character no topic may hold." Changing it leaves the topics already in the list as they are. ganterlab/ and the station's name
Group ID Sparkplug B only, in the prefix's place. The Sparkplug B group of the station's edge node on this broker; empty, it is GanterLab. A group holding /, + or # is refused under the field, and the broker is not saved until it is corrected. GanterLab

In Sparkplug B the line under the title says instead "The station is a Sparkplug B edge node on this broker: Logic variables and alarms are its own metrics, and each device with rows is a Sparkplug B device. It announces every metric when the connection opens and whenever the list changes, then publishes each value or quality that changes. Only a row set to Read and write takes a value from a command.", and under the fields Edge node ID (the station's name) shows the edge node it publishes as.

The table lists Source, Topic and Access, by topic; a row whose source is not chosen yet reads "No source yet". With none, it reads "Nothing is published to this broker yet. Add a row with New row." In Sparkplug B the Topic column is Metric: a tag after its device (Oven · Temperature), a variable or an alarm by its name on the edge node (Logic/Oven/Setpoint, Alarms/High). While the broker is disabled and the list has rows, a note says "The broker is disabled, so nothing is published until it is enabled." While a row of the broker is set to Read and write, a second note says "Rows set to Read and write take values published on their write topic. Anyone who can publish to this broker can change those sources, so the broker's username and password are their only guard." In Sparkplug B it says "Rows set to Read and write take values from Sparkplug B commands (NCMD, DCMD). Anyone who can publish to this broker can change those sources, so the broker's username and password are their only guard."

Command What it does Greyed when (situation) Not drawn when (role)
New row Adds a row that publishes nothing yet and selects it: "Row added to the broker's Publish list. Pick its source." A run holds the station, or the runtime is stopped. Without Configure connector.
Delete row (danger ink) Deletes the selected row at once, its tooltip naming what it publishes. The station stops publishing it; the message the broker retained on its topic stays there. No row is selected ("Select a row of the list to delete it."), a run holds the station, or the runtime is stopped. Without Configure connector.

A row's fields are stored the moment they change:

Field What it does
Source What the row publishes: "(none)", then every enabled tag of an enabled device ("Tag · Simulated · Oven · Temperature"), every Logic variable that holds a value and every alarm, named by its folders. A device command publishes nothing and is not offered. The row holds its source by identity, so a rename keeps the row on it, and the source's Used by card lists the row.
Topic JSON only. Where the row publishes. Picking a source suggests a topic from its name: <prefix>/<device>/<tag> for a tag, <prefix>/logic/<folders>/<variable> for a variable, <prefix>/alarms/<folders>/<alarm> for an alarm, each name one topic level (a space, a slash or a wildcard becomes _). Edit it freely: a topic still the one suggested follows a newly picked source, one you wrote stays, and renaming the source keeps it.
Access Read only, the default of every row: the station publishes the row and subscribes to nothing for it. Read and write: the station also takes the values published on the row's write topic, its topic followed by /set, or in Sparkplug B the values a command sends to its metric, and writes them to the source (Writing back). It is offered only for a tag with write access on a driver that writes, or a memory variable; elsewhere it stays listed, greyed: "This source takes no write: only a tag with write access or a memory variable does." Picking an alarm sets the row back to read only.

Under the fields a line says what the row publishes:

The card says When
"This row publishes nothing yet: pick its source." No source is chosen.
"Publishes the value, its quality and its time as JSON whenever the value or the quality changes." A tag or a variable, read only.
"Publishes the value, its quality and its time as JSON whenever the value or the quality changes, and writes to the source each value published on the write topic: each write is recorded in the journal with the broker's name." A tag or a variable set to Read and write.
"Publishes whether the alarm is active and acknowledged, its state, priority, message and time as JSON whenever one of them changes." An alarm.
"The source of this row no longer exists, so nothing more is published for it. The broker keeps the last message it retained." The tag, the variable or the alarm was deleted.
"Publishes the value and its quality in Sparkplug B whenever the value or the quality changes." Sparkplug B: a tag or a variable, read only.
"Publishes the value and its quality in Sparkplug B whenever the value or the quality changes, and writes to the source each value a Sparkplug B command sends to its metric: each write is recorded in the journal with the broker's name." Sparkplug B: a tag or a variable set to Read and write.
"Publishes whether the alarm is active and acknowledged, its state, priority and message in Sparkplug B whenever one of them changes." Sparkplug B: an alarm.
"The source of this row no longer exists, so the edge node announces itself again without it." Sparkplug B: the tag, the variable or the alarm was deleted.

Under that line, in JSON, a row set to Read and write shows its Write topic, the row's topic followed by /set. In Sparkplug B a second line names the metric the row is published as instead: of its Sparkplug B device for a tag, of the station's edge node for a variable, and for an alarm the five metrics under its name (Active, Acked, State, Priority and Message).

A change the list refuses is not stored: the reason is said in red above the row's fields, and the change stays on the screen so you can correct it.

The card says When
"This source is already published to this broker: edit the row that publishes it." Another row of this broker publishes the source.
"Another row of this broker already publishes on this topic: give this row a topic of its own." Two rows of one broker would publish on one topic.
"Nothing can be published on a filter: the topic cannot hold + or #." The topic holds a wildcard.
"A topic that starts with $ belongs to the broker: write another." The topic starts with $.
"This topic cannot travel: it holds a character no topic may hold, or it is too long." A control character, or more than an MQTT topic carries.
"A row that takes writes needs the tag or the variable they go to." Read and write was chosen before a source.
"The source cannot be written: pick a tag with write access or a memory variable, or keep the row read only." Read and write on an alarm, an expression variable, a read-only tag or a tag whose driver writes nothing.
"This topic and another row's topic on this broker differ only by /set, where a row takes writes: give this row another topic." One row's topic is another row's topic followed by /set.
"This row of the list no longer exists. Reload and retry." The row was deleted elsewhere while you edited it.

What is published, and when, is on Publishing.

Device

Field Meaning Default
Broker · fixed The broker's name over its address, read only: a device is created under its broker and stays there. "The broker has no host yet" stands where the address will be. "Missing broker" means the device lost its broker and cannot be saved: "An MQTT broker is required." the broker it is added under
Protocol How the device publishes. Plain MQTT: "Each tag names the topic it reads and, for a JSON payload, the path to its value." Sparkplug B: one edge node, or one device behind it: "Each tag names a metric as the edge node's birth certificate declares it; its type comes with the metric." Changing it checks every tag again, since an address then reads as a topic or as a metric. Plain MQTT
Maximum time without a message (s) Plain MQTT only, 0 to 86400, 0 meaning never: "Tags stop counting as readings when no message arrives on their topic for this long. 0: never, for a topic published only when its value changes. Set it for a device that publishes at a steady rate, like a sensor that reports every few seconds." Outside that range the device is not saved: "The maximum time without a message must be from 0 to 86400 s." A Sparkplug B device has none: its death certificate already says when it went away. 0
Group ID Sparkplug B only: the group the edge node publishes under, the level after spBv1.0 in its topics. "A Sparkplug B device needs its group id." empty
Edge node ID Sparkplug B only: the edge node that publishes the metrics, as its NBIRTH names it. "A Sparkplug B device needs its edge node id." empty
Device ID Sparkplug B only: the device behind the edge node, as its DBIRTH names it. Empty, shown as "The edge node itself", reads the edge node's own metrics. empty

The device has no host, port, credentials or poll interval of its own: all of that is the broker's. Each Sparkplug B ID is one topic level: "A Sparkplug B id may not contain '/', '+' or '#'." A broker names one Sparkplug B edge node or device once, so a second device with the same group, edge node and device IDs on that broker is refused: "Sparkplug B device Plant/Line1/Press1 on broker 'Plant' already belongs to device 'Press'." Plain devices name nothing of their own on the broker, and two of them may read the same topics.

The Base URI the header shows is the runtime address: mqtt://<broker id>?host=…&port=…&tls=0&keepAlive=60&clientId=…&mode=plain, with &silence=… when the device sets a maximum time without a message, and for a Sparkplug B device mode=sparkplug&group=…&node=…, with &device=… behind an edge node. It names the broker by its stable identity, never by its friendly name, and it never carries the username or the password.

A copy of an MQTT device stays on the same broker. A Sparkplug B copy takes the first identity no device on that broker has: its innermost ID, the device ID or, for the edge node's own metrics, the edge node ID, is numbered the way the copy's name is ("Press1 (2)").

Tag addressing

The address field is Topic on a Plain MQTT device and Metric on a Sparkplug B one, and it is required ("Topic is required for this driver."). A plain tag has three more fields; a Sparkplug B metric has none, because it carries its own type and is written by the protocol's own command.

Field Meaning Default
Topic The one topic the tag reads, e.g. plant/line1/temperature. "A tag reads one topic, so its topic cannot hold the wildcards + or #." required
Value path Where the value sits inside a JSON payload: members separated by dots and array indexes in brackets, e.g. sensors[0].value. A member whose name holds a dot or a bracket is written in quotes inside brackets (['flow.rate']), and a leading $. is accepted. Empty, shown as "Whole payload", reads the whole payload as the value. A malformed path: "Write the value path as members separated by dots and array indexes in brackets, e.g. sensors[0].value." empty
Command topic Where a write is published. Empty keeps the tag read only: a tag whose Access writes needs one ("A writable tag needs the topic its writes are published on."), and it holds no wildcard ("Nothing can be published on a filter: the command topic cannot hold + or #."). Equipment seldom listens on the topic it reports on, which is why this is a field of its own. empty
Payload template What a write publishes: JSON with {value} once, outside quotes, where the written value goes, e.g. {"setpoint": {value}}. Empty, shown as "The value as text", publishes the value as text. "The payload template holds exactly once, where the written value goes." / "Write the payload template as JSON, with outside quotes where a JSON value goes." empty
Metric Sparkplug B: the metric name as the birth certificate declares it, e.g. Inputs/Temperature. required

The data types are Boolean, Int16, Int32, Int64, Float, Double, String and DateTime. The read-only preview of the wire address is the topic followed by what applies of path, command and payload (plant/line1/temperature?path=sensors[0].value&command=plant/line1/setpoint), or the metric name; a character that would end a part (%, ?, &, =, #, +, a space) is written as %XX.

Reading

The session keeps the last message of every topic it subscribed to, and a tag reads from it:

Situation Plain MQTT tag Sparkplug B tag
The value The last message on the topic: the whole payload as text, or the JSON value at the value path, converted to the tag's type. The metric's value from the edge node's last birth or data message.
Its timestamp When the message arrived. A message the publisher retained gives the first value as soon as the station subscribes. The metric's own timestamp.
Nothing received yet The tag waits: no value and no error, as with any driver before its first answer. A topic whose publisher does not retain has no value until it publishes again. With a Maximum time without a message (s), the wait lasts that long from when the station began listening, and then the tag fails its read. The same until the birth certificate arrives; the station asks the edge node to announce itself again.
The publisher gone quiet, the broker still up With a Maximum time without a message (s), a tag whose topic has had no message for longer fails its read, and its policy for a device that did not answer decides what it shows; the next message brings it back. The time counts from the arrival of the last message, a retained one included. At 0 the tag keeps the last value it received. Does not apply: the death certificate says it.
A message the tag cannot read A failed read of that tag, with the reason: an empty message, a payload that is not JSON under a value path, nothing at the path, or a value the type cannot hold. A failed read of that tag: a metric the birth certificate does not declare, or a value its type cannot hold.
The broker unreachable Every device on the broker is disconnected, and its tags fail as any device's do. The same.
A death certificate (NDEATH, DDEATH) Does not apply. The edge node and its devices, or that one device, read no value until they are born again; every other device on the broker goes on.

A Boolean tag takes true or false, 1 or 0, on or off. An integer tag takes a fractional number rounded and refuses one outside its range. A DateTime tag takes ISO 8601 text or Unix milliseconds. A String tag takes the text as it is, and a JSON object or array as its raw JSON. A Sparkplug B metric of an unsigned type reads into the next wider signed one (UInt8 as Int16, UInt16 as Int32, UInt32 and UInt64 as Int64), so its whole range reads.

Writing

A write goes through the same funnel as any other driver's: the write box takes the engineering value, and the conversion stages are reversed before the raw value is published (see Connector).

Tag What is published QoS What the write reports
Plain MQTT The value on the tag's Command topic, as text (21.5, true, the text itself, a date in ISO 8601), or put into the Payload template as a JSON value (a number as a number, a boolean as true or false, a text quoted and escaped). 1, not retained Done when the broker acknowledges it; refused, with the broker's reason, when an MQTT 5 broker turns it down; no response when no acknowledgment comes in time.
Sparkplug B An NCMD (a metric of the edge node) or a DCMD (a metric of a device behind it) carrying the metric, under the alias its birth gave it and with the type its birth declared. 0, not retained, as the Sparkplug B specification requires of commands Done once sent. The edge node answers by publishing the new value, which is what the tag reads next.

While the broker is unreachable, or the edge node or device has announced its death, a write is refused as disconnected and nothing is sent. A Sparkplug B write to a metric the birth certificate does not declare is refused, since the edge node would not take it, and so is a value the metric's type cannot carry. A write is never retained: a retained setpoint would be applied again by every subscriber that connects later.

Publishing

With Publication format set to JSON, each row of a broker's Publish card is published on its topic, retained, at QoS 1, through the connection the broker's devices use: the station never opens a second one to a broker. A broker used only to publish needs no device. It stays under the MQTT driver, and its state (Connecting…, Online, Unreachable) is the state of that connection. It publishes while it is enabled and its list has rows; it has no switch of its own.

Source Message (JSON)
Tag {"value": 21.5, "quality": "good", "timestamp": "2026-10-04T12:30:15.250Z"}: the engineering value the tag shows; quality is good for a reading that landed, bad with value null for one that did not, and uncertain for a replacement value, for the last value held, and for readings that stopped arriving (the value last published stays); the stamp of the reading, in UTC.
Logic variable The same members: the variable's value and good while the variable is good, null and bad otherwise; the stamp of its last write or calculation.
Alarm {"active": true, "acked": false, "state": 1, "priority": "High", "message": "Oven above 80 °C", "timestamp": "2026-10-04T12:30:15.250Z"}: whether it is active and acknowledged, state as a number (0 normal, 1 active and unacknowledged, 2 active and acknowledged, 3 cleared and unacknowledged), the priority by name (Critical, High, Medium, Low), the message (empty when none) and the stamp of its last transition, as the OPC UA server serves it.

A value travels as its JSON type: a number, true or false, text, a date and time as ISO 8601 text; a number JSON cannot hold (NaN, an infinity) travels as null. A tag of a disabled device publishes null and bad.

Situation What is published
The value or the quality changes That row, checked five times a second. A tag read again at the same value is not published again: the stamp alone is no change.
The connection opens, or opens again Every row, with its present state.
The broker is down Nothing, and nothing waits in a queue: when the broker comes back, every row is published with its state at that moment.
The broker refuses a message, or does not acknowledge it It is tried again after 30 seconds. The journal says so under the Connector area and the component MQTT publication, once and then in a summary every few minutes while it lasts.
A source is deleted Nothing more; the broker keeps the last message it retained.
A row is deleted Nothing more; its retained message stays on the topic until something else is published there.

The station reads what it publishes and computes nothing for it: a tag's last reading, a variable's value, an alarm's state. It subscribes to no topic of a read-only row, so a message another system publishes there never reaches the station; a row set to Read and write takes values on its write topic (Writing back). An agent reads the lists with mqtt_publish_list and changes them with mqtt_publish_upsert and mqtt_publish_delete (Tools):

mqtt_publish_upsert(brokerId: "<id from mqtt_broker_list>", source: "Connector.Simulated.Oven.Temperature")
mqtt_publish_upsert(brokerId: "<id from mqtt_broker_list>", source: "Alarms.Oven.HighTemperature", topic: "plant/oven/alarms/high")
mqtt_publish_upsert(id: "<id from mqtt_publish_list>", access: "ReadAndWrite")
mqtt_publish_list()

Publishing in Sparkplug B

With Publication format set to Sparkplug B, the broker publishes the same list by the Sparkplug B specification, with the station as an edge node: a Sparkplug B host, Ignition's MQTT Engine for one, lists the station under its group and edge node, each device under it and every metric with its type, without any mapping built by hand. The connection is the one the broker's devices use, as in JSON.

Name What it is
Group Group ID, GanterLab while empty.
Edge node The station's name, with each /, + and # made _ (the three characters a Sparkplug B id cannot hold).
Device Each Connector device with a row of one of its tags, named the same way.
A tag's metric The tag's name, on its device.
A variable's metric Logic/<folders>/<variable>, on the edge node.
An alarm's metrics Five, on the edge node, under Alarms/<folders>/<alarm>/: Active and Acked (Boolean), State (a number, 0 normal, 1 active and unacknowledged, 2 active and acknowledged, 3 cleared and unacknowledged), Priority and Message (text).

A / inside one name becomes _, so the folders are the only levels. Two devices, or two metrics, whose names become the same take _2, _3 in the order of their own names. The rows keep their topics, which nothing is published on in Sparkplug B and which the list publishes on again when the broker goes back to JSON.

Message When
NDEATH, the edge node's death Handed to the broker as the connection's last will each time it opens, so the broker publishes it if the station drops off. The station also publishes it itself before it stops publishing: the broker set back to JSON, disabled or deleted, the list emptied, the group or the station's name changed, the runtime stopped.
NBIRTH, the edge node's birth Each time the connection opens, when a host asks for a rebirth (Node Control/Rebirth), and when a variable or an alarm joins, leaves or is renamed: every metric of the edge node with its present value, with bdSeq and Node Control/Rebirth.
DBIRTH, a device's birth After each NBIRTH, for every device, and for a device that joins the list: every tag of the device with its present value.
NDATA, DDATA Five times a second, the metrics whose value or quality changed: one message for the edge node, one per device.
DDEATH, a device's death When the last row of a device leaves the list, and before a device whose tags changed is born again.

Every message goes out at QoS 0, never retained, except the NDEATH. The sequence number seq starts at 0 with each NBIRTH and counts every message after it up to 255, then starts again; bdSeq counts the connections, so the NBIRTH and the NDEATH of one connection carry the same number. Nothing waits in a queue while the broker is down.

Type of the source Sparkplug B type
Boolean Boolean
SByte, Byte Int8, UInt8
Int16, UInt16 Int16, UInt16
Int32, UInt32 Int32, UInt32
Int64, UInt64 Int64, UInt64
Float, Double Float, Double
String String
DateTime DateTime
Guid, ByteString UUID, Bytes

A tag is published as its own type; a variable as the type the OPC UA server serves it as (a whole number as Int64, a real number as Double, a presentation value as text). Each metric carries its quality in the property Quality, the convention Sparkplug B hosts read: 192 good, 64 uncertain (with the value last published when the reading stopped arriving), 0 bad (the metric then has no value).

Command What the station does
NCMD Node Control/Rebirth Publishes NBIRTH and every DBIRTH again, with the present values.
NCMD or DCMD naming the metric of a row set to Read and write Writes the value to the source, as Writing back does: in the order commands arrive, recorded in the journal with the origin MQTT <broker name>.
Any other metric, a command without a value, or one the broker hands over retained Nothing is written; the journal says so under the Connector area and the component MQTT publication.

The station listens for commands to the edge node always, so a host can ask for a rebirth, and for commands to a device only while one of its rows is set to Read and write. Switching a broker to Sparkplug B makes its connection open again once, so the broker holds the edge node's last will; the broker's devices ride through it as through any reconnection. The station is an edge node and not a primary host application: it does not wait for a host's STATE message before it is born.

mqtt_broker_upsert(id: "<id from mqtt_broker_list>", publishFormat: "SparkplugB", sparkplugGroupId: "Plant1")
mqtt_publish_list()

Writing back

A row of a Publish card set to Read and write takes values on its write topic, the row's topic followed by /set: a cloud dashboard that reads plant/oven/setpoint sends a new setpoint on plant/oven/setpoint/set. The station writes it the way a client of its Modbus server or its OPC UA server writes a row marked read and write.

Situation What happens
A row is set to Read and write The station subscribes to its write topic, at QoS 1, on the connection the broker's devices use, and again each time that connection opens. A read-only row is not subscribed, so a message on its write topic never reaches the station.
A message arrives on the write topic {"value": 65} or the bare value (65, true, Recipe A) is written to the source, in the order messages arrive: a tag the way an operator's write reaches it, a memory variable as it is. A JSON number, true or false and JSON text arrive as what they carry; anything that is not JSON is read as text. The value is converted to the source's type the way every write is, and a value the type cannot hold is refused.
The message carries no value Nothing is written: an empty message, null, a JSON object without value, an array.
The broker hands over the message it retained Nothing is written. A broker hands it over when the station subscribes, and a setpoint left there an hour ago is not a value somebody sends now. A value published while the station listens is written, retained or not.
The source no longer takes writes Nothing is written: a tag set back to read only, a variable deleted.
The row is set back to Read only, moved to another topic or deleted, or the broker is disabled The station stops listening on that write topic within a moment, and a message that arrives meanwhile writes nothing.

The journal records each write with the origin MQTT <broker name> under the Connector area, as it records an operator's write. A message that was not written is recorded under the Connector area and the component MQTT publication, once and then in a summary every few minutes while it repeats.

The broker's username and password are the only guard: whoever the broker lets publish on a write topic can change that source, and on a cloud broker shared with other people that is more than the plant. Give the dashboard's account on the broker only the topics it needs. On a broker set to Sparkplug B the same rows take Sparkplug B commands (NCMD, DCMD) instead of a write topic (Publishing in Sparkplug B).

"C:\Program Files\mosquitto\mosquitto_pub" -h localhost -u dashboard -P <password> -t plant/oven/setpoint/set -m 65

Security

With Use TLS, the broker's certificate is accepted in one of two ways: its chain and host name validate against the Windows certificate store, as any HTTPS connection of this computer does, or you approved that very certificate under Equipment access on this driver's page. A broker whose certificate comes from a certificate authority of your own is trusted once that authority is installed in the Windows store, and one with a self-signed certificate needs nothing installed once its certificate is approved. Revocation is not checked, and nothing accepts a certificate on sight: an unknown one is refused, the broker reads Unreachable with the reason, and its devices wait for the approval. The password travels inside the TLS connection; without TLS it travels in the clear, as the card warns.

Equipment access

A broker with a self-signed certificate, or one issued by an authority of its own, presents a certificate no root installed in Windows vouches for. That root does not need to be installed on this computer. With Use TLS, the first connection is refused, the broker reads Unreachable with the reason, its devices read Waiting for certificate approval in the tree and on their pages, whose Review certificate button opens this card on the certificate, and the certificate waits under Equipment access at the end of the MQTT driver's page, the same card the OPC UA driver's page carries, until you decide:

Group What it lists What you can do
Waiting for approval Each refused certificate, under the name it claims: its SHA-256 fingerprint, the broker address it was met at, when it last tried and how many times, and its details (subject, issuer, domain names, validity dates). The one a device's Review certificate opened is marked Selected for review. Approve accepts the certificate from the session's next connection attempt. Block keeps refusing it without listing it here again. Discard all pending certificates empties the list without approving or blocking anything.
Approved equipment The certificates you approved or imported, with who decided and when, and the broker address each was last met at. Withdraw approval closes the broker's session and returns its certificate to the list. Import certificate approves a public certificate file you already have (.der, .cer, .crt or .pem) before the first connection.
Blocked equipment The certificates you blocked. A blocked certificate is refused even if Windows trusts it. Unblock returns the certificate to the list, without approving it.

Check the fingerprint with whoever manages the broker before you approve it. An approval accepts that certificate and no other, and only within its validity dates; it stands in for the root Windows lacks and for the name the certificate carries, so a broker reached by its IP address connects although its certificate names another host, and nothing else about the certificate is relaxed. When the broker answers an address whose certificate you approved with another one, its devices stop reading, the new one waits here, and the station raises "Certificate changed: <name>" with a Review button. A scan of the broker from the Discover devices card meets the same decisions. A certificate Windows already trusts is accepted as before and never appears here; nothing is approved automatically.

The broker's cause says the reason instead of a nameless TLS error: "The certificate of the broker at <host>:<port> (the first 16 characters of the fingerprint) is waiting for your approval under Equipment access on the MQTT driver's page. Reading starts once you approve it." A blocked one reads "…is blocked at this station. Review it under Equipment access on the MQTT driver's page." A certificate outside its validity dates, revoked or with a broken signature is refused with that reason and is never listed, since no approval makes it sound.

The decisions are kept in the station's data folder, under ua\pki\mqtt, and survive a restart. Only a role that may configure the Connector is drawn the verbs, and they act only in the station's own window: a browser on another device reads the card without changing it, and no agent tool approves a certificate (connector_browse reports the broker's devices with awaitingCertificate). The list holds up to 100 certificates waiting for approval; once it is full, a new one is still refused but not listed, and the card says so.

Prepare the broker

A broker has to accept connections from the station's computer, and to accept the station's sign-in. Eclipse Mosquitto 2, the most common broker, does neither out of the box: with no listener in its mosquitto.conf it listens only on its own computer, and once it has one it refuses a client without a user name. MQTT does not say which broker answered, so the station names Mosquitto as the common case, never as a certainty.

The broker answers What the broker's page says What changes it
Its computer refuses the port The broker accepts connections only from its own computer In mosquitto.conf, the line listener followed by the port (listener 1883), then a restart of the broker; the port allowed in its computer's firewall
It refuses a client without a user name The broker does not accept a client without a user name Username and Password on the Broker connection card, with an account of the broker; on Mosquitto 2 that account is made with mosquitto_passwd and named by password_file in mosquitto.conf, or anonymous clients are let in with allow_anonymous true
It refuses the user name or the password The broker's own refusal, pointing to Username and Password on the Broker connection card The account, on the broker or on the card

The first two are said on the broker's page, in place of its English error, with these steps and the port; its devices keep pointing up to the broker. The notice goes away by itself once the session connects. The tree's mark and the journal keep the English cause, which ends with the same Mosquitto steps.

Installing a broker

The station needs a broker to connect to, and so does the equipment. Where you have none, Eclipse Mosquitto is a small broker that runs as a Windows service:

  1. Download the Windows installer from the downloads page of mosquitto.org and run it. It installs the service Mosquitto Broker in C:\Program Files\mosquitto.

  2. Out of the box Mosquitto answers only on the computer it runs on. To let equipment on the network in, add a listener and a password file to mosquitto.conf in that folder:

    listener 1883
    password_file C:\Program Files\mosquitto\passwd
    

    Create the file with the account the station and the equipment will use (the command asks for the password):

    "C:\Program Files\mosquitto\mosquitto_passwd" -c "C:\Program Files\mosquitto\passwd" station
    
  3. Restart the service from an administrator prompt, and allow TCP port 1883 in through Windows Defender Firewall for the equipment's network:

    net stop mosquitto
    net start mosquitto
    
  4. On the broker's page, enter localhost as Host when the broker runs on the station's own computer, or that computer's address otherwise, Port 1883 and the account you created, then Enable.

To try a plain tag before the equipment is connected, publish a retained test value from the same folder and point a tag's Topic at it:

"C:\Program Files\mosquitto\mosquitto_pub" -h localhost -u station -P <password> -t plant/line1/temperature -m 21.5 -r

Limits worth knowing

  • The MQTT driver is a client, not a broker: the equipment and the station connect to a broker, the station's own MQTT broker or another one.
  • A broker's Publish card goes out as JSON or as Sparkplug B, never both at once. In Sparkplug B the station is an edge node, not a primary host application, and uses no aliases, templates or historical values.
  • The station is not a Sparkplug B host application: it publishes no STATE message. An edge node set to wait for a primary host before it publishes stays silent.
  • Plain MQTT says nothing about a publisher that went quiet: a plain tag keeps the last value it received while the broker connection holds, dated by its arrival, unless its device sets a Maximum time without a message (s). A topic published only when its value changes cannot tell a quiet publisher from a steady value, so its device keeps 0. Sparkplug B does say it, through the death certificate.
  • The session starts clean: what was published while the station was not connected is not replayed, and a topic's retained message is what gives its tag a value again.
  • A tag reads one topic, never a filter, and one value of it; a Sparkplug B metric that is an array, a data set, a template or a property set is not read.
  • Only TCP, with or without TLS: no MQTT over WebSockets, no client certificate, and no broker older than MQTT 3.1.1.
  • A device cannot be moved to another broker: create it under the other broker and delete the old one.