Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ examples/multithread/multithread
examples/wiot/wiot
examples/pub-sub/mqtt-pub
examples/pub-sub/mqtt-sub
examples/sparkplug/sparkplug
examples/websocket/websocket_client

# eclipse
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ if (WOLFMQTT_EXAMPLES)
add_mqtt_example(fwclient firmware/fwclient.c)
add_mqtt_example(mqtt-pub pub-sub/mqtt-pub.c)
add_mqtt_example(mqtt-sub pub-sub/mqtt-sub.c)
add_mqtt_example(sparkplug sparkplug/sparkplug.c)
endif()

####################################################
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,16 @@ The multi-threading feature can also be used with the non-blocking socket (--ena

If you are having issues with thread synchronization on Linux consider using not the conditional signal (`WOLFMQTT_NO_COND_SIGNAL`).

### Sparkplug Example
This example demonstrates the [Sparkplug B](https://sparkplug.eclipse.org/) industrial IoT protocol specification. Sparkplug defines a standard MQTT topic namespace and payload format for SCADA and industrial automation systems. The example creates two MQTT clients that communicate using Sparkplug:

* **Edge Node**: Publishes birth certificates (NBIRTH), device data (DDATA), and responds to commands (DCMD)
* **Host Application**: Subscribes to Sparkplug topics, receives telemetry data, and sends device commands

The example simulates sensor metrics (temperature, humidity, LED state) and demonstrates command/control where the Host toggles the Edge Node's LED. For full two-client communication, build with `--enable-mt`. The example is located in `/examples/sparkplug/`.

More details in [examples/sparkplug/README.md](examples/sparkplug/README.md)

### Atomic publish and subscribe examples
In the `examples/pub-sub` folder, there are two simple client examples:
* mqtt-pub - publishes to a topic
Expand Down
19 changes: 16 additions & 3 deletions examples/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ noinst_PROGRAMS += examples/mqttclient/mqttclient \
examples/nbclient/nbclient \
examples/multithread/multithread \
examples/pub-sub/mqtt-pub \
examples/pub-sub/mqtt-sub
examples/pub-sub/mqtt-sub \
examples/sparkplug/sparkplug
if BUILD_SN
noinst_PROGRAMS += examples/sn-client/sn-client \
examples/sn-client/sn-client_qos-1 \
Expand All @@ -32,7 +33,8 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \
examples/mqttport.h \
examples/nbclient/nbclient.h \
examples/multithread/multithread.h \
examples/pub-sub/mqtt-pub-sub.h
examples/pub-sub/mqtt-pub-sub.h \
examples/sparkplug/sparkplug.h
if BUILD_SN
noinst_HEADERS += examples/sn-client/sn-client.h
endif
Expand Down Expand Up @@ -151,6 +153,15 @@ examples_pub_sub_mqtt_sub_LDADD = src/libwolfmqtt.la
examples_pub_sub_mqtt_sub_DEPENDENCIES = src/libwolfmqtt.la
examples_pub_sub_mqtt_sub_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS)


# Sparkplug B Example (two-client industrial IoT)
examples_sparkplug_sparkplug_SOURCES = examples/sparkplug/sparkplug.c \
examples/mqttnet.c \
examples/mqttexample.c
examples_sparkplug_sparkplug_LDADD = src/libwolfmqtt.la
examples_sparkplug_sparkplug_DEPENDENCIES = src/libwolfmqtt.la
examples_sparkplug_sparkplug_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS)

# WebSocket example
if BUILD_WEBSOCKET
noinst_HEADERS += examples/websocket/net_libwebsockets.h
Expand Down Expand Up @@ -185,6 +196,7 @@ dist_example_DATA+= examples/sn-client/sn-multithread.c
endif
dist_example_DATA+= examples/pub-sub/mqtt-pub.c
dist_example_DATA+= examples/pub-sub/mqtt-sub.c
dist_example_DATA+= examples/sparkplug/sparkplug.c
if BUILD_WEBSOCKET
dist_example_DATA+= examples/websocket/websocket_client.c
dist_example_DATA+= examples/websocket/net_libwebsockets.c
Expand All @@ -199,7 +211,8 @@ DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \
examples/nbclient/.libs/nbclient \
examples/multithread/.libs/multithread \
examples/pub-sub/mqtt-pub \
examples/pub-sub/mqtt-sub
examples/pub-sub/mqtt-sub \
examples/sparkplug/.libs/sparkplug
if BUILD_SN
DISTCLEANFILES+= examples/sn-client/.libs/sn-client \
examples/sn-client/.libs/sn-client_qos-1 \
Expand Down
195 changes: 195 additions & 0 deletions examples/sparkplug/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# Sparkplug B Example

