MQTT Client Driver
PlantPulse Edge's MQTT client driver is a Java implementation that wraps the Eclipse Paho mqttv3 1.2.5 library. It subscribes to topics on an external IIoT broker (HiveMQ / Mosquitto / EMQX / AWS IoT Core / Azure IoT Hub, etc.), stores messages in an in-memory cache, and returns the cached value on each tag polling cycle. Writes are published immediately.
Source: plantpulse.driver.protocol.mqtt_client.*
| Class | Responsibility |
|---|---|
MQTTClientDriver | ProtocolDriver implementation — connect/read/write/close, topic cache, lazy subscribe |
Library: lib/org.eclipse.paho.client.mqttv3-1.2.5.jar
Differences from Sparkplug B
PlantPulse provides two separate MQTT-based drivers.
| Driver | Payload | Use Case |
|---|---|---|
MQTT_CLIENT (this page) | Arbitrary String/JSON/binary | General IIoT brokers, custom topics, ASCII / JSON payloads |
SPARKPLUG_B | Sparkplug B Payload (Protobuf) | Cirrus Link / Tahu / Ignition standard, NBIRTH/DBIRTH alias |
Sparkplug B includes its own wire codec and alias learning logic, so in environments that only need general MQTT, this driver is lighter and sufficient with the standard Paho API alone.
Operation Flow
broker ──PUBLISH──> Paho MqttClient ──MqttCallback.messageArrived(topic, payload)──> Driver
│
└─ lastValueByTopic.put(topic, new String(payload))
PlantPulse 폴러 ──read(addr)──> lastValueByTopic[addr] (in-memory hit)
write(addr, value) ──Paho.publish(topic, value, qos, retain=false)──> broker
On the first call to read, if a cache miss occurs, a lazy subscribe (with the QoS option value) is performed and an empty string is returned — the actual value arrives from the next cycle onward. Topics registered in tag_map are batch-subscribed via preSubscribeFromTagMap() immediately after connect, so values may arrive in the very first cycle (depending on publish frequency).
Options
| Key | Default | Description |
|---|---|---|
username | (none) | Broker authentication username |
password | (none) | Broker authentication password |
tls | false | true → ssl:// (default 8883), false → tcp:// (default 1883) |
qos | 0 | Subscribe / publish QoS (0 / 1 / 2). Values outside the range are automatically clamped. |
keep-alive | 60 | Keep-alive interval (seconds) — MqttConnectOptions.setKeepAliveInterval |
clean-session | true | MqttConnectOptions.setCleanSession |
client-id | (auto) | Explicit client ID. If not specified, PP-<opc_id>-<random6> (≤23 characters) is automatically generated |
MqttConnectOptions.setAutomaticReconnect(true) is always enabled, so automatic reconnection occurs after a temporary broker disconnection (Paho's default backoff applies).
Address Mapping
| Address | Behavior |
|---|---|
factory/line1/temp | Single topic — the last message for exactly this topic |
device/+/status | Wildcard (one level) — the last message among matching topics (most recently arrived) |
factory/# | Wildcard (multi-level) — last segment, all sub-topics |
"" / null | Returns empty |
Wildcard caution: Paho's callback does not cache a separate entry per actual arrival topic; the arriving topic itself becomes the key. In other words, even if you subscribe with device/+/status, lastValueByTopic will contain device/sensor01/status and device/sensor02/status separately, and if the user reads using the wildcard topic itself, there is no entry in the cache (since wildcards themselves are not publish destinations). When registering a wildcard pattern, register it as in device/+/status and then separately track the value of the last arrival topic as the read result — for general use, explicit registration per topic is recommended.
write (publish)
client.publish(topic, value.getBytes(), qos, /*retain=*/ false);
- The bytes from
valueare sent as the payload as-is (default charset: UTF-8). - Fixed to
retain=falseand cannot be changed — since the broker does not preserve the last message, clients that start subscribing later will not receive a value until the next write occurs. For consumers that need retained messages, set up a broker-side bridge or a separate publisher. - QoS uses the option value as-is. With QoS 1/2, Paho internally retransmits until acknowledgment.
Connection Lifecycle
| Method | Behavior |
|---|---|
connect() | Applies options → assembles broker URL → MqttClient.connect() → batch subscribes tag_map |
read() | Lazy subscribe on cache miss → returns value on cache hit |
write() | Publishes (retain=false) |
close() | disconnect() + close() (best-effort, exceptions ignored) → clears cache |
connectionLost(cause) | setConnected(false) in the callback — Paho attempts automatic reconnection |
Limitations and Future Work
- mTLS / client certificates — currently username/password only. X.509 mTLS environments like AWS IoT require a separate truststore/keystore configuration (Paho
MqttConnectOptions.setSocketFactoryexposure not implemented). - Retained / will message — currently fixed to
retain=false, LWT not set. - Wildcard cache separation — reading with the wildcard pattern itself returns an empty result (entries are keyed per arrival topic).
- JSON path extraction — even if the payload is JSON, the driver returns only the raw String. JSONPath extraction must be handled in the
format/ formula stage (or via a separate parser node). - Payload encoding — currently uses the platform default charset. Binary payloads may break under UTF-8 → consider exposing the
payload-encodingoption (utf8/iso-8859-1/base64) in the future.
Test Location
test/java/plantpulse/driver/protocol/mqtt_client/MQTTClientDriverTest.java — 31 tests.
- Initial state / meta (
isConnected/isWriteSupported/isExternal/getDriverSource) - Unconnected read/write/null/empty safety
getDebugBaseUrl— tcp/ssl, explicit port, default port (1883/8883)- Option parsing — username/password/tls/keep-alive/clean-session/qos/client-id, invalid values → default
- Static helpers —
clampQos,defaultClientId(length/random/null),nullIfEmpty,parseInt,parseBool - Constant verification (
DEFAULT_PORT,DEFAULT_PORT_TLS,DEFAULT_KEEP_ALIVE_SEC,DEFAULT_QOS)
Integration tests that depend on a real broker are separate — these unit tests operate without an external broker.