ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
sx127x.cpp
Go to the documentation of this file.
1#include "sx127x.h"
3#include "esphome/core/hal.h"
4#include "esphome/core/log.h"
5
6namespace esphome::sx127x {
7
8ESPHOME_LOG_TAG(TAG, "sx127x");
9static const uint32_t FXOSC = 32000000u;
10static const uint16_t RAMP[16] = {3400, 2000, 1000, 500, 250, 125, 100, 62, 50, 40, 31, 25, 20, 15, 12, 10};
11static const uint32_t BW_HZ[22] = {2604, 3125, 3906, 5208, 6250, 7812, 10416, 12500, 15625, 20833, 25000,
12 31250, 41666, 50000, 62500, 83333, 100000, 125000, 166666, 200000, 250000, 500000};
13static const uint8_t BW_LORA[22] = {BW_7_8, BW_7_8, BW_7_8, BW_7_8, BW_7_8, BW_7_8, BW_10_4, BW_15_6,
16static const uint8_t BW_FSK_OOK[22] = {RX_BW_2_6, RX_BW_3_1, RX_BW_3_9, RX_BW_5_2, RX_BW_6_3, RX_BW_7_8,
20static const int32_t RSSI_OFFSET_HF = 157;
21static const int32_t RSSI_OFFSET_LF = 164;
22
23uint8_t SX127x::read_register_(uint8_t reg) {
24 this->enable();
25 this->write_byte(reg & 0x7F);
26 uint8_t value = this->read_byte();
27 this->disable();
28 return value;
29}
30
31void SX127x::write_register_(uint8_t reg, uint8_t value) {
32 this->enable();
33 this->write_byte(reg | 0x80);
34 this->write_byte(value);
35 this->disable();
36}
37
38void SX127x::read_fifo_(std::vector<uint8_t> &packet) {
39 this->enable();
40 this->write_byte(REG_FIFO & 0x7F);
41 for (auto &byte : packet) {
42 byte = this->transfer_byte(0x00);
43 }
44 this->disable();
45}
46
47void SX127x::write_fifo_(const uint8_t *data, size_t len) {
48 this->enable();
49 this->write_byte(REG_FIFO | 0x80);
50 for (size_t i = 0; i < len; i++) {
51 this->transfer_byte(data[i]);
52 }
53 this->disable();
54}
55
57
59 // setup reset
60 this->rst_pin_->setup();
61
62 // setup dio0
63 if (this->dio0_pin_) {
64 this->dio0_pin_->setup();
66 }
67
68 // start spi
69 this->spi_setup();
70
71 // configure rf
72 this->configure();
73}
74
76 // toggle chip reset
77 this->rst_pin_->digital_write(false);
79 this->rst_pin_->digital_write(true);
80 delayMicroseconds(10000);
81
82 // check silicon version to make sure hw is ok
83 if (this->read_register_(REG_VERSION) != 0x12) {
84 this->mark_failed(LOG_STR("Unsupported chip version"));
85 return;
86 }
87
88 // enter sleep mode
90
91 // set freq
92 uint64_t frf = ((uint64_t) this->frequency_ << 19) / FXOSC;
93 this->write_register_(REG_FRF_MSB, (uint8_t) ((frf >> 16) & 0xFF));
94 this->write_register_(REG_FRF_MID, (uint8_t) ((frf >> 8) & 0xFF));
95 this->write_register_(REG_FRF_LSB, (uint8_t) ((frf >> 0) & 0xFF));
96
97 // enter standby mode
99
100 // run image cal
101 this->run_image_cal();
102
103 // go back to sleep
104 this->set_mode_sleep();
105
106 // config pa
107 if (this->pa_pin_ == PA_PIN_BOOST) {
108 this->pa_power_ = std::max(this->pa_power_, (uint8_t) 2);
109 this->pa_power_ = std::min(this->pa_power_, (uint8_t) 17);
110 this->write_register_(REG_PA_CONFIG, (this->pa_power_ - 2) | this->pa_pin_ | PA_MAX_POWER);
111 } else {
112 this->pa_power_ = std::min(this->pa_power_, (uint8_t) 14);
113 this->write_register_(REG_PA_CONFIG, (this->pa_power_ - 0) | this->pa_pin_ | PA_MAX_POWER);
114 }
115 if (this->modulation_ != MOD_LORA) {
116 this->write_register_(REG_PA_RAMP, this->pa_ramp_ | this->shaping_);
117 } else {
119 }
120
121 // configure modem
122 if (this->modulation_ != MOD_LORA) {
123 this->configure_fsk_ook_();
124 } else {
125 this->configure_lora_();
126 }
127
128 // switch to rx or sleep
129 if (this->rx_start_) {
130 this->set_mode_rx();
131 } else {
132 this->set_mode_sleep();
133 }
134}
135
137 // set the channel bw
138 this->write_register_(REG_RX_BW, BW_FSK_OOK[this->bandwidth_]);
139
140 // set fdev
141 uint32_t fdev = std::min((this->deviation_ * 4096) / 250000, (uint32_t) 0x3FFF);
142 this->write_register_(REG_FDEV_MSB, (uint8_t) ((fdev >> 8) & 0xFF));
143 this->write_register_(REG_FDEV_LSB, (uint8_t) ((fdev >> 0) & 0xFF));
144
145 // set bitrate
146 uint64_t bitrate = (FXOSC + this->bitrate_ / 2) / this->bitrate_; // round up
147 this->write_register_(REG_BITRATE_MSB, (uint8_t) ((bitrate >> 8) & 0xFF));
148 this->write_register_(REG_BITRATE_LSB, (uint8_t) ((bitrate >> 0) & 0xFF));
149
150 // configure rx and afc
151 uint8_t trigger = (this->preamble_detect_ > 0) ? TRIGGER_PREAMBLE : TRIGGER_RSSI;
153 if (this->modulation_ == MOD_FSK) {
155 } else {
156 this->write_register_(REG_RX_CONFIG, AGC_AUTO_ON | trigger);
157 }
158
159 // configure packet mode
160 if (this->packet_mode_) {
161 uint8_t crc_mode = (this->crc_enable_) ? CRC_ON : CRC_OFF;
163 if (this->payload_length_ > 0) {
166 } else {
169 }
171 } else {
173 }
175
176 // config bit synchronizer
177 uint8_t polarity = (this->preamble_polarity_ == 0xAA) ? PREAMBLE_AA : PREAMBLE_55;
178 if (!this->sync_value_.empty()) {
179 uint8_t size = this->sync_value_.size() - 1;
181 for (uint32_t i = 0; i < this->sync_value_.size(); i++) {
182 this->write_register_(REG_SYNC_VALUE1 + i, this->sync_value_[i]);
183 }
184 } else {
186 }
187
188 // config preamble detector
189 if (this->preamble_detect_ > 0) {
190 uint8_t size = (this->preamble_detect_ - 1) << PREAMBLE_DETECTOR_SIZE_SHIFT;
191 uint8_t tol = this->preamble_errors_ << PREAMBLE_DETECTOR_TOL_SHIFT;
193 } else {
195 }
198
199 // config sync generation and setup ook threshold
200 uint8_t bitsync = this->bitsync_ ? BIT_SYNC_ON : BIT_SYNC_OFF;
203
204 // set rx floor
205 this->write_register_(REG_OOK_FIX, 256 + int(this->rx_floor_ * 2.0f));
206 this->write_register_(REG_RSSI_THRESH, std::abs(int(this->rx_floor_ * 2.0f)));
207}
208
210 // config modem
211 uint8_t header_mode = this->payload_length_ > 0 ? IMPLICIT_HEADER : EXPLICIT_HEADER;
212 uint8_t crc_mode = (this->crc_enable_) ? RX_PAYLOAD_CRC_ON : RX_PAYLOAD_CRC_OFF;
213 uint8_t spreading_factor = this->spreading_factor_ << SPREADING_FACTOR_SHIFT;
214 this->write_register_(REG_MODEM_CONFIG1, BW_LORA[this->bandwidth_] | this->coding_rate_ | header_mode);
215 this->write_register_(REG_MODEM_CONFIG2, spreading_factor | crc_mode);
216
217 // config fifo and payload length
220 this->write_register_(REG_PAYLOAD_LENGTH, std::max(this->payload_length_, (uint32_t) 1));
221
222 // config preamble
223 if (this->preamble_size_ >= 6) {
226 }
227
228 // optimize detection
229 float duration = 1000.0f * (1UL << this->spreading_factor_) / BW_HZ[this->bandwidth_];
230 if (duration > 16) {
232 } else {
234 }
235 if (this->spreading_factor_ == 6) {
238 } else {
241 }
242
243 // config sync word
244 if (!this->sync_value_.empty()) {
246 }
247}
248
250 if (this->payload_length_ > 0) {
251 return this->payload_length_;
252 }
253 if (this->modulation_ == MOD_LORA) {
254 return SX127X_MAX_PACKET_SIZE;
255 }
256 return 64;
257}
258
259SX127xError SX127x::transmit_packet(const uint8_t *data, size_t len) {
260 // Callers such as a template number's on_value can run before setup() or after a failed setup
261 if (!this->is_ready()) {
262 ESP_LOGW(TAG, "Radio not ready, packet dropped");
264 }
265 if (this->payload_length_ > 0 && this->payload_length_ != len) {
266 ESP_LOGE(TAG, "Packet size does not match config");
268 }
269 if (len == 0 || len > this->get_max_packet_size()) {
270 ESP_LOGE(TAG, "Packet size out of range");
272 }
273
274 if (this->dio0_pin_ == nullptr) {
275 ESP_LOGE(TAG, "DIO0 pin not configured, cannot wait for transmit completion");
277 }
278
280 if (this->modulation_ == MOD_LORA) {
281 this->set_mode_standby();
282 if (this->payload_length_ == 0) {
284 }
285 this->write_register_(REG_IRQ_FLAGS, 0xFF);
287 this->write_fifo_(data, len);
288 this->set_mode_tx();
289 } else {
290 this->set_mode_standby();
291 if (this->payload_length_ == 0) {
292 this->write_register_(REG_FIFO, len);
293 }
294 this->write_fifo_(data, len);
295 this->set_mode_tx();
296 }
297
298 // wait until transmit completes, typically the delay will be less than 100 ms
299 uint32_t start = millis();
300 while (!this->dio0_pin_->digital_read()) {
301 App.feed_wdt();
302 if (millis() - start > 4000) {
303 ESP_LOGE(TAG, "Transmit packet failure");
305 break;
306 }
307 }
308 if (this->rx_start_) {
309 this->set_mode_rx();
310 } else {
311 this->set_mode_sleep();
312 }
313 return ret;
314}
315
316void SX127x::call_listeners_(const std::vector<uint8_t> &packet, float rssi, float snr) {
317 for (auto &listener : this->listeners_) {
318 listener->on_packet(packet, rssi, snr);
319 }
320 this->packet_trigger_.trigger(packet, rssi, snr);
321}
322
324 this->disable_loop();
325 if (this->dio0_pin_ == nullptr || !this->dio0_pin_->digital_read()) {
326 return;
327 }
328
329 if (this->modulation_ == MOD_LORA) {
330 uint8_t status = this->read_register_(REG_IRQ_FLAGS);
331 this->write_register_(REG_IRQ_FLAGS, 0xFF);
332 if ((status & PAYLOAD_CRC_ERROR) == 0) {
333 uint8_t bytes = this->read_register_(REG_NB_RX_BYTES);
334 uint8_t addr = this->read_register_(REG_FIFO_RX_CURR_ADDR);
335 uint8_t rssi = this->read_register_(REG_PKT_RSSI_VALUE);
336 int8_t snr = (int8_t) this->read_register_(REG_PKT_SNR_VALUE);
337 this->packet_.resize(bytes);
339 this->read_fifo_(this->packet_);
340 if (this->frequency_ > 700000000) {
341 this->call_listeners_(this->packet_, (float) rssi - RSSI_OFFSET_HF, (float) snr / 4);
342 } else {
343 this->call_listeners_(this->packet_, (float) rssi - RSSI_OFFSET_LF, (float) snr / 4);
344 }
345 }
346 } else if (this->packet_mode_) {
347 uint8_t payload_length = this->payload_length_;
348 if (payload_length == 0) {
349 payload_length = this->read_register_(REG_FIFO);
350 }
351 this->packet_.resize(payload_length);
352 this->read_fifo_(this->packet_);
353 this->call_listeners_(this->packet_, 0.0f, 0.0f);
354 }
355}
356
358 if (this->modulation_ == MOD_LORA) {
361 }
362 if (this->auto_cal_) {
364 } else {
366 }
367 uint32_t start = millis();
369 if (millis() - start > 20) {
370 this->mark_failed(LOG_STR("Image cal failure"));
371 break;
372 }
373 }
374 if (this->modulation_ == MOD_LORA) {
375 this->set_mode_(this->modulation_, MODE_SLEEP);
376 this->set_mode_(this->modulation_, MODE_STDBY);
377 }
378}
379
380void SX127x::set_mode_(uint8_t modulation, uint8_t mode) {
381 uint32_t start = millis();
382 this->write_register_(REG_OP_MODE, modulation | mode);
383 while (true) {
384 uint8_t curr = this->read_register_(REG_OP_MODE) & MODE_MASK;
385 if ((curr == mode) || (mode == MODE_RX && curr == MODE_RX_FS)) {
386 if (mode == MODE_SLEEP) {
387 this->write_register_(REG_OP_MODE, modulation | mode);
388 }
389 break;
390 }
391 if (millis() - start > 20) {
392 this->mark_failed(LOG_STR("Set mode failure"));
393 return;
394 }
395 }
396}
397
399 this->set_mode_(this->modulation_, MODE_RX);
400 if (this->modulation_ == MOD_LORA) {
403 }
404}
405
407 this->set_mode_(this->modulation_, MODE_TX);
408 if (this->modulation_ == MOD_LORA) {
411 }
412}
413
415
417
419 ESP_LOGCONFIG(TAG, "SX127x:");
420 LOG_PIN(" CS Pin: ", this->cs_);
421 LOG_PIN(" RST Pin: ", this->rst_pin_);
422 LOG_PIN(" DIO0 Pin: ", this->dio0_pin_);
423 const char *pa_pin = "RFO";
424 if (this->pa_pin_ == PA_PIN_BOOST) {
425 pa_pin = "BOOST";
426 }
427 ESP_LOGCONFIG(TAG,
428 " Auto Cal: %s\n"
429 " Frequency: %" PRIu32 " Hz\n"
430 " Bandwidth: %" PRIu32 " Hz\n"
431 " PA Pin: %s\n"
432 " PA Power: %" PRIu8 " dBm\n"
433 " PA Ramp: %" PRIu16 " us",
434 TRUEFALSE(this->auto_cal_), this->frequency_, BW_HZ[this->bandwidth_], pa_pin, this->pa_power_,
435 RAMP[this->pa_ramp_]);
436 if (this->modulation_ == MOD_FSK) {
437 ESP_LOGCONFIG(TAG, " Deviation: %" PRIu32 " Hz", this->deviation_);
438 }
439 if (this->modulation_ == MOD_LORA) {
440 const char *cr = "4/8";
441 if (this->coding_rate_ == CODING_RATE_4_5) {
442 cr = "4/5";
443 } else if (this->coding_rate_ == CODING_RATE_4_6) {
444 cr = "4/6";
445 } else if (this->coding_rate_ == CODING_RATE_4_7) {
446 cr = "4/7";
447 }
448 ESP_LOGCONFIG(TAG,
449 " Modulation: LORA\n"
450 " Preamble Size: %" PRIu16 "\n"
451 " Spreading Factor: %" PRIu8 "\n"
452 " Coding Rate: %s\n"
453 " CRC Enable: %s",
454 this->preamble_size_, this->spreading_factor_, cr, TRUEFALSE(this->crc_enable_));
455 if (this->payload_length_ > 0) {
456 ESP_LOGCONFIG(TAG, " Payload Length: %" PRIu32, this->payload_length_);
457 }
458 if (!this->sync_value_.empty()) {
459 ESP_LOGCONFIG(TAG, " Sync Value: 0x%02x", this->sync_value_[0]);
460 }
461 } else {
462 const char *shaping = "NONE";
463 if (this->modulation_ == MOD_FSK) {
464 if (this->shaping_ == GAUSSIAN_BT_0_3) {
465 shaping = "GAUSSIAN_BT_0_3";
466 } else if (this->shaping_ == GAUSSIAN_BT_0_5) {
467 shaping = "GAUSSIAN_BT_0_5";
468 } else if (this->shaping_ == GAUSSIAN_BT_1_0) {
469 shaping = "GAUSSIAN_BT_1_0";
470 }
471 } else {
472 if (this->shaping_ == CUTOFF_BR_X_2) {
473 shaping = "CUTOFF_BR_X_2";
474 } else if (this->shaping_ == CUTOFF_BR_X_1) {
475 shaping = "CUTOFF_BR_X_1";
476 }
477 }
478 ESP_LOGCONFIG(TAG,
479 " Shaping: %s\n"
480 " Modulation: %s\n"
481 " Bitrate: %" PRIu32 "b/s\n"
482 " Bitsync: %s\n"
483 " Rx Start: %s\n"
484 " Rx Floor: %.1f dBm\n"
485 " Packet Mode: %s",
486 shaping, this->modulation_ == MOD_FSK ? LOG_STR_LITERAL("FSK") : LOG_STR_LITERAL("OOK"),
487 this->bitrate_, TRUEFALSE(this->bitsync_), TRUEFALSE(this->rx_start_), this->rx_floor_,
488 TRUEFALSE(this->packet_mode_));
489 if (this->packet_mode_) {
490 ESP_LOGCONFIG(TAG, " CRC Enable: %s", TRUEFALSE(this->crc_enable_));
491 }
492 if (this->payload_length_ > 0) {
493 ESP_LOGCONFIG(TAG, " Payload Length: %" PRIu32, this->payload_length_);
494 }
495 if (!this->sync_value_.empty()) {
496 char hex_buf[17]; // 8 bytes max = 16 hex chars + null
497 ESP_LOGCONFIG(TAG, " Sync Value: 0x%s",
498 format_hex_to(hex_buf, this->sync_value_.data(), this->sync_value_.size()));
499 }
500 if (this->preamble_size_ > 0 || this->preamble_detect_ > 0) {
501 ESP_LOGCONFIG(TAG,
502 " Preamble Polarity: 0x%X\n"
503 " Preamble Size: %" PRIu16 "\n"
504 " Preamble Detect: %" PRIu8 "\n"
505 " Preamble Errors: %" PRIu8,
507 }
508 }
509}
510
511} // namespace esphome::sx127x
BedjetMode mode
BedJet operating mode.
uint8_t status
Definition bl0942.h:8
void feed_wdt()
Feed the task watchdog.
void mark_failed()
Mark this component as failed.
void enable_loop_soon_any_context()
Thread and ISR-safe version of enable_loop() that can be called from any context.
bool is_ready() const
void disable_loop()
Disable this component's loop.
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
Definition automation.h:556
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:29
void digital_write(bool value) override
Definition gpio.cpp:208
InternalGPIOPin * rst_pin_
Definition sx127x.h:108
uint8_t preamble_polarity_
Definition sx127x.h:122
InternalGPIOPin * dio0_pin_
Definition sx127x.h:107
Trigger< std::vector< uint8_t >, float, float > packet_trigger_
Definition sx127x.h:103
std::vector< SX127xListener * > listeners_
Definition sx127x.h:104
void write_fifo_(const uint8_t *data, size_t len)
Definition sx127x.cpp:47
std::vector< uint8_t > sync_value_
Definition sx127x.h:106
uint32_t payload_length_
Definition sx127x.h:113
void dump_config() override
Definition sx127x.cpp:418
uint8_t read_register_(uint8_t reg)
Definition sx127x.cpp:23
static void gpio_intr(SX127x *arg)
Definition sx127x.cpp:56
void loop() override
Definition sx127x.cpp:323
void set_mode_(uint8_t modulation, uint8_t mode)
Definition sx127x.cpp:380
void read_fifo_(std::vector< uint8_t > &packet)
Definition sx127x.cpp:38
uint16_t preamble_size_
Definition sx127x.h:114
std::vector< uint8_t > packet_
Definition sx127x.h:105
size_t get_max_packet_size()
Definition sx127x.cpp:249
void write_register_(uint8_t reg, uint8_t value)
Definition sx127x.cpp:31
void call_listeners_(const std::vector< uint8_t > &packet, float rssi, float snr)
Definition sx127x.cpp:316
void setup() override
Definition sx127x.cpp:58
uint8_t spreading_factor_
Definition sx127x.h:124
SX127xError transmit_packet(const uint8_t *data, size_t len)
Definition sx127x.cpp:259
int ret
uint8_t duration
Definition msa3xx.h:0
@ INTERRUPT_RISING_EDGE
Definition gpio_pin.h:52
@ PREAMBLE_DETECTOR_TOL_SHIFT
Definition sx127x_reg.h:208
@ PREAMBLE_DETECTOR_SIZE_SHIFT
Definition sx127x_reg.h:207
ESPHOME_LOG_TAG(TAG, "sx127x_transport")
const void size_t len
Definition hal.h:68
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition hal.cpp:48
uint16_t size
Definition helpers.cpp:34
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
char * format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length)
Format byte array as lowercase hex to buffer (base implementation).
Definition helpers.cpp:428
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t