Layer: Foundation ยท Build flags: PROTOCORE_ENABLE_DMA, PROTOCORE_ENABLE_PREEMPT_QUEUE, PROTOCORE_ENABLE_GATEWAY, PROTOCORE_DMA_SIMULATE
What this example teaches
The capstone of the v5 ingest pipeline: a wireless gateway. A southbound radio (LoRa / nRF24 / Zigbee / ... reached over SPI / I2C / UART) receives frames, and the device bridges them to the web stack - MQTT / HTTP / WebSocket.
radio RX --DMA--> callback --post--> FORWARD lane --> codec --> protocore_gateway_uplink()
|
envelope + topic lora/<port>/<addr>
|
northbound publish (MQTT/HTTP/WS)
Everything below the codec is a feature from earlier examples:
- DMA reads the radio's bytes into a static buffer and fires a completion.
- The FORWARD lane carries the frame off the interrupt to a task.
- A tiny per-radio codec (here: the first two bytes are the source node address, the rest is payload) turns raw bytes into an addressed frame.
- The gateway (
services/gateway) envelopes and publishes it.
The gateway
Register each radio as a port (with an optional transmit callback for downlink), and install the uplink - the northbound publish:
p.
port_id = 0; p.
kind = protocore_gateway_kind::PROTOCORE_GW_LORA; p.
tx = radio_tx;
void protocore_gateway_set_topic_prefix(uint8_t *work, const char *prefix)
Set the topic prefix used by protocore_gateway_topic() .
proto_bool protocore_gateway_add_port(uint8_t *work, const protocore_gateway_port_config *cfg)
Register a southbound port.
proto_bool protocore_gateway_uplink(uint8_t *work, uint8_t port_id, uint16_t src_addr, const uint8_t *payload, uint16_t len, int16_t rssi)
Bridge a received southbound frame northbound: envelope it and .
void protocore_gateway_set_uplink_cb(uint8_t *work, protocore_gateway_uplink_fn fn, void *ctx)
Install the northbound publish callback (required to publish .
Southbound port (radio / bus) configuration passed to protocore_gateway_add_port().
protocore_gateway_kind kind
protocore_gateway_kind.
protocore_gateway_tx_fn tx
downlink transmit (may be null for a receive-only port).
uint8_t port_id
caller-assigned id (used in topics and up/down-link calls).
- Envelope: each uplink builds a
protocore_gateway_msg with the source address, port, RSSI, and a sequence number. protocore_gateway_topic() formats a routing key <prefix>/<port>/<addr>.
- Downlink:
protocore_gateway_downlink(port, dst_addr, payload, len) transmits a northbound command back out the radio via the port's transmit callback.
- Rate cap: a per-port
rate_cap (frames/second) throttles a chatty radio; excess is dropped, not blocked.
- Fail-closed: no installed sink, an unknown port, an exceeded cap, or a callback refusing drops the frame and is counted (
protocore_gateway_get_stats()), never blocks.
The radio TX and the northbound publish are callbacks, so this runs with no radio hardware: the sketch feeds simulated frames through the DMA simulator and prints the publishes. A real build swaps the feed for the module's SPI RX and the publish for an MQTT client - the pipeline is unchanged.
Storage is static (zero heap): PROTOCORE_GW_MAX_PORTS ports.
Build-flag note
The flags must reach the library build, so pass them as build flags:
pio ci --board=esp32dev --project-option="framework=arduino" \
--project-option="build_flags=-DPROTOCORE_ENABLE_DMA=1 -DPROTOCORE_ENABLE_PREEMPT_QUEUE=1 -DPROTOCORE_ENABLE_GATEWAY=1 -DPROTOCORE_DMA_SIMULATE=1" \
--lib="." examples/Drivers/RadioGateway/RadioGateway.ino