Skip to main content
Version: Next

MQTT

The MQTT driver (io.scadex.driver.mqtt) connects the gateway to MQTT brokers. Each tag bound to it subscribes to a topic filter and decodes the payload into a value; a write to such a tag publishes to its topic. This page is for engineers who set up broker connections and tags.

It is built on MQTTnet and declares SDK contract 1.2.

What has been proven​

The driver has run against Mosquitto. No other broker has been tried.

Adding a connection​

Open Connections, then Devices, then MQTT (/connections/mqtt), and add a broker. The form is rendered from the driver's descriptor:

FieldKeyMeaningDefault
Broker URLurlRequired. Where the broker listens, such as mqtt://host:1883. See the schemes below.none
UsernameusernameCredentials sent at connect.empty
PasswordpasswordSecret; stored encrypted with the module's state and never returned by the API.empty
Client IDclientIdThe MQTT client id. Empty means scadex-<connection id>.empty
Default QoSqos0 (at most once), 1 (at least once) or 2 (exactly once). Used for every subscription and every publish of this connection.0

Every connection also has a unique name and an Enabled switch.

URL schemes​

SchemeTransportDefault port
mqtt (also tcp)TCP, plain1883
mqtts (also ssl, tls)TCP with TLS8883
wsWebSocket, plainthe port in the URL
wssWebSocket with TLSthe port in the URL

Any other scheme is refused with "Unsupported broker scheme". A username or password inside the URL is refused: put them in their own fields. With a TLS scheme the driver turns TLS on with the library's defaults; there are no further TLS options (no client certificate, no custom CA) in this version.

The session is opened with a clean session. The saved password is reused only while the host stays the same.

Test​

Test connection connects with a throwaway client id (<clientId or scadex>-test-<random>), gives up after 5 seconds, and reports success and latency. The running link is not touched.

  • The driver connects with a 10-second timeout and retries every 5 seconds after a failure or disconnect. While disconnected every bound tag is marked bad with its last value kept.
  • Each bound tag subscribes to its topic filter at the connection's QoS. A filter already held is re-subscribed, which also makes the broker resend what is retained.
  • A message larger than 512 KiB marks the tag bad with "The ... byte message is too large." Queued payloads are capped at 64 MiB per connection; beyond that samples are dropped and counted as Dropped samples.
  • Restarting a tag from the tag browser re-subscribes it, so the broker sends its retained value again.

Topics and tags​

A tag bound to this driver has the binding document:

{ "topic": "plant/line1/temperature", "payloadFormat": "number" }
FieldMeaning
topicA topic or a topic filter. + matches one level and # the rest; # must be last and alone in its level, + alone in its level. A filter starting with # or + does not match topics starting with $.
payloadFormatHow to decode the payload. Missing or raw keeps the text.
payloadFormatDecoding
raw (default)The payload as a UTF-8 string
numberA finite decimal number, otherwise bad quality "... is not a number."
booleantrue, 1, on or false, 0, off, case-insensitive; otherwise bad
jsonParsed JSON: an object or array becomes a JSON value, a scalar becomes that scalar; invalid JSON is bad

There is no JSON path selection: a json payload lands as one value. Pick a field in an expression tag or a derived member instead (see Tags).

Values are published with the gateway's receive time as the timestamp and quality Good when decoding succeeds.

Browsing for topics​

Browse topics on a connection page starts a scan: the driver subscribes to the connection's scan filters (the saved scanTopics, or # when none are set) and lists every topic it sees, with the first 256 bytes of the last payload and whether it was retained. Up to 5,000 topics are kept. The first look waits one second for retained messages. The scan subscription is dropped again two minutes after the last browse, so the broker is not flooded with wildcard traffic for longer than someone is looking.

Pick topics and choose Create a tag. The dashboard asks the driver for proposals (discover), which bind each topic with payloadFormat: "raw", and submits them to the gateway's tag API. Change the format on the tag afterwards. The suggested tag name is the last segment of the topic after /, ., ; or =, non-alphanumeric characters replaced by _.

Writing​

A write to a bound tag publishes the value to its topic at the connection's QoS, not retained. The payload is text: true/false for booleans, the shortest round-trip form for numbers, base64 for bytes, an empty payload for null, and the string form otherwise. A tag bound to a wildcard filter cannot be written. If the broker refuses the publish the write fails with "The broker refused the write: <reason>".

Connection status​

Each connection is published every two seconds under @gateway.connection.mqtt.<name>.*, where <name> is the connection name turned into a path segment. The leaves are name, state (disabled, connecting, connected or faulted), connected, enabled, address, tags, error and since, plus the driver's details: valuesPerSecond, messagesPerSecond, good, uncertain, bad and waiting (bound tags by last quality), topics (topics seen), lastMessage (ISO 8601) and dropped. Numbers and booleans are real tags with history. The connection page shows Tags subscribed, Topics seen, Last message and Dropped samples.

Routes​

The baseline driver routes are mapped under /api/v1/modules/io.scadex.driver.mqtt: GET|POST /connections, GET|PUT|DELETE /connections/{connectionId}, POST /connections/{connectionId}/test, GET /connections/{connectionId}/tags, POST /connections/{connectionId}/browse and POST /connections/{connectionId}/discover. The module adds:

RequestPurpose
GET /servers, GET /servers/{id}The saved brokers, passwords redacted
POST /servers, PUT /servers/{id}, DELETE /servers/{id}Create (name, url, username, password, clientId, qos, enabled), update and delete
GET /statusWhether the module runs and, per connection, connected, tagCount, topicCount, droppedSamples, lastMessageAt and error
GET /servers/{id}/tagsThe tags bound to this broker with their topic and payloadFormat
GET /servers/{id}/browseThe topics seen by the scan: topic, value, retained, source
PUT /servers/{id}/browseBody {"topics": [...]}: the filters the scan subscribes to; an invalid filter answers 400

GET routes need the viewer role; the others need engineer. While the module is stopped these routes answer 503; see Modules.