This example demonstrates the [Sparkplug B](https://sparkplug.eclipse.org/) industrial IoT protocol specification using wolfMQTT. It creates two MQTT clients that communicate using the Sparkplug topic namespace and message types.

## Overview

Sparkplug B is an open-source specification designed for industrial IoT and SCADA systems. It defines:

- **Topic Namespace**: `spBv1.0/{group_id}/{message_type}/{edge_node_id}[/{device_id}]`
- **Message Types**: Birth/Death certificates, Data, and Commands
- **Payload Format**: Google Protocol Buffers (this example uses a simplified encoding)

## Architecture

The example implements two clients:

### Edge Node (Publisher)
- Represents an industrial device or gateway
- Publishes **NBIRTH** (Node Birth Certificate) on startup
- Publishes **DDATA** (Device Data) with sensor metrics periodically
- Subscribes to **DCMD** (Device Command) topics to receive commands
- Configures **NDEATH** (Node Death Certificate) as Last Will and Testament

### Host Application (Subscriber)
- Represents a SCADA or supervisory system
- Subscribes to all Sparkplug messages in the group (`spBv1.0/{group}/#`)
- Receives and processes birth certificates and device data
- Sends **DCMD** (Device Command) messages to control devices

## Message Types

| Type | Description |
|------|-------------|
| NBIRTH | Node Birth Certificate - Edge node announces itself |
| NDEATH | Node Death Certificate - Edge node goes offline (LWT) |
| DBIRTH | Device Birth Certificate - Device announces itself |
| DDEATH | Device Death Certificate - Device goes offline |
| NDATA | Node Data - Metrics from the edge node |
| DDATA | Device Data - Metrics from a device |
| NCMD | Node Command - Command to the edge node |
| DCMD | Device Command - Command to a specific device |
| STATE | Host Application state |

Comment on lines +32 to +43
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The markdown tables in this README use || instead of standard table | delimiters (e.g., the “Message Types” table). As written, these won’t render as tables on GitHub. Convert them to proper GitHub-flavored markdown tables (single | with a header separator row).

Copilot uses AI. Check for mistakes.
## Simulated Metrics

The Edge Node simulates the following sensor data:

| Metric | Type | Description |
|--------|------|-------------|
| Temperature | Float | Simulated temperature in Celsius |
| Humidity | Float | Simulated humidity percentage |
| LED | Boolean | LED on/off state (controllable via command) |
| Counter | UInt32 | Message counter |

## Building

### Autotools

```bash
# Single-threaded (Edge Node only)
./configure --disable-tls
make

# Multi-threaded (both clients communicate)
./configure --enable-mt --disable-tls
make
```

### CMake

```bash
mkdir build && cd build

# Single-threaded
cmake -DWOLFMQTT_TLS=no ..
make sparkplug

# Multi-threaded
cmake -DWOLFMQTT_TLS=no -DWOLFMQTT_MT=yes ..
make sparkplug
```

## Running

```bash
# Using default broker (test.mosquitto.org)
./examples/sparkplug/sparkplug

# Specify broker
./examples/sparkplug/sparkplug -h <broker_host> -p <port>

# With TLS (if built with TLS support)
./examples/sparkplug/sparkplug -h <broker_host> -p 8883 -t
```

### Command-Line Options

| Option | Description |
|--------|-------------|
| `-h <host>` | MQTT broker hostname (default: test.mosquitto.org) |
| `-p <port>` | MQTT broker port (default: 1883) |
| `-t` | Enable TLS |
| `-c <file>` | TLS CA certificate file |
| `-q <qos>` | QoS level (0, 1, or 2) |
| `-C` | Clean session |

## Example Output

```
Sparkplug B Example
===================
This example demonstrates two MQTT clients communicating
using the Sparkplug B industrial IoT protocol.

Starting Edge Node and Host Application threads...

Sparkplug: Connecting WolfMQTT_Sparkplug_Edge to broker test.mosquitto.org:1883...
Sparkplug: Connected! (client_id=WolfMQTT_Sparkplug_Edge)
Sparkplug: Subscribing to spBv1.0/WolfMQTT/DCMD/EdgeNode1/#
Sparkplug: Subscribed (granted QoS=1)
Sparkplug: Published NBIRTH to spBv1.0/WolfMQTT/NBIRTH/EdgeNode1

Sparkplug: Connecting WolfMQTT_Sparkplug_Host to broker test.mosquitto.org:1883...
Sparkplug: Connected! (client_id=WolfMQTT_Sparkplug_Host)
Sparkplug: Subscribing to spBv1.0/WolfMQTT/#
Sparkplug: Subscribed (granted QoS=1)

Sparkplug [WolfMQTT_Sparkplug_Host]: Received NBIRTH from WolfMQTT/EdgeNode1
-> Edge Node came online (bdSeq=0)

Sparkplug: Published DDATA to spBv1.0/WolfMQTT/DDATA/EdgeNode1/Device1
Sparkplug [WolfMQTT_Sparkplug_Host]: Received DDATA from WolfMQTT/EdgeNode1/Device1
-> Device data received:
Temperature = 22.83
Humidity = 45.36
LED = OFF
Counter = 1

Sparkplug [Host]: Sending command to toggle LED ON
Sparkplug [Host]: Sending DCMD to spBv1.0/WolfMQTT/DCMD/EdgeNode1/Device1 (LED=ON)

Sparkplug [WolfMQTT_Sparkplug_Edge]: Received DCMD from WolfMQTT/EdgeNode1/Device1
-> Command received:
LED set to ON

Sparkplug: Published DDATA to spBv1.0/WolfMQTT/DDATA/EdgeNode1/Device1
Sparkplug [WolfMQTT_Sparkplug_Host]: Received DDATA from WolfMQTT/EdgeNode1/Device1
-> Device data received:
Temperature = 23.10
Humidity = 45.01
LED = ON
Counter = 2

Sparkplug: Disconnecting WolfMQTT_Sparkplug_Host...
Sparkplug: Disconnected WolfMQTT_Sparkplug_Host
Sparkplug: Disconnecting WolfMQTT_Sparkplug_Edge...
Sparkplug: Disconnected WolfMQTT_Sparkplug_Edge

Sparkplug example completed!
```

## Configuration

The following constants can be modified in `sparkplug.h`:

```c
#define SPARKPLUG_NAMESPACE "spBv1.0"
#define SPARKPLUG_GROUP_ID "WolfMQTT"
#define SPARKPLUG_EDGE_NODE_ID "EdgeNode1"
#define SPARKPLUG_DEVICE_ID "Device1"
#define SPARKPLUG_HOST_ID "HostApp1"
```

## Payload Format

This example uses a simplified binary payload format for demonstration purposes. Production Sparkplug implementations should use the official [Sparkplug B Protocol Buffer definitions](https://github.com/eclipse/tahu/tree/master/sparkplug_b).

The simplified format encodes:
- Timestamp (8 bytes)
- Sequence number (8 bytes)
- Metric count (4 bytes)
- For each metric: name, alias, timestamp, datatype, and value

## Notes

- **Multi-threading Required**: For full two-client communication, build with `--enable-mt` (Autotools) or `-DWOLFMQTT_MT=yes` (CMake). In single-threaded mode, only the Edge Node runs.
- **Birth/Death Sequence**: The `bdSeq` metric in NBIRTH and NDEATH allows hosts to correlate birth and death messages.
- **Sequence Numbers**: Data messages include a sequence number (0-255) for ordering and gap detection.
- **Last Will and Testament**: The Edge Node configures NDEATH as its LWT so the broker publishes it if the client disconnects unexpectedly.

## See Also

- [Sparkplug Specification](https://sparkplug.eclipse.org/specification/version/3.0/documents/sparkplug-specification-3.0.0.pdf)
- [Eclipse Tahu](https://github.com/eclipse/tahu) - Reference Sparkplug implementations
- [wolfMQTT Documentation](https://www.wolfssl.com/documentation/wolfMQTT-Manual.pdf)
Loading