/ RNode_Firmware_CE_G2 / sx126x.cpp
sx126x.cpp
  1  // Copyright Sandeep Mistry, Mark Qvist and Jacob Eva.
  2  // Licensed under the MIT license.
  3  
  4  #include "Boards.h"
  5  
  6  #if MODEM == SX1262
  7  #include "sx126x.h"
  8  
  9  #if MCU_VARIANT == MCU_ESP32
 10    #if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
 11      #include "soc/rtc_wdt.h"
 12    #endif
 13    #define ISR_VECT IRAM_ATTR
 14  #else
 15    #define ISR_VECT
 16  #endif
 17  
 18  #define OP_RF_FREQ_6X               0x86
 19  #define OP_SLEEP_6X                 0x84
 20  #define OP_STANDBY_6X               0x80
 21  #define OP_TX_6X                    0x83
 22  #define OP_RX_6X                    0x82
 23  #define OP_PA_CONFIG_6X             0x95
 24  #define OP_SET_IRQ_FLAGS_6X         0x08 // Also provides info such as
 25                                           // preamble detection, etc for
 26                                           // knowing when it's safe to switch
 27                                           // antenna modes
 28  #define OP_CLEAR_IRQ_STATUS_6X      0x02
 29  #define OP_GET_IRQ_STATUS_6X        0x12
 30  #define OP_RX_BUFFER_STATUS_6X      0x13
 31  #define OP_PACKET_STATUS_6X         0x14 // Get snr & rssi of last packet
 32  #define OP_CURRENT_RSSI_6X          0x15
 33  #define OP_MODULATION_PARAMS_6X     0x8B // BW, SF, CR, etc.
 34  #define OP_PACKET_PARAMS_6X         0x8C // CRC, preamble, payload length, etc.
 35  #define OP_STATUS_6X                0xC0
 36  #define OP_TX_PARAMS_6X             0x8E // Set dbm, etc
 37  #define OP_PACKET_TYPE_6X           0x8A
 38  #define OP_BUFFER_BASE_ADDR_6X      0x8F
 39  #define OP_READ_REGISTER_6X         0x1D
 40  #define OP_WRITE_REGISTER_6X        0x0D
 41  #define OP_DIO3_TCXO_CTRL_6X        0x97
 42  #define OP_DIO2_RF_CTRL_6X          0x9D
 43  #define OP_CAD_PARAMS               0x88
 44  #define OP_CALIBRATE_6X             0x89
 45  #define OP_RX_TX_FALLBACK_MODE_6X   0x93
 46  #define OP_REGULATOR_MODE_6X        0x96
 47  #define OP_CALIBRATE_IMAGE_6X       0x98
 48  
 49  #define MASK_CALIBRATE_ALL          0x7f
 50  
 51  #define IRQ_TX_DONE_MASK_6X         0x01
 52  #define IRQ_RX_DONE_MASK_6X         0x02
 53  #define IRQ_HEADER_DET_MASK_6X      0x10
 54  #define IRQ_PREAMBLE_DET_MASK_6X    0x04
 55  #define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40
 56  #define IRQ_ALL_MASK_6X             0b0100001111111111
 57  
 58  #define MODE_LONG_RANGE_MODE_6X     0x01
 59  
 60  #define OP_FIFO_WRITE_6X            0x0E
 61  #define OP_FIFO_READ_6X             0x1E
 62  #define REG_OCP_6X                0x08E7
 63  #define REG_LNA_6X                0x08AC // No agc in sx1262
 64  #define REG_SYNC_WORD_MSB_6X      0x0740
 65  #define REG_SYNC_WORD_LSB_6X      0x0741
 66  #define REG_PAYLOAD_LENGTH_6X     0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
 67  #define REG_RANDOM_GEN_6X         0x0819
 68  
 69  #define MODE_TCXO_3_3V_6X           0x07
 70  #define MODE_TCXO_3_0V_6X           0x06
 71  #define MODE_TCXO_2_7V_6X           0x06
 72  #define MODE_TCXO_2_4V_6X           0x06
 73  #define MODE_TCXO_2_2V_6X           0x03
 74  #define MODE_TCXO_1_8V_6X           0x02
 75  #define MODE_TCXO_1_7V_6X           0x01
 76  #define MODE_TCXO_1_6V_6X           0x00
 77  
 78  #define MODE_STDBY_RC_6X            0x00
 79  #define MODE_STDBY_XOSC_6X          0x01
 80  #define MODE_FALLBACK_STDBY_RC_6X   0x20
 81  #define MODE_IMPLICIT_HEADER        0x01
 82  #define MODE_EXPLICIT_HEADER        0x00
 83  
 84  #define SYNC_WORD_6X              0x1424
 85  
 86  #define XTAL_FREQ_6X (double)32000000
 87  #define FREQ_DIV_6X  (double)pow(2.0, 25.0)
 88  #define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X)
 89  
 90  #if BOARD_MODEL == BOARD_TECHO
 91    SPIClass spim3 = SPIClass(NRF_SPIM3, pin_miso, pin_sclk, pin_mosi) ;
 92    #define SPI spim3
 93  
 94  #elif defined(NRF52840_XXAA)
 95    extern SPIClass spiModem;
 96    #define SPI spiModem
 97  #endif
 98  
 99  extern SPIClass SPI;
