12#include "esphome/core/log.h"
13#include "esphome/core/application.h"
20static const char *
const TAG =
"home_io_control.sx1276";
43 this->
write_register_(REG_OP_MODE, (current & ~MODE_MASK) | (mode & MODE_MASK));
44 uint32_t
const start = millis();
46 uint8_t
const cur_mode = this->
read_register_(REG_OP_MODE) & MODE_MASK;
47 if (cur_mode == mode || (mode == MODE_RX && cur_mode == 0x04))
49 if (millis() - start > 50) {
50 ESP_LOGE(
TAG,
"Radio mode change timeout");
51 this->
fail_(
"SX1276 never reached the requested operating mode");
64 uint32_t
const start = millis();
66 if (millis() - start > 20) {
67 ESP_LOGE(
TAG,
"Image calibration timeout");
68 this->
fail_(
"SX1276 image calibration never completed");
78 uint8_t raw_len,
const uint8_t *frame, uint8_t frame_len) {
91 uint64_t
const frf = ((uint64_t) freq_hz << 19) / FXOSC;
113 uint32_t
const now = micros();
146 ESP_LOGE(
TAG,
"SX1276 not found");
147 this->
fail_(
"SX1276 not found (version register is not 0x12) -- check SPI wiring and radio_type");
155 ESP_LOGI(
TAG,
"SX1276 initialized");
171 uint64_t
const frf = ((uint64_t) FREQ_CH2 << 19) / FXOSC;
212 uint32_t
const br = FXOSC / 38400;
217 auto fd = (uint32_t) ((19200.0F / FXOSC) * (1 << 19));
223 uint8_t p = std::max(this->
tx_power_, (uint8_t) 2);
224 p = std::min(p, (uint8_t) 17);
225 this->
write_register_(REG_PA_CONFIG, SX1276_PA_SELECT_PA_BOOST | (p - 2));
259#ifdef IOHOME_FRAME_LOG
270 for (uint8_t i = 0; i < len; i++)
278 uint32_t
const start = millis();
280 if (millis() - start > 4000) {
281 ESP_LOGE(
TAG,
"TX timeout");
286 delayMicroseconds(100);
297 uint32_t
const start = millis();
305 if ((irq2 & 0x04) != 0)
307 if (millis() - start > timeout_ms)
322 while (((this->
read_register_(REG_IRQ_FLAGS2) & 0x40) == 0) && len < buf_size)
331 bool saw_dio0 =
false;
338 if ((irq2 & 0x04) == 0) {
346#ifdef IOHOME_FRAME_LOG
348 log_frame(
"RX", packet.
data, packet.
len, this->current_freq_);
350 return packet.
len > 0;
361 if ((irq2 & 0x04) != 0) {
365#ifdef IOHOME_FRAME_LOG
367 log_frame(
"RX", packet.
data, packet.
len, this->current_freq_);
369 return packet.
len > 0;
372 if ((irq2 & 0x10) != 0) {
379 ESP_LOGCONFIG(
TAG,
" SX1276 Diagnostic:");
380 ESP_LOGCONFIG(
TAG,
" PA output: %s", this->
uses_pa_boost_() ?
"PA_BOOST" :
"RFO");
381 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.
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.
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 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).
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
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
SX1276 radio driver for IO-Homecontrol.
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 and preamble length.
uint16_t preamble_len
Preamble length in symbol periods (bytes).
uint32_t freq_hz
Carrier frequency in Hz.