Skip to main content

Sparkplug B Driver

The Sparkplug B driver of PlantPulse Edge is a native Java implementation of an MQTT 3.1.1 client + Sparkplug B Payload Protobuf decoder. It has no dependency on external paho/tahu jars, so it works with no additional configuration when deployed as a single jar.

Source: plantpulse.driver.protocol.sparkplug.*

ClassResponsibility
SparkplugBDriverProtocolDriver implementation — connect/read/write/close, alias·metric cache
SparkplugMqttClientMQTT 3.1.1 self-contained client (CONNECT / SUBSCRIBE / PUBLISH / PINGREQ)
SparkplugBPayloadSparkplug B Payload protobuf wire encoder/decoder (varint / fixed32 / fixed64 / length-delim)

Operation Flow​

broker ──PUBLISH──> SparkplugMqttClient ──onMessage(topic, payload)──> SparkplugBDriver
│
├─ topic 파싱 (spBv1.0/G/<msgType>/edge[/dev])
├─ Payload 디코드 → metric list
├─ NBIRTH/DBIRTH 시 alias→name 등록
└─ metricCache[edge[/dev]/name] = formatValue()

PlantPulse 폴러 ──read(addr)──> metricCache[addr] (in-memory hit, 외부 RTT 없음)
write(addr, value) ──encode──> NCMD/DCMD publish → broker

Options​

KeyDefaultDescription
group-iddefaultSparkplug Group ID. Used as part of the wildcard when subscribing. Messages with a mismatching group are filtered out by the driver.
edge-node-id+Edge node filter. If +, all nodes within the group.
keep-alive60MQTT keep-alive in seconds. If 0, ping is disabled.
connect-timeout5000TCP connection timeout in ms.

The subscribe topic is composed of the spBv1.0/<group-id>/+/<edge-node-id>/# pattern (+ is the message_type, # is the device-part wildcard).


Sparkplug Datatype Mapping​

SparkplugBPayload.Metric.formatValue() converts to an appropriate String per wire datatype.

Sparkplug datatypeStored fieldformat result
Int8 / Int16 / Int32intValue (varint)signed int (Integer.toString)
UInt8 / UInt16 / UInt32intValueunsigned (& 0xFFFFFFFFL)
Int64 / DateTimelongValuesigned long
UInt64longValueLong.toUnsignedString
FloatfloatValue (fixed32)Float.toString
DoubledoubleValue (fixed64)Double.toString
BooleanbooleanValue (varint)"true" / "false"
String / Text / UUIDstringValueUTF-8 as-is
Bytes / FilebytesValuehex string

is_null=true metrics evaluate to an empty string.


Alias Learning in NBIRTH / DBIRTH​

Core to Sparkplug efficiency: BIRTH sends metric name and alias pairs → subsequent NDATA/DDATA only sends the alias.

1) edge1 의 NBIRTH:
metric { name="Temperature" alias=7 datatype=Float float_value=23.5 }
→ driver: aliasByPrefix.put("edge1", {7 → "Temperature"})
metricCache.put("edge1/Temperature", "23.5")

2) edge1 의 NDATA (이름 생략):
metric { alias=7 datatype=Float float_value=24.1 }
→ driver: name 없음 → alias=7 → "Temperature"
metricCache.put("edge1/Temperature", "24.1")

If NDATA arrives while NBIRTH is missing, the alias lookup fails and it is ignored (cache is not updated). It waits until the publisher republishes NBIRTH after the broker restarts.


NDEATH / DDEATH​

When an edge node or device terminates, the broker publishes NDEATH/DDEATH via LWT (Last Will and Testament). The driver only clears the alias map, keeping the cache intact — the last known value is preserved as the polling result (PlantPulse's monitor displays the lastValue UI as-is).


Sending NCMD / DCMD​

write(ProtocolAddress) automatically branches according to the number of address segments.

addresspublish topicNotes
edge1/SetPointspBv1.0/<group>/NCMD/edge1node-level command
edge1/dev1/SetPointspBv1.0/<group>/DCMD/edge1/dev1device-level command

Phase 1 publishes all commands with Sparkplug datatype String — the receiving side (Tahu / Ignition) converts it to the metric's defined datatype for processing.


Limitations and Future Work​

  • TLS / Authentication — Currently plaintext + anonymous only. username/password / mTLS options planned for later.
  • QoS — All publish/subscribe use QoS 0. Sparkplug recommends QoS 1 only for NBIRTH, and QoS 0 for NDATA/DDATA.
  • DataSet / Template metric — The wire preserves raw bytes (datasetRaw/templateRaw), but decode/format is not implemented.
  • Properties / Metadata — Only raw bytes are preserved; decode is not implemented.
  • bdSeq — bdSeq matching in NBIRTH / NDEATH verification is not implemented.

Test Location​

test/java/plantpulse/driver/protocol/sparkplug/SparkplugBDriverTest.java — 27 tests.

  • Initial state / option parsing
  • Safe read/write when not connected
  • BIRTH alias registration, NDATA alias-only resolve, group mismatch blocking, DEATH alias cleanup
  • formatValue accuracy per datatype (Int32 / Float / Double / Boolean / String / UInt32 / UInt64 / is_null)
  • Payload encode→decode round-trip
  • MQTT remaining length / utf8 wire utility
  • safeId clientId sanitization