Add Sparkplug B Industrial IoT Example#461
Conversation
New example demonstrating the Sparkplug B industrial IoT protocol:
- Edge Node client publishes sensor data and responds to commands
- Host Application client subscribes to data and sends commands
- Implements Sparkplug topic namespace (spBv1.0/{group}/{type}/{node}[/{device}])
- Demonstrates NBIRTH, NDEATH, DDATA, and DCMD message types
- Includes simplified payload encoding (not full protobuf)
- Supports both single-threaded and multi-threaded builds
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
README covers: - Protocol overview and architecture - Message types and simulated metrics - Build instructions (Autotools and CMake) - Command-line options and example output - Configuration and payload format notes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Link to examples/sparkplug/README.md for detailed documentation. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use WOLF_CRYPT_TYPES_H include guard to skip the word64 typedef when wolfSSL has already defined it, matching the pattern used in wolfmqtt/mqtt_types.h. Fixes build error on macOS where wolfSSL defines word64 as unsigned long vs uint64_t (unsigned long long). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a new Sparkplug B Industrial IoT example to wolfMQTT, demonstrating Sparkplug-style topic namespaces and message flows using two MQTT clients (Edge Node + Host Application), plus build-system and documentation integration.
Changes:
- Added new Sparkplug example implementation (
examples/sparkplug/sparkplug.c/.h) with simplified (non-protobuf) payload encoding/decoding. - Integrated the new example into Autotools and CMake builds, and updated
.gitignore. - Added user documentation for the example and linked it from the project README.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| examples/sparkplug/sparkplug.h | Defines Sparkplug message/topic helpers and simplified payload encode/decode utilities. |
| examples/sparkplug/sparkplug.c | Implements the Edge Node + Host example clients, message callback handling, and publish/subscribe loops. |
| examples/sparkplug/README.md | Documents Sparkplug concepts, build/run instructions, and example output. |
| examples/include.am | Adds the new example to Autotools build targets and distribution lists. |
| README.md | Adds a top-level section pointing users to the Sparkplug example. |
| CMakeLists.txt | Registers the Sparkplug example target under WOLFMQTT_EXAMPLES. |
| .gitignore | Ignores the built Sparkplug example binary. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| dist_example_DATA+= examples/mqttnet.c \ | ||
| examples/mqttexample.c \ | ||
| examples/mqttport.c \ | ||
| examples/mqttclient/mqttclient.c \ | ||
| examples/mqttsimple/mqttsimple.c \ | ||
| examples/firmware/fwpush.c \ | ||
| examples/firmware/fwclient.c \ | ||
| examples/azure/azureiothub.c \ | ||
| examples/aws/awsiot.c \ | ||
| examples/wiot/wiot.c | ||
| dist_example_DATA+= examples/nbclient/nbclient.c | ||
| dist_example_DATA+= examples/multithread/multithread.c | ||
| if BUILD_SN | ||
| dist_example_DATA+= examples/sn-client/sn-client.c | ||
| dist_example_DATA+= examples/sn-client/sn-client_qos-1.c | ||
| 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 |
There was a problem hiding this comment.
examples/include.am adds the Sparkplug example source to dist_example_DATA, but does not include the new examples/sparkplug/README.md (and possibly sparkplug.h) anywhere for make dist packaging. If the project distributes example docs/headers, add these files to the appropriate dist_*/EXTRA_DIST lists so they’re included in release tarballs.
| /* Initialize outputs */ | ||
| if (group_id) group_id[0] = '\0'; | ||
| if (edge_node_id) edge_node_id[0] = '\0'; | ||
| if (device_id) device_id[0] = '\0'; | ||
|
|
||
| /* Parse topic: spBv1.0/group/type/node[/device] */ | ||
| matched = XSSCANF(topic, "%15[^/]/%63[^/]/%15[^/]/%63[^/]/%63s", | ||
| namespace_buf, group_id, type_buf, edge_node_id, device_id); | ||
|
|
There was a problem hiding this comment.
SparkplugTopic_Parse initializes outputs only when the pointers are non-NULL, but then unconditionally passes group_id, edge_node_id, and device_id to XSSCANF. This will crash if any of those pointers are NULL, despite the signature implying they’re optional. Make the parameters required (and assert/return BAD_ARG when NULL) or scan into local temp buffers and copy out conditionally.
| /* Parse topic: spBv1.0/group/type/node[/device] */ | ||
| matched = XSSCANF(topic, "%15[^/]/%63[^/]/%15[^/]/%63[^/]/%63s", | ||
| namespace_buf, group_id, type_buf, edge_node_id, device_id); | ||
|
|
||
| if (matched < 4) { | ||
| return MQTT_CODE_ERROR_BAD_ARG; | ||
| } | ||
|
|
||
| /* Verify namespace */ | ||
| if (XSTRCMP(namespace_buf, SPARKPLUG_NAMESPACE) != 0) { | ||
| return MQTT_CODE_ERROR_BAD_ARG; | ||
| } | ||
|
|
There was a problem hiding this comment.
SparkplugTopic_Parse ignores the *_len parameters and uses hard-coded %63 field widths when scanning into group_id/edge_node_id/device_id. If a caller passes smaller buffers, this can overflow. Use widths derived from the provided lengths (len-1) and/or parse manually using delimiters.
Add Sparkplug B Industrial IoT Example
Summary
This PR adds a new example demonstrating the https://sparkplug.eclipse.org/
industrial IoT protocol specification using wolfMQTT. The example creates two
MQTT clients that communicate using the Sparkplug topic namespace and message
types.
Changes
(CMakeLists.txt)
Features
Two Communicating Clients:
--enable-mt)
Sparkplug Protocol Support:
spBv1.0/{group_id}/{message_type}/{edge_node_id}[/{device_id}]
protobuf dependency)
Simulated Metrics:
Build & Test
Autotools - single-threaded (Edge Node only)
./configure --disable-tls
make
Autotools - multi-threaded (both clients)
./configure --enable-mt --disable-tls
make
CMake - multi-threaded
mkdir build && cd build
cmake -DWOLFMQTT_TLS=no -DWOLFMQTT_MT=yes ..
make sparkplug
Run
./examples/sparkplug/sparkplug -h test.mosquitto.org -p 1883
Example Output
Sparkplug B Example
Starting Edge Node and Host Application threads...
Sparkplug: Connected! (client_id=WolfMQTT_Sparkplug_Edge)
Sparkplug: Published NBIRTH to spBv1.0/WolfMQTT/NBIRTH/EdgeNode1
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
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 example completed!
Test Plan