ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
nrf24.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4#ifndef PROTOCORE_NRF24_H
5#define PROTOCORE_NRF24_H
6
7#include "protocore_config.h" // the entry point: protocore_types.h for the widths
8
10
11/**
12 * @file nrf24.h
13 * @brief nRF24L01+ radio driver (PROTOCORE_ENABLE_NRF24) - Nordic 2.4 GHz over SPI.
14 *
15 * A radio driver plugin for the gateway (PROTOCORE_ENABLE_GATEWAY): cheap point-to-multipoint
16 * 2.4 GHz sensor links bridged to the web stack. Unlike the SX127x (plain register
17 * read/write), the nRF24L01+ speaks an **SPI command protocol** (each transaction is a
18 * command byte + data, and every command returns the STATUS register) and needs a separate
19 * **CE** pin to key RX/TX - so the driver runs over an @ref nrf_bus that carries a
20 * full-duplex SPI transfer plus a CE-set callback. That is the only board-specific code.
21 *
22 * The nRF24 does its own **hardware addressing** (5-byte pipe addresses), so a received
23 * frame's "source" is the pipe number it arrived on - there is no in-payload header and
24 * therefore no separate codec. It uses a **static payload width** (PROTOCORE_NRF24_PAYLOAD):
25 * every frame is that many bytes (a short send is zero-padded). Bridge received payloads
26 * northbound with protocore_gateway_uplink(port, pipe, payload, width, 0). The register/command
27 * protocol is host-testable against a mock; the RF link needs the module.
28 *
29 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
30 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
31 * a caller drives every namespace the same way.
32 *
33 * @author Douglas Quigg (dstroy0)
34 * @date 2026
35 */
36
37// PROTOCORE_NRF24_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
38// it into its arena. Its size and its offset are each a static_assert, so a feature
39// combination that does not fit fails to compile rather than overrunning at run time.
40
41/** @brief Full-duplex SPI transfer of @p len bytes (chip-select toggled by the callback). */
42typedef void (*nrf_spi_fn)(const uint8_t *tx, uint8_t *rx, uint8_t len, void *ctx);
43
44/** @brief Drive the CE pin (true = high). */
45typedef void (*nrf_ce_fn)(proto_bool level, void *ctx);
46
47/** @brief The bus a driver call uses: your SPI transfer + CE control behind it. */
48typedef struct
49{
52 void *ctx;
53} nrf_bus;
54
55/** @brief Radio configuration applied by protocore_nrf24_init(). */
56typedef struct
57{
58 const uint8_t *address; ///< 5-byte pipe-0 / TX address (RX and TX share it here).
59 uint8_t channel; ///< RF channel 0..125 (2400 + channel MHz).
60 uint8_t data_rate; ///< 0 = 1 Mbps, 1 = 2 Mbps, 2 = 250 kbps.
61 uint8_t tx_power; ///< power level 0..3 (-18, -12, -6, 0 dBm).
63
64/** @brief Dispatch table. Addressed by offset, so the layout is asserted below. */
65typedef struct
66{
67 proto_bool (*init)(uint8_t *, const nrf_bus *, const nrf_config *);
68 proto_bool (*send)(uint8_t *, const nrf_bus *, const uint8_t *, uint8_t);
69 proto_bool (*tx_done)(uint8_t *, const nrf_bus *);
70 void (*set_rx)(uint8_t *, const nrf_bus *);
71 int (*recv)(uint8_t *, const nrf_bus *, uint8_t *, uint8_t, uint8_t *);
72} Nrf24Ns;
73PROTOCORE_NS_LAYOUT(Nrf24Ns, init, send, tx_done, set_rx, recv);
74
75/**
76 * @brief Configure the nRF24L01+ and power it up (standby).
77 * @param work PROTOCORE_NRF24_BORROW bytes the caller took. Not held past the call.
78 * @param bus Bus
79 * @param cfg Cfg
80 * @return PROTO_TRUE on success.
81 */
82proto_bool protocore_nrf24_init(uint8_t *work, const nrf_bus *bus, const nrf_config *cfg);
83/**
84 * @brief Transmit len bytes (zero-padded to PROTOCORE_NRF24_PAYLOAD). Poll .
85 * @param work PROTOCORE_NRF24_BORROW bytes the caller took. Not held past the call.
86 * @param bus Bus
87 * @param data Data
88 * @param len Len
89 * @return PROTO_TRUE on success.
90 */
91proto_bool protocore_nrf24_send(uint8_t *work, const nrf_bus *bus, const uint8_t *data, uint8_t len);
92/**
93 * @brief True once a transmit has finished (STATUS TX_DS); clears the flag.
94 * @param work PROTOCORE_NRF24_BORROW bytes the caller took. Not held past the call.
95 * @param bus Bus
96 * @return PROTO_TRUE on success.
97 */
98proto_bool protocore_nrf24_tx_done(uint8_t *work, const nrf_bus *bus);
99/**
100 * @brief Enter receive mode (PRX + CE high); then poll protocore_nrf24_recv().
101 * @param work PROTOCORE_NRF24_BORROW bytes the caller took. Not held past the call.
102 * @param bus Bus
103 */
104void protocore_nrf24_set_rx(uint8_t *work, const nrf_bus *bus);
105/**
106 * @brief If a frame is waiting, copy it into buf and report the pipe it .
107 * @param work PROTOCORE_NRF24_BORROW bytes the caller took. Not held past the call.
108 * @param bus Bus
109 * @param buf Buf
110 * @param cap Cap
111 * @param pipe Pipe
112 * @return The int.
113 */
114int protocore_nrf24_recv(uint8_t *work, const nrf_bus *bus, uint8_t *buf, uint8_t cap, uint8_t *pipe);
115
116/** @brief Full-duplex SPI transfer of @p len bytes (chip-select toggled by the callback). */
117typedef void (*nrf_spi_fn)(const uint8_t *tx, uint8_t *rx, uint8_t len, void *ctx);
118
119/** @brief Module namespace. */
125
127
128#endif // PROTOCORE_NRF24_H
proto_bool protocore_nrf24_tx_done(uint8_t *work, const nrf_bus *bus)
True once a transmit has finished (STATUS TX_DS); clears the flag.
PROTOCORE_NS Nrf24Ns Nrf24 PROTOCORE_UNUSED
Module namespace.
Definition nrf24.h:120
void(* nrf_spi_fn)(const uint8_t *tx, uint8_t *rx, uint8_t len, void *ctx)
Full-duplex SPI transfer of len bytes (chip-select toggled by the callback).
Definition nrf24.h:42
int protocore_nrf24_recv(uint8_t *work, const nrf_bus *bus, uint8_t *buf, uint8_t cap, uint8_t *pipe)
If a frame is waiting, copy it into buf and report the pipe it .
proto_bool protocore_nrf24_init(uint8_t *work, const nrf_bus *bus, const nrf_config *cfg)
Configure the nRF24L01+ and power it up (standby).
void protocore_nrf24_set_rx(uint8_t *work, const nrf_bus *bus)
Enter receive mode (PRX + CE high); then poll protocore_nrf24_recv().
proto_bool protocore_nrf24_send(uint8_t *work, const nrf_bus *bus, const uint8_t *data, uint8_t len)
Transmit len bytes (zero-padded to PROTOCORE_NRF24_PAYLOAD). Poll .
void(* nrf_ce_fn)(proto_bool level, void *ctx)
Drive the CE pin (true = high).
Definition nrf24.h:45
#define PROTOCORE_NS_LAYOUT(T,...)
Pin every dispatch slot of a table that is nothing but function pointers.
#define PROTOCORE_NS
Storage for a dispatch table. The const is load bearing.
Dispatch table. Addressed by offset, so the layout is asserted below.
Definition nrf24.h:66
proto_bool(* init)(uint8_t *, const nrf_bus *, const nrf_config *)
Definition nrf24.h:67
The bus a driver call uses: your SPI transfer + CE control behind it.
Definition nrf24.h:49
void * ctx
Definition nrf24.h:52
nrf_spi_fn spi
Definition nrf24.h:50
nrf_ce_fn ce
Definition nrf24.h:51
Radio configuration applied by protocore_nrf24_init().
Definition nrf24.h:57
uint8_t data_rate
0 = 1 Mbps, 1 = 2 Mbps, 2 = 250 kbps.
Definition nrf24.h:60
uint8_t tx_power
power level 0..3 (-18, -12, -6, 0 dBm).
Definition nrf24.h:61
uint8_t channel
RF channel 0..125 (2400 + channel MHz).
Definition nrf24.h:59
const uint8_t * address
5-byte pipe-0 / TX address (RX and TX share it here).
Definition nrf24.h:58
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97