100  
101  #define MAX_PKT_LENGTH 255
102  
103  sx126x::sx126x() :
104    #if BOARD_MODEL == BOARD_STATION_G2
105    _spiSettings(10E6, MSBFIRST, SPI_MODE0),  // 10 MHz for Station G2 PCB signal integrity per spec
106    #else
107    _spiSettings(16E6, MSBFIRST, SPI_MODE0),
108    #endif
109    _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN),
110    _frequency(0),
111    _txp(0),
112    _sf(0x07),
113    _bw(0x04),
114    _cr(0x01),
115    _ldro(0x00),
116    _packetIndex(0),
117    _preambleLength(18),
118    _implicitHeaderMode(0),
119    _payloadLength(255),
120    _crcMode(1),
121    _fifo_tx_addr_ptr(0),
122    _fifo_rx_addr_ptr(0),
123    _packet({0}),
124    _preinit_done(false),
125    _onReceive(NULL)
126  { setTimeout(0); }
127  
128  bool sx126x::preInit() {
129    pinMode(_ss, OUTPUT);
130    digitalWrite(_ss, HIGH);
131    
132    #if BOARD_MODEL == BOARD_T3S3 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_HELTEC32_V4 || BOARD_MODEL == BOARD_TDECK || BOARD_MODEL == BOARD_XIAO_S3 || BOARD_MODEL == BOARD_STATION_G2
133      SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs);
134    #elif BOARD_MODEL == BOARD_TECHO
135      SPI.setPins(pin_miso, pin_sclk, pin_mosi);
136      SPI.begin();
137    #else
138      SPI.begin();
139    #endif
140  
141    // Check version (retry for up to 2 seconds)
142    // TODO: Actually read version registers, not syncwords
143    long start = millis();
144    uint8_t syncmsb;
145    uint8_t synclsb;
146    while (((millis() - start) < 2000) && (millis() >= start)) {
147        syncmsb = readRegister(REG_SYNC_WORD_MSB_6X);
148        synclsb = readRegister(REG_SYNC_WORD_LSB_6X);
149        if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) {
150            break;
151        }
152        delay(100);
153    }
154    if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) {
155        return false;
156    }
157  
158    _preinit_done = true;
159    return true;
160  }
161  
162  uint8_t ISR_VECT sx126x::readRegister(uint16_t address) {
163    return singleTransfer(OP_READ_REGISTER_6X, address, 0x00);
164  }
165  
166  void sx126x::writeRegister(uint16_t address, uint8_t value) {
167    singleTransfer(OP_WRITE_REGISTER_6X, address, value);
168  }
169  
170  uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) {
171    waitOnBusy();
172    
173    uint8_t response;
174    digitalWrite(_ss, LOW);
175    SPI.beginTransaction(_spiSettings);
176    SPI.transfer(opcode);
177    SPI.transfer((address & 0xFF00) >> 8);
178    SPI.transfer(address & 0x00FF);
179    if (opcode == OP_READ_REGISTER_6X) { SPI.transfer(0x00); }
180    response = SPI.transfer(value);
181    SPI.endTransaction();
182  
183    digitalWrite(_ss, HIGH);
184  
185    return response;
186  }
187  
188  void sx126x::rxAntEnable() {
189    if (_rxen != -1) { digitalWrite(_rxen, HIGH); }
190  }
191  
192  void sx126x::loraMode() {
193    // Enable lora mode on the SX1262 chip
194    uint8_t mode = MODE_LONG_RANGE_MODE_6X;
195    executeOpcode(OP_PACKET_TYPE_6X, &mode, 1);
196  }
197  
198  void sx126x::waitOnBusy() {
199    unsigned long time = millis();
200    if (_busy != -1) {
201      while (digitalRead(_busy) == HIGH) {
202          if (millis() >= (time + 100)) { break; }
203      }
204    }
205  }
206  
207  void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) {
208    waitOnBusy();
209    digitalWrite(_ss, LOW);
210    SPI.beginTransaction(_spiSettings);
211    SPI.transfer(opcode);
212    for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); }
213    SPI.endTransaction();
214    digitalWrite(_ss, HIGH);
215  }
216  
217  void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) {
218    waitOnBusy();
219    digitalWrite(_ss, LOW);
220    SPI.beginTransaction(_spiSettings);
221    SPI.transfer(opcode);
222    SPI.transfer(0x00);
223    for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); }
224    SPI.endTransaction();
225    digitalWrite(_ss, HIGH);
226  }
227  
228  void sx126x::writeBuffer(const uint8_t* buffer, size_t size) {
229    waitOnBusy();
230    digitalWrite(_ss, LOW);
231    SPI.beginTransaction(_spiSettings);
232    SPI.transfer(OP_FIFO_WRITE_6X);
233    SPI.transfer(_fifo_tx_addr_ptr);
234    for (int i = 0; i < size; i++) { SPI.transfer(buffer[i]); _fifo_tx_addr_ptr++; }
235    SPI.endTransaction();
236    digitalWrite(_ss, HIGH);
237  }
238  
239  void sx126x::readBuffer(uint8_t* buffer, size_t size) {
240    waitOnBusy();
241    digitalWrite(_ss, LOW);
242    SPI.beginTransaction(_spiSettings);
243    SPI.transfer(OP_FIFO_READ_6X);
244    SPI.transfer(_fifo_rx_addr_ptr);
245    SPI.transfer(0x00);
246    for (int i = 0; i < size; i++) { buffer[i] = SPI.transfer(0x00); }
247    SPI.endTransaction();
248    digitalWrite(_ss, HIGH);
249  }
250  
251  void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
252    // Because there is no access to these registers on the sx1262, we have
253    // to set all these parameters at once or not at all.
254    uint8_t buf[8];
255    buf[0] = sf;
256    buf[1] = bw;
257    buf[2] = cr; 
258    buf[3] = ldro; // Low data rate toggle
259    buf[4] = 0x00; // Unused params in LoRa mode
260    buf[5] = 0x00;
261    buf[6] = 0x00;
262    buf[7] = 0x00;
263    executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8);
264  }
265  
266  void sx126x::setPacketParams(long preamble_symbols, uint8_t headermode, uint8_t payload_length, uint8_t crc) {
267    // Because there is no access to these registers on the sx1262, we have
268    // to set all these parameters at once or not at all.
269    uint8_t buf[9];
270    buf[0] = uint8_t((preamble_symbols & 0xFF00) >> 8);
271    buf[1] = uint8_t((preamble_symbols & 0x00FF));
272    buf[2] = headermode;
273    buf[3] = payload_length;
274    buf[4] = crc;
275    buf[5] = 0x00; // standard IQ setting (no inversion)
276    buf[6] = 0x00; // unused params
277    buf[7] = 0x00; 
278    buf[8] = 0x00; 
279    executeOpcode(OP_PACKET_PARAMS_6X, buf, 9);
280  }
281  
282  void sx126x::reset(void) {
283    if (_reset != -1) {
284      pinMode(_reset, OUTPUT);
285      digitalWrite(_reset, LOW);
286      delay(10);
287      digitalWrite(_reset, HIGH);
288      delay(10);
289    }
290  }
291  
292  void sx126x::calibrate(void) {
293    // Put in STDBY_RC mode before calibration
294    uint8_t mode_byte = MODE_STDBY_RC_6X;
295    executeOpcode(OP_STANDBY_6X, &mode_byte, 1);
296  
297    // Calibrate RC64k, RC13M, PLL, ADC and image
298    uint8_t calibrate = MASK_CALIBRATE_ALL;
299    executeOpcode(OP_CALIBRATE_6X, &calibrate, 1);
300  
301    delay(5);
302    waitOnBusy();
303  }
304  
305  void sx126x::calibrate_image(long frequency) {
306    uint8_t image_freq[2] = {0};
307    if      (frequency >= 430E6 && frequency <= 440E6) { image_freq[0] = 0x6B; image_freq[1] = 0x6F; }
308    else if (frequency >= 470E6 && frequency <= 510E6) { image_freq[0] = 0x75; image_freq[1] = 0x81; }
309    else if (frequency >= 779E6 && frequency <= 787E6) { image_freq[0] = 0xC1; image_freq[1] = 0xC5; }
310    else if (frequency >= 863E6 && frequency <= 870E6) { image_freq[0] = 0xD7; image_freq[1] = 0xDB; }
311    else if (frequency >= 902E6 && frequency <= 928E6) { image_freq[0] = 0xE1; image_freq[1] = 0xE9; } // TODO: Allow higher freq calibration
312    executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2);
313    waitOnBusy();
314  }
315  
316  int sx126x::begin(long frequency) {
317    reset();
318    
319    if (_busy != -1) { pinMode(_busy, INPUT); }
320    if (!_preinit_done) { if (!preInit()) { return false; } }
321    if (_rxen != -1) { pinMode(_rxen, OUTPUT); }
322  
323    calibrate();
324    calibrate_image(frequency);
325    enableTCXO();
326    loraMode();
327    standby();
328  
329    // Set sync word
330    setSyncWord(SYNC_WORD_6X);
331  
332    #if DIO2_AS_RF_SWITCH
333      // enable dio2 rf switch
334      uint8_t byte = 0x01;
335      executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1);
336    #endif
337  
338    rxAntEnable();
339    setFrequency(frequency);
340    setTxPower(2);
341    enableCrc();
342    writeRegister(REG_LNA_6X, 0x96); // Set LNA boost
343    uint8_t basebuf[2] = {0}; // Set base addresses
344    executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2);
345  
346    setModulationParams(_sf, _bw, _cr, _ldro);
347    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
348  
349    #if HAS_LORA_PA
350      #if LORA_PA_GC1109
351        // Enable Vfem_ctl for supply to
352        // PA power net.
353        pinMode(LORA_PA_PWR_EN, OUTPUT);
354        digitalWrite(LORA_PA_PWR_EN, HIGH);
355  
356        // Enable PA LNA and TX standby
357        pinMode(LORA_PA_CSD, OUTPUT);
358        digitalWrite(LORA_PA_CSD, HIGH);
359  
360        // Keep PA CPS low until actual
361        // transmit. Does it save power?
362        // Who knows? Will have to measure.
363        pinMode(LORA_PA_CPS, OUTPUT);
364        digitalWrite(LORA_PA_CPS, LOW);
365  
366        // On Heltec V4, the PA CTX pin
367        // is driven by the SX1262 DIO2
368        // pin directly, so we do not
369        // need to manually raise this.
370      #endif
371    #endif
372  
373    return 1;
374  }
375  
376  void sx126x::end() { sleep(); SPI.end(); _preinit_done = false; }
377  
378  int sx126x::beginPacket(int implicitHeader) {
379    #if HAS_LORA_PA
380      #if LORA_PA_GC1109
381        // Enable PA CPS for transmit
382        digitalWrite(LORA_PA_CPS, HIGH);
383      #endif
384    #endif
385  
386    standby();
387    if (implicitHeader) { implicitHeaderMode(); }
388    else { explicitHeaderMode(); }
389  
390    _payloadLength = 0;
391    _fifo_tx_addr_ptr = 0;
392    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
393  
394    return 1;
395  }
396  
397  int sx126x::endPacket() {
398    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
399    uint8_t timeout[3] = {0}; // Put in single TX mode
400    executeOpcode(OP_TX_6X, timeout, 3);
401  
402    uint8_t buf[2];
403    buf[0] = 0x00;
404    buf[1] = 0x00;
405    executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
406  
407    // Wait for TX done
408    bool timed_out = false;
409    uint32_t w_timeout = millis()+LORA_MODEM_TIMEOUT_MS;
410    while ((millis() < w_timeout) && ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0)) {
411      buf[0] = 0x00;
412      buf[1] = 0x00;
413      executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
414      yield();
415    }
416  
417    if (!(millis() < w_timeout)) { timed_out = true; }
418  
419    // Clear IRQs
420    uint8_t mask[2];
421    mask[0] = 0x00;
422    mask[1] = IRQ_TX_DONE_MASK_6X;
423    executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2);
424    if (timed_out) { return 0; } else { return 1; }
425  }
426  
427  unsigned long preamble_detected_at = 0;
428  extern long lora_preamble_time_ms;
429  extern long lora_header_time_ms;
430  bool false_preamble_detected = false;
431  
432  bool sx126x::dcd() {
433    uint8_t buf[2] = {0}; executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
434    uint32_t now = millis();
435  
436    bool header_detected = false;
437    bool carrier_detected = false;
438  
439    if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) { header_detected = true; carrier_detected = true; }
440    else { header_detected = false; }
441  
442    if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) {
443      carrier_detected = true;
444      if (preamble_detected_at == 0) { preamble_detected_at = now; }
445      if (now - preamble_detected_at > lora_preamble_time_ms + lora_header_time_ms) {
446        preamble_detected_at = 0;
447        if (!header_detected) { false_preamble_detected = true; }
448        uint8_t clearbuf[2] = {0};
449        clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X;
450        executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2);
451      }
452    }
453  
454    // TODO: Maybe there's a way of unlatching the RSSI
455    // status without re-activating receive mode?
456    if (false_preamble_detected) { sx126x_modem.receive(); false_preamble_detected = false; }
457    return carrier_detected;
458  }
459  
460  uint8_t sx126x::currentRssiRaw() {
461    uint8_t byte = 0;
462    executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
463    return byte;
464  }
465  
466  int ISR_VECT sx126x::currentRssi() {
467    uint8_t byte = 0;
468    executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
469    int rssi = -(int(byte)) / 2;
470    #if HAS_LORA_LNA
471      rssi -= LORA_LNA_GAIN;
472    #endif
473    return rssi;
474  }
475  
476  uint8_t sx126x::packetRssiRaw() {
477    uint8_t buf[3] = {0};
478    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
479    return buf[2];
480  }
481  
482  int ISR_VECT sx126x::packetRssi() {
483    // TODO: May need more calculations here
484    uint8_t buf[3] = {0};
485    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
486    int pkt_rssi = -buf[0] / 2;
487    #if HAS_LORA_LNA
488      pkt_rssi -= LORA_LNA_GAIN;
489    #endif
490    return pkt_rssi;
491  }
492  
493  int ISR_VECT sx126x::packetRssi(uint8_t pkt_snr_raw) {
494    // TODO: May need more calculations here
495    uint8_t buf[3] = {0};
496    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
497    int pkt_rssi = -buf[0] / 2;
498    return pkt_rssi;
499  }
500  
501  uint8_t ISR_VECT sx126x::packetSnrRaw() {
502    uint8_t buf[3] = {0};
503    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
504    return buf[1];
505  }
506  
507  float ISR_VECT sx126x::packetSnr() {
508    uint8_t buf[3] = {0};
509    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
510    return float(buf[1]) * 0.25;
511  }
512  
513  long sx126x::packetFrequencyError() {
514    // TODO: Implement this, no idea how to check it on the sx1262
515    const float fError = 0.0;
516    return static_cast<long>(fError);
517  }
518  
519  size_t sx126x::write(uint8_t byte) { return write(&byte, sizeof(byte)); }
520  size_t sx126x::write(const uint8_t *buffer, size_t size) {
521    if ((_payloadLength + size) > MAX_PKT_LENGTH) { size = MAX_PKT_LENGTH - _payloadLength; }
522    writeBuffer(buffer, size);
523    _payloadLength = _payloadLength + size;
524    return size;
525  }
526  
527  int ISR_VECT sx126x::available() {
528    uint8_t buf[2] = {0};
529    executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2);
530    return buf[0] - _packetIndex;
531  }
532  
533  int ISR_VECT sx126x::read(){
534    if (!available()) { return -1; }
535    if (_packetIndex == 0) {
536      uint8_t rxbuf[2] = {0};
537      executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
538      int size = rxbuf[0];
539      _fifo_rx_addr_ptr = rxbuf[1];
540      readBuffer(_packet, size);
541    }
542  
543    uint8_t byte = _packet[_packetIndex];
544    _packetIndex++;
545    return byte;
546  }
547  
548  int sx126x::peek() {
549    if (!available()) { return -1; }
550    if (_packetIndex == 0) {
551        uint8_t rxbuf[2] = {0};
552        executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
553        int size = rxbuf[0];
554        _fifo_rx_addr_ptr = rxbuf[1];
555        readBuffer(_packet, size);
556    }
557  
558    uint8_t b = _packet[_packetIndex];
559    return b;
560  }
561  
562  void sx126x::flush() { }
563  
564  void sx126x::onReceive(void(*callback)(int)){
565    _onReceive = callback;
566  
567    if (callback) {
568      pinMode(_dio0, INPUT);
569      uint8_t buf[8]; // Set preamble and header detection irqs, plus dio0 mask
570      buf[0] = 0xFF;  // Set irq masks, enable all
571      buf[1] = 0xFF;
572      buf[2] = 0x00;  // Set dio0 masks
573      buf[3] = IRQ_RX_DONE_MASK_6X; 
574      buf[4] = 0x00;  // Set dio1 masks
575      buf[5] = 0x00;
576      buf[6] = 0x00;  // Set dio2 masks 
577      buf[7] = 0x00;
578      executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8);
579  
580      #ifdef SPI_HAS_NOTUSINGINTERRUPT
581        SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
582      #endif
583      attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING);
584  
585    } else {
586      detachInterrupt(digitalPinToInterrupt(_dio0));
587      #ifdef SPI_HAS_NOTUSINGINTERRUPT
588        SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
589      #endif
590    }
591  }
592  
593  void sx126x::receive(int size) {
594    #if HAS_LORA_PA
595      #if LORA_PA_GC1109
596        // Disable PA CPS for receive
597        digitalWrite(LORA_PA_CPS, LOW);
598      #endif
599    #endif
600  
601    if (size > 0) {
602      implicitHeaderMode();
603      _payloadLength = size;
604      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
605    } else { explicitHeaderMode(); }
606  
607    if (_rxen != -1) { rxAntEnable(); }
608    uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // Continuous mode
609    executeOpcode(OP_RX_6X, mode, 3);
610  }
611  
612  void sx126x::standby() {
613    uint8_t byte = MODE_STDBY_XOSC_6X; // STDBY_XOSC
614    executeOpcode(OP_STANDBY_6X, &byte, 1); 
615  }
616  
617  void sx126x::sleep() { uint8_t byte = 0x00; executeOpcode(OP_SLEEP_6X, &byte, 1); }
618  
619  void sx126x::enableTCXO() {
620    #if HAS_TCXO
621      #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3 || BOARD_MODEL == BOARD_XIAO_S3
622        uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF};
623      #elif BOARD_MODEL == BOARD_TBEAM
624        uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
625      #elif BOARD_MODEL == BOARD_TDECK
626        uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
627      #elif BOARD_MODEL == BOARD_TBEAM_S_V1
628        uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
629      #elif BOARD_MODEL == BOARD_T3S3
630        uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
631      #elif BOARD_MODEL == BOARD_HELTEC_T114
632        uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
633      #elif BOARD_MODEL == BOARD_TECHO
634        uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
635      #elif BOARD_MODEL == BOARD_HELTEC32_V4
636        uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
637      #elif BOARD_MODEL == BOARD_STATION_G2
638        uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
639      #endif
640      executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4);
641      #if BOARD_MODEL == BOARD_STATION_G2
642      delay(5);  // TCXO stabilization delay for Station G2 (typ. 2ms, 5ms for margin)
643      #endif
644    #endif
645  }
646  
647  // TODO: Once enabled, SX1262 needs a complete reset to disable TCXO
648  void sx126x::disableTCXO() { }
649  
650  void sx126x::setTxPower(int level, int outputPin) {
651    // Currently no low power mode for SX1262 implemented, assuming PA boost
652    
653    // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2
654    // RegTxClampConfig = @address 0x08D8
655    writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1));
656  
657    uint8_t pa_buf[4];
658    pa_buf[0] = 0x04; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs
659    pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262
660    pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261)
661    pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet)
662    executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power
663  
664    if (level > 22) { level = 22; }
665    else if (level < -9) { level = -9; }
666    writeRegister(REG_OCP_6X, OCP_TUNED); // Use board-specific tuned OCP
667  
668    uint8_t tx_buf[2];
669    tx_buf[0] = level;
670    tx_buf[1] = 0x02; // PA ramping time - 40 microseconds
671    executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2);
672  
673    _txp = level;
674  }
675  
676  uint8_t sx126x::getTxPower() { return _txp; }
677  
678  void sx126x::setFrequency(long frequency) {
679    _frequency = frequency;
680    uint8_t buf[4];
681    uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X);
682    buf[0] = ((freq >> 24) & 0xFF);
683    buf[1] = ((freq >> 16) & 0xFF);
684    buf[2] = ((freq >> 8) & 0xFF);
685    buf[3] = (freq & 0xFF);
686    executeOpcode(OP_RF_FREQ_6X, buf, 4);
687  }
688  
689  uint32_t sx126x::getFrequency() {
690      // We can't read the frequency on the sx1262 / 80
691      uint32_t frequency = _frequency;
692      return frequency;
693  }
694  
695  void sx126x::setSpreadingFactor(int sf) {
696    if (sf < 5)       { sf = 5; }
697    else if (sf > 12) { sf = 12; }
698    _sf = sf;
699  
700    handleLowDataRate();
701    setModulationParams(sf, _bw, _cr, _ldro);
702  }
703  
704  long sx126x::getSignalBandwidth() {
705    int bw = _bw;
706    switch (bw) {
707      case 0x00: return 7.8E3;
708      case 0x01: return 15.6E3;
709      case 0x02: return 31.25E3;
710      case 0x03: return 62.5E3;
711      case 0x04: return 125E3;
712      case 0x05: return 250E3;
713      case 0x06: return 500E3;
714      case 0x08: return 10.4E3;
715      case 0x09: return 20.8E3;
716      case 0x0A: return 41.7E3;
717    }
718    return 0;
719  }
720  
721  extern bool lora_low_datarate;
722  void sx126x::handleLowDataRate() {
723    if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16)
724           { _ldro = 0x01; lora_low_datarate = true;  }
725      else { _ldro = 0x00; lora_low_datarate = false; }
726  }
727  
728  // TODO: Check if there's anything the sx1262 can do here
729  void sx126x::optimizeModemSensitivity(){ }
730  
731  void sx126x::setSignalBandwidth(long sbw) {
732    if (sbw <= 7.8E3)        { _bw = 0x00; }
733    else if (sbw <= 10.4E3)  { _bw = 0x08; }
734    else if (sbw <= 15.6E3)  { _bw = 0x01; }
735    else if (sbw <= 20.8E3)  { _bw = 0x09; }
736    else if (sbw <= 31.25E3) { _bw = 0x02; }
737    else if (sbw <= 41.7E3)  { _bw = 0x0A; }
738    else if (sbw <= 62.5E3)  { _bw = 0x03; }
739    else if (sbw <= 125E3)   { _bw = 0x04; }
740    else if (sbw <= 250E3)   { _bw = 0x05; } 
741    else                     { _bw = 0x06; }
742  
743    handleLowDataRate();
744    setModulationParams(_sf, _bw, _cr, _ldro);
745    optimizeModemSensitivity();
746  }
747  
748  void sx126x::setCodingRate4(int denominator) {
749    if (denominator < 5) { denominator = 5; }
750    else if (denominator > 8) { denominator = 8; }
751    int cr = denominator - 4;
752    _cr = cr;
753    setModulationParams(_sf, _bw, cr, _ldro);
754  }
755  
756  void sx126x::setPreambleLength(long preamble_symbols) {
757    _preambleLength = preamble_symbols;
758    setPacketParams(preamble_symbols, _implicitHeaderMode, _payloadLength, _crcMode);
759  }
760  
761  void sx126x::setSyncWord(uint16_t sw) {
762    // TODO: Why was this hardcoded instead of using the config value?
763    // writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8);
764    // writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF);
765    writeRegister(REG_SYNC_WORD_MSB_6X, 0x14);
766    writeRegister(REG_SYNC_WORD_LSB_6X, 0x24);
767  }
768  
769  void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen) {
770    _ss = ss;
771    _reset = reset;
772    _dio0 = dio0;
773    _busy = busy;
774    _rxen = rxen;
775  }
776  
777  void sx126x::dumpRegisters(Stream& out) {
778    for (int i = 0; i < 128; i++) {
779      out.print("0x");
780      out.print(i, HEX);
781      out.print(": 0x");
782      out.println(readRegister(i), HEX);
783    }
784  }
785  
786  void ISR_VECT sx126x::handleDio0Rise() {
787    uint8_t buf[2];
788    buf[0] = 0x00;
789    buf[1] = 0x00;
790    executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
791    executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2);
792  
793    if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) {
794      _packetIndex = 0;
795      uint8_t rxbuf[2] = {0}; // Read packet length
796      executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
797      int packetLength = rxbuf[0];
798      if (_onReceive) { _onReceive(packetLength); }
799    }
800  }
801  
802  void ISR_VECT sx126x::onDio0Rise() { sx126x_modem.handleDio0Rise(); }
803  void sx126x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
804  void sx126x::enableCrc() { _crcMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
805  void sx126x::disableCrc() { _crcMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
806  void sx126x::explicitHeaderMode() { _implicitHeaderMode = 0; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
807  void sx126x::implicitHeaderMode() { _implicitHeaderMode = 1; setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); }
808  byte sx126x::random() { return readRegister(REG_RANDOM_GEN_6X); }
809  
810  sx126x sx126x_modem;
811  
812  #endif