Eclipse Tahu is the open source reference implementation of the Sparkplug specification, with libraries and examples in Java, Python and C. In this guide you will learn what each part of the Tahu repository does, how to build it, the pitfalls that trip people up on first use, and how to connect a Tahu-based edge node to a real MQTT broker and watch its births and data arrive.
What Eclipse Tahu Is and Is Not
Sparkplug is governed at the Eclipse Foundation, and Tahu is the project that turns the specification into working code. It provides three things:
- The Protobuf definition for Sparkplug B payloads,
sparkplug_b.proto, plus code generated from it for each language. - Helper libraries that build and parse payloads: creating BIRTH and DEATH payloads, adding metrics with the right datatypes, handling sequence numbers.
- Examples and compatibility tooling that show a minimal edge node and host application, and help vendors check their implementations against the specification.
What Eclipse Tahu is not: it is not an MQTT broker, not a SCADA system and not a gateway product. Every Tahu example connects as an ordinary MQTT client to a broker you provide. That separation is the whole point of Sparkplug. The edge node and host speak Sparkplug, and the MQTT broker in the middle only has to be a compliant MQTT server.
Tahu is also reference code. It is written to be clear and correct rather than to be dropped unchanged into a production gateway. Many teams use the Protobuf definitions and payload helpers directly and write their own session handling around them, which is exactly what we will do below.
A Tour of the Repository
The source lives in the Eclipse Tahu repository on GitHub. Clone it to explore:
git clone https://github.com/eclipse-tahu/tahu.git
cd tahu
ls
The layout is organised by language. Folder names have shifted between releases, so treat this table as a map rather than an exact listing, and check the README of the branch you clone.
| Area | What you find | Typical use |
|---|---|---|
sparkplug_b (proto) |
The .proto definition for Sparkplug B payloads |
Generate bindings for any language |
java |
Core library, payload builders and decoders, edge and host examples, compatibility tests | JVM gateways and host applications |
python |
core with sparkplug_b.py helpers and generated sparkplug_b_pb2.py, plus examples |
Scripting, simulators, test tools |
c |
Core payload encoding built on nanopb, plus an example client | Embedded gateways and constrained devices |
javascript |
Payload encoding helpers for Node.js | Node-based tools and Node-RED style flows |
Java
The Java implementation is the most complete. It contains model classes for payloads and metrics, encoders and decoders, and higher-level edge node and host examples. It builds with Maven:
cd java
mvn clean install -DskipTests
The resulting artifacts can be used from your own Maven or Gradle project. Java is the natural choice if you are writing a host application or integrating with a JVM-based SCADA or MES.
Python
The Python folder contains two files you will use constantly. sparkplug_b_pb2.py is the generated Protobuf module, and sparkplug_b.py adds helpers such as getNodeBirthPayload, getDeviceBirthPayload, getDdataPayload, getNodeDeathPayload and addMetric, plus a MetricDataType enumeration. The examples folder shows a simple edge node that publishes births, sends data and answers commands.
C
The C implementation encodes and decodes Sparkplug payloads using nanopb, a small Protobuf runtime designed for microcontrollers. It is the starting point if you need Sparkplug on an embedded Linux gateway or firmware with tight memory. The example client pairs the encoder with an MQTT library; you build it with the provided Makefile after installing that library's development headers.
Common Eclipse Tahu Pitfalls
Most first-day problems with Tahu are environment problems rather than Sparkplug problems.
- paho-mqtt 2.x breaks older Python examples. The Tahu Python examples were written against paho-mqtt 1.x. With paho-mqtt 2.x,
mqtt.Client()requires a callback API version as its first argument. Either pinpip install "paho-mqtt<2"for the examples, or create the client asmqtt.Client(mqtt.CallbackAPIVersion.VERSION1, client_id=...)to keep the old callback signatures. - Protobuf runtime mismatch. A
sparkplug_b_pb2.pygenerated by an oldprotoccan fail on a newprotobufpackage with "Descriptors cannot be created directly". Regenerate it withprotoc --python_out=. sparkplug_b.protousing a current compiler. - Import paths. The examples import the core helpers with a relative path insert. If you copy an example elsewhere, copy
sparkplug_b.pyandsparkplug_b_pb2.pyalongside it. - Hardcoded connection settings. Examples point at
localhostby default. Edit the broker host, port, group ID, edge node ID and credentials at the top of the file. - Shared group IDs on public brokers. Sparkplug topics always start with
spBv1.0, so on a public broker use a random group ID to avoid colliding with other people's tests.
Connecting a Tahu Edge Node to an MQTT Broker
The following script uses Tahu's Python helpers with paho-mqtt 2.x. Copy sparkplug_b.py and sparkplug_b_pb2.py from Tahu's Python core folder into an empty directory, add this file next to them, and install the dependencies with pip install "paho-mqtt>=2.0" protobuf.
import random
import time
import paho.mqtt.client as mqtt
from sparkplug_b import (
MetricDataType,
addMetric,
getDdataPayload,
getDeviceBirthPayload,
getNodeBirthPayload,
getNodeDeathPayload,
)
BROKER = "broker.connectmq.com"
PORT = 1883
GROUP = "tahu-demo-5mz8" # random group ID for the public broker
NODE = "tahu-edge-01"
DEVICE = "pump-01"
CLIENT_ID = f"tahu-edge-{random.randint(1000, 9999)}"
def t(msg_type, device=None):
base = f"spBv1.0/{GROUP}/{msg_type}/{NODE}"
return f"{base}/{device}" if device else base
def publish_births(client):
nbirth = getNodeBirthPayload()
addMetric(nbirth, "Node Control/Rebirth", None, MetricDataType.Boolean, False)
client.publish(t("NBIRTH"), bytearray(nbirth.SerializeToString()), 0, False)
dbirth = getDeviceBirthPayload()
addMetric(dbirth, "Flow", None, MetricDataType.Float, 12.5)
addMetric(dbirth, "Pressure", None, MetricDataType.Float, 3.1)
addMetric(dbirth, "Running", None, MetricDataType.Boolean, True)
client.publish(t("DBIRTH", DEVICE), bytearray(dbirth.SerializeToString()), 0, False)
print("NBIRTH and DBIRTH published")
def on_connect(client, userdata, flags, reason_code, properties):
print("connected:", reason_code)
client.subscribe(t("NCMD"))
publish_births(client)
def on_message(client, userdata, msg):
print("command received on", msg.topic)
publish_births(client) # treat any NCMD as a rebirth request in this demo
# The death payload must be built before the birth so both carry the same bdSeq
death = getNodeDeathPayload()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.on_message = on_message
client.will_set(t("NDEATH"), bytearray(death.SerializeToString()), 1, False)
client.connect(BROKER, PORT, 60)
client.loop_start()
try:
while True:
time.sleep(3)
ddata = getDdataPayload()
addMetric(ddata, "Flow", None, MetricDataType.Float, round(random.uniform(11, 14), 2))
addMetric(ddata, "Pressure", None, MetricDataType.Float, round(random.uniform(2.8, 3.4), 2))
client.publish(t("DDATA", DEVICE), bytearray(ddata.SerializeToString()), 0, False)
print("DDATA published")
except KeyboardInterrupt:
client.loop_stop()
client.disconnect()
The order of getNodeDeathPayload() and getNodeBirthPayload() matters because Tahu's helpers manage the bdSeq counter internally, and the NDEATH registered as the will must carry the same value as the NBIRTH that follows. If your copy of the helpers behaves differently, inspect the bdSeq metric in both payloads and adjust.
Run the script and subscribe to the group in another terminal:
mosquitto_sub -h broker.connectmq.com -p 1883 -i tahu-watch-5mz8 -t "spBv1.0/tahu-demo-5mz8/#" -v
You will see the topics arrive with binary payloads. To read the metrics, open the traffic in a ConnectMQ portal: the live topic explorer decodes Sparkplug B payloads, whether Protobuf or JSON, into named metrics with datatypes and values. That makes it quick to confirm that Tahu produced what you expected before a SCADA host is involved. The broker you just used is the free public MQTT broker; it is readable by anyone, so keep real tag names and values off it.
From Reference Code to a Real Edge Node
Once the example works, a production edge node needs more than Tahu provides out of the box:
- Persistent bdSeq. Store the counter on disk so it increments across process restarts.
- Report by exception. Publish only changed values, with deadbands for noisy analog signals.
- Store and forward. Buffer data during broker outages and replay it with historical flags.
- Aliases. Enable Sparkplug metric aliases on constrained links.
- Primary host awareness. In Sparkplug 3.0, edge nodes can wait for the primary host's STATE message before publishing births.
- TLS and credentials. Switch to port 8883 with per-client credentials.
If you want a fully self-contained script without the Tahu helpers, our Sparkplug B edge node simulator builds payloads directly from the generated Protobuf module and handles sequence numbers and rebirths explicitly. For payload internals, see Sparkplug B data types. The specification itself is published by the Eclipse Sparkplug working group.
For production, connect Tahu-based nodes to a private broker. On ConnectMQ, create a broker in the portal, copy the generated YOUR_USERNAME, YOUR_PASSWORD and YOUR_CLIENT_ID, set the client ID in the script, add client.username_pw_set(...) and client.tls_set(), and connect to port 8883. ConnectMQ can then republish the decoded Sparkplug B data into ISA-95 topics such as connectmq/factory1/production/line1/cnc1/telemetry, which non-Sparkplug consumers can read as plain data. The broader architecture is covered in our industrial IoT MQTT guide.
Connect Eclipse Tahu to ConnectMQ
Tahu gives you correct payloads; you still need a dependable broker between edge and host. ConnectMQ is a hosted MQTT broker with MQTT 3.1.1 and 5.0, TLS on 8883, Sparkplug B decoding and ISA-95 republishing. Create a broker on the free Serverless plan at app.connectmq.com, no credit card required, and point your Tahu edge node at it in a few minutes.
Frequently Asked Questions
Is Eclipse Tahu a broker?
No. Eclipse Tahu provides Sparkplug libraries and examples. Edge nodes and hosts built with it connect as MQTT clients to any compliant MQTT broker.
Which Tahu language should I use?
Use Java for host applications and JVM gateways, Python for scripting, simulators and tests, and C for embedded gateways with tight memory budgets.
Why do the Tahu Python examples fail with paho-mqtt 2.x?
paho-mqtt 2.x requires a callback API version when creating the client. Pin paho-mqtt below 2.0 for the examples, or pass mqtt.CallbackAPIVersion.VERSION1 to keep their callback signatures.
Is Tahu production ready?
It is reference code focused on correctness. Many teams use its Protobuf definitions and payload helpers in production while adding their own session management, buffering and security.



