DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
lora.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 lora.h
6 * @brief LoRa radio codec + driver (DETWS_ENABLE_LORA) - Semtech SX127x / RFM95-96.
7 *
8 * A per-radio plugin for the gateway (DETWS_ENABLE_GATEWAY): the southbound-radio half of
9 * a LoRa-to-web bridge. Two layers:
10 *
11 * - **Codec** - the RadioHead-compatible 4-byte frame header (`to` / `from` / `id` /
12 * `flags`) that virtually every hobby / sensor LoRa deployment uses on top of the
13 * header-less LoRa PHY. lora_frame_parse() splits a received frame into that header and
14 * the payload; lora_frame_build() prepends it. Pure, no hardware.
15 * - **Driver** - the SX127x register protocol (init / send / receive / enter-RX) over a
16 * caller-supplied register-access **bus** (@ref lora_bus). The SPI transfer and the
17 * chip-select / reset GPIOs are the integration's - you implement two callbacks that
18 * read and write a chip register - so the register sequence is host-testable with a mock
19 * bus and portable across whatever SPI peripheral you wire the module to.
20 *
21 * Wiring to the gateway (see example 11.LoRaGateway): poll lora_recv(); on a frame,
22 * lora_frame_parse() then det_gw_uplink(port, header.from, payload, len, rssi). A downlink
23 * builds a frame with lora_frame_build() and lora_send()s it. The codec + register protocol
24 * are verified on the host; the RF link itself needs the module.
25 *
26 * @author Douglas Quigg (dstroy0)
27 * @date 2026
28 */
29
30#ifndef DETERMINISTICESPASYNCWEBSERVER_LORA_H
31#define DETERMINISTICESPASYNCWEBSERVER_LORA_H
32
33#include "ServerConfig.h"
34
35#if DETWS_ENABLE_LORA
36
37#include <stddef.h>
38#include <stdint.h>
39
40// --- Codec: the RadioHead RH_RF95 4-byte header ---------------------------------------
41
42/** @brief RadioHead-compatible LoRa frame header (precedes the payload). */
43struct lora_header
44{
45 uint8_t to; ///< destination node address (0xFF = broadcast)
46 uint8_t from; ///< source node address
47 uint8_t id; ///< sequence / message id
48 uint8_t flags; ///< application flags
49};
50
51/**
52 * @brief Split a received frame into its header and payload.
53 * @param[out] payload set to the first payload byte (points into @p raw).
54 * @param[out] payload_len set to the payload length.
55 * @return true; false if @p raw is shorter than the 4-byte header.
56 */
57bool lora_frame_parse(const uint8_t *raw, uint16_t len, lora_header *hdr, const uint8_t **payload,
58 uint16_t *payload_len);
59
60/**
61 * @brief Build a frame (header + payload) into @p out.
62 * @return the total frame length, or 0 if it would not fit @p cap or exceeds the payload max.
63 */
64uint16_t lora_frame_build(const lora_header *hdr, const uint8_t *payload, uint16_t len, uint8_t *out, uint16_t cap);
65
66// --- Driver: SX127x over a register-access bus ----------------------------------------
67
68/** @brief Read one SX127x register (@p reg is the bare 7-bit address). */
69typedef uint8_t (*lora_reg_read_fn)(uint8_t reg, void *ctx);
70/** @brief Write one SX127x register (@p reg is the bare 7-bit address). */
71typedef void (*lora_reg_write_fn)(uint8_t reg, uint8_t val, void *ctx);
72
73/** @brief The register-access bus a driver call uses (your SPI + chip-select behind it). */
74struct lora_bus
75{
76 lora_reg_read_fn read;
77 lora_reg_write_fn write;
78 void *ctx;
79};
80
81/** @brief Radio configuration applied by lora_init(). */
82struct lora_config
83{
84 uint32_t freq_hz; ///< carrier frequency in Hz (e.g. 868100000 / 915000000).
85 uint8_t spreading; ///< spreading factor 6..12 (SF7 default is a good start).
86 uint8_t bandwidth; ///< bandwidth code 0..9 (7 = 125 kHz - the common default).
87 uint8_t coding_rate; ///< coding rate 1..4 (1 = 4/5).
88 uint8_t sync_word; ///< 0x12 private / 0x34 LoRaWAN.
89 uint8_t tx_power; ///< PA_BOOST power 2..17 dBm.
90};
91
92/**
93 * @brief Initialise the SX127x: verify the chip, switch to LoRa mode, and apply @p cfg.
94 * @return true; false if the register at RegVersion is not the SX127x id (0x12) - i.e. the
95 * bus is not talking to the chip.
96 */
97bool lora_init(const lora_bus *bus, const lora_config *cfg);
98
99/**
100 * @brief Load @p frame into the FIFO and start a transmit (the radio returns to standby on
101 * TxDone). Poll lora_tx_done() for completion.
102 * @return true; false if @p len exceeds DETWS_LORA_MAX_PAYLOAD + 4.
103 */
104bool lora_send(const lora_bus *bus, const uint8_t *frame, uint8_t len);
105
106/** @brief True once a transmit has finished (RegIrqFlags TxDone); clears the flag. */
107bool lora_tx_done(const lora_bus *bus);
108
109/** @brief Put the radio in continuous-receive mode (call once, then poll lora_recv()). */
110void lora_set_rx(const lora_bus *bus);
111
112/**
113 * @brief If a frame has been received, copy it into @p buf and report its RSSI.
114 * @param[out] rssi set to the packet RSSI in dBm (may be null).
115 * @return the frame length (>=0), or -1 if no frame is ready or the CRC failed.
116 */
117int lora_recv(const lora_bus *bus, uint8_t *buf, uint8_t cap, int16_t *rssi);
118
119#endif // DETWS_ENABLE_LORA
120
121#endif // DETERMINISTICESPASYNCWEBSERVER_LORA_H
User-facing configuration for DeterministicESPAsyncWebServer.