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