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
33void RadioSX1262::write_opcode_(uint8_t opcode, const uint8_t *params, uint8_t len) {
34 this->wait_busy_();
35 this->spi_->spi_enable();
36 this->spi_->spi_transfer(opcode);
37 for (uint8_t i = 0; i < len; i++)
38 this->spi_->spi_transfer(params[i]);
39 this->spi_->spi_disable();
40}
41
42void RadioSX1262::read_opcode_(uint8_t opcode, uint8_t *data, uint8_t len) {
43 this->wait_busy_();
44 this->spi_->spi_enable();
45 this->spi_->spi_transfer(opcode);
46 this->spi_->spi_transfer(0x00); // NOP — status byte
47 for (uint8_t i = 0; i < len; i++)
48 data[i] = this->spi_->spi_transfer(0x00);
49 this->spi_->spi_disable();
50}
51
53 uint8_t irq_raw[2] = {0};
54 this->read_opcode_(SX1262_GET_IRQ_STATUS, irq_raw, 2);
55 return (static_cast<uint32_t>(irq_raw[0]) << 8) | static_cast<uint32_t>(irq_raw[1]);
56}
57
58void RadioSX1262::write_register_(uint16_t addr, const uint8_t *data, uint8_t len) {
59 this->wait_busy_();
60 this->spi_->spi_enable();
61 this->spi_->spi_transfer(SX1262_WRITE_REGISTER);
62 this->spi_->spi_transfer((addr >> 8) & 0xFF); // Address MSB
63 this->spi_->spi_transfer(addr & 0xFF); // Address LSB
64 for (uint8_t i = 0; i < len; i++)
65 this->spi_->spi_transfer(data[i]);
66 this->spi_->spi_disable();
67}
68
69void RadioSX1262::read_register_(uint16_t addr, uint8_t *data, uint8_t len) {
70 this->wait_busy_();
71 this->spi_->spi_enable();
72 this->spi_->spi_transfer(SX1262_READ_REGISTER);
73 this->spi_->spi_transfer((addr >> 8) & 0xFF); // Address MSB
74 this->spi_->spi_transfer(addr & 0xFF); // Address LSB
75 this->spi_->spi_transfer(0x00); // NOP — status byte
76 for (uint8_t i = 0; i < len; i++)
77 data[i] = this->spi_->spi_transfer(0x00);
78 this->spi_->spi_disable();
79}
80
81void RadioSX1262::write_buffer_(uint8_t offset, const uint8_t *data, uint8_t len) {
82 this->wait_busy_();
83 this->spi_->spi_enable();
84 this->spi_->spi_transfer(SX1262_WRITE_BUFFER);
85 this->spi_->spi_transfer(offset);
86 for (uint8_t i = 0; i < len; i++)
87 this->spi_->spi_transfer(data[i]);
88 this->spi_->spi_disable();
89}
90
91void RadioSX1262::read_buffer_(uint8_t offset, uint8_t *data, uint8_t len) {
92 this->wait_busy_();
93 this->spi_->spi_enable();
94 this->spi_->spi_transfer(SX1262_READ_BUFFER);
95 this->spi_->spi_transfer(offset);
96 this->spi_->spi_transfer(0x00); // NOP — status byte
97 for (uint8_t i = 0; i < len; i++)
98 data[i] = this->spi_->spi_transfer(0x00);
99 this->spi_->spi_disable();
100}
101
102// === Packet params helper ===
103
104void RadioSX1262::set_packet_params_(uint16_t preamble_len, uint8_t payload_len, uint8_t packet_type,
105 uint8_t crc_type) {
106 // Field order and the byte->bit preamble conversion are shared with LR1121 — see
107 // SoftPhyDriverBase::build_gfsk_packet_params. Only the detector length (8 bits / 1 byte here),
108 // the sync-word selector, and the opcode/transport are SX1262-specific.
109 uint8_t params[GFSK_PACKET_PARAMS_LEN];
110 build_gfsk_packet_params({.preamble_bytes = preamble_len,
111 .preamble_detector = 0x04, // 8 bits (1 byte)
112 .sync_word_param = SX1262_SYNC_WORD_PARAM_24_BITS,
113 .packet_type = packet_type,
114 .payload_len = payload_len,
115 .crc_type = crc_type},
116 params);
117 this->write_opcode_(SX1262_SET_PACKET_PARAMS, params, sizeof(params));
118}
119
121 // The variable-size packet engine recovers a stable 15-byte boundary, but that boundary is
122 // too short for a full software UART decode and buffer reads past it are not trustworthy.
123 // Probe again with a fixed raw packet size that matches typical UART-packed 23-25 byte
124 // IO-homecontrol frames (SOFT_PHY_RX_PROBE_PACKET_LEN, shared with LR1121 — see its doc
125 // comment in radio_soft_phy_driver_base.h).
127}
128
130 // SX1262 GFSK modulation parameters for the IO-Homecontrol 868 MHz waveform.
131 //
132 // BitRate = 32 * Fxosc / BR_reg → BR_reg = 32 * 32MHz / 38400 = 26667 = 0x00682B
133 // Pulse shape: Gaussian BT=1.0 (0x0B) — reduces TX spectral occupation.
134 // Bandwidth: the register byte is runtime-tunable via rx_bandwidth_ — see
135 // TuningConfig::sx1262_rx_bandwidth (tuning_config.h) for the current default (58.6 kHz). The
136 // narrower filter is safe because the measured TX->RX turnaround (~390 us plus a 500 us settle)
137 // is well inside what it tolerates, and reception improves as the filter narrows on this
138 // waveform — matching the SX1276's long-validated 41.7 kHz default on the identical waveform.
139 // Fdev = fdev_hz * 2^25 / 32e6 → 19200 * 2^25 / 32e6 = 20133 = 0x004EA5
140 uint8_t mod_params[8] = {
141 0x00,
142 0x68,
143 0x2B, // Bitrate: 38400 bps
144 0x0B, // Pulse shape: Gaussian BT=1.0
145 static_cast<uint8_t>(this->rx_bandwidth_), // Bandwidth: runtime-tunable
146 0x00,
147 0x4E,
148 0xA5, // Fdev: 19200 Hz
149 };
150 this->write_opcode_(SX1262_SET_MODULATION_PARAMS, mod_params, sizeof(mod_params));
151}
152
153void RadioSX1262::clear_irq_status(uint32_t irq_mask) {
154 uint8_t clear_irq[2] = {
155 (uint8_t) ((irq_mask >> 8) & 0xFF),
156 (uint8_t) (irq_mask & 0xFF),
157 };
158 this->write_opcode_(SX1262_CLEAR_IRQ_STATUS, clear_irq, sizeof(clear_irq));
159}
160
162 uint8_t errors_raw[2] = {0};
163 this->read_opcode_(SX1262_GET_DEVICE_ERRORS, errors_raw, sizeof(errors_raw));
164 return (uint16_t) (((uint16_t) errors_raw[0] << 8) | errors_raw[1]);
165}
166
168 uint8_t clear_errors[2] = {0x00, 0x00};
169 this->write_opcode_(SX1262_CLEAR_DEVICE_ERRORS, clear_errors, sizeof(clear_errors));
170}
171
173 uint8_t buf_base[2] = {SX1262_TX_BUFFER_BASE, SX1262_RX_BUFFER_BASE};
174 this->write_opcode_(SX1262_SET_BUFFER_BASE_ADDRESS, buf_base, sizeof(buf_base));
175}
176
177void RadioSX1262::fill_capture_info(bool blocking_wait, uint32_t irq_status, uint8_t rx_offset, uint8_t reported_len,
178 const uint8_t *raw, uint8_t raw_len, const uint8_t *frame, uint8_t frame_len) {
179 uint8_t packet_status[3] = {0};
180 this->read_opcode_(SX1262_GET_PACKET_STATUS, packet_status, sizeof(packet_status));
181
182 this->populate_capture_base_(blocking_wait, this->current_freq_, -(int16_t) packet_status[1] / 2, raw, raw_len, frame,
183 frame_len);
184 this->last_capture_.rx_done = (irq_status & SX1262_IRQ_RX_DONE) != 0;
185 this->last_capture_.crc_error = (irq_status & SX1262_IRQ_CRC_ERR) != 0;
186 this->last_capture_.irq_status = static_cast<uint16_t>(irq_status);
187 this->last_capture_.packet_status = packet_status[0];
188 this->last_capture_.rx_offset = rx_offset;
189 this->last_capture_.reported_len = reported_len;
190}
191
193 uint8_t raw = 0;
194 this->read_opcode_(SX1262_GET_RSSI_INST, &raw, 1);
195 return raw;
196}
197
198void RadioSX1262::get_rx_buffer_status(uint8_t &reported_len, uint8_t &rx_offset) {
199 uint8_t rx_status[2] = {0};
200 this->read_opcode_(SX1262_GET_RX_BUFFER_STATUS, rx_status, sizeof(rx_status));
201 reported_len = rx_status[0];
202 rx_offset = rx_status[1];
203}
204
206 // Read-modify-write so the reserved bits of the register keep whatever the chip put there.
207 uint8_t tx_modulation = 0;
208 this->read_register_(SX1262_REG_TX_MODULATION, &tx_modulation, 1);
209 tx_modulation |= SX1262_TX_MODULATION_GFSK_BIT;
210 this->write_register_(SX1262_REG_TX_MODULATION, &tx_modulation, 1);
211}
212
214 // Start TX with 4s timeout (256000 ticks at 15.625us/tick = 0x03E800).
215 uint8_t tx_timeout[3] = {0x03, 0xE8, 0x00};
216 this->write_opcode_(SX1262_SET_TX, tx_timeout, sizeof(tx_timeout));
217}
218
219// === ISR ===
220
222
223// === Initialization ===
224
226 // --- Pin setup ---
227 this->rst_pin_->setup();
228 this->dio1_pin_->setup();
229 this->busy_pin_->setup();
230
231 // Front-end module pins (e.g., Heltec V4)
232 if (this->fem_en_pin_ != nullptr) {
233 this->fem_en_pin_->setup();
234 this->fem_en_pin_->digital_write(true);
235 }
236 if (this->vfem_pin_ != nullptr) {
237 this->vfem_pin_->setup();
238 this->vfem_pin_->digital_write(true);
239 }
240 if (this->fem_pa_pin_ != nullptr) {
241 this->fem_pa_pin_->setup();
242 this->fem_pa_pin_->digital_write(true);
243 }
244
245 // --- Hardware reset ---
246 this->reset_hardware_();
247 this->wait_busy_();
248 if (this->failed_)
249 return false;
250
251 this->configure_radio_();
252 if (this->failed_)
253 return false;
254
255 ESP_LOGI(TAG, "SX1262 initialized");
256 return true;
257}
258
260 this->wait_busy_();
261 this->spi_->spi_enable();
262 uint8_t const chip_status = this->spi_->spi_transfer(SX1262_GET_STATUS);
263 this->spi_->spi_transfer(0x00);
264 this->spi_->spi_disable();
265
266 uint8_t const chip_mode = (chip_status >> 4) & 0x07;
267 uint8_t const cmd_status = (chip_status >> 1) & 0x07;
268 const char *mode_str = "?";
269 switch (chip_mode) {
270 case 2:
271 mode_str = "STDBY_RC";
272 break;
273 case 3:
274 mode_str = "STDBY_XOSC";
275 break;
276 case 4:
277 mode_str = "FS";
278 break;
279 case 5:
280 mode_str = "RX";
281 break;
282 case 6:
283 mode_str = "TX";
284 break;
285 default:
286 break;
287 }
288
289 uint8_t sync[3];
290 this->read_register_(SX1262_REG_SYNC_WORD, sync, 3);
291
292 uint8_t irq_raw[2];
293 this->read_opcode_(SX1262_GET_IRQ_STATUS, irq_raw, 2);
294 uint16_t const irq = ((uint16_t) irq_raw[0] << 8) | irq_raw[1];
295 uint16_t const errors = this->get_device_errors_();
296
297 ESP_LOGCONFIG(TAG, " SX1262 Diagnostic:");
298 ESP_LOGCONFIG(TAG, " Chip status: 0x%02X (mode=%s, cmd=%u)", chip_status, mode_str, cmd_status);
299 ESP_LOGCONFIG(TAG, " BUSY=%d DIO1=%d", this->busy_pin_->digital_read(), this->dio1_pin_->digital_read());
300 ESP_LOGCONFIG(TAG, " Sync word: %02X %02X %02X (expect 57 FD 99)", sync[0], sync[1], sync[2]);
301 ESP_LOGCONFIG(TAG, " IRQ status: 0x%04X", irq);
302 ESP_LOGCONFIG(TAG, " Device errors: 0x%04X", errors);
303}
304
306 // 1. Standby on RC oscillator (safe starting point)
307 uint8_t const stdby_rc = 0x00;
308 this->write_opcode_(SX1262_SET_STANDBY, &stdby_rc, 1);
309
310 // 2. Configure TCXO via DIO3 — voltage + 5ms timeout (320 ticks at 15.625us/tick)
311 uint8_t tcxo_params[4] = {this->tcxo_voltage_, 0x00, 0x01, 0x40};
312 this->write_opcode_(SX1262_SET_DIO3_AS_TCXO_CTRL, tcxo_params, sizeof(tcxo_params));
313
314 // 3. Calibrate all blocks
315 uint8_t const cal = 0x7F;
316 this->write_opcode_(SX1262_CALIBRATE, &cal, 1);
317 delay(5); // Wait for calibration to complete
318
319 // 4. Standby on XOSC (TCXO now running)
320 uint8_t const stdby_xosc = 0x01;
321 this->write_opcode_(SX1262_SET_STANDBY, &stdby_xosc, 1);
322
323 // 5. Use DC-DC regulator for better efficiency
324 uint8_t const reg_mode = 0x01;
325 this->write_opcode_(SX1262_SET_REGULATOR_MODE, &reg_mode, 1);
326
327 // 6. DIO2 as RF switch control (for boards with integrated RF switch)
328 uint8_t const dio2_rf = 0x01;
330
331 // 6b. Keep the crystal path alive after RX/TX completion instead of relying on the chip default.
332 uint8_t const fallback_mode = SX1262_FALLBACK_STDBY_XOSC;
333 this->write_opcode_(SX1262_SET_RX_TX_FALLBACK_MODE, &fallback_mode, 1);
334
335 // 7. FSK packet type
336 uint8_t const pkt_type = 0x00;
337 this->write_opcode_(SX1262_SET_PACKET_TYPE, &pkt_type, 1);
338
339 // 8. Set frequency to channel 2 (868.95 MHz)
341
342 // 9. Calibrate image for 863-870 MHz band
343 uint8_t cal_img[2] = {0xD7, 0xDB};
344 this->write_opcode_(SX1262_CALIBRATE_IMAGE, cal_img, sizeof(cal_img));
345
346 // 9b. Use boosted RX gain for maximum sensitivity.
347 uint8_t const rx_gain = 0x96;
348 this->write_register_(SX1262_REG_RX_GAIN, &rx_gain, 1);
349
350 // 10. Apply GFSK modulation parameters (detailed values live in write_modulation_params_()).
352
353 // 11. Default RX packet params: variable-size GFSK with hardware CCITT CRC validation.
354 this->set_rx_packet_params();
355
356 // 12. Sync word: 0x57 0xFD 0x99 (24 bits). Not an independent value: it is exactly
357 // uart_encode_packet({0x55, 0xFF, 0x33}) — SX1276's own raw, confirmed-working sync bytes — read
358 // starting 6 bits into the first UART cell instead of at the start bit. SyncWordDerivation
359 // (radio_soft_phy_test.cpp) confirms this is the unique 10-bit-cell offset that reproduces it.
360 uint8_t sync_word[8] = {0x57, 0xFD, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00};
361 this->write_register_(SX1262_REG_SYNC_WORD, sync_word, sizeof(sync_word));
362
363 // 12b. CRC registers are configured for potential future hardware-CRC use, but RX uses
364 // CRC_OFF because the UART encoding makes hardware CRC checking impossible — the chip
365 // sees UART-packed bits, not raw protocol bytes.
366 uint8_t crc_init[2] = {0x1D, 0x0F};
367 this->write_register_(0x06BC, crc_init, 2);
368 uint8_t crc_poly[2] = {0x10, 0x21};
369 this->write_register_(0x06BE, crc_poly, 2);
370
371 // 13. Buffer base addresses: TX at 0x00, RX at 0x80
372 this->configure_buffer_base();
373
374 // 14. PA config: SX1262 high power PA (paDutyCycle=0x04, hpMax=0x07, deviceSel=0x00=SX1262, paLut=0x01)
375 uint8_t pa_config[4] = {0x04, 0x07, 0x00, 0x01};
376 this->write_opcode_(SX1262_SET_PA_CONFIG, pa_config, sizeof(pa_config));
377
378 // 14b. Apply Semtech's SX1262 clamp workaround for better tolerance of RF mismatch.
379 uint8_t tx_clamp = 0;
380 this->read_register_(SX1262_REG_TX_CLAMP_CONFIG, &tx_clamp, 1);
381 tx_clamp |= 0x1E;
382 this->write_register_(SX1262_REG_TX_CLAMP_CONFIG, &tx_clamp, 1);
383
384 // 15. TX params: power in dBm (SX1262 accepts -9 to +22 directly), ramp 200us (0x04)
385 int8_t const power = std::max((int8_t) -9, std::min((int8_t) 22, (int8_t) this->tx_power_));
386 uint8_t tx_params[2] = {(uint8_t) power, 0x04};
387 this->write_opcode_(SX1262_SET_TX_PARAMS, tx_params, sizeof(tx_params));
388
389 // 16. IRQ config: map TxDone + RxDone + SyncWordValid + CrcErr to DIO1. irqMask additionally
390 // enables PreambleDetected system-wide (SX126x irqMask gates GetIrqStatus itself, not just DIO
391 // routing — confirmed against Semtech's own driver docs) so is_preamble_detected() can finally
392 // return true on this chip; dio1Mask deliberately leaves it out so the ISR still only wakes on
393 // a terminal event, not on every preamble. See
394 // SX1262_IRQ_ACTIVITY_MASK's doc comment (radio_sx1262.h) for the other half of this change —
395 // poll_until_activity_() must not treat a bare preamble as terminal, or this unmask tears down
396 // RX mid-reception instead of fixing anything.
397 uint8_t irq_params[8] = {
398 0x00, 0x4F, // irqMask: TxDone(0x0001) | RxDone(0x0002) | PreambleDetected(0x0004) |
399 // SyncWordValid(0x0008) | CrcErr(0x0040)
400 0x00, 0x4B, // dio1Mask: unchanged — TxDone|RxDone|SyncWordValid|CrcErr only
401 0x00, 0x00, // dio2Mask: none
402 0x00, 0x00, // dio3Mask: none
403 };
404 this->write_opcode_(SX1262_SET_DIO_IRQ_PARAMS, irq_params, sizeof(irq_params));
405
406 // 17. Attach DIO1 interrupt
407 this->dio1_pin_->attach_interrupt(&RadioSX1262::gpio_intr, this, gpio::INTERRUPT_RISING_EDGE);
408
409 // 18. Clear any pending IRQs, and report the device-error word before clearing it. A chip that
410 // came up with XOSC_START_ERR (wrong tcxo_voltage for the board), PLL_LOCK_ERR or IMG_CALIB_ERR
411 // still initializes and still transmits — it just does so off-frequency or off-calibration,
412 // which on air looks like flaky exchanges at any range rather than an outright failure. Clearing
413 // it unseen threw away the one cheap piece of evidence for that.
414 this->clear_irq_status(0xFFFF);
415 uint16_t const init_errors = this->get_device_errors_();
416 if (init_errors != 0)
417 ESP_LOGW(TAG, "SX1262 device errors after init: 0x%04X — check tcxo_voltage for this board", init_errors);
418 this->clear_device_errors_();
419
420 // 19. Enter continuous receive
421 uint8_t rx_continuous[3] = {0xFF, 0xFF, 0xFF}; // 0xFFFFFF = continuous
422 this->write_opcode_(SX1262_SET_RX, rx_continuous, sizeof(rx_continuous));
423}
424
425// === Mode control ===
426
428 uint8_t const stdby = 0x01; // STDBY_XOSC
429 this->write_opcode_(SX1262_SET_STANDBY, &stdby, 1);
430}
431
433 uint8_t rx_continuous[3] = {0xFF, 0xFF, 0xFF};
434 this->write_opcode_(SX1262_SET_RX, rx_continuous, sizeof(rx_continuous));
435}
436
437// === Frequency control ===
438
440 auto freq_reg = (uint32_t) ((double) freq_hz * (1 << 25) / 32e6);
441 uint8_t params[4] = {
442 (uint8_t) (freq_reg >> 24),
443 (uint8_t) (freq_reg >> 16),
444 (uint8_t) (freq_reg >> 8),
445 (uint8_t) freq_reg,
446 };
447 this->write_opcode_(SX1262_SET_RF_FREQUENCY, params, sizeof(params));
448 this->current_freq_ = freq_hz;
449}
450
452 this->rx_bandwidth_ = bandwidth;
454}
455
456} // namespace home_io_control
457} // namespace esphome
458
459// 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 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).
void wait_busy_()
Wait until busy_pin_ reads low, feeding the watchdog while polling.
static void build_gfsk_packet_params(const SoftPhyPacketParams &p, uint8_t out[GFSK_PACKET_PARAMS_LEN])
Fill the nine-byte GFSK SetPacketParams payload shared by both chips.
bool failed_
Set on a BUSY timeout or a chip-identity check failing; see is_failed.
InternalGPIOPin * busy_pin_
BUSY line, read directly by both concrete drivers' own dump_debug() in addition to wait_busy_,...
static constexpr uint8_t GFSK_PACKET_PARAMS_LEN
Byte count of the GFSK SetPacketParams payload — identical on both software-PHY chips.
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.