ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
gateway.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 gateway.h
6 * @brief Radio / wireless gateway bridge (PC_ENABLE_GATEWAY) - the v5 southbound-to-
7 * northbound bridge.
8 *
9 * The generic gateway pattern that ties the hardware-ingest pipeline to the web stack. A
10 * southbound radio (LoRa / nRF24 / CC1101 / Zigbee / Z-Wave / ... reached over SPI / I2C /
11 * UART) is a **port**. When it receives a frame - the data-ready ISR reads it over DMA
12 * (services/system/dma), posts it onto the FORWARD lane (services/system/preempt_queue), and a per-radio
13 * codec extracts the source node address and payload - you call pc_gateway_uplink(). The
14 * gateway **envelopes** the frame (source address, port, RSSI, a sequence number) and
15 * **publishes it northbound** through the uplink callback, which you wire to MQTT / HTTP /
16 * WebSocket / UDP. A northbound command runs the other way through pc_gateway_downlink() to the
17 * port's transmit callback (the radio's SPI / UART write).
18 *
19 * The radio transmit and the northbound publish are **callbacks** - the seam a real radio
20 * driver and a real protocol binding plug into - so the bridge is fully host- and
21 * device-testable with no radio hardware (the tests / example supply capturing callbacks
22 * and feed simulated frames). This is the northbound half; the DMA + FORWARD lane carry the
23 * bytes, and each radio's frame format is its own codec.
24 *
25 * Per-port uplink rate cap (fail-closed), a routing-key helper (pc_gateway_topic() formats
26 * `<prefix>/<port>/<addr>`), and static tables (zero heap): PC_GW_MAX_PORTS ports.
27 *
28 * @author Douglas Quigg (dstroy0)
29 * @date 2026
30 */
31
32#ifndef PROTOCORE_GATEWAY_H
33#define PROTOCORE_GATEWAY_H
34
35#include "protocore_config.h"
36
37#if PC_ENABLE_GATEWAY
38
39#include <stddef.h>
40#include <stdint.h>
41
42/** @brief Southbound radio / bus kind a port bridges (informational + topic hint). */
43enum class pc_gateway_kind : uint8_t
44{
45 PC_GW_OTHER = 0,
46 PC_GW_LORA,
47 PC_GW_NRF24,
48 PC_GW_CC1101,
49 PC_GW_THREAD,
50 PC_GW_ZIGBEE,
51 PC_GW_ZWAVE,
52 PC_GW_ENOCEAN,
53 PC_GW_SIGFOX,
54 PC_GW_WISUN,
55 PC_GW_NFC,
56 PC_GW_BLE,
57};
58
59/**
60 * @brief A northbound message: a southbound frame enveloped with its routing metadata.
61 * @ref payload points at the caller's bytes and is valid only for the duration of
62 * the uplink callback - copy what you publish asynchronously.
63 */
64struct pc_gateway_msg
65{
66 const uint8_t *payload; ///< frame payload bytes
67 uint32_t seq; ///< per-gateway uplink sequence (wraps)
68 uint16_t len; ///< payload length
69 uint16_t src_addr; ///< source node address on the radio
70 int16_t rssi; ///< received signal strength (0 if the driver has none)
71 uint8_t port_id; ///< the port the frame arrived on
72 pc_gateway_kind kind; ///< pc_gateway_kind of that port
73};
74
75/**
76 * @brief Northbound publish: emit @p msg to MQTT / HTTP / WebSocket / UDP.
77 * @return true if the northbound stack accepted it; false drops (counted).
78 */
79typedef bool (*pc_gateway_uplink_fn)(const pc_gateway_msg *msg, void *ctx);
80
81/**
82 * @brief Southbound transmit (downlink): send @p payload to @p dst_addr on @p port_id.
83 * @return true if the radio accepted the frame; false drops (counted).
84 */
85typedef bool (*pc_gateway_tx_fn)(uint8_t port_id, uint16_t dst_addr, const uint8_t *payload, uint16_t len, void *ctx);
86
87/** @brief Southbound port (radio / bus) configuration passed to pc_gateway_add_port(). */
88struct pc_gateway_port_config
89{
90 uint8_t port_id; ///< caller-assigned id (used in topics and up/down-link calls).
91 pc_gateway_kind kind; ///< pc_gateway_kind.
92 pc_gateway_tx_fn tx; ///< downlink transmit (may be null for a receive-only port).
93 void *ctx; ///< opaque, forwarded to @ref tx.
94 uint16_t rate_cap; ///< max uplink frames/second from this port (0 = unlimited).
95};
96
97/** @brief Gateway counters (monotonic since the last pc_gateway_reset()). */
98struct pc_gateway_stats
99{
100 uint32_t up_in; ///< pc_gateway_uplink() calls
101 uint32_t up_published; ///< frames the uplink callback accepted
102 uint32_t up_dropped; ///< uplinks dropped (rate cap / no sink / refused / bad port)
103 uint32_t down_in; ///< pc_gateway_downlink() calls
104 uint32_t down_sent; ///< downlinks the port transmit accepted
105 uint32_t down_dropped; ///< downlinks dropped (bad port / no tx / refused)
106};
107
108/** @brief Clear all ports, the uplink sink, the topic prefix, and stats. */
109void pc_gateway_reset(void);
110
111/**
112 * @brief Register a southbound port.
113 * @return true; false if @p cfg is null, the id is already registered, or the table is
114 * full (PC_GW_MAX_PORTS).
115 */
116bool pc_gateway_add_port(const pc_gateway_port_config *cfg);
117
118/** @brief Install the northbound publish callback (required to publish anything). */
119void pc_gateway_set_uplink_cb(pc_gateway_uplink_fn fn, void *ctx);
120
121/** @brief Set the topic prefix used by pc_gateway_topic() (caller-owned string; default "gw"). */
122void pc_gateway_set_topic_prefix(const char *prefix);
123
124/**
125 * @brief Bridge a received southbound frame northbound: envelope it and publish.
126 *
127 * Applies the port's uplink rate cap, then calls the uplink callback. Fail-closed: an
128 * unknown port, no installed sink, an exceeded cap, or a callback returning false drops
129 * the frame (counted), never blocks.
130 * @return true if published.
131 */
132bool pc_gateway_uplink(uint8_t port_id, uint16_t src_addr, const uint8_t *payload, uint16_t len, int16_t rssi);
133
134/**
135 * @brief Bridge a northbound command southbound: transmit it on @p port_id's radio.
136 * @return true if the port's transmit callback accepted it; false drops (counted).
137 */
138bool pc_gateway_downlink(uint8_t port_id, uint16_t dst_addr, const uint8_t *payload, uint16_t len);
139
140/**
141 * @brief Format a northbound routing key `<prefix>/<port>/<addr>` for @p msg into @p buf.
142 * @return the string length written (excluding the NUL), or 0 if @p buf is too small.
143 */
144uint16_t pc_gateway_topic(const pc_gateway_msg *msg, char *buf, uint16_t buflen);
145
146/** @brief Copy the current gateway counters into @p out. */
147void pc_gateway_get_stats(pc_gateway_stats *out);
148
149#if !defined(ARDUINO)
150/** @brief Host only: set the millisecond clock the rate cap uses (tests drive the window). */
151void pc_gateway_test_set_now(uint32_t ms);
152#endif
153
154#endif // PC_ENABLE_GATEWAY
155
156#endif // PROTOCORE_GATEWAY_H
User-facing configuration for ProtoCore.