Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
radio_sx1262.cpp
Go to the documentation of this file.
1/// @file radio_sx1262.cpp
2/// @brief SX1262 radio driver implementation for IO-Homecontrol.
3/// @ingroup hioc_radio
4///
5/// Unlike the SX1276, the SX1262 does not provide Semtech's IoHomeOn mode for
6/// IO-Homecontrol. On SX1276 that mode handles key protocol details in hardware:
7/// CRC generation and checking, packet boundary handling, and delivery of
8/// already-decoded protocol bytes in the FIFO. On SX1262 those pieces are reproduced
9/// in software by SoftPhyDriverBase (shared with RadioLR1121) on top of generic GFSK
10/// support; this file supplies the SPI transport and every register/opcode encoding
11/// SoftPhyDriverBase's shared RX/TX orchestration calls through virtual primitives.
12///
13/// The chip interface itself is opcode-based SPI instead of the SX1276 register model, and
14/// every transaction must respect the BUSY line. For experiment builds, the RX path
15/// prioritizes preserving the chip-reported bytes and metadata verbatim.
16
17// The SX1262 path is intentionally low-level: opcode payloads, line-coding widths, and recovery
18// thresholds are written in the same shape as the chip protocol and on-air framing.
19// NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
20
21#include "radio_sx1262.h"
22#include "esphome/core/log.h"
23#include "esphome/core/application.h"
24
25namespace esphome {
26namespace home_io_control {
27
28static const char *const TAG = "home_io_control.sx1262";
29static const uint8_t SX1262_SYNC_WORD_PARAM_24_BITS = 0x18;
30
31// === SPI Communication (opcode-based) ===
32
34 uint32_t const start = millis();
35 while (this->busy_pin_->digital_read()) {
36 if (millis() - start > 10) {
37 ESP_LOGE(TAG, "BUSY timeout");
38 this->failed_ = true;
39 return;
40 }
41 App.feed_wdt();
42 }
43}
44
45void RadioSX1262::write_opcode_(uint8_t opcode, const uint8_t *params, uint8_t len) {
46 this->wait_busy_();
47 this->spi_->spi_enable();
48 this->spi_->spi_transfer(opcode);
49 for (uint8_t i = 0; i < len; i++)
50 this->spi_->spi_transfer(params[i]);
51 this->spi_->spi_disable();
52}
53
54void RadioSX1262::read_opcode_(uint8_t opcode, uint8_t *data, uint8_t len) {
55 this->wait_busy_();
56 this->spi_->spi_enable();
57 this->spi_->spi_transfer(opcode);
58 this->spi_->spi_transfer(0x00); // NOP — status byte
59 for (uint8_t i = 0; i < len; i++)
60 data[i] = this->spi_->spi_transfer(0x00);
61 this->spi_->spi_disable();
62}
63
65 uint8_t irq_raw[2] = {0};
66 this->read_opcode_(SX1262_GET_IRQ_STATUS, irq_raw, 2);
67 return (static_cast<uint32_t>(irq_raw[0]) << 8) | static_cast<uint32_t>(irq_raw[1]);
68}
69
70void RadioSX1262::write_register_(uint16_t addr, const uint8_t *data, uint8_t len) {
71 this->wait_busy_();
72 this->spi_->spi_enable();
73 this->spi_->spi_transfer(SX1262_WRITE_REGISTER);
74 this->spi_->spi_transfer((addr >> 8) & 0xFF); // Address MSB
75 this->spi_->spi_transfer(addr & 0xFF); // Address LSB
76 for (uint8_t i = 0; i < len; i++)
77 this->spi_->spi_transfer(data[i]);
78 this->spi_->spi_disable();
79}
80
81void RadioSX1262::read_register_(uint16_t addr, uint8_t *data, uint8_t len) {
82 this->wait_busy_();
83 this->spi_->spi_enable();
84 this->spi_->spi_transfer(SX1262_READ_REGISTER);
85 this->spi_->spi_transfer((addr >> 8) & 0xFF); // Address MSB
86 this->spi_->spi_transfer(addr & 0xFF); // Address LSB
87 this->spi_->spi_transfer(0x00); // NOP — status byte
88 for (uint8_t i = 0; i < len; i++)
89 data[i] = this->spi_->spi_transfer(0x00);
90 this->spi_->spi_disable();
91}
92
93void RadioSX1262::write_buffer_(uint8_t offset, const uint8_t *data, uint8_t len) {
94 this->wait_busy_();
95 this->spi_->spi_enable();
96 this->spi_->spi_transfer(SX1262_WRITE_BUFFER);
97 this->spi_->spi_transfer(offset);
98 for (uint8_t i = 0; i < len; i++)
99 this->spi_->spi_transfer(data[i]);
100 this->spi_->spi_disable();
101}
102
103void RadioSX1262::read_buffer_(uint8_t offset, uint8_t *data, uint8_t len) {
104 this->wait_busy_();
105 this->spi_->spi_enable();
106 this->spi_->spi_transfer(SX1262_READ_BUFFER);
107 this->spi_->spi_transfer(offset);
108 this->spi_->spi_transfer(0x00); // NOP — status byte
109 for (uint8_t i = 0; i < len; i++)
110 data[i] = this->spi_->spi_transfer(0x00);
111 this->spi_->spi_disable();
112}
113
114// === Packet params helper ===
115
116void RadioSX1262::set_packet_params_(uint16_t preamble_len, uint8_t payload_len, uint8_t packet_type,
117 uint8_t crc_type) {
118 // preamble_len arrives in bytes, matching every other layer in this codebase (LONG_PREAMBLE,
119 // SHORT_PREAMBLE, the tuning defaults) and the SX1276's byte-wide RegPreambleMsb/Lsb. This
120 // chip's SetPacketParams field is bit-denominated instead — Semtech's own struct names it
121 // sx126x_pkt_params_gfsk_t.preamble_len_in_bits — so convert at this last step, right before
122 // the value leaves for the wire.
123 const uint16_t preamble_bits = preamble_len * 8;
124 uint8_t params[9] = {
125 (uint8_t) (preamble_bits >> 8), // Preamble length MSB
126 (uint8_t) preamble_bits, // Preamble length LSB
127 0x04, // Preamble detector: 8 bits (1 byte)
128 SX1262_SYNC_WORD_PARAM_24_BITS, // Sync word length: 24 bits (3 bytes)
129 0x00, // Address comparison: off
130 packet_type, // GFSK packet type: known length or variable size
131 payload_len, // Configured payload length
132 crc_type, // CRC type: SX1262 GFSK CRC mode
133 0x00, // Whitening: off
134 };
135 this->write_opcode_(SX1262_SET_PACKET_PARAMS, params, sizeof(params));
136}
137
139 // The variable-size packet engine recovers a stable 15-byte boundary, but that boundary is
140 // too short for a full software UART decode and buffer reads past it are not trustworthy.
141 // Probe again with a fixed raw packet size that matches typical UART-packed 23-25 byte
142 // IO-homecontrol frames (SOFT_PHY_RX_PROBE_PACKET_LEN, shared with LR1121 — see its doc
143 // comment in radio_soft_phy_driver_base.h).
145}
146
148 // SX1262 GFSK modulation parameters for the IO-Homecontrol 868 MHz waveform.
149 //
150 // BitRate = 32 * Fxosc / BR_reg → BR_reg = 32 * 32MHz / 38400 = 26667 = 0x00682B
151 // Pulse shape: Gaussian BT=1.0 (0x0B) — reduces TX spectral occupation.
152 // Bandwidth: the register byte is runtime-tunable via rx_bandwidth_. The default
153 // 117.3 kHz (0x0B) is wider than the Carson-rule minimum (77 kHz) but needed
154 // to tolerate the SX1262 LO frequency offset after the TX→RX transition. At
155 // 58.6 kHz the demodulator produced ~50% bit errors on post-TX frames; 117.3 kHz
156 // gives a clean decode (confirmed via loopback turnaround test).
157 // Fdev = fdev_hz * 2^25 / 32e6 → 19200 * 2^25 / 32e6 = 20133 = 0x004EA5
158 uint8_t mod_params[8] = {
159 0x00,
160 0x68,
161 0x2B, // Bitrate: 38400 bps
162 0x0B, // Pulse shape: Gaussian BT=1.0
163 static_cast<uint8_t>(this->rx_bandwidth_), // Bandwidth: runtime-tunable
164 0x00,
165 0x4E,
166 0xA5, // Fdev: 19200 Hz
167 };
168 this->write_opcode_(SX1262_SET_MODULATION_PARAMS, mod_params, sizeof(mod_params));
169}
170
171void RadioSX1262::clear_irq_status(uint32_t irq_mask) {
172 uint8_t clear_irq[2] = {
173 (uint8_t) ((irq_mask >> 8) & 0xFF),
174 (uint8_t) (irq_mask & 0xFF),
175 };
176 this->write_opcode_(SX1262_CLEAR_IRQ_STATUS, clear_irq, sizeof(clear_irq));
177}
178
180 uint8_t errors_raw[2] = {0};
181 this->read_opcode_(SX1262_GET_DEVICE_ERRORS, errors_raw, sizeof(errors_raw));
182 return (uint16_t) (((uint16_t) errors_raw[0] << 8) | errors_raw[1]);
183}
184
186 uint8_t clear_errors[2] = {0x00, 0x00};
187 this->write_opcode_(SX1262_CLEAR_DEVICE_ERRORS, clear_errors, sizeof(clear_errors));
188}
189
191 uint8_t buf_base[2] = {SX1262_TX_BUFFER_BASE, SX1262_RX_BUFFER_BASE};
192 this->write_opcode_(SX1262_SET_BUFFER_BASE_ADDRESS, buf_base, sizeof(buf_base));
193}
194
195void RadioSX1262::fill_capture_info(bool blocking_wait, uint32_t irq_status, uint8_t rx_offset, uint8_t reported_len,
196 const uint8_t *raw, uint8_t raw_len, const uint8_t *frame, uint8_t frame_len) {
197 uint8_t packet_status[3] = {0};
198 this->read_opcode_(SX1262_GET_PACKET_STATUS, packet_status, sizeof(packet_status));
199
200 this->populate_capture_base_(blocking_wait, this->current_freq_, -(int16_t) packet_status[1] / 2, raw, raw_len, frame,
201 frame_len);
202 this->last_capture_.rx_done = (irq_status & SX1262_IRQ_RX_DONE) != 0;
203 this->last_capture_.crc_error = (irq_status & SX1262_IRQ_CRC_ERR) != 0;
204 this->last_capture_.irq_status = static_cast<uint16_t>(irq_status);
205 this->last_capture_.packet_status = packet_status[0];
206 this->last_capture_.rx_offset = rx_offset;
207 this->last_capture_.reported_len = reported_len;
208}
209
211 uint8_t raw = 0;
212 this->read_opcode_(SX1262_GET_RSSI_INST, &raw, 1);
213 return raw;
214}
215
216void RadioSX1262::get_rx_buffer_status(uint8_t &reported_len, uint8_t &rx_offset) {
217 uint8_t rx_status[2] = {0};
218 this->read_opcode_(SX1262_GET_RX_BUFFER_STATUS, rx_status, sizeof(rx_status));
219 reported_len = rx_status[0];
220 rx_offset = rx_status[1];
221}
222
224 // Read-modify-write so the reserved bits of the register keep whatever the chip put there.
225 uint8_t tx_modulation = 0;
226 this->read_register_(SX1262_REG_TX_MODULATION, &tx_modulation, 1);
227 tx_modulation |= SX1262_TX_MODULATION_GFSK_BIT;
228 this->write_register_(SX1262_REG_TX_MODULATION, &tx_modulation, 1);
229}
230
232 // Start TX with 4s timeout (256000 ticks at 15.625us/tick = 0x03E800).
233 uint8_t tx_timeout[3] = {0x03, 0xE8, 0x00};
234 this->write_opcode_(SX1262_SET_TX, tx_timeout, sizeof(tx_timeout));
235}
236
237// === ISR ===
238
240
241// === Initialization ===
242
244 // --- Pin setup ---
245 this->rst_pin_->setup();
246 this->dio1_pin_->setup();
247 this->busy_pin_->setup();
248
249 // Front-end module pins (e.g., Heltec V4)
250 if (this->fem_en_pin_ != nullptr) {
251 this->fem_en_pin_->setup();
252 this->fem_en_pin_->digital_write(true);
253 }
254 if (this->vfem_pin_ != nullptr) {
255 this->vfem_pin_->setup();
256 this->vfem_pin_->digital_write(true);
257 }
258 if (this->fem_pa_pin_ != nullptr) {
259 this->fem_pa_pin_->setup();
260 this->fem_pa_pin_->digital_write(true);
261 }
262
263 // --- Hardware reset ---
264 this->reset_hardware_();
265 this->wait_busy_();
266 if (this->failed_)
267 return false;
268
269 this->configure_radio_();
270 if (this->failed_)
271 return false;
272
273 ESP_LOGI(TAG, "SX1262 initialized");
274 return true;
275}
276
278 this->wait_busy_();
279 this->spi_->spi_enable();
280 uint8_t const chip_status = this->spi_->spi_transfer(SX1262_GET_STATUS);
281 this->spi_->spi_transfer(0x00);
282 this->spi_->spi_disable();
283
284 uint8_t const chip_mode = (chip_status >> 4) & 0x07;
285 uint8_t const cmd_status = (chip_status >> 1) & 0x07;
286 const char *mode_str = "?";
287 switch (chip_mode) {
288 case 2:
289 mode_str = "STDBY_RC";
290 break;
291 case 3:
292 mode_str = "STDBY_XOSC";
293 break;
294 case 4:
295 mode_str = "FS";
296 break;
297 case 5:
298 mode_str = "RX";
299 break;
300 case 6:
301 mode_str = "TX";
302 break;
303 default:
304 break;
305 }
306
307 uint8_t sync[3];
308 this->read_register_(SX1262_REG_SYNC_WORD, sync, 3);
309
310 uint8_t irq_raw[2];
311 this->read_opcode_(SX1262_GET_IRQ_STATUS, irq_raw, 2);
312 uint16_t const irq = ((uint16_t) irq_raw[0] << 8) | irq_raw[1];
313 uint16_t const errors = this->get_device_errors_();
314
315 ESP_LOGCONFIG(TAG, " SX1262 Diagnostic:");
316 ESP_LOGCONFIG(TAG, " Chip status: 0x%02X (mode=%s, cmd=%u)", chip_status, mode_str, cmd_status);
317 ESP_LOGCONFIG(TAG, " BUSY=%d DIO1=%d", this->busy_pin_->digital_read(), this->dio1_pin_->digital_read());
318 ESP_LOGCONFIG(TAG, " Sync word: %02X %02X %02X (expect 57 FD 99)", sync[0], sync[1], sync[2]);
319 ESP_LOGCONFIG(TAG, " IRQ status: 0x%04X", irq);
320 ESP_LOGCONFIG(TAG, " Device errors: 0x%04X", errors);
321}
322
324 // 1. Standby on RC oscillator (safe starting point)
325 uint8_t const stdby_rc = 0x00;
326 this->write_opcode_(SX1262_SET_STANDBY, &stdby_rc, 1);
327
328 // 2. Configure TCXO via DIO3 — voltage + 5ms timeout (320 ticks at 15.625us/tick)
329 uint8_t tcxo_params[4] = {this->tcxo_voltage_, 0x00, 0x01, 0x40};
330 this->write_opcode_(SX1262_SET_DIO3_AS_TCXO_CTRL, tcxo_params, sizeof(tcxo_params));
331
332 // 3. Calibrate all blocks
333 uint8_t const cal = 0x7F;
334 this->write_opcode_(SX1262_CALIBRATE, &cal, 1);
335 delay(5); // Wait for calibration to complete
336
337 // 4. Standby on XOSC (TCXO now running)
338 uint8_t const stdby_xosc = 0x01;
339 this->write_opcode_(SX1262_SET_STANDBY, &stdby_xosc, 1);
340
341 // 5. Use DC-DC regulator for better efficiency
342 uint8_t const reg_mode = 0x01;
343 this->write_opcode_(SX1262_SET_REGULATOR_MODE, &reg_mode, 1);
344
345 // 6. DIO2 as RF switch control (for boards with integrated RF switch)
346 uint8_t const dio2_rf = 0x01;
348
349 // 6b. Keep the crystal path alive after RX/TX completion instead of relying on the chip default.
350 uint8_t const fallback_mode = SX1262_FALLBACK_STDBY_XOSC;
351 this->write_opcode_(SX1262_SET_RX_TX_FALLBACK_MODE, &fallback_mode, 1);
352
353 // 7. FSK packet type
354 uint8_t const pkt_type = 0x00;
355 this->write_opcode_(SX1262_SET_PACKET_TYPE, &pkt_type, 1);
356
357 // 8. Set frequency to channel 2 (868.95 MHz)
359
360 // 9. Calibrate image for 863-870 MHz band
361 uint8_t cal_img[2] = {0xD7, 0xDB};
362 this->write_opcode_(SX1262_CALIBRATE_IMAGE, cal_img, sizeof(cal_img));
363
364 // 9b. Use boosted RX gain for maximum sensitivity.
365 uint8_t const rx_gain = 0x96;
366 this->write_register_(SX1262_REG_RX_GAIN, &rx_gain, 1);
367
368 // 10. Apply GFSK modulation parameters (detailed values live in write_modulation_params_()).
370
371 // 11. Default RX packet params: variable-size GFSK with hardware CCITT CRC validation.
372 this->set_rx_packet_params();
373
374 // 12. Sync word: 0x57 0xFD 0x99 (24-bit UART-derived IO-homecontrol hypothesis)
375 uint8_t sync_word[8] = {0x57, 0xFD, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00};
376 this->write_register_(SX1262_REG_SYNC_WORD, sync_word, sizeof(sync_word));
377
378 // 12b. CRC registers are configured for potential future hardware-CRC use, but RX uses
379 // CRC_OFF because the UART encoding makes hardware CRC checking impossible — the chip
380 // sees UART-packed bits, not raw protocol bytes.
381 uint8_t crc_init[2] = {0x1D, 0x0F};
382 this->write_register_(0x06BC, crc_init, 2);
383 uint8_t crc_poly[2] = {0x10, 0x21};
384 this->write_register_(0x06BE, crc_poly, 2);
385
386 // 13. Buffer base addresses: TX at 0x00, RX at 0x80
387 this->configure_buffer_base();
388
389 // 14. PA config: SX1262 high power PA (paDutyCycle=0x04, hpMax=0x07, deviceSel=0x00=SX1262, paLut=0x01)
390 uint8_t pa_config[4] = {0x04, 0x07, 0x00, 0x01};
391 this->write_opcode_(SX1262_SET_PA_CONFIG, pa_config, sizeof(pa_config));
392
393 // 14b. Apply Semtech's SX1262 clamp workaround for better tolerance of RF mismatch.
394 uint8_t tx_clamp = 0;
395 this->read_register_(SX1262_REG_TX_CLAMP_CONFIG, &tx_clamp, 1);
396 tx_clamp |= 0x1E;
397 this->write_register_(SX1262_REG_TX_CLAMP_CONFIG, &tx_clamp, 1);
398
399 // 15. TX params: power in dBm (SX1262 accepts -9 to +22 directly), ramp 200us (0x04)
400 int8_t const power = std::max((int8_t) -9, std::min((int8_t) 22, (int8_t) this->tx_power_));
401 uint8_t tx_params[2] = {(uint8_t) power, 0x04};
402 this->write_opcode_(SX1262_SET_TX_PARAMS, tx_params, sizeof(tx_params));
403
404 // 16. IRQ config: map TxDone + RxDone + SyncWordValid + CrcErr to DIO1
405 uint8_t irq_params[8] = {
406 0x00, 0x4B, // irqMask: TxDone(0x0001) | RxDone(0x0002) | SyncWordValid(0x0008) | CrcErr(0x0040)
407 0x00, 0x4B, // dio1Mask: same
408 0x00, 0x00, // dio2Mask: none
409 0x00, 0x00, // dio3Mask: none
410 };
411 this->write_opcode_(SX1262_SET_DIO_IRQ_PARAMS, irq_params, sizeof(irq_params));
412
413 // 17. Attach DIO1 interrupt
414 this->dio1_pin_->attach_interrupt(&RadioSX1262::gpio_intr, this, gpio::INTERRUPT_RISING_EDGE);
415
416 // 18. Clear any pending IRQs, and report the device-error word before clearing it. A chip that
417 // came up with XOSC_START_ERR (wrong tcxo_voltage for the board), PLL_LOCK_ERR or IMG_CALIB_ERR
418 // still initializes and still transmits — it just does so off-frequency or off-calibration,
419 // which on air looks like flaky exchanges at any range rather than an outright failure. Clearing
420 // it unseen threw away the one cheap piece of evidence for that.
421 this->clear_irq_status(0xFFFF);
422 uint16_t const init_errors = this->get_device_errors_();
423 if (init_errors != 0)
424 ESP_LOGW(TAG, "SX1262 device errors after init: 0x%04X — check tcxo_voltage for this board", init_errors);
425 this->clear_device_errors_();
426
427 // 19. Enter continuous receive
428 uint8_t rx_continuous[3] = {0xFF, 0xFF, 0xFF}; // 0xFFFFFF = continuous
429 this->write_opcode_(SX1262_SET_RX, rx_continuous, sizeof(rx_continuous));
430}
431
432// === Mode control ===
433
435 uint8_t const stdby = 0x01; // STDBY_XOSC
436 this->write_opcode_(SX1262_SET_STANDBY, &stdby, 1);
437}
438
440 uint8_t rx_continuous[3] = {0xFF, 0xFF, 0xFF};
441 this->write_opcode_(SX1262_SET_RX, rx_continuous, sizeof(rx_continuous));
442}
443
444// === Frequency control ===
445
447 auto freq_reg = (uint32_t) ((double) freq_hz * (1 << 25) / 32e6);
448 uint8_t params[4] = {
449 (uint8_t) (freq_reg >> 24),
450 (uint8_t) (freq_reg >> 16),
451 (uint8_t) (freq_reg >> 8),
452 (uint8_t) freq_reg,
453 };
454 this->write_opcode_(SX1262_SET_RF_FREQUENCY, params, sizeof(params));
455 this->current_freq_ = freq_hz;
456}
457
459 this->rx_bandwidth_ = bandwidth;
461}
462
463} // namespace home_io_control
464} // namespace esphome
465
466// NOLINTEND(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
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.
void reset_hardware_()
Shared hardware reset sequence for chips with an active-low RST pin.
void set_rx_packet_params() override
Configure RX-specific packet parameters (preamble detector length, fixed probe length).
void wait_busy_()
Wait until BUSY pin is low before any SPI transaction.
void set_mode_rx() override
Switch to continuous receive mode.
void get_rx_buffer_status(uint8_t &reported_len, uint8_t &rx_offset) override
Read the chip-reported RX length and buffer offset (raw, before any clamping).
void write_opcode_(uint8_t opcode, const uint8_t *params, uint8_t len)
Write an opcode with optional parameter bytes.
void set_mode_standby() override
Switch to standby mode.
void clear_irq_status(uint32_t irq_mask) override
Clear IRQ status bits.
RadioSX1262(SpiAccess *spi, InternalGPIOPin *rst_pin, InternalGPIOPin *dio1_pin, InternalGPIOPin *busy_pin, uint8_t tx_power, uint8_t tcxo_voltage, InternalGPIOPin *fem_en_pin=nullptr, InternalGPIOPin *vfem_pin=nullptr, InternalGPIOPin *fem_pa_pin=nullptr)
void write_modulation_params_()
Apply the runtime bandwidth setting to the SX1262 modulation parameters.
void write_buffer_(uint8_t offset, const uint8_t *data, uint8_t len)
Write into the SX1262 TX/RX buffer at a given offset.
uint8_t read_rssi_raw_byte() override
Read the single raw RSSI byte (chip-specific opcode); formula is shared, see read_rssi.
uint32_t read_irq_status_raw() override
Read the raw IRQ status word from the radio.
void clear_device_errors_()
Clear device error flags.
void set_frequency_register(uint32_t freq_hz) override
Set RF frequency via the chip's own frequency register/opcode encoding, and update current_freq_.
void write_register_(uint16_t addr, const uint8_t *data, uint8_t len)
Write to a register (SX1262 uses opcodes for register access).
void configure_buffer_base() override
Hook run as part of reset_rx_state_, before re-entering RX.
void set_packet_params_(uint16_t preamble_len, uint8_t payload_len, uint8_t packet_type, uint8_t crc_type)
Configure packet parameters (preamble, payload length, CRC).
void dump_debug() override
Dump SX1262‑specific debug info.
void configure_radio_()
Full radio initialization (called from init()).
void start_tx() override
Issue the SetTx opcode with the fixed TX timeout — identical 3-byte payload on both chips,...
void apply_tx_modulation_workaround_()
Apply the Semtech TX modulation-quality erratum workaround (datasheet §15.1).
static void gpio_intr(RadioSX1262 *arg)
DIO1 ISR — sets dio_fired flag. Runs in interrupt context.
void read_opcode_(uint8_t opcode, uint8_t *data, uint8_t len)
Read response from an opcode.
bool init() override
Initialize the radio hardware. Returns true on success.
void set_rx_bandwidth_(SX1262RxBandwidth bandwidth)
Apply the RX bandwidth selector and rewrite the modulation parameters.
void fill_capture_info(bool blocking_wait, uint32_t irq_status, uint8_t rx_offset, uint8_t reported_len, const uint8_t *raw, uint8_t raw_len, const uint8_t *frame, uint8_t frame_len) override
Populate the RadioCaptureInfo from chip-specific telemetry (RSSI opcode, packet-status byte,...
void read_buffer_(uint8_t offset, uint8_t *data, uint8_t len)
Read from the SX1262 RX buffer.
void read_register_(uint16_t addr, uint8_t *data, uint8_t len)
Read from a register.
uint16_t get_device_errors_()
Read device error flags (and clear them).
static constexpr uint8_t SX1262_SET_DIO3_AS_TCXO_CTRL
static constexpr uint8_t SX1262_CALIBRATE
static constexpr uint16_t SX1262_REG_RX_GAIN
static constexpr uint8_t SOFT_PHY_RX_PROBE_PACKET_LEN
Fixed raw-RX probe length: chosen from captures of 23-25 byte protocol frames after UART packing and ...
static constexpr uint8_t SX1262_SET_BUFFER_BASE_ADDRESS
static constexpr uint8_t SX1262_CLEAR_DEVICE_ERRORS
static constexpr uint8_t SX1262_GFSK_CRC_OFF
SX1262RxBandwidth
Valid SX1262 RX bandwidth options (kHz register values).
static constexpr uint8_t SX1262_READ_REGISTER
static constexpr uint8_t SX1262_SET_PACKET_TYPE
static constexpr uint8_t SX1262_GET_DEVICE_ERRORS
static constexpr const char * TAG
static constexpr uint16_t SX1262_IRQ_CRC_ERR
static constexpr uint8_t SX1262_SET_MODULATION_PARAMS
static constexpr uint8_t SX1262_GET_RX_BUFFER_STATUS
static constexpr uint16_t SX1262_REG_TX_MODULATION
TX modulation-quality erratum register (SX1262 datasheet §15.1, "Modulation Quality with500 kHz LoRa ...
static constexpr uint8_t SX1262_CLEAR_IRQ_STATUS
static constexpr uint8_t SX1262_SET_RX
static constexpr uint8_t SX1262_SET_TX
static constexpr uint8_t SX1262_GFSK_PACKET_TYPE_KNOWN_LENGTH
static constexpr uint8_t SX1262_CALIBRATE_IMAGE
static constexpr uint8_t SX1262_SET_DIO2_AS_RF_SWITCH_CTRL
static constexpr uint8_t SX1262_WRITE_BUFFER
static constexpr uint8_t SX1262_READ_BUFFER
static constexpr uint16_t SX1262_IRQ_RX_DONE
static constexpr uint8_t SX1262_SET_PA_CONFIG
static constexpr uint8_t SX1262_SET_TX_PARAMS
static constexpr uint8_t SX1262_SET_DIO_IRQ_PARAMS
static constexpr uint32_t FREQ_CH2
Channel 2: 868.95 MHz (1W and 2W, TX channel).
static constexpr uint8_t SX1262_SET_PACKET_PARAMS
static constexpr uint8_t SX1262_TX_MODULATION_GFSK_BIT
Bit 2 of SX1262_REG_TX_MODULATION — the (G)FSK-correct value is 1.
static constexpr uint8_t SX1262_GET_STATUS
static constexpr uint16_t SX1262_REG_TX_CLAMP_CONFIG
static constexpr uint8_t SX1262_SET_RX_TX_FALLBACK_MODE
static constexpr uint8_t SX1262_TX_BUFFER_BASE
Data-buffer split programmed by configure_buffer_base(): TX packets build from 0x00,...
static constexpr uint8_t SX1262_RX_BUFFER_BASE
static constexpr uint8_t SX1262_GET_IRQ_STATUS
static constexpr uint8_t SX1262_SET_REGULATOR_MODE
static constexpr uint8_t SX1262_GET_PACKET_STATUS
static constexpr uint16_t SX1262_REG_SYNC_WORD
static constexpr uint8_t SX1262_FALLBACK_STDBY_XOSC
static constexpr uint8_t SX1262_SET_RF_FREQUENCY
static const uint8_t SX1262_SYNC_WORD_PARAM_24_BITS
static constexpr uint8_t SX1262_SET_STANDBY
static constexpr uint8_t SX1262_WRITE_REGISTER
static constexpr uint8_t SX1262_GET_RSSI_INST
SX1262 radio driver for IO-Homecontrol.