Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
radio_sx1276.h
Go to the documentation of this file.
1#pragma once
2
3/// @file radio_sx1276.h
4/// @brief SX1276 radio driver for IO-Homecontrol.
5/// @ingroup hioc_radio
6///
7/// Implements the RadioDriver interface for the Semtech SX1276 transceiver.
8/// Configures the chip in FSK mode with the IoHomeOn hardware feature that
9/// handles CRC and packet framing specific to the IO-Homecontrol protocol.
10
11#include "radio_interface.h"
12#include "esphome/core/hal.h"
13
14namespace esphome {
15namespace home_io_control {
16
17// ============================================================================
18// SX1276 Register Addresses (subset needed for IO-Homecontrol)
19// Full register map: see Semtech SX1276 datasheet or sx1276Regs-Fsk.h
20// ============================================================================
21
22static constexpr uint8_t REG_FIFO = 0x00; ///< FIFO read/write access
23static constexpr uint8_t REG_OP_MODE = 0x01; ///< Operating mode (sleep/standby/tx/rx)
24static constexpr uint8_t REG_BITRATE_MSB = 0x02; ///< Bit rate MSB = FXOSC / bitrate
25static constexpr uint8_t REG_BITRATE_LSB = 0x03;
26static constexpr uint8_t REG_FDEV_MSB = 0x04; ///< Frequency deviation MSB
27static constexpr uint8_t REG_FDEV_LSB = 0x05;
28static constexpr uint8_t REG_FRF_MSB = 0x06; ///< Carrier frequency MSB (freq = FRF * FXOSC / 2^19)
29static constexpr uint8_t REG_FRF_MID = 0x07;
30static constexpr uint8_t REG_FRF_LSB = 0x08;
31static constexpr uint8_t REG_PA_CONFIG = 0x09; ///< Power amplifier config (pin select + power level)
32static constexpr uint8_t REG_PA_RAMP = 0x0A; ///< PA ramp time and modulation shaping
33static constexpr uint8_t REG_LNA = 0x0C; ///< Low noise amplifier gain and boost
34static constexpr uint8_t REG_RX_CONFIG = 0x0D; ///< Receiver configuration (AFC, AGC, trigger)
35static constexpr uint8_t REG_RSSI_CONFIG = 0x0E; ///< RSSI smoothing
36static constexpr uint8_t REG_RX_BW = 0x12; ///< Receiver bandwidth
37static constexpr uint8_t REG_AFC_BW = 0x13; ///< AFC bandwidth
38static constexpr uint8_t REG_AFC_FEI = 0x1A; ///< AFC auto clear
39static constexpr uint8_t REG_PREAMBLE_DETECT = 0x1F; ///< Preamble detector config
40static constexpr uint8_t REG_OSC = 0x24; ///< Oscillator / clock output
41static constexpr uint8_t REG_RSSI_VALUE = 0x11; ///< Instant RSSI value in FSK mode
42static constexpr uint8_t REG_PREAMBLE_MSB = 0x25; ///< TX preamble length MSB
43static constexpr uint8_t REG_PREAMBLE_LSB = 0x26;
44static constexpr uint8_t REG_SYNC_CONFIG = 0x27; ///< Sync word config (size, polarity, enable)
45static constexpr uint8_t REG_SYNC_VALUE1 = 0x28; ///< Sync word byte 1 (registers 0x28-0x2F for bytes 1-8)
46static constexpr uint8_t REG_PACKET_CONFIG1 = 0x30; ///< Packet format, CRC, encoding
47static constexpr uint8_t REG_PACKET_CONFIG2 = 0x31; ///< Packet mode, IoHomeOn, PowerFrame
48static constexpr uint8_t REG_PAYLOAD_LENGTH = 0x32; ///< Max payload length
49static constexpr uint8_t REG_FIFO_THRESH = 0x35; ///< FIFO threshold for TX start condition
50static constexpr uint8_t REG_IRQ_FLAGS1 = 0x3E; ///< IRQ flags: mode ready, preamble detect, etc.
51static constexpr uint8_t REG_IRQ_FLAGS2 = 0x3F; ///< IRQ flags: FIFO full/empty, payload ready, CRC ok
52static constexpr uint8_t REG_DIO_MAPPING1 = 0x40; ///< DIO0-DIO3 pin mapping
53static constexpr uint8_t REG_DIO_MAPPING2 = 0x41; ///< DIO4-DIO5 pin mapping
54static constexpr uint8_t REG_VERSION = 0x42; ///< Chip version (should read 0x12 for SX1276)
55static constexpr uint8_t REG_PLLHOP = 0x44; ///< PLL hop: fast frequency change without standby
56static constexpr uint8_t REG_IMAGE_CAL = 0x3B; ///< Image calibration
57
58// SX1276 operating modes (bits [2:0] of REG_OP_MODE)
59static constexpr uint8_t MODE_SLEEP = 0x00;
60static constexpr uint8_t MODE_STDBY = 0x01;
61static constexpr uint8_t MODE_TX = 0x03;
62static constexpr uint8_t MODE_RX = 0x05;
63static constexpr uint8_t MODE_MASK = 0x07;
64
65/// SX1276 crystal oscillator frequency (32 MHz). Used to calculate register values
66/// for bitrate, frequency deviation, and carrier frequency.
67static constexpr uint32_t FXOSC = 32000000U;
68
69// ============================================================================
70// SX1276 Radio Driver
71// ============================================================================
72
73/// @brief SX1276 implementation of RadioDriver.
74/// @ingroup hioc_radio
75///
76/// Manages the SX1276 via SPI using the SpiAccess interface. Configures the chip
77/// in FSK mode with IoHomeOn for hardware CRC and IO‑Homecontrol packet framing.
78class RadioSX1276 : public RadioDriver {
79 public:
80 RadioSX1276(SpiAccess *spi, InternalGPIOPin *rst_pin, InternalGPIOPin *dio0_pin, InternalGPIOPin *dio4_pin,
81 uint8_t tx_power, uint8_t pa_pin)
82 : RadioDriver(rst_pin),
83 spi_(spi),
84 dio0_pin_(dio0_pin),
85 dio4_pin_(dio4_pin),
86 tx_power_(tx_power),
87 pa_pin_(pa_pin) {}
88
89 /// @brief Initialize the SX1276 radio (reset, calibrate, configure registers).
90 /// @return true on success; false on failure (e.g., version check).
91 bool init() override;
92 /// @brief Transmit a frame with specified frequency and preamble.
93 /// @param data Pointer to payload bytes.
94 /// @param len Payload length.
95 /// @param tx_config Transmission config (frequency, preamble).
96 /// @return true if transmit succeeded.
97 bool send_packet(const uint8_t *data, uint8_t len, const RadioTxConfig &tx_config) override;
98 /// @brief Blocking wait for a packet with timeout.
99 /// @param packet Output: received packet (freq, len, data).
100 /// @param timeout_ms Maximum time to wait.
101 /// @return true if a packet was received; false on timeout.
102 bool wait_for_packet(RadioRxPacket &packet, uint32_t timeout_ms) override;
103 /// @brief Non‑blocking check for a received packet (called from loop).
104 /// @param packet Output: received packet if any.
105 /// @return true if a packet was read; false if no interrupt fired.
106 bool check_for_packet(RadioRxPacket &packet) override;
107 /// @brief Change RF frequency using fast hop (no standby needed).
108 /// @param freq_hz New frequency in Hz.
109 void change_frequency(uint32_t freq_hz) override;
110 /// @brief Read instantaneous RSSI (dBm) while in RX mode (used for LBT).
111 /// @return RSSI in dBm (negative).
112 int16_t read_rssi() override;
113 /// @copydoc RadioDriver::is_sync_detected
114 bool is_sync_detected() override;
115 /// @copydoc RadioDriver::is_preamble_detected
116 bool is_preamble_detected() override;
117 /// @brief Per-channel dwell while pairing discovery hops (SX1276).
118 ///
119 /// The SX1276 supports FastHop (PllHop), so frequency changes need no standby
120 /// transition and a short slice is enough. The value comes from the user-facing
121 /// `sx1276_discovery_hop_slice_ms` tuning field.
122 [[nodiscard]] uint16_t discovery_hop_slice_ms(const TuningConfig &tuning) const override {
123 return tuning.sx1276_discovery_hop_slice_ms;
124 }
125 /// @brief TX→RX turnaround capability (SX1276): fast.
126 ///
127 /// The IoHomeOn TX path returns to RX quickly enough that immediate replies
128 /// (including the pairing key-confirm 0x33) are caught through the standard
129 /// exchange wait — validated by real-hardware pairing on this driver.
130 [[nodiscard]] bool has_fast_tx_rx_turnaround() const override { return true; }
131 /// @brief Preamble for response/continuation frames (SX1276).
132 ///
133 /// Runtime-tunable; the default and its hardware rationale are documented at
134 /// @ref SX1276_RESPONSE_PREAMBLE.
135 [[nodiscard]] uint16_t response_preamble() const override { return this->response_preamble_; }
136 /// @brief Apply SX1276 runtime tuning: RX bandwidth and response preamble.
137 void apply_tuning(const TuningConfig &tuning) override {
140 }
141 /// @brief Switch radio into continuous receive mode.
142 void set_mode_rx() override;
143 /// @brief Switch radio into standby mode.
144 void set_mode_standby() override;
145 /// @copydoc RadioDriver::is_failed
146 [[nodiscard]] bool is_failed() const override { return this->failed_; }
147 /// @copydoc RadioDriver::chip_name
148 [[nodiscard]] const char *chip_name() const override { return "sx1276"; }
149 /// @brief Dump radio‑specific debug info to log.
150 void dump_debug() override;
151
152 protected:
153 // --- Tuning helpers (applied via apply_tuning) ---
154 /// Apply the RX bandwidth selector to REG_RX_BW and REG_AFC_BW.
155 /// @param bandwidth Bandwidth selector enum (register byte written verbatim).
156 void set_rx_bandwidth_(SX1276RxBandwidth bandwidth);
157 /// Set the preamble length used for response/continuation frames within an exchange.
158 /// @param preamble Preamble length in bytes.
159 void set_response_preamble_(uint16_t preamble) { this->response_preamble_ = preamble; }
160
161 // --- SPI register access ---
162 /// Read an SX1276 register over SPI.
163 /// @param reg Register address (7 bits, MSB clear for read).
164 /// @return Register value.
165 uint8_t read_register_(uint8_t reg);
166 /// Write an SX1276 register over SPI.
167 /// @param reg Register address (7 bits, MSB set for write).
168 /// @param value Byte to write.
169 void write_register_(uint8_t reg, uint8_t value);
170
171 // --- Radio hardware control ---
172 /// Set the operating mode (sleep/standby/tx/rx) and wait for mode completion.
173 /// @param mode One of MODE_SLEEP, MODE_STDBY, MODE_TX, or MODE_RX.
174 void set_mode_(uint8_t mode);
175 /// Perform full radio configuration (called during init).
176 void configure_radio_();
177 /// Run image calibration routine (required after reset).
178 void run_image_cal_();
179 /// Wait until FIFO payload is ready (polling for TX/RX readiness).
180 /// @param timeout_ms How long to wait.
181 /// @param saw_dio0 Output: true if DIO0 fired.
182 /// @param irq1 Output: IRQ flags 1.
183 /// @param irq2 Output: IRQ flags 2.
184 /// @return true if payload ready before timeout; false otherwise.
185 bool poll_until_payload_ready_(uint32_t timeout_ms, bool &saw_dio0, uint8_t &irq1, uint8_t &irq2);
186 /// Read a packet from the RX FIFO into a buffer.
187 /// @param buf Output buffer.
188 /// @param buf_size Size of buf.
189 /// @return Number of bytes read.
190 uint8_t read_fifo_packet_(uint8_t *buf, uint8_t buf_size);
191 /// Populate last_capture_ from raw telemetry.
192 /// Note: `crc_error` is never set on this driver — in IoHomeOn mode the hardware
193 /// silently drops frames with a bad CRC before they reach the FIFO.
194 /// @param blocking_wait if this was a blocking receive.
195 /// @param irq1 IRQ flags 1.
196 /// @param irq2 IRQ flags 2.
197 /// @param rssi RSSI value.
198 /// @param raw Pointer to raw bytes (may be nullptr).
199 /// @param raw_len Length of raw buffer.
200 /// @param frame Pointer to parsed frame bytes (may be nullptr).
201 /// @param frame_len Length of parsed frame.
202 void fill_capture_info_(bool blocking_wait, uint8_t irq1, uint8_t irq2, uint8_t rssi, const uint8_t *raw,
203 uint8_t raw_len, const uint8_t *frame, uint8_t frame_len);
204
205 /// DIO0 ISR — sets dio_fired flag. Runs in interrupt context.
206 static void gpio_intr(RadioSX1276 *arg);
207
209 InternalGPIOPin *dio0_pin_;
210 InternalGPIOPin *dio4_pin_;
211 uint8_t tx_power_;
212 uint8_t pa_pin_;
213 bool failed_{false};
215 uint16_t response_preamble_{SX1276_RESPONSE_PREAMBLE}; ///< Runtime-tunable response preamble.
216};
217
218} // namespace home_io_control
219} // namespace esphome
RadioDriver(InternalGPIOPin *rst_pin=nullptr)
SX1276 implementation of RadioDriver.
void configure_radio_()
Perform full radio configuration (called during init).
int16_t read_rssi() override
Read instantaneous RSSI (dBm) while in RX mode (used for LBT).
bool wait_for_packet(RadioRxPacket &packet, uint32_t timeout_ms) override
Blocking wait for a packet with timeout.
bool has_fast_tx_rx_turnaround() const override
TX→RX turnaround capability (SX1276): fast.
void set_response_preamble_(uint16_t preamble)
Set the preamble length used for response/continuation frames within an exchange.
uint8_t read_register_(uint8_t reg)
Read an SX1276 register over SPI.
SX1276RxBandwidth rx_bandwidth_
Runtime-tunable RX bandwidth.
void fill_capture_info_(bool blocking_wait, uint8_t irq1, uint8_t irq2, uint8_t rssi, const uint8_t *raw, uint8_t raw_len, const uint8_t *frame, uint8_t frame_len)
Populate last_capture_ from raw telemetry.
uint16_t response_preamble_
Runtime-tunable response preamble.
void apply_tuning(const TuningConfig &tuning) override
Apply SX1276 runtime tuning: RX bandwidth and response preamble.
bool is_failed() const override
Returns true if the radio failed to initialize or encountered a fatal error.
void set_rx_bandwidth_(SX1276RxBandwidth bandwidth)
Apply the RX bandwidth selector to REG_RX_BW and REG_AFC_BW.
void set_mode_(uint8_t mode)
Set the operating mode (sleep/standby/tx/rx) and wait for mode completion.
RadioSX1276(SpiAccess *spi, InternalGPIOPin *rst_pin, InternalGPIOPin *dio0_pin, InternalGPIOPin *dio4_pin, uint8_t tx_power, uint8_t pa_pin)
void set_mode_standby() override
Switch radio into standby mode.
static void gpio_intr(RadioSX1276 *arg)
DIO0 ISR — sets dio_fired flag. Runs in interrupt context.
bool poll_until_payload_ready_(uint32_t timeout_ms, bool &saw_dio0, uint8_t &irq1, uint8_t &irq2)
Wait until FIFO payload is ready (polling for TX/RX readiness).
void dump_debug() override
Dump radio‑specific debug info to log.
void change_frequency(uint32_t freq_hz) override
Change RF frequency using fast hop (no standby needed).
bool init() override
Initialize the SX1276 radio (reset, calibrate, configure registers).
void write_register_(uint8_t reg, uint8_t value)
Write an SX1276 register over SPI.
const char * chip_name() const override
Get a human‑readable chip name.
void set_mode_rx() override
Switch radio into continuous receive mode.
bool is_sync_detected() override
Check if sync word has been detected (while in RX).
uint16_t response_preamble() const override
Preamble for response/continuation frames (SX1276).
uint8_t read_fifo_packet_(uint8_t *buf, uint8_t buf_size)
Read a packet from the RX FIFO into a buffer.
bool send_packet(const uint8_t *data, uint8_t len, const RadioTxConfig &tx_config) override
Transmit a frame with specified frequency and preamble.
bool check_for_packet(RadioRxPacket &packet) override
Non‑blocking check for a received packet (called from loop).
uint16_t discovery_hop_slice_ms(const TuningConfig &tuning) const override
Per-channel dwell while pairing discovery hops (SX1276).
void run_image_cal_()
Run image calibration routine (required after reset).
bool is_preamble_detected() override
Check if preamble has been detected (while in RX).
Interface for SPI bus access.
static constexpr uint8_t REG_RX_CONFIG
Receiver configuration (AFC, AGC, trigger).
static constexpr uint8_t REG_IRQ_FLAGS1
IRQ flags: mode ready, preamble detect, etc.
static constexpr uint8_t REG_FRF_MSB
Carrier frequency MSB (freq = FRF * FXOSC / 2^19).
static constexpr uint8_t REG_PACKET_CONFIG2
Packet mode, IoHomeOn, PowerFrame.
static constexpr uint8_t REG_PA_CONFIG
Power amplifier config (pin select + power level).
static constexpr uint8_t REG_IRQ_FLAGS2
IRQ flags: FIFO full/empty, payload ready, CRC ok.
static constexpr uint8_t MODE_RX
static constexpr uint8_t REG_DIO_MAPPING1
DIO0-DIO3 pin mapping.
static constexpr uint8_t REG_FDEV_LSB
static constexpr uint8_t REG_DIO_MAPPING2
DIO4-DIO5 pin mapping.
static constexpr uint8_t REG_FIFO_THRESH
FIFO threshold for TX start condition.
static constexpr uint8_t REG_FIFO
FIFO read/write access.
static constexpr uint8_t REG_OP_MODE
Operating mode (sleep/standby/tx/rx).
static constexpr uint16_t SX1276_RESPONSE_PREAMBLE
SX1276 preamble for response/continuation frames within an exchange.
static constexpr uint8_t REG_FDEV_MSB
Frequency deviation MSB.
static constexpr uint8_t MODE_SLEEP
static constexpr uint8_t MODE_TX
static constexpr uint8_t REG_PA_RAMP
PA ramp time and modulation shaping.
static constexpr uint8_t MODE_STDBY
static constexpr uint8_t REG_PAYLOAD_LENGTH
Max payload length.
static constexpr uint8_t REG_AFC_BW
AFC bandwidth.
static constexpr uint8_t REG_RX_BW
Receiver bandwidth.
static constexpr uint8_t REG_PACKET_CONFIG1
Packet format, CRC, encoding.
static constexpr uint8_t REG_BITRATE_MSB
Bit rate MSB = FXOSC / bitrate.
static constexpr uint8_t REG_SYNC_CONFIG
Sync word config (size, polarity, enable).
static constexpr uint8_t REG_AFC_FEI
AFC auto clear.
static constexpr uint8_t REG_PREAMBLE_LSB
static constexpr uint8_t REG_PLLHOP
PLL hop: fast frequency change without standby.
static constexpr uint8_t REG_LNA
Low noise amplifier gain and boost.
static constexpr uint8_t MODE_MASK
static constexpr uint32_t FXOSC
SX1276 crystal oscillator frequency (32 MHz).
static constexpr uint8_t REG_IMAGE_CAL
Image calibration.
static constexpr uint8_t REG_RSSI_CONFIG
RSSI smoothing.
static constexpr uint8_t REG_OSC
Oscillator / clock output.
static constexpr uint8_t REG_VERSION
Chip version (should read 0x12 for SX1276).
SX1276RxBandwidth
Valid SX1276 RX bandwidth options (RegRxBw register bytes).
@ BW_41_7_KHZ
41.7 kHz — default (validated against real devices).
static constexpr uint8_t REG_PREAMBLE_DETECT
Preamble detector config.
static constexpr uint8_t REG_SYNC_VALUE1
Sync word byte 1 (registers 0x28-0x2F for bytes 1-8).
static constexpr uint8_t REG_RSSI_VALUE
Instant RSSI value in FSK mode.
static constexpr uint8_t REG_FRF_MID
static constexpr uint8_t REG_PREAMBLE_MSB
TX preamble length MSB.
static constexpr uint8_t REG_BITRATE_LSB
static constexpr uint8_t REG_FRF_LSB
Radio abstraction layer for IO-Homecontrol.
Raw packet received from the radio.
Configuration for transmitting a packet: carrier frequency and preamble length.
All runtime tunable parameters for pairing and radio diagnostics.
SX1276RxBandwidth sx1276_rx_bandwidth
SX1276 RX bandwidth selector.
uint16_t sx1276_response_preamble
SX1276 response preamble in bytes.
uint16_t sx1276_discovery_hop_slice_ms
Per-channel dwell while SX1276 discovery hops.