13#include "esphome/core/log.h"
14#include "esphome/core/application.h"
21static const char *
const TAG =
"home_io_control.sx1276";
44 this->
write_register_(REG_OP_MODE, (current & ~MODE_MASK) | (mode & MODE_MASK));
49 uint8_t
const cur_mode = this->
read_register_(REG_OP_MODE) & MODE_MASK;
50 return cur_mode == mode || (mode == MODE_RX && cur_mode == 0x04);
54 ESP_LOGE(
TAG,
"Radio mode change timeout");
55 this->
fail_(
"SX1276 never reached the requested operating mode");
67 ESP_LOGE(
TAG,
"Image calibration timeout");
68 this->
fail_(
"SX1276 image calibration never completed");
75 uint8_t raw_len,
const uint8_t *frame, uint8_t frame_len) {
88 uint64_t
const frf = ((uint64_t) freq_hz << 19) / FXOSC;
100 this->
write_register_(REG_RX_CONFIG, RX_CONFIG_VALUE | RX_CONFIG_RESTART_RX_WITH_PLL_LOCK);
118 uint32_t
const now = micros();
137 uint32_t
const now = micros();
170 ESP_LOGE(
TAG,
"SX1276 not found");
171 this->
fail_(
"SX1276 not found (version register is not 0x12) -- check SPI wiring and radio_type");
179 ESP_LOGI(
TAG,
"SX1276 initialized");
195 uint64_t
const frf = ((uint64_t) FREQ_CH2 << 19) / FXOSC;
236 uint32_t
const br = FXOSC / 38400;
241 auto fd = (uint32_t) ((19200.0F / FXOSC) * (1 << 19));
247 uint8_t p = std::max(this->
tx_power_, (uint8_t) 2);
248 p = std::min(p, (uint8_t) 17);
249 this->
write_register_(REG_PA_CONFIG, SX1276_PA_SELECT_PA_BOOST | (p - 2));
286#ifdef IOHOME_FRAME_LOG
297 for (uint8_t i = 0; i < len; i++)
308 uint32_t
const start = millis();
310 if (millis() - start > timeout_ms) {
311 ESP_LOGE(
TAG,
"TX timeout after %" PRIu32
" ms — DIO0 never fired (RegOpMode 0x%02X, RegIrqFlags2 0x%02X)",
317 delayMicroseconds(100);
328 uint32_t
const start = millis();
336 if ((irq2 & 0x04) != 0)
338 if (millis() - start > timeout_ms)
353 while (((this->
read_register_(REG_IRQ_FLAGS2) & 0x40) == 0) && len < buf_size)
362 bool saw_dio0 =
false;
369 if ((irq2 & 0x04) == 0) {
377#ifdef IOHOME_FRAME_LOG
379 log_frame(
"RX", packet.
data, packet.
len, this->current_freq_);
381 return packet.
len > 0;
392 if ((irq2 & 0x04) != 0) {
396#ifdef IOHOME_FRAME_LOG
398 log_frame(
"RX", packet.
data, packet.
len, this->current_freq_);
400 return packet.
len > 0;
403 if ((irq2 & 0x10) != 0) {
410 ESP_LOGCONFIG(
TAG,
" SX1276 Diagnostic:");
411 ESP_LOGCONFIG(
TAG,
" PA output: %s", this->
uses_pa_boost_() ?
"PA_BOOST" :
"RFO");
412 ESP_LOGCONFIG(
TAG,
" Opmode=0x%02X irq1=0x%02X irq2=0x%02X freq=%" PRIu32, this->
read_register_(REG_OP_MODE),
void populate_capture_base_(bool blocking_wait, uint32_t freq_hz, int16_t rssi_dbm, const uint8_t *raw, uint8_t raw_len, const uint8_t *frame, uint8_t frame_len)
Populate the common fields of RadioCaptureInfo from raw telemetry.
InternalGPIOPin * rst_pin_
void mark_dio_fired_from_isr()
void reset_hardware_()
Shared hardware reset sequence for chips with an active-low RST pin.
bool is_dio_fired() const
Set by the ISR when DIO fires.
std::optional< int16_t > channel_busy_level_(const RadioTxConfig &tx_config, uint32_t settle_us)
The clear-channel check a driver runs at the top of send_packet().
void fail_(const char *reason)
Latch the failed state and record why, so is_failed and failure_reason can never disagree.
RadioCaptureInfo last_capture_
void prepare_nonblocking_receive_(RadioRxPacket &packet)
Common preamble for non‑blocking receive: clear diagnostics, output packet, and DIO latch.
bool failed_
Latched by fail_; see is_failed.
void prepare_blocking_receive_(RadioRxPacket &packet)
Common preamble for blocking receive: clear diagnostics and output packet.
void configure_radio_()
Perform full radio configuration (called during init).
uint32_t sync_hold_since_us_
First-sighting timestamp for reception_in_progress()'s un-stick bound.
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.
uint8_t read_register_(uint8_t reg)
Read an SX1276 register over SPI.
InternalGPIOPin * dio4_pin_
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.
uint32_t preamble_hold_since_us_
First-sighting timestamp for reception_in_progress()'s preamble hold.
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.
InternalGPIOPin * dio0_pin_
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).
bool reception_in_progress() override
True while a frame is arriving on the current channel and retuning would destroy it.
void write_register_(uint8_t reg, uint8_t value)
Write an SX1276 register over SPI.
bool uses_pa_boost_() const
True when TX goes out through PA_BOOST rather than RFO; the PA config and the dump both key on it.
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).
uint8_t read_fifo_packet_(uint8_t *buf, uint8_t buf_size)
Read a packet from the RX FIFO into a buffer.
bool preamble_hold_()
The PreambleDetect half of reception_in_progress(): true while a fresh preamble detection is younger ...
uint32_t tx_air_time_us(uint8_t len, const RadioTxConfig &cfg) const override
Transmit time (SX1276): the preamble, sync word and the IoHomeOn-coded frame at the protocol line rat...
bool check_for_packet(RadioRxPacket &packet) override
Non‑blocking check for a received packet (called from loop).
TxResult send_packet(const uint8_t *data, uint8_t len, const RadioTxConfig &tx_config) override
Transmit a frame with specified frequency and preamble.
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).
virtual void spi_enable()=0
Enable the SPI bus (assert CS low).
virtual void spi_write(uint8_t data)=0
Write one byte (MOSI only, MISO ignored).
virtual uint8_t spi_transfer(uint8_t data)=0
Transfer one byte full‑duplex (MOSI→MISO).
virtual void spi_disable()=0
Disable the SPI bus (deassert CS).
virtual uint8_t spi_read()=0
Read one byte (MISO only, MOSI driven with 0).
Shared frame logging helpers for IO-Homecontrol.
constexpr uint32_t RX_HOP_HOLDOFF_US
Longest a frame arriving on the current channel may hold off an idle-path channel hop,...
static constexpr const char * TAG
PollResult poll_until(Ready ready, uint32_t timeout_ms)
Poll ready() until it is true or timeout_ms has passed, feeding the watchdog between samples.
constexpr uint32_t tx_done_timeout_ms(uint32_t air_time_us)
How long a driver waits for TX-done after starting a transmission.
SX1276RxBandwidth
Valid SX1276 RX bandwidth options (RegRxBw register bytes).
constexpr uint8_t RADIO_PACKET_BUFFER_SIZE
Scratch buffer size for raw radio packets and recovered frames.
static const char *const TAG
Preemption-safe "poll a condition until it holds or a deadline passes" helper.
SX1276 radio driver for IO-Homecontrol.
bool ready
The condition held on the deciding sample.
uint8_t irq_flags2
IRQ flags group 2 (chip-specific, includes CRC flag).
bool crc_error
True if a CRC error was detected.
uint8_t reported_len
Length reported by the radio chip.
bool rx_done
True if RxDone IRQ fired.
uint8_t irq_flags1
IRQ flags group 1 (chip-specific).
Raw packet received from the radio.
uint8_t len
Length of packet in bytes.
uint32_t freq_hz
Frequency the packet was received on (Hz).
uint8_t data[RADIO_PACKET_BUFFER_SIZE]
Raw packet data buffer.
Configuration for transmitting a packet: carrier frequency, preamble length, wake-up level and clear-...
uint16_t preamble_len
Preamble length in symbol periods (bytes).
uint32_t freq_hz
Carrier frequency in Hz.
What send_packet() did with a frame.
static TxResult channel_busy(int16_t level_dbm)