ESPHome 2026.5.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 {
6namespace sx127x {
7
8static const char *const 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 std::vector<uint8_t> &packet) {
48 this->enable();
49 this->write_byte(REG_FIFO | 0x80);
50 for (const auto &byte : packet) {
51 this->transfer_byte(byte);
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();
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.0));
206 this->write_register_(REG_RSSI_THRESH, std::abs(int(this->rx_floor_ * 2.0)));
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 * std::pow(2, 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 256;
255 } else {
256 return 64;
257 }
258}
259
260SX127xError SX127x::transmit_packet(const std::vector<uint8_t> &packet) {
261 if (this->payload_length_ > 0 && this->payload_length_ != packet.size()) {
262 ESP_LOGE(TAG, "Packet size does not match config");
264 }
265 if (packet.empty() || packet.size() > this->get_max_packet_size()) {
266 ESP_LOGE(TAG, "Packet size out of range");
268 }
269
270 if (this->dio0_pin_ == nullptr) {
271 ESP_LOGE(TAG, "DIO0 pin not configured, cannot wait for transmit completion");
273 }
274
276 if (this->modulation_ == MOD_LORA) {
277 this->set_mode_standby();
278 if (this->payload_length_ == 0) {
279 this->write_register_(REG_PAYLOAD_LENGTH, packet.size());
280 }
281 this->write_register_(REG_IRQ_FLAGS, 0xFF);
283 this->write_fifo_(packet);
284 this->set_mode_tx();
285 } else {
286 this->set_mode_standby();
287 if (this->payload_length_ == 0) {
288 this->write_register_(REG_FIFO, packet.size());
289 }
290 this->write_fifo_(packet);
291 this->set_mode_tx();
292 }
293
294 // wait until transmit completes, typically the delay will be less than 100 ms
295 uint32_t start = millis();
296 while (!this->dio0_pin_->digital_read()) {
297 if (millis() - start > 4000) {
298 ESP_LOGE(TAG, "Transmit packet failure");
300 break;
301 }
302 }
303 if (this->rx_start_) {
304 this->set_mode_rx();
305 } else {
306 this->set_mode_sleep();
307 }
308 return ret;
309}
310
311void SX127x::call_listeners_(const std::vector<uint8_t> &packet, float rssi, float snr) {
312 for (auto &listener : this->listeners_) {
313 listener->on_packet(packet, rssi, snr);
314 }
315 this->packet_trigger_.trigger(packet, rssi, snr);
316}
317
319 this->disable_loop();
320 if (this->dio0_pin_ == nullptr || !this->dio0_pin_->digital_read()) {
321 return;
322 }
323
324 if (this->modulation_ == MOD_LORA) {
325 uint8_t status = this->read_register_(REG_IRQ_FLAGS);
326 this->write_register_(REG_IRQ_FLAGS, 0xFF);
327 if ((status & PAYLOAD_CRC_ERROR) == 0) {
328 uint8_t bytes = this->read_register_(REG_NB_RX_BYTES);
329 uint8_t addr = this->read_register_(REG_FIFO_RX_CURR_ADDR);
330 uint8_t rssi = this->read_register_(REG_PKT_RSSI_VALUE);
331 int8_t snr = (int8_t) this->read_register_(REG_PKT_SNR_VALUE);
332 this->packet_.resize(bytes);
334 this->read_fifo_(this->packet_);
335 if (this->frequency_ > 700000000) {
336 this->call_listeners_(this->packet_, (float) rssi - RSSI_OFFSET_HF, (float) snr / 4);
337 } else {
338 this->call_listeners_(this->packet_, (float) rssi - RSSI_OFFSET_LF, (float) snr / 4);
339 }
340 }
341 } else if (this->packet_mode_) {
342 uint8_t payload_length = this->payload_length_;
343 if (payload_length == 0) {
344 payload_length = this->read_register_(REG_FIFO);
345 }
346 this->packet_.resize(payload_length);
347 this->read_fifo_(this->packet_);
348 this->call_listeners_(this->packet_, 0.0f, 0.0f);
349 }
350}
351
353 if (this->modulation_ == MOD_LORA) {
356 }
357 if (this->auto_cal_) {
359 } else {
361 }
362 uint32_t start = millis();
364 if (millis() - start > 20) {
365 ESP_LOGE(TAG, "Image cal failure");
366 this->mark_failed();
367 break;
368 }
369 }
370 if (this->modulation_ == MOD_LORA) {
371 this->set_mode_(this->modulation_, MODE_SLEEP);
372 this->set_mode_(this->modulation_, MODE_STDBY);
373 }
374}
375
376void SX127x::set_mode_(uint8_t modulation, uint8_t mode) {
377 uint32_t start = millis();
378 this->write_register_(REG_OP_MODE, modulation | mode);
379 while (true) {
380 uint8_t curr = this->read_register_(REG_OP_MODE) & MODE_MASK;
381 if ((curr == mode) || (mode == MODE_RX && curr == MODE_RX_FS)) {
382 if (mode == MODE_SLEEP) {
383 this->write_register_(REG_OP_MODE, modulation | mode);
384 }
385 break;
386 }
387 if (millis() - start > 20) {
388 ESP_LOGE(TAG, "Set mode failure");
389 this->mark_failed();
390 return;
391 }
392 }
393}
394
396 this->set_mode_(this->modulation_, MODE_RX);
397 if (this->modulation_ == MOD_LORA) {
400 }
401}
402
404 this->set_mode_(this->modulation_, MODE_TX);
405 if (this->modulation_ == MOD_LORA) {
408 }
409}
410
412
414
416 ESP_LOGCONFIG(TAG, "SX127x:");
417 LOG_PIN(" CS Pin: ", this->cs_);
418 LOG_PIN(" RST Pin: ", this->rst_pin_);
419 LOG_PIN(" DIO0 Pin: ", this->dio0_pin_);
420 const char *pa_pin = "RFO";
421 if (this->pa_pin_ == PA_PIN_BOOST) {
422 pa_pin = "BOOST";
423 }
424 ESP_LOGCONFIG(TAG,
425 " Auto Cal: %s\n"
426 " Frequency: %" PRIu32 " Hz\n"
427 " Bandwidth: %" PRIu32 " Hz\n"
428 " PA Pin: %s\n"
429 " PA Power: %" PRIu8 " dBm\n"
430 " PA Ramp: %" PRIu16 " us",
431 TRUEFALSE(this->auto_cal_), this->frequency_, BW_HZ[this->bandwidth_], pa_pin, this->pa_power_,
432 RAMP[this->pa_ramp_]);
433 if (this->modulation_ == MOD_FSK) {
434 ESP_LOGCONFIG(TAG, " Deviation: %" PRIu32 " Hz", this->deviation_);
435 }
436 if (this->modulation_ == MOD_LORA) {
437 const char *cr = "4/8";
438 if (this->coding_rate_ == CODING_RATE_4_5) {
439 cr = "4/5";
440 } else if (this->coding_rate_ == CODING_RATE_4_6) {
441 cr = "4/6";
442 } else if (this->coding_rate_ == CODING_RATE_4_7) {
443 cr = "4/7";
444 }
445 ESP_LOGCONFIG(TAG,
446 " Modulation: LORA\n"
447 " Preamble Size: %" PRIu16 "\n"
448 " Spreading Factor: %" PRIu8 "\n"
449 " Coding Rate: %s\n"
450 " CRC Enable: %s",
451 this->preamble_size_, this->spreading_factor_, cr, TRUEFALSE(this->crc_enable_));
452 if (this->payload_length_ > 0) {
453 ESP_LOGCONFIG(TAG, " Payload Length: %" PRIu32, this->payload_length_);
454 }
455 if (!this->sync_value_.empty()) {
456 ESP_LOGCONFIG(TAG, " Sync Value: 0x%02x", this->sync_value_[0]);
457 }
458 } else {
459 const char *shaping = "NONE";
460 if (this->modulation_ == MOD_FSK) {
461 if (this->shaping_ == GAUSSIAN_BT_0_3) {
462 shaping = "GAUSSIAN_BT_0_3";
463 } else if (this->shaping_ == GAUSSIAN_BT_0_5) {
464 shaping = "GAUSSIAN_BT_0_5";
465 } else if (this->shaping_ == GAUSSIAN_BT_1_0) {
466 shaping = "GAUSSIAN_BT_1_0";
467 }
468 } else {
469 if (this->shaping_ == CUTOFF_BR_X_2) {
470 shaping = "CUTOFF_BR_X_2";
471 } else if (this->shaping_ == CUTOFF_BR_X_1) {
472 shaping = "CUTOFF_BR_X_1";
473 }
474 }
475 ESP_LOGCONFIG(TAG,
476 " Shaping: %s\n"
477 " Modulation: %s\n"
478 " Bitrate: %" PRIu32 "b/s\n"
479 " Bitsync: %s\n"
480 " Rx Start: %s\n"
481 " Rx Floor: %.1f dBm\n"
482 " Packet Mode: %s",
483 shaping, this->modulation_ == MOD_FSK ? "FSK" : "OOK", this->bitrate_, TRUEFALSE(this->bitsync_),
484 TRUEFALSE(this->rx_start_), this->rx_floor_, TRUEFALSE(this->packet_mode_));
485 if (this->packet_mode_) {
486 ESP_LOGCONFIG(TAG, " CRC Enable: %s", TRUEFALSE(this->crc_enable_));
487 }
488 if (this->payload_length_ > 0) {
489 ESP_LOGCONFIG(TAG, " Payload Length: %" PRIu32, this->payload_length_);
490 }
491 if (!this->sync_value_.empty()) {
492 char hex_buf[17]; // 8 bytes max = 16 hex chars + null
493 ESP_LOGCONFIG(TAG, " Sync Value: 0x%s",
494 format_hex_to(hex_buf, this->sync_value_.data(), this->sync_value_.size()));
495 }
496 if (this->preamble_size_ > 0 || this->preamble_detect_ > 0) {
497 ESP_LOGCONFIG(TAG,
498 " Preamble Polarity: 0x%X\n"
499 " Preamble Size: %" PRIu16 "\n"
500 " Preamble Detect: %" PRIu8 "\n"
501 " Preamble Errors: %" PRIu8,
503 }
504 }
505 if (this->is_failed()) {
506 ESP_LOGE(TAG, "Configuring SX127x failed");
507 }
508}
509
510} // namespace sx127x
511} // namespace esphome
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:284
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:482
InternalGPIOPin * rst_pin_
Definition sx127x.h:104
uint8_t preamble_polarity_
Definition sx127x.h:118
InternalGPIOPin * dio0_pin_
Definition sx127x.h:103
Trigger< std::vector< uint8_t >, float, float > packet_trigger_
Definition sx127x.h:99
std::vector< SX127xListener * > listeners_
Definition sx127x.h:100
void write_fifo_(const std::vector< uint8_t > &packet)
Definition sx127x.cpp:47
SX127xError transmit_packet(const std::vector< uint8_t > &packet)
Definition sx127x.cpp:260
std::vector< uint8_t > sync_value_
Definition sx127x.h:102
uint32_t payload_length_
Definition sx127x.h:109
void dump_config() override
Definition sx127x.cpp:415
uint8_t read_register_(uint8_t reg)
Definition sx127x.cpp:23
static void IRAM_ATTR gpio_intr(SX127x *arg)
Definition sx127x.cpp:56
void loop() override
Definition sx127x.cpp:318
void set_mode_(uint8_t modulation, uint8_t mode)
Definition sx127x.cpp:376
void read_fifo_(std::vector< uint8_t > &packet)
Definition sx127x.cpp:38
uint16_t preamble_size_
Definition sx127x.h:110
std::vector< uint8_t > packet_
Definition sx127x.h:101
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:311
void setup() override
Definition sx127x.cpp:58
uint8_t spreading_factor_
Definition sx127x.h:120
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:209
@ PREAMBLE_DETECTOR_SIZE_SHIFT
Definition sx127x_reg.h:208
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:30
uint16_t size
Definition helpers.cpp:25
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:26
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:397
static void uint32_t