DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
cc1101.cpp
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 cc1101.cpp
6 * @brief CC1101 sub-GHz radio driver (see cc1101.h).
7 */
8
10
11#if DETWS_ENABLE_CC1101
12
13namespace
14{
15// SPI header bits.
16const uint8_t READ = 0x80;
17const uint8_t BURST = 0x40;
18
19// Register / strobe / FIFO addresses.
20const uint8_t REG_CHANNR = 0x0A;
21const uint8_t STROBE_SRES = 0x30; ///< reset chip.
22const uint8_t STROBE_SRX = 0x34; ///< enable RX.
23const uint8_t STROBE_STX = 0x35; ///< enable TX.
24const uint8_t STROBE_SIDLE = 0x36;
25const uint8_t STROBE_SFRX = 0x3A; ///< flush RX FIFO.
26const uint8_t STROBE_SFTX = 0x3B; ///< flush TX FIFO.
27const uint8_t STAT_VERSION = 0x31;
28const uint8_t STAT_RXBYTES = 0x3B;
29const uint8_t FIFO = 0x3F;
30
31// The chip status byte's state field (bits 6-4).
32const uint8_t STATE_IDLE = 0;
33
34void write_reg(const cc1101_bus *b, uint8_t addr, uint8_t val)
35{
36 uint8_t tx[2] = {addr, val}; // header = address (write, single), then value
37 uint8_t rx[2] = {0, 0};
38 b->spi(tx, rx, 2, b->ctx);
39}
40
41uint8_t read_reg(const cc1101_bus *b, uint8_t addr, bool status)
42{
43 // Status registers (0x30-0x3D) require the burst bit to distinguish them from strobes.
44 uint8_t hdr = (uint8_t)(addr | READ | (status ? BURST : 0));
45 uint8_t tx[2] = {hdr, 0};
46 uint8_t rx[2] = {0, 0};
47 b->spi(tx, rx, 2, b->ctx);
48 return rx[1];
49}
50
51void strobe(const cc1101_bus *b, uint8_t cmd)
52{
53 uint8_t tx[1] = {cmd};
54 uint8_t rx[1] = {0};
55 b->spi(tx, rx, 1, b->ctx);
56}
57
58uint8_t status_byte(const cc1101_bus *b)
59{
60 uint8_t tx[1] = {(uint8_t)(0x3D | READ | BURST)}; // SNOP as a read returns the status byte
61 uint8_t rx[1] = {0};
62 b->spi(tx, rx, 1, b->ctx);
63 return rx[0];
64}
65} // namespace
66
67int16_t cc1101_rssi_dbm(uint8_t raw)
68{
69 // TI CC1101 datasheet: dBm = (raw >= 128 ? (raw - 256) : raw) / 2 - 74.
70 int16_t r = raw >= 128 ? (int16_t)raw - 256 : (int16_t)raw;
71 return (int16_t)(r / 2 - 74);
72}
73
74bool cc1101_init(const cc1101_bus *bus, const cc1101_config *cfg)
75{
76 if (!bus || !bus->spi || !cfg)
77 return false;
78 strobe(bus, STROBE_SRES);
79 for (size_t i = 0; i < cfg->nregs && cfg->regs; i++)
80 write_reg(bus, cfg->regs[i].addr, cfg->regs[i].value);
81 write_reg(bus, REG_CHANNR, cfg->channel);
82 uint8_t ver = read_reg(bus, STAT_VERSION, true);
83 return ver != 0x00 && ver != 0xFF; // a floating bus reads all-0 or all-1
84}
85
86bool cc1101_send(const cc1101_bus *bus, const uint8_t *data, uint8_t len)
87{
88 if (!bus || !bus->spi || !data || len == 0 || len > 63)
89 return false;
90 strobe(bus, STROBE_SIDLE);
91 strobe(bus, STROBE_SFTX);
92 // Burst-write the FIFO: header, length byte, payload.
93 uint8_t tx[65];
94 uint8_t rx[65];
95 tx[0] = (uint8_t)(FIFO | BURST);
96 tx[1] = len;
97 for (uint8_t i = 0; i < len; i++)
98 tx[2 + i] = data[i];
99 bus->spi(tx, rx, (uint8_t)(2 + len), bus->ctx);
100 strobe(bus, STROBE_STX);
101 return true;
102}
103
104bool cc1101_tx_done(const cc1101_bus *bus)
105{
106 if (!bus || !bus->spi)
107 return false;
108 uint8_t st = (uint8_t)((status_byte(bus) >> 4) & 0x07);
109 return st == STATE_IDLE;
110}
111
112void cc1101_set_rx(const cc1101_bus *bus)
113{
114 if (!bus || !bus->spi)
115 return;
116 strobe(bus, STROBE_SIDLE);
117 strobe(bus, STROBE_SFRX);
118 strobe(bus, STROBE_SRX);
119}
120
121int cc1101_recv(const cc1101_bus *bus, uint8_t *buf, uint8_t cap, int16_t *rssi_dbm)
122{
123 if (!bus || !bus->spi || !buf)
124 return -1;
125 uint8_t rxbytes = (uint8_t)(read_reg(bus, STAT_RXBYTES, true) & 0x7F); // low 7 bits = count
126 if (rxbytes == 0)
127 return -1;
128 uint8_t len = read_reg(bus, FIFO, false); // variable-length: leading length byte
129 if (len == 0 || len > 63)
130 {
131 strobe(bus, STROBE_SFRX); // corrupt length: flush and bail
132 return -1;
133 }
134 // Burst-read payload + 2 appended status bytes (RSSI, LQI/CRC).
135 uint8_t tx[66];
136 uint8_t rx[66];
137 uint8_t n = (uint8_t)(len + 2);
138 tx[0] = (uint8_t)(FIFO | READ | BURST);
139 for (uint8_t i = 0; i < n; i++)
140 tx[1 + i] = 0;
141 bus->spi(tx, rx, (uint8_t)(1 + n), bus->ctx);
142 if (rssi_dbm)
143 *rssi_dbm = cc1101_rssi_dbm(rx[1 + len]); // first appended status byte is raw RSSI
144 uint8_t out = len < cap ? len : cap;
145 for (uint8_t i = 0; i < out; i++)
146 buf[i] = rx[1 + i];
147 return out;
148}
149
150#endif // DETWS_ENABLE_CC1101
CC1101 sub-GHz radio driver (DETWS_ENABLE_CC1101) - TI 300-928 MHz over SPI.