ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
sparkplug.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file sparkplug.h
6 * @brief Sparkplug B payload + topic codec (PC_ENABLE_SPARKPLUG) - zero-heap builder for
7 * the Eclipse Sparkplug B industrial-IoT MQTT payload (a Protobuf message) and its
8 * topic namespace. Builds on the Protobuf codec (services/iot/protobuf) and is published
9 * with the MQTT client.
10 *
11 * Topic: `spBv1.0/<group_id>/<message_type>/<edge_node_id>[/<device_id>]`, where message_type
12 * is NBIRTH / NDEATH / NDATA / DBIRTH / DDEATH / DDATA / STATE.
13 *
14 * Payload (Tahu `Payload` protobuf): timestamp(1), metrics(2, repeated), seq(3), uuid(4),
15 * body(5). Each Metric: name(1), alias(2), timestamp(3), datatype(4), and a value in the
16 * oneof - int_value(10) / long_value(11) / float_value(12) / double_value(13) /
17 * boolean_value(14) / string_value(15). Field numbers + datatype codes verified against the
18 * Eclipse Tahu sparkplug_b.proto.
19 *
20 * @author Douglas Quigg (dstroy0)
21 * @date 2026
22 */
23
24#ifndef PROTOCORE_SPARKPLUG_H
25#define PROTOCORE_SPARKPLUG_H
26
27#include "protocore_config.h"
28
29#if PC_ENABLE_SPARKPLUG
30
31#include <stddef.h>
32#include <stdint.h>
33
34// Sparkplug B data type codes (a subset; Tahu DataType enum).
35#define SPB_DT_INT8 1
36#define SPB_DT_INT16 2
37#define SPB_DT_INT32 3
38#define SPB_DT_INT64 4
39#define SPB_DT_UINT8 5
40#define SPB_DT_UINT16 6
41#define SPB_DT_UINT32 7
42#define SPB_DT_UINT64 8
43#define SPB_DT_FLOAT 9
44#define SPB_DT_DOUBLE 10
45#define SPB_DT_BOOLEAN 11
46#define SPB_DT_STRING 12
47
48/** @brief Which value the metric carries (selects the Tahu Metric value-oneof field). */
49enum class SpbMetricKind : uint8_t
50{
51 SPB_M_INT, ///< int_value (field 10, uint32)
52 SPB_M_LONG, ///< long_value (field 11, uint64)
53 SPB_M_FLOAT, ///< float_value (field 12)
54 SPB_M_DOUBLE, ///< double_value (field 13)
55 SPB_M_BOOL, ///< boolean_value (field 14)
56 SPB_M_STRING, ///< string_value (field 15)
57};
58
59/** @brief One Sparkplug B metric. nullptr name / has_* false fields are omitted. */
60struct SpbMetric
61{
62 const char *name; ///< metric name (omit on DATA when using an alias)
63 bool has_alias;
64 uint64_t alias;
65 bool has_timestamp;
66 uint64_t timestamp;
67 uint32_t datatype; ///< SPB_DT_*
68 SpbMetricKind kind;
69 uint32_t int_value;
70 uint64_t long_value;
71 float float_value;
72 double double_value;
73 bool bool_value;
74 const char *string_value;
75};
76
77/** @brief Build the `spBv1.0/...` topic. @p device may be null for a node-level topic. */
78size_t pc_spb_build_topic(char *buf, size_t cap, const char *group, const char *message_type, const char *edge_node,
79 const char *device);
80
81/** @brief Serialize one Metric (a Tahu Metric protobuf message). Returns length, or 0. */
82size_t pc_spb_build_metric(uint8_t *buf, size_t cap, const SpbMetric *m);
83
84/** @brief Serialize a Payload: timestamp + the @p n metrics + seq. Returns length, or 0. */
85size_t pc_spb_build_payload(uint8_t *buf, size_t cap, uint64_t timestamp, uint64_t seq, const SpbMetric *metrics,
86 size_t n);
87
88// ---- decoding (the subscriber side, built on the protobuf reader) ----
89
90/** @brief The top-level fields of a decoded Sparkplug B Payload; metrics are iterated separately. */
91struct SpbPayloadHeader
92{
93 bool has_timestamp;
94 uint64_t timestamp;
95 bool has_seq;
96 uint64_t seq; ///< Sparkplug sequence number
97};
98
99/**
100 * @brief Parse a Sparkplug B Payload's top-level fields: the timestamp (field 1) and sequence number
101 * (field 3). The repeated metrics (field 2) are read with pc_spb_payload_next_metric.
102 * @return true iff the protobuf parses without truncation; false otherwise.
103 */
104bool pc_spb_parse_payload(const uint8_t *buf, size_t len, SpbPayloadHeader *out);
105
106/**
107 * @brief Iterate the metric sub-messages (field 2) of a Payload. Start @p pos at 0; each call points
108 * @p metric / @p metric_len at the next metric's protobuf bytes and advances @p pos.
109 * @return true while a metric remains; false at the end of the payload / on a malformed field.
110 */
111bool pc_spb_payload_next_metric(const uint8_t *buf, size_t len, size_t *pos, const uint8_t **metric,
112 size_t *metric_len);
113
114/** @brief A decoded Sparkplug B Metric. The name / string_value point INTO the buffer (NOT NUL-terminated). */
115struct SpbMetricDecoded
116{
117 const char *name; ///< metric name, or nullptr if omitted (a DATA metric addressed by alias)
118 size_t name_len;
119 bool has_alias;
120 uint64_t alias;
121 bool has_timestamp;
122 uint64_t timestamp;
123 uint32_t datatype; ///< SPB_DT_*
124 bool has_value; ///< false if no value oneof field was present
125 SpbMetricKind kind; ///< which value member is set (valid when @ref has_value)
126 uint32_t int_value;
127 uint64_t long_value;
128 float float_value;
129 double double_value;
130 bool bool_value;
131 const char *string_value; ///< string_value bytes (NOT NUL-terminated), or nullptr
132 size_t string_value_len;
133};
134
135/**
136 * @brief Decode a Metric message (a slice from pc_spb_payload_next_metric) into @p out: the name, alias,
137 * timestamp, datatype, and the value oneof (int / long / float / double / boolean / string).
138 * @return true iff the protobuf parses without truncation; false otherwise.
139 */
140bool pc_spb_parse_metric(const uint8_t *buf, size_t len, SpbMetricDecoded *out);
141
142#endif // PC_ENABLE_SPARKPLUG
143
144#endif // PROTOCORE_SPARKPLUG_H
User-facing configuration for ProtoCore.