Type: how-to · Last reviewed: 2026-07-18
Applies to: Pulse Historian · edge/on-prem
Goal: receive freshest-value tag updates in real time by subscribing to Pulse Historian's MQTT fan-out — for a live wallboard, a local HMI, or a downstream integration.
Prerequisites:
MQTT_BROKER_USERNAME / MQTT_BROKER_PASSWORD).mosquitto_sub, MQTTX, or a library).The MQTT plane is a lossy, freshest-wins display feed, not the source of truth. The historian is written first and losslessly; MQTT publishing can drop the odd sample under load. For gap-free history, read the REST/SDK query path. Internals → Processing API § MQTT fan-out.
Publishing is gated by clarity.ingest.mqtt.publish. The code default is true, but the shipped clarity.properties sets it OFF — so on a stock install it must be enabled:
# clarity.properties
clarity.ingest.mqtt.publish=true
Restart the server (properties are read at startup). No fan-out messages are published until this is on.
Two topic families are published. The secondary family is always published and keys on the full resolved tag name — use it unless you specifically need unit-UID routing:
{clientId}/{ingestConfigId}/{tagName}
mosquitto_sub -h plant-server -p 1883 -u "$MQTT_USER" -P "$MQTT_PASS" \
-t 'acme-power-client/+/steam_pressure' -v
acme-power-client/42/steam_pressure [{"t":1720008000000,"v":78.4}]
The primary family is published only when the tag's unit resolves to a numeric UID:
u/{uid}/{tagName}/r u/{uid}/{tagName}/sd u/{uid}/{tagName}/v u/{uid}/{tagName}/e
Payload on every topic is a one-element array: [{"t": <epoch_ms>, "v": <number>}]. All messages are QoS 0 (no delivery guarantee, no retention).
Browser JS served from the app can reach the broker through the built-in bridge at GET /mqtt (a raw TCP↔WebSocket proxy to 127.0.0.1:1883) — no separate WS-MQTT proxy needed. Detail → MQTT § WebSocket Bridge.
GET /exactapi/ingest/health reports {"mqtt_connected": true}.mosquitto_sub on the secondary topic prints a line each time new data is ingested for that tag.| Symptom | Cause | Fix |
|---|---|---|
| Connection refused on :1883 | Broker not running / firewalled | Confirm the broker service is up; open port 1883 on the LAN |
| Connects but no messages | Fan-out gate off, or no ingest happening | Set clarity.ingest.mqtt.publish=true + restart; confirm ingest is live |
| Auth failure | Broker requires credentials | Pass -u/-P; the broker runs allow_anonymous false when credentials were provisioned |
Primary u/{uid}/… topics silent, secondary works |
Unit didn't resolve to a UID | Use the secondary {clientId}/{configId}/{tag} topic; it's always published |
| Occasional gaps | Fan-out is lossy under load (queue cap 8, freshest-wins) | Expected — use the historian query path for complete data |