Industrial IoT

Eclipse Tahu: Sparkplug Reference Implementations in Java, Python and C

Eclipse Tahu is the reference implementation of Sparkplug. This guide tours its Java, Python and C libraries, shows how to build them, and connects a Tahu-based Python edge node to an MQTT broker.

Eclipse tahu libraries in Java, Python and C connecting a Sparkplug edge node to an MQTT broker
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

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.

  1. 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 pin pip install "paho-mqtt<2" for the examples, or create the client as mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, client_id=...) to keep the old callback signatures.
  2. Protobuf runtime mismatch. A sparkplug_b_pb2.py generated by an old protoc can fail on a new protobuf package with "Descriptors cannot be created directly". Regenerate it with protoc --python_out=. sparkplug_b.proto using a current compiler.
  3. Import paths. The examples import the core helpers with a relative path insert. If you copy an example elsewhere, copy sparkplug_b.py and sparkplug_b_pb2.py alongside it.
  4. Hardcoded connection settings. Examples point at localhost by default. Edit the broker host, port, group ID, edge node ID and credentials at the top of the file.
  5. 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.

Related articles

IIoT Edge Gateway: What It Does and How to Choose One

An IIoT edge gateway connects machines that speak Modbus, OPC UA or proprietary protocols to MQTT and the cloud. Learn its core roles, common architectures and how to choose the right one.

7 min read

Industry 5.0 vs Industry 4.0: Human-Centric Manufacturing Explained

Industry 5.0 does not replace Industry 4.0. It adds human-centric, sustainable and resilient goals on top of the same connected technologies. This comparison explains the differences and why live plant data remains the foundation for both.

8 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.