]> git.tnoah.ca Git - lora-radio.git/commitdiff
Add SX1280 driver and split driver in three parts
authorjacob.eva <x00010@disroot.org>
Fri, 9 Feb 2024 20:46:39 +0000 (20:46 +0000)
committerjacob.eva <x00010@disroot.org>
Fri, 9 Feb 2024 20:56:26 +0000 (20:56 +0000)
12 files changed:
Config.h
LoRa.cpp [deleted file]
LoRa.h [deleted file]
Modem.h
RNode_Firmware.ino
Utilities.h
sx126x.cpp [new file with mode: 0644]
sx126x.h [new file with mode: 0644]
sx127x.cpp [new file with mode: 0644]
sx127x.h [new file with mode: 0644]
sx128x.cpp [new file with mode: 0644]
sx128x.h [new file with mode: 0644]

index bee7efbc792860956eccf425b417ec4471b934ac..90883b652386905329fa1d0f11560a608825b8a0 100644 (file)
--- a/Config.h
+++ b/Config.h
             #define HAS_NP false
             #define HAS_SD false
             #define HAS_TCXO true
-            #define HAS_RXEN_BUSY true
-            #define MODEM SX1262 
+            #define HAS_RF_SWITCH_RX_TX true
+            #define HAS_BUSY true
+            #define MODEM SX1280
             
             #define CONFIG_UART_BUFFER_SIZE 6144
             #define CONFIG_QUEUE_SIZE 6144
             #define EEPROM_OFFSET EEPROM_SIZE+0xED000-EEPROM_RESERVED
 
             // following pins are for the sx1262
-            const int pin_rxen = 37;
+            /*const int pin_rxen = 37;
             const int pin_reset = 38;
             const int pin_cs = 42;
             const int pin_sclk = 43;
             const int pin_mosi = 44;
             const int pin_miso = 45;
             const int pin_busy = 46;
-            const int pin_dio = 47;
+            const int pin_dio = 47;*/
             const int pin_led_rx = LED_BLUE;
             const int pin_led_tx = LED_GREEN;
             const int pin_tcxo_enable = -1;
 
        #define eeprom_addr(a) (a+EEPROM_OFFSET)
 
-    #ifndef HAS_RXEN_BUSY
+    #ifndef HAS_RF_SWITCH_RX_TX
         const int pin_rxen = -1;
+        const int pin_txen = -1;
+    #endif
+
+    #ifndef HAS_BUSY
         const int pin_busy = -1;
     #endif
 
-    #if MODEM == SX1262 && defined(NRF52840_XXAA)
+    #if (MODEM == SX1262 || MODEM == SX1280) && defined(NRF52840_XXAA)
         SPIClass spiModem(NRF_SPIM2, pin_miso, pin_sclk, pin_mosi);
     #endif
 
diff --git a/LoRa.cpp b/LoRa.cpp
deleted file mode 100644 (file)
index 3637a08..0000000
--- a/LoRa.cpp
+++ /dev/null
@@ -1,1505 +0,0 @@
-// Copyright (c) Sandeep Mistry. All rights reserved.
-// Licensed under the MIT license.
-
-// Modifications and additions copyright 2023 by Mark Qvist
-// Obviously still under the MIT license.
-
-#include "Boards.h"
-#include "LoRa.h"
-#include "Modem.h"
-
-#define MCU_1284P 0x91
-#define MCU_2560  0x92
-#define MCU_ESP32 0x81
-#define MCU_NRF52 0x71
-#if defined(__AVR_ATmega1284P__)
-  #define PLATFORM PLATFORM_AVR
-  #define MCU_VARIANT MCU_1284P
-#elif defined(__AVR_ATmega2560__)
-  #define PLATFORM PLATFORM_AVR
-  #define MCU_VARIANT MCU_2560
-#elif defined(ESP32)
-  #define PLATFORM PLATFORM_ESP32
-  #define MCU_VARIANT MCU_ESP32
-#elif defined(NRF52840_XXAA)
-  #define PLATFORM PLATFORM_NRF52
-  #define MCU_VARIANT MCU_NRF52
-#endif
-
-#ifndef MCU_VARIANT
-  #error No MCU variant defined, cannot compile
-#endif
-
-#if MCU_VARIANT == MCU_ESP32
-  #if BOARD_MODEL != BOARD_RNODE_NG_22
-    #include "soc/rtc_wdt.h"
-  #endif
-  #define ISR_VECT IRAM_ATTR
-#else
-  #define ISR_VECT
-#endif
-
-#if BOARD_MODEL == BOARD_RNODE_NG_22
-  #define MODEM_MISO 3
-  #define MODEM_MOSI 6
-  #define MODEM_CLK  5
-#endif
-
-#if MODEM == SX1262
-    #define OP_FIFO_WRITE            0x0E
-    #define OP_FIFO_READ             0x1E
-    #define OP_RF_FREQ               0x86
-    #define OP_SLEEP                 0x84
-    #define OP_STANDBY               0x80
-    #define OP_TX                    0x83
-    #define OP_RX                    0x82
-    #define OP_PA_CONFIG             0x95
-    #define OP_SET_IRQ_FLAGS         0x08 // also provides info such as
-                                          // preamble detection, etc for
-                                          // knowing when it's safe to switch
-                                          // antenna modes
-    #define OP_CLEAR_IRQ_STATUS      0x02
-    #define OP_GET_IRQ_STATUS        0x12
-    #define OP_RX_BUFFER_STATUS      0x13
-    #define OP_PACKET_STATUS         0x14 // get snr & rssi of last packet
-    #define OP_CURRENT_RSSI          0x15
-    #define OP_MODULATION_PARAMS     0x8B // bw, sf, cr, etc.
-    #define OP_PACKET_PARAMS         0x8C // crc, preamble, payload length, etc.
-    #define OP_STATUS                0xC0
-    #define OP_TX_PARAMS             0x8E // set dbm, etc
-    #define OP_PACKET_TYPE           0x8A
-    #define OP_BUFFER_BASE_ADDR      0x8F
-    #define OP_READ_REGISTER         0x1D
-    #define OP_WRITE_REGISTER        0x0D
-    #define OP_DIO3_TCXO_CTRL        0x97
-    #define OP_DIO2_RF_CTRL          0x9D
-    #define OP_CALIBRATE             0x89
-
-    #define REG_OCP                0x08E7
-    #define REG_LNA                0x08AC // no agc in sx1262
-    #define REG_SYNC_WORD_MSB      0x0740
-    #define REG_SYNC_WORD_LSB      0x0741
-    #define REG_PAYLOAD_LENGTH     0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
-    #define REG_RANDOM_GEN         0x0819
-
-    #define MODE_LONG_RANGE_MODE     0x01
-
-    #define MODE_TCXO_3_3V           0x07
-
-    #define IRQ_TX_DONE_MASK         0x01
-    #define IRQ_RX_DONE_MASK         0x02
-    #define IRQ_PREAMBLE_DET_MASK    0x04
-    #define IRQ_HEADER_DET_MASK      0x10
-    #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x40
-
-    #define XTAL_FREQ (double)32000000
-    #define FREQ_DIV (double)pow(2.0, 25.0)
-    #define FREQ_STEP (double)(XTAL_FREQ / FREQ_DIV)
-    int fifo_tx_addr_ptr = 0;
-    int fifo_rx_addr_ptr = 0;
-    uint8_t packet[256] = {0};
-
-    #if defined(NRF52840_XXAA)
-      extern SPIClass spiModem;
-      #define SPI spiModem
-    #endif
-
-#elif MODEM == SX1276 || MODEM == SX1278
-    // Registers
-    #define REG_FIFO                 0x00
-    #define REG_OP_MODE              0x01
-    #define REG_FRF_MSB              0x06
-    #define REG_FRF_MID              0x07
-    #define REG_FRF_LSB              0x08
-    #define REG_PA_CONFIG            0x09
-    #define REG_OCP                  0x0b
-    #define REG_LNA                  0x0c
-    #define REG_FIFO_ADDR_PTR        0x0d
-    #define REG_FIFO_TX_BASE_ADDR    0x0e
-    #define REG_FIFO_RX_BASE_ADDR    0x0f
-    #define REG_FIFO_RX_CURRENT_ADDR 0x10
-    #define REG_IRQ_FLAGS            0x12
-    #define REG_RX_NB_BYTES          0x13
-    #define REG_MODEM_STAT           0x18
-    #define REG_PKT_SNR_VALUE        0x19
-    #define REG_PKT_RSSI_VALUE       0x1a
-    #define REG_RSSI_VALUE           0x1b
-    #define REG_MODEM_CONFIG_1       0x1d
-    #define REG_MODEM_CONFIG_2       0x1e
-    #define REG_PREAMBLE_MSB         0x20
-    #define REG_PREAMBLE_LSB         0x21
-    #define REG_PAYLOAD_LENGTH       0x22
-    #define REG_MODEM_CONFIG_3       0x26
-    #define REG_FREQ_ERROR_MSB       0x28
-    #define REG_FREQ_ERROR_MID       0x29
-    #define REG_FREQ_ERROR_LSB       0x2a
-    #define REG_RSSI_WIDEBAND        0x2c
-    #define REG_DETECTION_OPTIMIZE   0x31
-    #define REG_HIGH_BW_OPTIMIZE_1   0x36
-    #define REG_DETECTION_THRESHOLD  0x37
-    #define REG_SYNC_WORD            0x39
-    #define REG_HIGH_BW_OPTIMIZE_2   0x3a
-    #define REG_DIO_MAPPING_1        0x40
-    #define REG_VERSION              0x42
-    #define REG_TCXO                 0x4b
-    #define REG_PA_DAC               0x4d
-    
-    // Modes
-    #define MODE_LONG_RANGE_MODE     0x80
-    #define MODE_SLEEP               0x00
-    #define MODE_STDBY               0x01
-    #define MODE_TX                  0x03
-    #define MODE_RX_CONTINUOUS       0x05
-    #define MODE_RX_SINGLE           0x06
-    
-    // PA config
-    #define PA_BOOST                 0x80
-    
-    // IRQ masks
-    #define IRQ_TX_DONE_MASK           0x08
-    #define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
-    #define IRQ_RX_DONE_MASK           0x40
-#endif
-
-#define MAX_PKT_LENGTH           255
-
-extern SPIClass SPI;
-
-bool lora_preinit_done = false;
-
-LoRaClass::LoRaClass() :
-  _spiSettings(8E6, MSBFIRST, SPI_MODE0),
-  _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN),
-  _frequency(0),
-  _txp(0),
-  _sf(0x07),
-  _bw(0x04),
-  _cr(0x01),
-  _ldro(0x00),
-  _packetIndex(0),
-  _preambleLength(18),
-  _implicitHeaderMode(0),
-  _payloadLength(255),
-  _crcMode(1),
-  _onReceive(NULL)
-{
-  // overide Stream timeout value
-  setTimeout(0);
-}
-
-bool LoRaClass::preInit() {
-  // setup pins
-  pinMode(_ss, OUTPUT);
-  // set SS high
-  digitalWrite(_ss, HIGH);
-  
-  #if BOARD_MODEL == BOARD_RNODE_NG_22
-    SPI.begin(MODEM_CLK, MODEM_MISO, MODEM_MOSI);
-  #else
-    SPI.begin();
-  #endif
-
-  // check version (retry for up to 2 seconds)
-  #if MODEM == SX1276 || MODEM == SX1278
-    uint8_t version;
-    long start = millis();
-    while (((millis() - start) < 2000) && (millis() >= start)) {
-      version = readRegister(REG_VERSION);
-      if (version == 0x12) {
-        break;
-      }
-      delay(100);
-    }
-    if (version != 0x12) {
-      return false;
-    }
-  
-    lora_preinit_done = true;
-    return true;
-
-  #elif MODEM == SX1262
-    long start = millis();
-    uint8_t syncmsb;
-    uint8_t synclsb;
-    while (((millis() - start) < 2000) && (millis() >= start)) {
-      syncmsb = readRegister(REG_SYNC_WORD_MSB);
-      synclsb = readRegister(REG_SYNC_WORD_LSB);
-      if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) {
-          break;
-      }
-      delay(100);
-    }
-
-    if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) {
-        return false;
-    }
-
-    lora_preinit_done = true;
-    return true;
-
-  #else
-    return false;
-  #endif
-}
-
-#if MODEM == SX1276 || MODEM == SX1278
-    uint8_t ISR_VECT LoRaClass::readRegister(uint8_t address)
-    {
-      return singleTransfer(address & 0x7f, 0x00);
-    }
-    void LoRaClass::writeRegister(uint8_t address, uint8_t value)
-    {
-      singleTransfer(address | 0x80, value);
-    }
-
-    uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t address, uint8_t value)
-    {
-      uint8_t response;
-
-      digitalWrite(_ss, LOW);
-
-      SPI.beginTransaction(_spiSettings);
-      SPI.transfer(address);
-      response = SPI.transfer(value);
-      SPI.endTransaction();
-
-      digitalWrite(_ss, HIGH);
-
-      return response;
-    }
-#elif MODEM == SX1262
-    uint8_t ISR_VECT LoRaClass::readRegister(uint16_t address)
-    {
-      return singleTransfer(OP_READ_REGISTER, address, 0x00);
-    }
-
-    void LoRaClass::writeRegister(uint16_t address, uint8_t value)
-    {
-        singleTransfer(OP_WRITE_REGISTER, address, value);
-    }
-
-    uint8_t ISR_VECT LoRaClass::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value)
-    {
-        waitOnBusy();
-
-        uint8_t response;
-
-        digitalWrite(_ss, LOW);
-
-        SPI.beginTransaction(_spiSettings);
-        SPI.transfer(opcode);
-        SPI.transfer((address & 0xFF00) >> 8);
-        SPI.transfer(address & 0x00FF);
-        if (opcode == OP_READ_REGISTER) {
-            SPI.transfer(0x00);
-        }
-        response = SPI.transfer(value);
-        SPI.endTransaction();
-
-        digitalWrite(_ss, HIGH);
-
-        return response;
-    }
-
-    void LoRaClass::enableAntenna()
-    {
-        uint8_t byte = 0x01;
-        // enable dio2 rf switch
-        executeOpcode(OP_DIO2_RF_CTRL, &byte, 1); 
-        digitalWrite(_rxen, HIGH);
-    }
-
-    void LoRaClass::disableAntenna()
-    {
-        digitalWrite(_rxen, LOW);
-    }
-
-    void LoRaClass::loraMode() {
-        // enable lora mode on the SX1262 chip
-        uint8_t mode = MODE_LONG_RANGE_MODE;
-        executeOpcode(OP_PACKET_TYPE, &mode, 1);
-    }
-
-    void LoRaClass::waitOnBusy() {
-        if (_busy != -1) {
-            while (digitalRead(_busy) == HIGH)
-            {
-                // do nothing
-            }
-        }
-    }
-
-    void LoRaClass::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
-    {
-        waitOnBusy();
-
-        digitalWrite(_ss, LOW);
-
-        SPI.beginTransaction(_spiSettings);
-        SPI.transfer(opcode);
-
-        for (int i = 0; i < size; i++)
-        {
-            SPI.transfer(buffer[i]);
-        }
-
-        SPI.endTransaction();
-
-        digitalWrite(_ss, HIGH);
-    }
-
-    void LoRaClass::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
-    {
-        waitOnBusy();
-
-        digitalWrite(_ss, LOW);
-
-        SPI.beginTransaction(_spiSettings);
-        SPI.transfer(opcode);
-        SPI.transfer(0x00);
-
-        for (int i = 0; i < size; i++)
-        {
-            buffer[i] = SPI.transfer(0x00);
-        }
-
-        SPI.endTransaction();
-
-        digitalWrite(_ss, HIGH);
-    }
-
-    void LoRaClass::writeBuffer(const uint8_t* buffer, size_t size)
-    {
-        waitOnBusy();
-
-        digitalWrite(_ss, LOW);
-
-        SPI.beginTransaction(_spiSettings);
-        SPI.transfer(OP_FIFO_WRITE);
-        SPI.transfer(fifo_tx_addr_ptr);
-
-        for (int i = 0; i < size; i++)
-        {
-            SPI.transfer(buffer[i]);
-            fifo_tx_addr_ptr++;
-        }
-
-        SPI.endTransaction();
-
-        digitalWrite(_ss, HIGH);
-    }
-
-    void LoRaClass::readBuffer(uint8_t* buffer, size_t size)
-    {
-        waitOnBusy();
-
-        digitalWrite(_ss, LOW);
-
-        SPI.beginTransaction(_spiSettings);
-        SPI.transfer(OP_FIFO_READ);
-        SPI.transfer(fifo_rx_addr_ptr);
-        SPI.transfer(0x00);
-
-        for (int i = 0; i < size; i++)
-        {
-            buffer[i] = SPI.transfer(0x00);
-        }
-
-        SPI.endTransaction();
-
-        digitalWrite(_ss, HIGH);
-    }
-
-    void LoRaClass::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
-      // because there is no access to these registers on the sx1262, we have
-      // to set all these parameters at once or not at all.
-      uint8_t buf[8];
-
-      buf[0] = sf;
-      buf[1] = bw;
-      buf[2] = cr; 
-      // low data rate toggle
-      buf[3] = ldro;
-      // unused params in LoRa mode
-      buf[4] = 0x00; 
-      buf[5] = 0x00;
-      buf[6] = 0x00;
-      buf[7] = 0x00;
-
-      executeOpcode(OP_MODULATION_PARAMS, buf, 8);
-    }
-
-    void LoRaClass::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
-      // because there is no access to these registers on the sx1262, we have
-      // to set all these parameters at once or not at all.
-      uint8_t buf[9];
-
-      buf[0] = uint8_t((preamble & 0xFF00) >> 8);
-      buf[1] = uint8_t((preamble & 0x00FF));
-      buf[2] = headermode;
-      buf[3] = length;
-      buf[4] = crc;
-      // standard IQ setting (no inversion)
-      buf[5] = 0x00; 
-      // unused params
-      buf[6] = 0x00; 
-      buf[7] = 0x00; 
-      buf[8] = 0x00; 
-    
-      executeOpcode(OP_PACKET_PARAMS, buf, 9);
-    }
-#endif
-
-
-int LoRaClass::begin(long frequency)
-{
-  if (_reset != -1) {
-    pinMode(_reset, OUTPUT);
-
-    // perform reset
-    digitalWrite(_reset, LOW);
-    delay(10);
-    digitalWrite(_reset, HIGH);
-    delay(10);
-  }
-
-  if (_busy != -1) {
-      pinMode(_busy, INPUT);
-  }
-
-  if (!lora_preinit_done) {
-    if (!preInit()) {
-      return false;
-    }
-  }
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      // put in sleep mode
-      sleep();
-
-      // set frequency
-      setFrequency(frequency);
-
-      // set base addresses
-      writeRegister(REG_FIFO_TX_BASE_ADDR, 0);
-      writeRegister(REG_FIFO_RX_BASE_ADDR, 0);
-
-      // set LNA boost
-      writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03);
-
-      // set auto AGC
-      writeRegister(REG_MODEM_CONFIG_3, 0x04);
-
-      // set output power to 2 dBm
-      setTxPower(2);
-
-      // put in standby mode
-      idle();
-  #elif MODEM == SX1262
-      //#if HAS_TCXO
-          // turn TCXO on
-          enableTCXO();
-      //#endif
-      loraMode();
-      idle();
-      // cannot access registers in sleep mode on sx1262, set to idle instead
-      if (_rxen != -1) {
-          pinMode(_rxen, OUTPUT);
-          enableAntenna();
-      }
-      // calibrate RC64k, RC13M, PLL, ADC and image
-      uint8_t calibrate = 0x7F; 
-      executeOpcode(OP_CALIBRATE, &calibrate, 1);
-
-      setFrequency(frequency);
-
-      // set output power to 2 dBm
-      setTxPower(2);
-
-      // set LNA boost
-      writeRegister(REG_LNA, 0x96);
-
-      // set base addresses
-      uint8_t basebuf[2] = {0};
-      executeOpcode(OP_BUFFER_BASE_ADDR, basebuf, 2);
-
-      setModulationParams(_sf, _bw, _cr, _ldro);
-      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-  #endif
-
-  return 1;
-}
-
-void LoRaClass::end()
-{
-  // put in sleep mode
-  sleep();
-
-  // stop SPI
-  SPI.end();
-
-  lora_preinit_done = false;
-}
-
-int LoRaClass::beginPacket(int implicitHeader)
-{
-  // put in standby mode
-  idle();
-
-  if (implicitHeader) {
-    implicitHeaderMode();
-  } else {
-    explicitHeaderMode();
-  }
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      // reset FIFO address and paload length
-      writeRegister(REG_FIFO_ADDR_PTR, 0);
-      writeRegister(REG_PAYLOAD_LENGTH, 0);
-  #elif MODEM == SX1262
-      _payloadLength = 0;
-      fifo_tx_addr_ptr = 0;
-      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-  #endif
-
-  return 1;
-}
-
-int LoRaClass::endPacket()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-      // put in TX mode
-      writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);
-
-      // wait for TX done
-      while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {
-        yield();
-      }
-
-      // clear IRQ's
-      writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);
-  #elif MODEM == SX1262
-      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-      // put in single TX mode
-      uint8_t timeout[3] = {0};
-      executeOpcode(OP_TX, timeout, 3);
-
-      uint8_t buf[2];
-
-      buf[0] = 0x00;
-      buf[1] = 0x00;
-
-      executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
-
-      // wait for TX done
-      while ((buf[1] & IRQ_TX_DONE_MASK) == 0) {
-        buf[0] = 0x00;
-        buf[1] = 0x00;
-        executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
-        yield();
-      }
-
-      // clear IRQ's
-
-      uint8_t mask[2];
-      mask[0] = 0x00;
-      mask[1] = IRQ_TX_DONE_MASK;
-      executeOpcode(OP_CLEAR_IRQ_STATUS, mask, 2);
-  #endif
-  return 1;
-}
-
-int LoRaClass::parsePacket(int size)
-{
-  int packetLength = 0;
-  #if MODEM == SX1276 || MODEM == SX1278
-    int irqFlags = readRegister(REG_IRQ_FLAGS);
-  #elif MODEM == SX1262
-    uint8_t buf[2];
-    buf[0] = 0x00;
-    buf[1] = 0x00;
-
-    executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
-  #endif
-
-  if (size > 0) {
-    implicitHeaderMode();
-
-    #if MODEM == SX1276 || MODEM == SX1278
-        writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
-    #elif MODEM == SX1262
-        // tell radio payload length
-        _payloadLength = size;
-        setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-    #endif
-  } else {
-    explicitHeaderMode();
-  }
-
-  #if MODEM == SX1276 || MODEM == SX1278
-    // clear IRQ's
-    writeRegister(REG_IRQ_FLAGS, irqFlags);
-    if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
-  #elif MODEM == SX1262
-    uint8_t irqBufFlags[2];
-
-    irqBufFlags[0] = buf[0];
-    irqBufFlags[1] = buf[1];
-
-    executeOpcode(OP_CLEAR_IRQ_STATUS, irqBufFlags, 2);
-
-    if ((buf[0] & IRQ_RX_DONE_MASK) && (buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
-  #endif
-
-        // received a packet
-        _packetIndex = 0;
-
-    #if MODEM == SX1276 || MODEM == SX1278
-        // read packet length
-        if (_implicitHeaderMode) {
-          packetLength = readRegister(REG_PAYLOAD_LENGTH);
-        } else {
-          packetLength = readRegister(REG_RX_NB_BYTES);
-        }
-    #elif MODEM == SX1262
-        buf[0] = 0x00;
-        buf[1] = 0x00;
-        executeOpcodeRead(OP_RX_BUFFER_STATUS, buf, 2);
-        packetLength = buf[0];
-    #endif
-
-    #if MODEM == SX1276 || MODEM == SX1278
-        // set FIFO address to current RX address
-        writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
-    #endif
-
-    // put in standby mode
-    idle();
-  
-  #if MODEM == SX1276 || MODEM == SX1278
-      } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) {
-        // not currently in RX mode
-
-        // reset FIFO address
-        writeRegister(REG_FIFO_ADDR_PTR, 0);
-
-        // put in single RX mode
-        writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE);
-      }
-  #elif MODEM == SX1262
-  } else {
-    uint8_t status;
-    status = 0x00;
-    executeOpcodeRead(OP_STATUS, &status, 1); 
-    if ((status >> 4 & 0x7) != 0x5) {
-        // not currently in RX mode
-        
-        // put in single RX mode
-        uint8_t buf[3] = {0};
-        executeOpcode(OP_RX, buf, 3);
-    }
-  }
-  #endif
-  return packetLength;
-}
-
-uint8_t LoRaClass::modemStatus() {
-  #if MODEM == SX1276 || MODEM == SX1278 
-    return readRegister(REG_MODEM_STAT);
-  #elif MODEM == SX1262
-    // imitate the register status from the sx1276 / 78
-    uint8_t buf[2] = {0};
-
-
-    executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
-
-    uint8_t clearbuf[2] = {0};
-
-    uint8_t byte = 0x00;
-
-    if (buf[1] & IRQ_PREAMBLE_DET_MASK != 0) {
-        byte = byte | 0x01 | 0x04;
-        // clear register after reading
-        clearbuf[1] = IRQ_PREAMBLE_DET_MASK; 
-    }
-
-    if (buf[1] & IRQ_HEADER_DET_MASK != 0) {
-        byte = byte | 0x02 | 0x04;
-        // clear register after reading
-        clearbuf[1] = clearbuf[1] | IRQ_HEADER_DET_MASK; 
-    }
-
-    executeOpcode(OP_CLEAR_IRQ_STATUS, clearbuf, 2);
-
-    return byte; 
-  #endif
-}
-
-
-uint8_t LoRaClass::currentRssiRaw() {
-  #if MODEM == SX1276 || MODEM == SX1278
-    uint8_t rssi = readRegister(REG_RSSI_VALUE);
-    return rssi;
-  #elif MODEM == SX1262
-    uint8_t byte = 0;
-    executeOpcodeRead(OP_CURRENT_RSSI, &byte, 1);
-    return byte;
-  #endif
-}
-
-int ISR_VECT LoRaClass::currentRssi() {
-  #if MODEM == SX1276 || MODEM == SX1278
-    int rssi = (int)readRegister(REG_RSSI_VALUE) - RSSI_OFFSET;
-    if (_frequency < 820E6) rssi -= 7;
-    return rssi;
-  #elif MODEM == SX1262
-    uint8_t byte = 0;
-    executeOpcodeRead(OP_CURRENT_RSSI, &byte, 1);
-    int rssi = -(int(byte)) / 2;
-    return rssi;
-  #endif
-}
-
-uint8_t LoRaClass::packetRssiRaw() {
-  #if MODEM == SX1276 || MODEM == SX1278
-    uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE);
-    return pkt_rssi_value;
-  #elif MODEM == SX1262
-    uint8_t buf[3] = {0};
-    executeOpcodeRead(OP_PACKET_STATUS, buf, 3);
-    return buf[2];
-  #endif
-}
-
-int ISR_VECT LoRaClass::packetRssi() {
-  #if MODEM == SX1276 || MODEM == SX1278
-      int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE) - RSSI_OFFSET;
-      int pkt_snr = packetSnr();
-
-      if (_frequency < 820E6) pkt_rssi -= 7;
-
-      if (pkt_snr < 0) {
-        pkt_rssi += pkt_snr;
-      } else {
-        // Slope correction is (16/15)*pkt_rssi,
-        // this estimation looses one floating point
-        // operation, and should be precise enough.
-        pkt_rssi = (int)(1.066 * pkt_rssi);
-      }
-      return pkt_rssi;
-
-  #elif MODEM == SX1262
-    // may need more calculations here
-    uint8_t buf[3] = {0};
-    executeOpcodeRead(OP_PACKET_STATUS, buf, 3);
-    int pkt_rssi = -buf[0] / 2;
-    return pkt_rssi;
-  #endif
-}
-
-uint8_t ISR_VECT LoRaClass::packetSnrRaw() {
-  #if MODEM == SX1276 || MODEM == SX1278
-    return readRegister(REG_PKT_SNR_VALUE);
-  #elif MODEM == SX1262
-    uint8_t buf[3] = {0};
-    executeOpcodeRead(OP_PACKET_STATUS, buf, 3);
-    return buf[1];
-  #endif
-}
-
-float ISR_VECT LoRaClass::packetSnr() {
-  #if MODEM == SX1276 || MODEM == SX1278
-    return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25;
-  #elif MODEM == SX1262
-    uint8_t buf[3] = {0};
-    executeOpcodeRead(OP_PACKET_STATUS, buf, 3);
-    return float(buf[1]) * 0.25;
-  #endif
-}
-
-long LoRaClass::packetFrequencyError()
-{
-  int32_t freqError = 0;
-  #if MODEM == SX1276 || MODEM == SX1278
-    freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB) & B111);
-    freqError <<= 8L;
-    freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID));
-    freqError <<= 8L;
-    freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB));
-
-    if (readRegister(REG_FREQ_ERROR_MSB) & B1000) { // Sign bit is on
-       freqError -= 524288; // B1000'0000'0000'0000'0000
-    }
-
-    const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
-    const float fError = ((static_cast<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37
-
-    return static_cast<long>(fError);
-  #elif MODEM == SX1262
-    // todo: implement this, no idea how to check it on the sx1262
-    const float fError = 0.0;
-    return static_cast<long>(fError);
-  #endif
-}
-
-size_t LoRaClass::write(uint8_t byte)
-{
-  return write(&byte, sizeof(byte));
-}
-
-size_t LoRaClass::write(const uint8_t *buffer, size_t size)
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-      int currentLength = readRegister(REG_PAYLOAD_LENGTH);
-
-      // check size
-      if ((currentLength + size) > MAX_PKT_LENGTH) {
-        size = MAX_PKT_LENGTH - currentLength;
-      }
-  #elif MODEM == SX1262
-      if ((_payloadLength + size) > MAX_PKT_LENGTH) {
-          size = MAX_PKT_LENGTH - _payloadLength;
-      }
-  #endif
-
-  // write data
-  #if MODEM == SX1276 || MODEM == SX1278
-    for (size_t i = 0; i < size; i++) {
-        writeRegister(REG_FIFO, buffer[i]);
-    }
-
-    // update length
-    writeRegister(REG_PAYLOAD_LENGTH, currentLength + size);
-  #elif MODEM == SX1262
-    writeBuffer(buffer, size);
-    _payloadLength = _payloadLength + size;
-  #endif
-  return size;
-}
-
-int ISR_VECT LoRaClass::available()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-    return (readRegister(REG_RX_NB_BYTES) - _packetIndex);
-  #elif MODEM == SX1262
-    uint8_t buf[2] = {0};
-    executeOpcodeRead(OP_RX_BUFFER_STATUS, buf, 2);
-    return buf[0] - _packetIndex;
-  #endif
-}
-
-int ISR_VECT LoRaClass::read()
-{
-  if (!available()) {
-    return -1;
-  }
-
-  #if MODEM == SX1276 || MODEM == SX1278
-    _packetIndex++;
-    return readRegister(REG_FIFO);
-  #elif MODEM == SX1262
-    // if received new packet
-    if (_packetIndex == 0) {
-        uint8_t rxbuf[2] = {0};
-        executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2);
-        int size = rxbuf[0];
-        fifo_rx_addr_ptr = rxbuf[1];
-
-        readBuffer(packet, size);
-    }
-
-    uint8_t byte = packet[_packetIndex];
-    _packetIndex++;
-    return byte;
-  #endif
-
-}
-
-int LoRaClass::peek()
-{
-  if (!available()) {
-    return -1;
-  }
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      // store current FIFO address
-      int currentAddress = readRegister(REG_FIFO_ADDR_PTR);
-
-      // read
-      uint8_t b = readRegister(REG_FIFO);
-
-      // restore FIFO address
-      writeRegister(REG_FIFO_ADDR_PTR, currentAddress);
-
-  #elif MODEM == SX1262
-    // if received new packet
-    if (_packetIndex == 0) {
-        uint8_t rxbuf[2] = {0};
-        executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2);
-        int size = rxbuf[0];
-        fifo_rx_addr_ptr = rxbuf[1];
-
-        readBuffer(packet, size);
-    }
-
-    uint8_t b = packet[_packetIndex];
-  #endif
-  return b;
-}
-
-void LoRaClass::flush()
-{
-}
-
-void LoRaClass::onReceive(void(*callback)(int))
-{
-  _onReceive = callback;
-
-  if (callback) {
-    pinMode(_dio0, INPUT);
-
-    #if MODEM == SX1276 || MODEM == SX1278
-      writeRegister(REG_DIO_MAPPING_1, 0x00);
-    #elif MODEM == SX1262
-      // set preamble and header detection irqs, plus dio0 mask
-      uint8_t buf[8];
-
-      // set irq masks, enable all
-      buf[0] = 0xFF; 
-      buf[1] = 0xFF;
-
-      // set dio0 masks
-      buf[2] = 0x00;
-      buf[3] = IRQ_RX_DONE_MASK; 
-
-      // set dio1 masks
-      buf[4] = 0x00; 
-      buf[5] = 0x00;
-
-      // set dio2 masks
-      buf[6] = 0x00; 
-      buf[7] = 0x00;
-
-      executeOpcode(OP_SET_IRQ_FLAGS, buf, 8);
-    #endif
-#ifdef SPI_HAS_NOTUSINGINTERRUPT
-    SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
-#endif
-    attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
-  } else {
-    detachInterrupt(digitalPinToInterrupt(_dio0));
-#ifdef SPI_HAS_NOTUSINGINTERRUPT
-    SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
-#endif
-  }
-}
-
-void LoRaClass::receive(int size)
-{
-  if (size > 0) {
-    implicitHeaderMode();
-
-    #if MODEM == SX1276 || MODEM == SX1278
-        writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
-    #elif MODEM == SX1262
-        // tell radio payload length
-        _payloadLength = size;
-        setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-    #endif
-  } else {
-    explicitHeaderMode();
-  }
-
-  #if MODEM == SX1276 || MODEM == SX1278
-    writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
-  #elif MODEM == SX1262
-    uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
-    executeOpcode(OP_RX, mode, 3);
-  #endif
-}
-
-void LoRaClass::idle()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-      writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
-  #elif MODEM == SX1262
-      //#if HAS_TCXO
-          // STDBY_XOSC
-          uint8_t byte = 0x01;
-      //#else
-      //    // STDBY_RC
-      //    uint8_t byte = 0x00;
-      //#endif
-      executeOpcode(OP_STANDBY, &byte, 1); 
-  #endif
-}
-
-void LoRaClass::sleep()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-    writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
-  #elif MODEM == SX1262
-    if (_rxen != -1) {
-       disableAntenna();
-     }
-     uint8_t byte = 0x00;
-     executeOpcode(OP_SLEEP, &byte, 1);
-  #endif
-}
-
-void LoRaClass::enableTCXO() {
-  #if MODEM == SX1276 || MODEM == SX1278
-      uint8_t tcxo_reg = readRegister(REG_TCXO);
-      writeRegister(REG_TCXO, tcxo_reg | 0x10);
-  #elif MODEM == SX1262
-    // only tested for RAK4630, voltage may be different on other platforms
-    uint8_t buf[4] = {MODE_TCXO_3_3V, 0x00, 0x00, 0xFF};
-    executeOpcode(OP_DIO3_TCXO_CTRL, buf, 4);
-  #endif
-}
-
-void LoRaClass::disableTCXO() {
-  #if MODEM == SX1276 || MODEM == SX1278
-      uint8_t tcxo_reg = readRegister(REG_TCXO);
-      writeRegister(REG_TCXO, tcxo_reg & 0xEF);
-  #elif MODEM == SX1262
-      // currently cannot disable on SX1262?
-  #endif
-}
-
-void LoRaClass::setTxPower(int level, int outputPin) {
-  #if MODEM == SX1276 || MODEM == SX1278
-      if (PA_OUTPUT_RFO_PIN == outputPin) {
-        // RFO
-        if (level < 0) {
-          level = 0;
-        } else if (level > 14) {
-          level = 14;
-        }
-
-        writeRegister(REG_PA_DAC, 0x84);
-        writeRegister(REG_PA_CONFIG, 0x70 | level);
-
-      } else {
-        // PA BOOST
-        if (level < 2) {
-          level = 2;
-        } else if (level > 17) {
-          level = 17;
-        }
-
-        writeRegister(REG_PA_DAC, 0x84);
-        writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2));
-      }
-  #elif MODEM == SX1262
-    // currently no low power mode for SX1262 implemented, assuming PA boost
-    
-    // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2
-    // RegTxClampConfig = @address 0x08D8
-    writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1));
-
-    uint8_t pa_buf[4];
-
-    pa_buf[0] = 0x04;
-    pa_buf[1] = 0x07;
-    pa_buf[2] = 0x00;
-    pa_buf[3] = 0x01;
-
-    executeOpcode(OP_PA_CONFIG, pa_buf, 4); // set pa_config for high power
-
-    if (level > 22) {
-        level = 22;
-    }
-    else if (level < -9) {
-        level = -9;
-    }
-
-    writeRegister(REG_OCP, 0x38); // 160mA limit, overcurrent protection
-
-    uint8_t tx_buf[2];
-
-    tx_buf[0] = level;
-    tx_buf[1] = 0x02; // ramping time - 40 microseconds
-    
-    executeOpcode(OP_TX_PARAMS, tx_buf, 2);
-
-    _txp = level;
-  #endif
-}
-
-uint8_t LoRaClass::getTxPower() {
-  #if MODEM == SX1276 || MODEM == SX1278
-      byte txp = readRegister(REG_PA_CONFIG);
-      return txp;
-  #elif MODEM == SX1262
-      return _txp;
-  #endif
-}
-
-void LoRaClass::setFrequency(long frequency) {
-  _frequency = frequency;
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      uint32_t frf = ((uint64_t)frequency << 19) / 32000000;
-
-      writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16));
-      writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8));
-      writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0));
-
-      optimizeModemSensitivity();
-  #elif MODEM == SX1262
-      uint8_t buf[4];
-
-      uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP);
-
-      buf[0] = ((freq >> 24) & 0xFF);
-      buf[1] = ((freq >> 16) & 0xFF);
-      buf[2] = ((freq >> 8) & 0xFF);
-      buf[3] = (freq & 0xFF);
-      executeOpcode(OP_RF_FREQ, buf, 4);
-  #endif
-}
-
-uint32_t LoRaClass::getFrequency() {
-  #if MODEM == SX1276 || MODEM == SX1278
-      uint8_t msb = readRegister(REG_FRF_MSB);
-      uint8_t mid = readRegister(REG_FRF_MID);
-      uint8_t lsb = readRegister(REG_FRF_LSB);
-
-      uint32_t frf = ((uint32_t)msb << 16) | ((uint32_t)mid << 8) | (uint32_t)lsb;
-      uint64_t frm = (uint64_t)frf*32000000;
-      uint32_t frequency = (frm >> 19);
-
-  #elif MODEM == SX1262
-      // we can't read the frequency on the sx1262
-      uint32_t frequency = _frequency;
-  #endif
-
-  return frequency;
-}
-
-void LoRaClass::setSpreadingFactor(int sf)
-{
-  if (sf < 6) {
-    sf = 6;
-  } else if (sf > 12) {
-    sf = 12;
-  }
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      if (sf == 6) {
-        writeRegister(REG_DETECTION_OPTIMIZE, 0xc5);
-        writeRegister(REG_DETECTION_THRESHOLD, 0x0c);
-      } else {
-        writeRegister(REG_DETECTION_OPTIMIZE, 0xc3);
-        writeRegister(REG_DETECTION_THRESHOLD, 0x0a);
-      }
-
-      writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0));
-  #elif MODEM == SX1262
-      setModulationParams(sf, _bw, _cr, _ldro);
-  #endif
-  handleLowDataRate();
-}
-
-long LoRaClass::getSignalBandwidth()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-      byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4);
-      switch (bw) {
-        case 0: return 7.8E3;
-        case 1: return 10.4E3;
-        case 2: return 15.6E3;
-        case 3: return 20.8E3;
-        case 4: return 31.25E3;
-        case 5: return 41.7E3;
-        case 6: return 62.5E3;
-        case 7: return 125E3;
-        case 8: return 250E3;
-        case 9: return 500E3;
-      }
-  #elif MODEM == SX1262
-      int bw = _bw;
-      switch (bw) {
-          case 0x00: return 7.8E3;
-          case 0x01: return 15.6E3;
-          case 0x02: return 31.25E3;
-          case 0x03: return 62.5E3;
-          case 0x04: return 125E3;
-          case 0x05: return 250E3;
-          case 0x06: return 500E3;
-          case 0x08: return 10.4E3;
-          case 0x09: return 20.8E3;
-          case 0x0A: return 41.7E3;
-      }
-  #endif
-  
-  return 0;
-}
-
-void LoRaClass::handleLowDataRate(){
-  #if MODEM == SX1276 || MODEM == SX1278
-      int sf = (readRegister(REG_MODEM_CONFIG_2) >> 4);
-      if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) {
-        // set auto AGC and LowDataRateOptimize
-        writeRegister(REG_MODEM_CONFIG_3, (1<<3)|(1<<2));
-      } else {
-        // set auto AGC
-        writeRegister(REG_MODEM_CONFIG_3, (1<<2));
-      }
-  #elif MODEM == SX1262
-      _ldro = 1;
-      setModulationParams(_sf, _bw, _cr, _ldro);
-  #endif
-}
-
-void LoRaClass::optimizeModemSensitivity(){
-  #if MODEM == SX1276 || MODEM == SX1278
-      byte bw = (readRegister(REG_MODEM_CONFIG_1) >> 4);
-      uint32_t freq = getFrequency();
-
-      if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) {
-        writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x02);
-        writeRegister(REG_HIGH_BW_OPTIMIZE_2, 0x7f);
-      } else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) {
-        writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x02);
-        writeRegister(REG_HIGH_BW_OPTIMIZE_2, 0x64);
-      } else {
-        writeRegister(REG_HIGH_BW_OPTIMIZE_1, 0x03);
-      }
-  #elif MODEM == SX1262
-      // todo: check if there's anything the sx1262 can do here
-  #endif
-}
-
-void LoRaClass::setSignalBandwidth(long sbw)
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-      int bw;
-
-      if (sbw <= 7.8E3) {
-        bw = 0;
-      } else if (sbw <= 10.4E3) {
-        bw = 1;
-      } else if (sbw <= 15.6E3) {
-        bw = 2;
-      } else if (sbw <= 20.8E3) {
-        bw = 3;
-      } else if (sbw <= 31.25E3) {
-        bw = 4;
-      } else if (sbw <= 41.7E3) {
-        bw = 5;
-      } else if (sbw <= 62.5E3) {
-        bw = 6;
-      } else if (sbw <= 125E3) {
-        bw = 7;
-      } else if (sbw <= 250E3) {
-        bw = 8;
-      } else /*if (sbw <= 250E3)*/ {
-        bw = 9;
-      }
-
-      writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4));
-
-  #elif MODEM == SX1262
-      uint8_t bw;
-
-      if (sbw <= 7.8E3) {
-        bw = 0x00;
-      } else if (sbw <= 10.4E3) {
-        bw = 0x08;
-      } else if (sbw <= 15.6E3) {
-        bw = 0x01;
-      } else if (sbw <= 20.8E3) {
-        bw = 0x09;
-      } else if (sbw <= 31.25E3) {
-        bw = 0x02;
-      } else if (sbw <= 41.7E3) {
-        bw = 0x0A;
-      } else if (sbw <= 62.5E3) {
-        bw = 0x03;
-      } else if (sbw <= 125E3) {
-        bw = 0x04;
-      } else if (sbw <= 250E3) {
-        bw = 0x05;
-      } else /*if (sbw <= 250E3)*/ {
-        bw = 0x06;
-      }
-
-      setModulationParams(_sf, bw, _cr, _ldro);
-  #endif
-
-  handleLowDataRate();
-  optimizeModemSensitivity();
-}
-
-void LoRaClass::setCodingRate4(int denominator)
-{
-  if (denominator < 5) {
-    denominator = 5;
-  } else if (denominator > 8) {
-    denominator = 8;
-  }
-
-  int cr = denominator - 4;
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1));
-  #elif MODEM == SX1262
-      setModulationParams(_sf, _bw, cr, _ldro);
-  #endif
-}
-
-void LoRaClass::setPreambleLength(long length)
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-      writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8));
-      writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0));
-  #elif MODEM == SX1262
-      setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
-  #endif
-}
-
-void LoRaClass::setSyncWord(int sw)
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-    writeRegister(REG_SYNC_WORD, sw);
-  #elif MODEM == SX1262
-    writeRegister(REG_SYNC_WORD_MSB, sw & 0xFF00);
-    writeRegister(REG_SYNC_WORD_LSB, sw & 0x00FF);
-  #endif
-}
-
-void LoRaClass::enableCrc()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-      writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04);
-  #elif MODEM == SX1262
-      _crcMode = 1;
-      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-  #endif
-}
-
-void LoRaClass::disableCrc()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-    writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb);
-  #elif MODEM == SX1262
-    _crcMode = 0;
-    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-  #endif
-}
-
-byte LoRaClass::random()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-    return readRegister(REG_RSSI_WIDEBAND);
-  #elif MODEM == SX1262
-    return readRegister(REG_RANDOM_GEN);
-  #endif
-}
-
-void LoRaClass::setPins(int ss, int reset, int dio0, int rxen, int busy)
-{
-  _ss = ss;
-  _reset = reset;
-  _dio0 = dio0;
-  _rxen = rxen;
-  _busy = busy;
-}
-
-void LoRaClass::setSPIFrequency(uint32_t frequency)
-{
-  _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
-}
-
-void LoRaClass::dumpRegisters(Stream& out)
-{
-  for (int i = 0; i < 128; i++) {
-    out.print("0x");
-    out.print(i, HEX);
-    out.print(": 0x");
-    out.println(readRegister(i), HEX);
-  }
-}
-
-void LoRaClass::explicitHeaderMode()
-{
-  _implicitHeaderMode = 0;
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe);
-  #elif MODEM == SX1262
-      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-  #endif
-}
-
-void LoRaClass::implicitHeaderMode()
-{
-  _implicitHeaderMode = 1;
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01);
-  #elif MODEM == SX1262
-      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
-  #endif
-}
-
-
-void ISR_VECT LoRaClass::handleDio0Rise()
-{
-  #if MODEM == SX1276 || MODEM == SX1278
-    int irqFlags = readRegister(REG_IRQ_FLAGS);
-
-    // clear IRQ's
-    writeRegister(REG_IRQ_FLAGS, irqFlags);
-    if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
-  #elif MODEM == SX1262
-    uint8_t buf[2];
-
-    buf[0] = 0x00;
-    buf[1] = 0x00;
-
-    executeOpcodeRead(OP_GET_IRQ_STATUS, buf, 2);
-
-    executeOpcode(OP_CLEAR_IRQ_STATUS, buf, 2);
-
-
-    if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
-  #endif
-
-
-  // received a packet
-  _packetIndex = 0;
-
-    // read packet length
-  #if MODEM == SX1276 || MODEM == SX1278
-    int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES);
-
-    // set FIFO address to current RX address
-    writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));
-  #elif MODEM == SX1262
-    uint8_t rxbuf[2] = {0};
-    executeOpcodeRead(OP_RX_BUFFER_STATUS, rxbuf, 2);
-    int packetLength = rxbuf[0];
-  #endif
-
-  if (_onReceive) {
-    _onReceive(packetLength);
-  }
-
-  #if MODEM == SX1276 || MODEM == SX1278
-    // reset FIFO address
-    writeRegister(REG_FIFO_ADDR_PTR, 0);
-  #endif
-  }
-}
-
-void ISR_VECT LoRaClass::onDio0Rise()
-{
-  LoRa.handleDio0Rise();
-}
-
-LoRaClass LoRa;
diff --git a/LoRa.h b/LoRa.h
deleted file mode 100644 (file)
index 102010d..0000000
--- a/LoRa.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) Sandeep Mistry. All rights reserved.
-// Licensed under the MIT license.
-
-// Modifications and additions copyright 2023 by Mark Qvist
-// Obviously still under the MIT license.
-
-#ifndef LORA_H
-#define LORA_H
-
-#include <Arduino.h>
-#include <SPI.h>
-#include "Modem.h"
-
-#define LORA_DEFAULT_SS_PIN    10
-#define LORA_DEFAULT_RESET_PIN 9
-#define LORA_DEFAULT_DIO0_PIN  2
-#define LORA_DEFAULT_RXEN_PIN  -1
-#define LORA_DEFAULT_BUSY_PIN  -1
-
-#define PA_OUTPUT_RFO_PIN      0
-#define PA_OUTPUT_PA_BOOST_PIN 1
-
-#define RSSI_OFFSET 157
-
-class LoRaClass : public Stream {
-public:
-  LoRaClass();
-
-  int begin(long frequency);
-  void end();
-
-  int beginPacket(int implicitHeader = false);
-  int endPacket();
-
-  int parsePacket(int size = 0);
-  int packetRssi();
-  int currentRssi();
-  uint8_t packetRssiRaw();
-  uint8_t currentRssiRaw();
-  uint8_t packetSnrRaw();
-  float packetSnr();
-  long packetFrequencyError();
-
-  // from Print
-  virtual size_t write(uint8_t byte);
-  virtual size_t write(const uint8_t *buffer, size_t size);
-
-  // from Stream
-  virtual int available();
-  virtual int read();
-  virtual int peek();
-  virtual void flush();
-
-  void onReceive(void(*callback)(int));
-
-  void receive(int size = 0);
-  void idle();
-  void sleep();
-
-  bool preInit();
-  uint8_t getTxPower();
-  void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
-  uint32_t getFrequency();
-  void setFrequency(long frequency);
-  void setSpreadingFactor(int sf);
-  long getSignalBandwidth();
-  void setSignalBandwidth(long sbw);
-  void setCodingRate4(int denominator);
-  void setPreambleLength(long length);
-  void setSyncWord(int sw);
-  uint8_t modemStatus();
-  void enableCrc();
-  void disableCrc();
-  void enableTCXO();
-  void disableTCXO();
-
-  #if MODEM == SX1262
-      void enableAntenna();
-      void disableAntenna();
-      void loraMode();
-      void waitOnBusy();
-      void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
-      void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
-      void writeBuffer(const uint8_t* buffer, size_t size);
-      void readBuffer(uint8_t* buffer, size_t size);
-      void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro);
-      void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc);
-  #endif
-
-  // deprecated
-  void crc() { enableCrc(); }
-  void noCrc() { disableCrc(); }
-
-  byte random();
-
-  void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int busy = LORA_DEFAULT_BUSY_PIN);
-  void setSPIFrequency(uint32_t frequency);
-
-  void dumpRegisters(Stream& out);
-
-private:
-  void explicitHeaderMode();
-  void implicitHeaderMode();
-
-  void handleDio0Rise();
-
-  #if MODEM == SX1276 || MODEM == SX1278
-      uint8_t readRegister(uint8_t address);
-      void writeRegister(uint8_t address, uint8_t value);
-      uint8_t singleTransfer(uint8_t address, uint8_t value);
-  #elif MODEM == SX1262
-      uint8_t readRegister(uint16_t address);
-      void writeRegister(uint16_t address, uint8_t value);
-      uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
-  #endif
-
-  static void onDio0Rise();
-
-  void handleLowDataRate();
-  void optimizeModemSensitivity();
-
-private:
-  SPISettings _spiSettings;
-  int _ss;
-  int _reset;
-  int _dio0;
-  int _rxen;
-  int _busy;
-  long _frequency;
-  int _txp;
-  uint8_t _sf;
-  uint8_t _bw;
-  uint8_t _cr;
-  uint8_t _ldro;
-  int _packetIndex;
-  int _preambleLength;
-  int _implicitHeaderMode;
-  int _payloadLength;
-  int _crcMode;
-  void (*_onReceive)(int);
-};
-
-extern LoRaClass LoRa;
-
-#endif
diff --git a/Modem.h b/Modem.h
index bb23a5750ac3581c549e4d2c1c9ba9e3c1966f14..027e3140494fc61bbb4ec105d27129dae13c6f0e 100644 (file)
--- a/Modem.h
+++ b/Modem.h
@@ -1,3 +1,4 @@
 #define SX1276 0x01
 #define SX1278 0x02
 #define SX1262 0x03
+#define SX1280 0x04
index 54e06e2d7c4480f787be25b3abb0968e97972e54..d18aa986d63f357638f4a4f2c7e6aaa90a3e767a 100644 (file)
@@ -93,16 +93,22 @@ void setup() {
 
   // Set chip select, reset and interrupt
   // pins for the LoRa module
-  LoRa.setPins(pin_cs, pin_reset, pin_dio, pin_rxen, pin_busy);
+  #if MODEM == SX1276 || MODEM == SX1278
+  LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy);
+  #elif MODEM == SX1262
+  LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy, pin_rxen);
+  #elif MODEM == SX1280
+  LoRa->setPins(pin_cs, pin_reset, pin_dio, pin_busy, pin_rxen, pin_txen);
+  #endif
   
   #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
     init_channel_stats();
 
     // Check installed transceiver chip and
     // probe boot parameters.
-    if (LoRa.preInit()) {
+    if (LoRa->preInit()) {
       modem_installed = true;
-      uint32_t lfr = LoRa.getFrequency();
+      uint32_t lfr = LoRa->getFrequency();
       if (lfr == 0) {
         // Normal boot
       } else if (lfr == M_FRQ_R) {
@@ -115,7 +121,7 @@ void setup() {
       } else {
         // Unknown boot
       }
-      LoRa.setFrequency(M_FRQ_S);
+      LoRa->setFrequency(M_FRQ_S);
     } else {
       modem_installed = false;
     }
@@ -158,14 +164,14 @@ void setup() {
   // Validate board health, EEPROM and config
   validate_status();
 
-  if (op_mode != MODE_TNC) LoRa.setFrequency(0);
+  if (op_mode != MODE_TNC) LoRa->setFrequency(0);
 }
 
 void lora_receive() {
   if (!implicit) {
-    LoRa.receive();
+    LoRa->receive();
   } else {
-    LoRa.receive(implicit_l);
+    LoRa->receive(implicit_l);
   }
 }
 
@@ -187,7 +193,7 @@ inline void kiss_write_packet() {
 
 inline void getPacketData(uint16_t len) {
   while (len-- && read_len < MTU) {
-    pbuf[read_len++] = LoRa.read();
+    pbuf[read_len++] = LoRa->read();
   }
 }
 
@@ -198,7 +204,7 @@ void ISR_VECT receive_callback(int packet_size) {
     // by combining two raw LoRa packets.
     // We read the 1-byte header and extract
     // packet sequence number and split flags
-    uint8_t header   = LoRa.read(); packet_size--;
+    uint8_t header   = LoRa->read(); packet_size--;
     uint8_t sequence = packetSequence(header);
     bool    ready    = false;
 
@@ -210,8 +216,8 @@ void ISR_VECT receive_callback(int packet_size) {
       seq = sequence;
 
       #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        last_rssi = LoRa.packetRssi();
-        last_snr_raw = LoRa.packetSnrRaw();
+        last_rssi = LoRa->packetRssi();
+        last_snr_raw = LoRa->packetSnrRaw();
       #endif
 
       getPacketData(packet_size);
@@ -223,8 +229,8 @@ void ISR_VECT receive_callback(int packet_size) {
       
 
       #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        last_rssi = (last_rssi+LoRa.packetRssi())/2;
-        last_snr_raw = (last_snr_raw+LoRa.packetSnrRaw())/2;
+        last_rssi = (last_rssi+LoRa->packetRssi())/2;
+        last_snr_raw = (last_snr_raw+LoRa->packetSnrRaw())/2;
       #endif
 
       getPacketData(packet_size);
@@ -241,8 +247,8 @@ void ISR_VECT receive_callback(int packet_size) {
       seq = sequence;
 
       #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        last_rssi = LoRa.packetRssi();
-        last_snr_raw = LoRa.packetSnrRaw();
+        last_rssi = LoRa->packetRssi();
+        last_snr_raw = LoRa->packetSnrRaw();
       #endif
 
       getPacketData(packet_size);
@@ -260,8 +266,8 @@ void ISR_VECT receive_callback(int packet_size) {
       }
 
       #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        last_rssi = LoRa.packetRssi();
-        last_snr_raw = LoRa.packetSnrRaw();
+        last_rssi = LoRa->packetRssi();
+        last_snr_raw = LoRa->packetSnrRaw();
       #endif
 
       getPacketData(packet_size);
@@ -287,8 +293,8 @@ void ISR_VECT receive_callback(int packet_size) {
     read_len = 0;
 
     #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-      last_rssi = LoRa.packetRssi();
-      last_snr_raw = LoRa.packetSnrRaw();
+      last_rssi = LoRa->packetRssi();
+      last_snr_raw = LoRa->packetSnrRaw();
       getPacketData(packet_size);
 
       // We first signal the RSSI of the
@@ -310,7 +316,7 @@ bool startRadio() {
   update_radio_lock();
   if (!radio_online && !console_active) {
     if (!radio_locked && hw_ready) {
-      if (!LoRa.begin(lora_freq)) {
+      if (!LoRa->begin(lora_freq)) {
         // The radio could not be started.
         // Indicate this failure over both the
         // serial port and with the onboard LEDs
@@ -329,9 +335,9 @@ bool startRadio() {
         setCodingRate();
         getFrequency();
 
-        LoRa.enableCrc();
+        LoRa->enableCrc();
 
-        LoRa.onReceive(receive_callback);
+        LoRa->onReceive(receive_callback);
 
         lora_receive();
 
@@ -360,7 +366,7 @@ bool startRadio() {
 }
 
 void stopRadio() {
-  LoRa.end();
+  LoRa->end();
   radio_online = false;
 }
 
@@ -469,23 +475,23 @@ void transmit(uint16_t size) {
         header = header | FLAG_SPLIT;
       }
 
-      LoRa.beginPacket();
-      LoRa.write(header); written++;
+      LoRa->beginPacket();
+      LoRa->write(header); written++;
 
       for (uint16_t i=0; i < size; i++) {
-        LoRa.write(tbuf[i]);
+        LoRa->write(tbuf[i]);
 
         written++;
 
         if (written == 255) {
-          LoRa.endPacket(); add_airtime(written);
-          LoRa.beginPacket();
-          LoRa.write(header);
+          LoRa->endPacket(); add_airtime(written);
+          LoRa->beginPacket();
+          LoRa->write(header);
           written = 1;
         }
       }
 
-      LoRa.endPacket(); add_airtime(written);
+      LoRa->endPacket(); add_airtime(written);
     } else {
       // In promiscuous mode, we only send out
       // plain raw LoRa packets with a maximum
@@ -501,17 +507,17 @@ void transmit(uint16_t size) {
       // If implicit header mode has been set,
       // set packet length to payload data length
       if (!implicit) {
-        LoRa.beginPacket();
+        LoRa->beginPacket();
       } else {
-        LoRa.beginPacket(size);
+        LoRa->beginPacket(size);
       }
 
       for (uint16_t i=0; i < size; i++) {
-        LoRa.write(tbuf[i]);
+        LoRa->write(tbuf[i]);
 
         written++;
       }
-      LoRa.endPacket(); add_airtime(written);
+      LoRa->endPacket(); add_airtime(written);
     }
   } else {
     kiss_indicate_error(ERROR_TXFAILED);
@@ -631,7 +637,7 @@ void serialCallback(uint8_t sbyte) {
         kiss_indicate_spreadingfactor();
       } else {
         int sf = sbyte;
-        if (sf < 6) sf = 6;
+        if (sf < 5) sf = 5;
         if (sf > 12) sf = 12;
 
         lora_sf = sf;
@@ -938,8 +944,8 @@ void updateModemStatus() {
     portENTER_CRITICAL();
   #endif
 
-  uint8_t status = LoRa.modemStatus();
-  current_rssi = LoRa.currentRssi();
+  uint8_t status = LoRa->modemStatus();
+  current_rssi = LoRa->currentRssi();
   last_status_update = millis();
 
   #if MCU_VARIANT == MCU_ESP32
@@ -1158,8 +1164,8 @@ void loop() {
     #if MCU_VARIANT == MCU_ESP32
       if (packet_ready) {
         portENTER_CRITICAL(&update_lock);
-        last_rssi = LoRa.packetRssi();
-        last_snr_raw = LoRa.packetSnrRaw();
+        last_rssi = LoRa->packetRssi();
+        last_snr_raw = LoRa->packetSnrRaw();
         portEXIT_CRITICAL(&update_lock);
         kiss_indicate_stat_rssi();
         kiss_indicate_stat_snr();
@@ -1173,8 +1179,8 @@ void loop() {
     #elif MCU_VARIANT == MCU_NRF52
       if (packet_ready) {
         portENTER_CRITICAL();
-        last_rssi = LoRa.packetRssi();
-        last_snr_raw = LoRa.packetSnrRaw();
+        last_rssi = LoRa->packetRssi();
+        last_snr_raw = LoRa->packetSnrRaw();
         portEXIT_CRITICAL();
         kiss_indicate_stat_rssi();
         kiss_indicate_stat_snr();
@@ -1199,7 +1205,7 @@ void loop() {
               }
 
               if (!dcd) {
-                uint8_t csma_r = (uint8_t)random(256);
+                uint8_t csma_r = (uint8_t)random(20); // updated to increase bitrate: todo check
                 if (csma_p >= csma_r) {
                   flushQueue();
                 } else {
index a360f47384817ef9a537bfb9cac888e75bd13625..b93e70e4d498b9abcb3ec5e0af9c74fc3e467d6b 100644 (file)
     int written_bytes = 0;
 #endif
 #include <stddef.h>
-#include "LoRa.h"
+
+#if MODEM == SX1262
+#include "sx126x.h"
+sx126x *LoRa = &sx126x_modem;
+#elif MODEM == SX1276 || MODEM == SX1278
+#include "sx127x.h"
+sx127x *LoRa = &sx127x_modem;
+#elif MODEM == SX1280
+#include "sx128x.h"
+sx128x *LoRa = &sx128x_modem;
+#endif
+
 #include "ROM.h"
 #include "Framing.h"
 #include "MD5.h"
@@ -657,11 +668,7 @@ void kiss_indicate_stat_tx() {
 }
 
 void kiss_indicate_stat_rssi() {
-    #if MODEM == SX1276 || MODEM == SX1278
-        uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset);
-    #elif MODEM == SX1262
-        int8_t packet_rssi_val = (int8_t)(last_rssi+rssi_offset);
-    #endif
+    uint8_t packet_rssi_val = (uint8_t)(last_rssi+rssi_offset);
        serial_write(FEND);
        serial_write(CMD_STAT_RSSI);
        escaped_serial_write(packet_rssi_val);
@@ -975,7 +982,7 @@ inline uint8_t packetSequence(uint8_t header) {
 }
 
 void setPreamble() {
-       if (radio_online) LoRa.setPreambleLength(lora_preamble_symbols);
+       if (radio_online) LoRa->setPreambleLength(lora_preamble_symbols);
        kiss_indicate_phy_stats();
 }
 
@@ -1002,12 +1009,12 @@ void updateBitrate() {
 }
 
 void setSpreadingFactor() {
-       if (radio_online) LoRa.setSpreadingFactor(lora_sf);
+       if (radio_online) LoRa->setSpreadingFactor(lora_sf);
        updateBitrate();
 }
 
 void setCodingRate() {
-       if (radio_online) LoRa.setCodingRate4(lora_cr);
+       if (radio_online) LoRa->setCodingRate4(lora_cr);
        updateBitrate();
 }
 
@@ -1021,66 +1028,66 @@ void set_implicit_length(uint8_t len) {
 }
 
 int getTxPower() {
-       uint8_t txp = LoRa.getTxPower();
+       uint8_t txp = LoRa->getTxPower();
        return (int)txp;
 }
 
 void setTXPower() {
        if (radio_online) {
-               if (model == MODEL_A2) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_A3) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
-               if (model == MODEL_A4) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
-               if (model == MODEL_A7) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_A8) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_A9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
-               if (model == MODEL_B3) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_B4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_B8) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_B9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
-               if (model == MODEL_C4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_C9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
-               if (model == MODEL_E4) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_E9) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
-               if (model == MODEL_FE) LoRa.setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_FF) LoRa.setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
+               if (model == MODEL_A2) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_A3) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
+               if (model == MODEL_A4) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
+               if (model == MODEL_A7) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_A8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_A9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+
+               if (model == MODEL_B3) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_B4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_B8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_B9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+
+               if (model == MODEL_C4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_C9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+
+               if (model == MODEL_E4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_E9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+
+               if (model == MODEL_FE) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+               if (model == MODEL_FF) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
        }
 }
 
 
 void getBandwidth() {
        if (radio_online) {
-                       lora_bw = LoRa.getSignalBandwidth();
+                       lora_bw = LoRa->getSignalBandwidth();
        }
        updateBitrate();
 }
 
 void setBandwidth() {
        if (radio_online) {
-               LoRa.setSignalBandwidth(lora_bw);
+               LoRa->setSignalBandwidth(lora_bw);
                getBandwidth();
        }
 }
 
 void getFrequency() {
        if (radio_online) {
-               lora_freq = LoRa.getFrequency();
+               lora_freq = LoRa->getFrequency();
        }
 }
 
 void setFrequency() {
        if (radio_online) {
-               LoRa.setFrequency(lora_freq);
+               LoRa->setFrequency(lora_freq);
                getFrequency();
        }
 }
 
 uint8_t getRandom() {
        if (radio_online) {
-               return LoRa.random();
+               return LoRa->random();
        } else {
                return 0x00;
        }
diff --git a/sx126x.cpp b/sx126x.cpp
new file mode 100644 (file)
index 0000000..f6ac76e
--- /dev/null
@@ -0,0 +1,911 @@
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2023 by Mark Qvist
+// Obviously still under the MIT license.
+
+#include "sx126x.h"
+
+#define MCU_1284P 0x91
+#define MCU_2560  0x92
+#define MCU_ESP32 0x81
+#define MCU_NRF52 0x71
+#if defined(__AVR_ATmega1284P__)
+  #define PLATFORM PLATFORM_AVR
+  #define MCU_VARIANT MCU_1284P
+#elif defined(__AVR_ATmega2560__)
+  #define PLATFORM PLATFORM_AVR
+  #define MCU_VARIANT MCU_2560
+#elif defined(ESP32)
+  #define PLATFORM PLATFORM_ESP32
+  #define MCU_VARIANT MCU_ESP32
+#elif defined(NRF52840_XXAA)
+  #define PLATFORM PLATFORM_NRF52
+  #define MCU_VARIANT MCU_NRF52
+#endif
+
+#ifndef MCU_VARIANT
+  #error No MCU variant defined, cannot compile
+#endif
+
+#if MCU_VARIANT == MCU_ESP32
+  #include "soc/rtc_wdt.h"
+  #define ISR_VECT IRAM_ATTR
+#else
+  #define ISR_VECT
+#endif
+
+#define OP_RF_FREQ_6X               0x86
+#define OP_SLEEP_6X                 0x84
+#define OP_STANDBY_6X               0x80
+#define OP_TX_6X                    0x83
+#define OP_RX_6X                    0x82
+#define OP_PA_CONFIG_6X             0x95
+#define OP_SET_IRQ_FLAGS_6X         0x08 // also provides info such as
+                                      // preamble detection, etc for
+                                      // knowing when it's safe to switch
+                                      // antenna modes
+#define OP_CLEAR_IRQ_STATUS_6X      0x02
+#define OP_GET_IRQ_STATUS_6X        0x12
+#define OP_RX_BUFFER_STATUS_6X      0x13
+#define OP_PACKET_STATUS_6X         0x14 // get snr & rssi of last packet
+#define OP_CURRENT_RSSI_6X          0x15
+#define OP_MODULATION_PARAMS_6X     0x8B // bw, sf, cr, etc.
+#define OP_PACKET_PARAMS_6X         0x8C // crc, preamble, payload length, etc.
+#define OP_STATUS_6X                0xC0
+#define OP_TX_PARAMS_6X             0x8E // set dbm, etc
+#define OP_PACKET_TYPE_6X           0x8A
+#define OP_BUFFER_BASE_ADDR_6X      0x8F
+#define OP_READ_REGISTER_6X         0x1D
+#define OP_WRITE_REGISTER_6X        0x0D
+#define OP_DIO3_TCXO_CTRL_6X        0x97
+#define OP_DIO2_RF_CTRL_6X          0x9D
+#define OP_CALIBRATE_6X             0x89
+#define IRQ_TX_DONE_MASK_6X         0x01
+#define IRQ_RX_DONE_MASK_6X         0x02
+#define IRQ_HEADER_DET_MASK_6X      0x10
+#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40
+
+#define MODE_LONG_RANGE_MODE_6X     0x01
+
+#define OP_FIFO_WRITE_6X            0x0E
+#define OP_FIFO_READ_6X             0x1E
+#define REG_OCP_6X                0x08E7
+#define REG_LNA_6X                0x08AC // no agc in sx1262
+#define REG_SYNC_WORD_MSB_6X      0x0740
+#define REG_SYNC_WORD_LSB_6X      0x0741
+#define REG_PAYLOAD_LENGTH_6X     0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
+#define REG_RANDOM_GEN_6X         0x0819
+
+#define MODE_TCXO_3_3V_6X           0x07
+
+#define IRQ_PREAMBLE_DET_MASK_6X    0x04
+#define XTAL_FREQ_6X (double)32000000
+#define FREQ_DIV_6X (double)pow(2.0, 25.0)
+#define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X)
+extern SPIClass spiModem;
+#define SPI spiModem
+
+extern SPIClass SPI;
+
+#define MAX_PKT_LENGTH           255
+
+sx126x::sx126x() :
+  _spiSettings(8E6, MSBFIRST, SPI_MODE0),
+  _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),
+  _frequency(0),
+  _txp(0),
+  _sf(0x07),
+  _bw(0x04),
+  _cr(0x01),
+  _ldro(0x00),
+  _packetIndex(0),
+  _preambleLength(18),
+  _implicitHeaderMode(0),
+  _payloadLength(255),
+  _crcMode(1),
+  _fifo_tx_addr_ptr(0),
+  _fifo_rx_addr_ptr(0),
+  _packet({0}),
+  _preinit_done(false),
+  _onReceive(NULL)
+{
+  // overide Stream timeout value
+  setTimeout(0);
+}
+
+bool sx126x::preInit() {
+  // setup pins
+  pinMode(_ss, OUTPUT);
+  // set SS high
+  digitalWrite(_ss, HIGH);
+  
+  SPI.begin();
+
+  // check version (retry for up to 2 seconds)
+  long start = millis();
+  uint8_t syncmsb;
+  uint8_t synclsb;
+  while (((millis() - start) < 2000) && (millis() >= start)) {
+      syncmsb = readRegister(REG_SYNC_WORD_MSB_6X);
+      synclsb = readRegister(REG_SYNC_WORD_LSB_6X);
+      if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) {
+          break;
+      }
+      delay(100);
+  }
+  if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) {
+      return false;
+  }
+
+  _preinit_done = true;
+  return true;
+}
+
+uint8_t ISR_VECT sx126x::readRegister(uint16_t address)
+{
+  return singleTransfer(OP_READ_REGISTER_6X, address, 0x00);
+}
+
+void sx126x::writeRegister(uint16_t address, uint8_t value)
+{
+    singleTransfer(OP_WRITE_REGISTER_6X, address, value);
+}
+
+uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value)
+{
+    waitOnBusy();
+
+    uint8_t response;
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(opcode);
+    SPI.transfer((address & 0xFF00) >> 8);
+    SPI.transfer(address & 0x00FF);
+    if (opcode == OP_READ_REGISTER_6X) {
+        SPI.transfer(0x00);
+    }
+    response = SPI.transfer(value);
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+
+    return response;
+}
+
+void sx126x::rxAntEnable()
+{
+        uint8_t byte = 0x01;
+        // enable dio2 rf switch
+        executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1); 
+        digitalWrite(_rxen, HIGH);
+}
+
+void sx126x::loraMode() {
+    // enable lora mode on the SX1262 chip
+    uint8_t mode = MODE_LONG_RANGE_MODE_6X;
+    executeOpcode(OP_PACKET_TYPE_6X, &mode, 1);
+}
+
+void sx126x::waitOnBusy() {
+    if (_busy != -1) {
+        while (digitalRead(_busy) == HIGH)
+        {
+            // do nothing
+        }
+    }
+}
+
+void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(opcode);
+
+    for (int i = 0; i < size; i++)
+    {
+        SPI.transfer(buffer[i]);
+    }
+
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(opcode);
+    SPI.transfer(0x00);
+
+    for (int i = 0; i < size; i++)
+    {
+        buffer[i] = SPI.transfer(0x00);
+    }
+
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx126x::writeBuffer(const uint8_t* buffer, size_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(OP_FIFO_WRITE_6X);
+    SPI.transfer(_fifo_tx_addr_ptr);
+
+    for (int i = 0; i < size; i++)
+    {
+        SPI.transfer(buffer[i]);
+        _fifo_tx_addr_ptr++;
+    }
+
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx126x::readBuffer(uint8_t* buffer, size_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(OP_FIFO_READ_6X);
+    SPI.transfer(_fifo_rx_addr_ptr);
+    SPI.transfer(0x00);
+
+    for (int i = 0; i < size; i++)
+    {
+        buffer[i] = SPI.transfer(0x00);
+    }
+
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
+  // because there is no access to these registers on the sx1262, we have
+  // to set all these parameters at once or not at all.
+  uint8_t buf[8];
+
+  buf[0] = sf;
+  buf[1] = bw;
+  buf[2] = cr; 
+  // low data rate toggle
+  buf[3] = ldro;
+  // unused params in LoRa mode
+  buf[4] = 0x00; 
+  buf[5] = 0x00;
+  buf[6] = 0x00;
+  buf[7] = 0x00;
+
+  executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8);
+}
+
+void sx126x::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
+  // because there is no access to these registers on the sx1262, we have
+  // to set all these parameters at once or not at all.
+  uint8_t buf[9];
+
+  buf[0] = uint8_t((preamble & 0xFF00) >> 8);
+  buf[1] = uint8_t((preamble & 0x00FF));
+  buf[2] = headermode;
+  buf[3] = length;
+  buf[4] = crc;
+  // standard IQ setting (no inversion)
+  buf[5] = 0x00; 
+  // unused params
+  buf[6] = 0x00; 
+  buf[7] = 0x00; 
+  buf[8] = 0x00; 
+
+  executeOpcode(OP_PACKET_PARAMS_6X, buf, 9);
+}
+
+
+int sx126x::begin(long frequency)
+{
+  if (_reset != -1) {
+    pinMode(_reset, OUTPUT);
+
+    // perform reset
+    digitalWrite(_reset, LOW);
+    delay(10);
+    digitalWrite(_reset, HIGH);
+    delay(10);
+  }
+
+  if (_busy != -1) {
+      pinMode(_busy, INPUT);
+  }
+
+  if (!_preinit_done) {
+    if (!preInit()) {
+      return false;
+    }
+  }
+
+      //#if HAS_TCXO
+          // turn TCXO on
+          enableTCXO();
+      //#endif
+      loraMode();
+      idle();
+      // cannot access registers in sleep mode on sx1262, set to idle instead
+      if (_rxen != -1) {
+          pinMode(_rxen, OUTPUT);
+          rxAntEnable();
+      }
+      // calibrate RC64k, RC13M, PLL, ADC and image
+      uint8_t calibrate = 0x7F; 
+      executeOpcode(OP_CALIBRATE_6X, &calibrate, 1);
+
+      setFrequency(frequency);
+
+      // set output power to 2 dBm
+      setTxPower(2);
+
+      // set LNA boost
+      writeRegister(REG_LNA_6X, 0x96);
+
+      // set base addresses
+      uint8_t basebuf[2] = {0};
+      executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2);
+
+      setModulationParams(_sf, _bw, _cr, _ldro);
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+  return 1;
+}
+
+void sx126x::end()
+{
+  // put in sleep mode
+  sleep();
+
+  // stop SPI
+  SPI.end();
+
+  _preinit_done = false;
+}
+
+int sx126x::beginPacket(int implicitHeader)
+{
+  // put in standby mode
+  idle();
+
+  if (implicitHeader) {
+    implicitHeaderMode();
+  } else {
+    explicitHeaderMode();
+  }
+
+      _payloadLength = 0;
+      _fifo_tx_addr_ptr = 0;
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+  return 1;
+}
+
+int sx126x::endPacket()
+{
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+      // put in single TX mode
+      uint8_t timeout[3] = {0};
+      executeOpcode(OP_TX_6X, timeout, 3);
+
+      uint8_t buf[2];
+
+      buf[0] = 0x00;
+      buf[1] = 0x00;
+
+      executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
+
+      // wait for TX done
+      while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) {
+        buf[0] = 0x00;
+        buf[1] = 0x00;
+        executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
+        yield();
+      }
+
+      // clear IRQ's
+
+      uint8_t mask[2];
+      mask[0] = 0x00;
+      mask[1] = IRQ_TX_DONE_MASK_6X;
+      executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2);
+  return 1;
+}
+
+uint8_t sx126x::modemStatus() {
+    // imitate the register status from the sx1276 / 78
+    uint8_t buf[2] = {0};
+
+
+    executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
+
+    uint8_t clearbuf[2] = {0};
+
+    uint8_t byte = 0x00;
+
+    if (buf[1] & IRQ_PREAMBLE_DET_MASK_6X != 0) {
+        byte = byte | 0x01 | 0x04;
+        // clear register after reading
+        clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X; 
+    }
+
+    if (buf[1] & IRQ_HEADER_DET_MASK_6X != 0) {
+        byte = byte | 0x02 | 0x04;
+        // clear register after reading
+        clearbuf[1] = clearbuf[1] | IRQ_HEADER_DET_MASK_6X; 
+    }
+
+    executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2);
+
+    return byte; 
+}
+
+
+uint8_t sx126x::currentRssiRaw() {
+    uint8_t byte = 0;
+    executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
+    return byte;
+}
+
+int ISR_VECT sx126x::currentRssi() {
+    uint8_t byte = 0;
+    executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
+    int rssi = -(int(byte)) / 2;
+    return rssi - RSSI_OFFSET;
+}
+
+uint8_t sx126x::packetRssiRaw() {
+    uint8_t buf[3] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
+    return buf[2];
+}
+
+int ISR_VECT sx126x::packetRssi() {
+    // may need more calculations here
+    uint8_t buf[3] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
+    int pkt_rssi = -buf[0] / 2;
+    return pkt_rssi - RSSI_OFFSET;
+}
+
+uint8_t ISR_VECT sx126x::packetSnrRaw() {
+    uint8_t buf[3] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
+    return buf[1];
+}
+
+float ISR_VECT sx126x::packetSnr() {
+    uint8_t buf[3] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
+    return float(buf[1]) * 0.25;
+}
+
+long sx126x::packetFrequencyError()
+{
+    // todo: implement this, no idea how to check it on the sx1262
+    const float fError = 0.0;
+    return static_cast<long>(fError);
+}
+
+size_t sx126x::write(uint8_t byte)
+{
+  return write(&byte, sizeof(byte));
+}
+
+size_t sx126x::write(const uint8_t *buffer, size_t size)
+{
+    if ((_payloadLength + size) > MAX_PKT_LENGTH) {
+        size = MAX_PKT_LENGTH - _payloadLength;
+    }
+
+    // write data
+    writeBuffer(buffer, size);
+    _payloadLength = _payloadLength + size;
+    return size;
+}
+
+int ISR_VECT sx126x::available()
+{
+    uint8_t buf[2] = {0};
+    executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2);
+    return buf[0] - _packetIndex;
+}
+
+int ISR_VECT sx126x::read()
+{
+  if (!available()) {
+    return -1;
+  }
+
+  // if received new packet
+  if (_packetIndex == 0) {
+      uint8_t rxbuf[2] = {0};
+      executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
+      int size = rxbuf[0];
+      _fifo_rx_addr_ptr = rxbuf[1];
+
+      readBuffer(_packet, size);
+  }
+
+  uint8_t byte = _packet[_packetIndex];
+  _packetIndex++;
+  return byte;
+}
+
+int sx126x::peek()
+{
+  if (!available()) {
+    return -1;
+  }
+
+  // if received new packet
+  if (_packetIndex == 0) {
+      uint8_t rxbuf[2] = {0};
+      executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
+      int size = rxbuf[0];
+      _fifo_rx_addr_ptr = rxbuf[1];
+
+      readBuffer(_packet, size);
+  }
+
+  uint8_t b = _packet[_packetIndex];
+  return b;
+}
+
+void sx126x::flush()
+{
+}
+
+void sx126x::onReceive(void(*callback)(int))
+{
+  _onReceive = callback;
+
+  if (callback) {
+    pinMode(_dio0, INPUT);
+
+    // set preamble and header detection irqs, plus dio0 mask
+    uint8_t buf[8];
+
+    // set irq masks, enable all
+    buf[0] = 0xFF; 
+    buf[1] = 0xFF;
+
+    // set dio0 masks
+    buf[2] = 0x00;
+    buf[3] = IRQ_RX_DONE_MASK_6X; 
+
+    // set dio1 masks
+    buf[4] = 0x00; 
+    buf[5] = 0x00;
+
+    // set dio2 masks
+    buf[6] = 0x00; 
+    buf[7] = 0x00;
+
+    executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8);
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+    SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+    attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING);
+  } else {
+    detachInterrupt(digitalPinToInterrupt(_dio0));
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+    SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+  }
+}
+
+void sx126x::receive(int size)
+{
+    if (size > 0) {
+        implicitHeaderMode();
+
+        // tell radio payload length
+        _payloadLength = size;
+        setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+    } else {
+        explicitHeaderMode();
+    }
+
+    if (_rxen != -1) {
+        rxAntEnable();
+    }
+    uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
+    executeOpcode(OP_RX_6X, mode, 3);
+}
+
+void sx126x::idle()
+{
+      //#if HAS_TCXO
+          // STDBY_XOSC
+          uint8_t byte = 0x01;
+      //#else
+      //    // STDBY_RC
+      //    uint8_t byte = 0x00;
+      //#endif
+      executeOpcode(OP_STANDBY_6X, &byte, 1); 
+}
+
+void sx126x::sleep()
+{
+    uint8_t byte = 0x00;
+    executeOpcode(OP_SLEEP_6X, &byte, 1);
+}
+
+void sx126x::enableTCXO() {
+    // only tested for RAK4630, voltage may be different on other platforms
+    uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF};
+    executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4);
+}
+
+void sx126x::disableTCXO() {
+    // currently cannot disable on SX1262?
+}
+
+void sx126x::setTxPower(int level, int outputPin) {
+    // currently no low power mode for SX1262 implemented, assuming PA boost
+    
+    // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2
+    // RegTxClampConfig = @address 0x08D8
+    writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1));
+
+    uint8_t pa_buf[4];
+
+    pa_buf[0] = 0x04;
+    pa_buf[1] = 0x07;
+    pa_buf[2] = 0x00;
+    pa_buf[3] = 0x01;
+
+    executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power
+
+    if (level > 22) {
+        level = 22;
+    }
+    else if (level < -9) {
+        level = -9;
+    }
+
+    writeRegister(REG_OCP_6X, 0x38); // 160mA limit, overcurrent protection
+
+    uint8_t tx_buf[2];
+
+    tx_buf[0] = level;
+    tx_buf[1] = 0x02; // ramping time - 40 microseconds
+    
+    executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2);
+
+    _txp = level;
+}
+
+uint8_t sx126x::getTxPower() {
+    return _txp;
+}
+
+void sx126x::setFrequency(long frequency) {
+  _frequency = frequency;
+
+  uint8_t buf[4];
+
+  uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X);
+
+  buf[0] = ((freq >> 24) & 0xFF);
+  buf[1] = ((freq >> 16) & 0xFF);
+  buf[2] = ((freq >> 8) & 0xFF);
+  buf[3] = (freq & 0xFF);
+
+  executeOpcode(OP_RF_FREQ_6X, buf, 4);
+}
+
+uint32_t sx126x::getFrequency() {
+    // we can't read the frequency on the sx1262 / 80
+    uint32_t frequency = _frequency;
+
+    return frequency;
+}
+
+void sx126x::setSpreadingFactor(int sf)
+{
+  if (sf < 5) {
+      sf = 5;
+  } else if (sf > 12) {
+    sf = 12;
+  }
+
+  _sf = sf;
+
+      setModulationParams(sf, _bw, _cr, _ldro);
+  handleLowDataRate();
+}
+
+long sx126x::getSignalBandwidth()
+{
+    int bw = _bw;
+    switch (bw) {
+        case 0x00: return 7.8E3;
+        case 0x01: return 15.6E3;
+        case 0x02: return 31.25E3;
+        case 0x03: return 62.5E3;
+        case 0x04: return 125E3;
+        case 0x05: return 250E3;
+        case 0x06: return 500E3;
+        case 0x08: return 10.4E3;
+        case 0x09: return 20.8E3;
+        case 0x0A: return 41.7E3;
+    }
+  return 0;
+}
+
+void sx126x::handleLowDataRate(){
+    _ldro = 1;
+    setModulationParams(_sf, _bw, _cr, _ldro);
+}
+
+void sx126x::optimizeModemSensitivity(){
+    // todo: check if there's anything the sx1262 can do here
+}
+
+void sx126x::setSignalBandwidth(long sbw)
+{
+    if (sbw <= 7.8E3) {
+        _bw = 0x00;
+    } else if (sbw <= 10.4E3) {
+        _bw = 0x08;
+    } else if (sbw <= 15.6E3) {
+        _bw = 0x01;
+    } else if (sbw <= 20.8E3) {
+        _bw = 0x09;
+    } else if (sbw <= 31.25E3) {
+        _bw = 0x02;
+    } else if (sbw <= 41.7E3) {
+        _bw = 0x0A;
+    } else if (sbw <= 62.5E3) {
+        _bw = 0x03;
+    } else if (sbw <= 125E3) {
+        _bw = 0x04;
+    } else if (sbw <= 250E3) {
+        _bw = 0x05;
+    } else /*if (sbw <= 250E3)*/ {
+        _bw = 0x06;
+    }
+
+    setModulationParams(_sf, _bw, _cr, _ldro);
+
+    handleLowDataRate();
+  optimizeModemSensitivity();
+}
+
+void sx126x::setCodingRate4(int denominator)
+{
+  if (denominator < 5) {
+    denominator = 5;
+  } else if (denominator > 8) {
+    denominator = 8;
+  }
+
+  int cr = denominator - 4;
+
+  _cr = cr;
+
+  setModulationParams(_sf, _bw, cr, _ldro);
+}
+
+void sx126x::setPreambleLength(long length)
+{
+    setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx126x::setSyncWord(int sw)
+{
+    writeRegister(REG_SYNC_WORD_MSB_6X, sw & 0xFF00);
+    writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF);
+}
+
+void sx126x::enableCrc()
+{
+    _crcMode = 1;
+    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx126x::disableCrc()
+{
+    _crcMode = 0;
+    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+byte sx126x::random()
+{
+    return readRegister(REG_RANDOM_GEN_6X);
+}
+
+void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen)
+{
+  _ss = ss;
+  _reset = reset;
+  _dio0 = dio0;
+  _busy = busy;
+  _rxen = rxen;
+}
+
+void sx126x::setSPIFrequency(uint32_t frequency)
+{
+  _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
+}
+
+void sx126x::dumpRegisters(Stream& out)
+{
+  for (int i = 0; i < 128; i++) {
+    out.print("0x");
+    out.print(i, HEX);
+    out.print(": 0x");
+    out.println(readRegister(i), HEX);
+  }
+}
+
+void sx126x::explicitHeaderMode()
+{
+  _implicitHeaderMode = 0;
+  setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx126x::implicitHeaderMode()
+{
+  _implicitHeaderMode = 1;
+  setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+
+void ISR_VECT sx126x::handleDio0Rise()
+{
+    uint8_t buf[2];
+
+    buf[0] = 0x00;
+    buf[1] = 0x00;
+
+    executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
+
+    executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2);
+
+    if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) {
+        // received a packet
+        _packetIndex = 0;
+
+        // read packet length
+        uint8_t rxbuf[2] = {0};
+        executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
+        int packetLength = rxbuf[0];
+
+        if (_onReceive) {
+            _onReceive(packetLength);
+        }
+    }
+}
+
+void ISR_VECT sx126x::onDio0Rise()
+{
+    sx126x_modem.handleDio0Rise();
+}
+
+sx126x sx126x_modem;
diff --git a/sx126x.h b/sx126x.h
new file mode 100644 (file)
index 0000000..a08b11c
--- /dev/null
+++ b/sx126x.h
@@ -0,0 +1,142 @@
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2023 by Mark Qvist
+// Obviously still under the MIT license.
+
+#ifndef SX126X_H
+#define SX126X_H
+
+#include <Arduino.h>
+#include <SPI.h>
+#include "Modem.h"
+
+#define LORA_DEFAULT_SS_PIN    10
+#define LORA_DEFAULT_RESET_PIN 9
+#define LORA_DEFAULT_DIO0_PIN  2
+#define LORA_DEFAULT_RXEN_PIN  -1
+#define LORA_DEFAULT_TXEN_PIN  -1
+#define LORA_DEFAULT_BUSY_PIN  -1
+
+#define PA_OUTPUT_RFO_PIN      0
+#define PA_OUTPUT_PA_BOOST_PIN 1
+
+#define RSSI_OFFSET 157
+
+class sx126x : public Stream {
+public:
+  sx126x();
+
+  int begin(long frequency);
+  void end();
+
+  int beginPacket(int implicitHeader = false);
+  int endPacket();
+
+  int parsePacket(int size = 0);
+  int packetRssi();
+  int currentRssi();
+  uint8_t packetRssiRaw();
+  uint8_t currentRssiRaw();
+  uint8_t packetSnrRaw();
+  float packetSnr();
+  long packetFrequencyError();
+
+  // from Print
+  virtual size_t write(uint8_t byte);
+  virtual size_t write(const uint8_t *buffer, size_t size);
+
+  // from Stream
+  virtual int available();
+  virtual int read();
+  virtual int peek();
+  virtual void flush();
+
+  void onReceive(void(*callback)(int));
+
+  void receive(int size = 0);
+  void idle();
+  void sleep();
+
+  bool preInit();
+  uint8_t getTxPower();
+  void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+  uint32_t getFrequency();
+  void setFrequency(long frequency);
+  void setSpreadingFactor(int sf);
+  long getSignalBandwidth();
+  void setSignalBandwidth(long sbw);
+  void setCodingRate4(int denominator);
+  void setPreambleLength(long length);
+  void setSyncWord(int sw);
+  uint8_t modemStatus();
+  void enableCrc();
+  void disableCrc();
+  void enableTCXO();
+  void disableTCXO();
+
+  void rxAntEnable();
+  void loraMode();
+  void waitOnBusy();
+  void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
+  void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
+  void writeBuffer(const uint8_t* buffer, size_t size);
+  void readBuffer(uint8_t* buffer, size_t size);
+  void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc);
+
+  void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro);
+
+  // deprecated
+  void crc() { enableCrc(); }
+  void noCrc() { disableCrc(); }
+
+  byte random();
+
+  void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN);
+  void setSPIFrequency(uint32_t frequency);
+
+  void dumpRegisters(Stream& out);
+
+private:
+  void explicitHeaderMode();
+  void implicitHeaderMode();
+
+  void handleDio0Rise();
+
+  uint8_t readRegister(uint16_t address);
+  void writeRegister(uint16_t address, uint8_t value);
+  uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
+
+  static void onDio0Rise();
+
+  void handleLowDataRate();
+  void optimizeModemSensitivity();
+
+private:
+  SPISettings _spiSettings;
+  int _ss;
+  int _reset;
+  int _dio0;
+  int _rxen;
+  int _busy;
+  long _frequency;
+  int _txp;
+  uint8_t _sf;
+  uint8_t _bw;
+  uint8_t _cr;
+  uint8_t _ldro;
+  int _packetIndex;
+  int _preambleLength;
+  int _implicitHeaderMode;
+  int _payloadLength;
+  int _crcMode;
+  int _fifo_tx_addr_ptr;
+  int _fifo_rx_addr_ptr;
+  uint8_t _packet[255];
+  bool _preinit_done;
+  void (*_onReceive)(int);
+};
+
+extern sx126x sx126x_modem;
+
+#endif
diff --git a/sx127x.cpp b/sx127x.cpp
new file mode 100644 (file)
index 0000000..f0ae16e
--- /dev/null
@@ -0,0 +1,691 @@
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2023 by Mark Qvist
+// Obviously still under the MIT license.
+
+#include "sx127x.h"
+
+#define MCU_1284P 0x91
+#define MCU_2560  0x92
+#define MCU_ESP32 0x81
+#define MCU_NRF52 0x71
+#if defined(__AVR_ATmega1284P__)
+  #define PLATFORM PLATFORM_AVR
+  #define MCU_VARIANT MCU_1284P
+#elif defined(__AVR_ATmega2560__)
+  #define PLATFORM PLATFORM_AVR
+  #define MCU_VARIANT MCU_2560
+#elif defined(ESP32)
+  #define PLATFORM PLATFORM_ESP32
+  #define MCU_VARIANT MCU_ESP32
+#elif defined(NRF52840_XXAA)
+  #define PLATFORM PLATFORM_NRF52
+  #define MCU_VARIANT MCU_NRF52
+#endif
+
+#ifndef MCU_VARIANT
+  #error No MCU variant defined, cannot compile
+#endif
+
+#if MCU_VARIANT == MCU_ESP32
+  #include "soc/rtc_wdt.h"
+  #define ISR_VECT IRAM_ATTR
+#else
+  #define ISR_VECT
+#endif
+
+// Registers
+#define REG_FIFO_7X                 0x00
+#define REG_OP_MODE_7X              0x01
+#define REG_FRF_MSB_7X              0x06
+#define REG_FRF_MID_7X              0x07
+#define REG_FRF_LSB_7X              0x08
+#define REG_PA_CONFIG_7X            0x09
+#define REG_OCP_7X                  0x0b
+#define REG_LNA_7X                  0x0c
+#define REG_FIFO_ADDR_PTR_7X        0x0d
+#define REG_FIFO_TX_BASE_ADDR_7X    0x0e
+#define REG_FIFO_RX_BASE_ADDR_7X    0x0f
+#define REG_FIFO_RX_CURRENT_ADDR_7X 0x10
+#define REG_IRQ_FLAGS_7X            0x12
+#define REG_RX_NB_BYTES_7X          0x13
+#define REG_MODEM_STAT_7X           0x18
+#define REG_PKT_SNR_VALUE_7X        0x19
+#define REG_PKT_RSSI_VALUE_7X       0x1a
+#define REG_RSSI_VALUE_7X           0x1b
+#define REG_MODEM_CONFIG_1_7X       0x1d
+#define REG_MODEM_CONFIG_2_7X       0x1e
+#define REG_PREAMBLE_MSB_7X         0x20
+#define REG_PREAMBLE_LSB_7X         0x21
+#define REG_PAYLOAD_LENGTH_7X       0x22
+#define REG_MODEM_CONFIG_3_7X       0x26
+#define REG_FREQ_ERROR_MSB_7X       0x28
+#define REG_FREQ_ERROR_MID_7X       0x29
+#define REG_FREQ_ERROR_LSB_7X       0x2a
+#define REG_RSSI_WIDEBAND_7X        0x2c
+#define REG_DETECTION_OPTIMIZE_7X   0x31
+#define REG_HIGH_BW_OPTIMIZE_1_7X   0x36
+#define REG_DETECTION_THRESHOLD_7X  0x37
+#define REG_SYNC_WORD_7X            0x39
+#define REG_HIGH_BW_OPTIMIZE_2_7X   0x3a
+#define REG_DIO_MAPPING_1_7X        0x40
+#define REG_VERSION_7X              0x42
+#define REG_TCXO_7X                 0x4b
+#define REG_PA_DAC_7X               0x4d
+
+// Modes
+#define MODE_LONG_RANGE_MODE_7X     0x80
+#define MODE_SLEEP_7X               0x00
+#define MODE_STDBY_7X               0x01
+#define MODE_TX_7X                  0x03
+#define MODE_RX_CONTINUOUS_7X       0x05
+#define MODE_RX_SINGLE_7X           0x06
+
+// PA config
+#define PA_BOOST_7X                 0x80
+
+// IRQ masks
+#define IRQ_TX_DONE_MASK_7X         0x08
+#define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20
+#define IRQ_RX_DONE_MASK_7X         0x40
+
+extern SPIClass SPI;
+
+#define MAX_PKT_LENGTH           255
+
+sx127x::sx127x() :
+  _spiSettings(8E6, MSBFIRST, SPI_MODE0),
+  _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
+  _frequency(0),
+  _packetIndex(0),
+  _preinit_done(false),
+  _onReceive(NULL)
+{
+  // overide Stream timeout value
+  setTimeout(0);
+}
+
+bool sx127x::preInit() {
+  // setup pins
+  pinMode(_ss, OUTPUT);
+  // set SS high
+  digitalWrite(_ss, HIGH);
+  
+  SPI.begin();
+
+  // check version (retry for up to 2 seconds)
+  uint8_t version;
+  long start = millis();
+  while (((millis() - start) < 2000) && (millis() >= start)) {
+      version = readRegister(REG_VERSION_7X);
+      if (version == 0x12) {
+          break;
+      }
+      delay(100);
+  }
+  if (version != 0x12) {
+      return false;
+  }
+
+  _preinit_done = true;
+  return true;
+}
+
+uint8_t ISR_VECT sx127x::readRegister(uint8_t address)
+{
+  return singleTransfer(address & 0x7f, 0x00);
+}
+
+void sx127x::writeRegister(uint8_t address, uint8_t value)
+{
+  singleTransfer(address | 0x80, value);
+}
+
+uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value)
+{
+  uint8_t response;
+
+  digitalWrite(_ss, LOW);
+
+  SPI.beginTransaction(_spiSettings);
+  SPI.transfer(address);
+  response = SPI.transfer(value);
+  SPI.endTransaction();
+
+  digitalWrite(_ss, HIGH);
+
+  return response;
+}
+
+
+int sx127x::begin(long frequency)
+{
+  if (_reset != -1) {
+    pinMode(_reset, OUTPUT);
+
+    // perform reset
+    digitalWrite(_reset, LOW);
+    delay(10);
+    digitalWrite(_reset, HIGH);
+    delay(10);
+  }
+
+  if (_busy != -1) {
+      pinMode(_busy, INPUT);
+  }
+
+  if (!_preinit_done) {
+    if (!preInit()) {
+      return false;
+    }
+  }
+
+  // put in sleep mode
+  sleep();
+
+  // set frequency
+  setFrequency(frequency);
+
+  // set base addresses
+  writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0);
+  writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0);
+
+  // set LNA boost
+  writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03);
+
+  // set auto AGC
+  writeRegister(REG_MODEM_CONFIG_3_7X, 0x04);
+
+  // set output power to 2 dBm
+  setTxPower(2);
+
+  // put in standby mode
+  idle();
+
+  return 1;
+}
+
+void sx127x::end()
+{
+  // put in sleep mode
+  sleep();
+
+  // stop SPI
+  SPI.end();
+
+  _preinit_done = false;
+}
+
+int sx127x::beginPacket(int implicitHeader)
+{
+  // put in standby mode
+  idle();
+
+  if (implicitHeader) {
+    implicitHeaderMode();
+  } else {
+    explicitHeaderMode();
+  }
+
+  // reset FIFO address and payload length
+  writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
+  writeRegister(REG_PAYLOAD_LENGTH_7X, 0);
+
+  return 1;
+}
+
+int sx127x::endPacket()
+{
+  // put in TX mode
+  writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X);
+
+  // wait for TX done
+  while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) {
+    yield();
+  }
+
+  // clear IRQ's
+  writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X);
+  return 1;
+}
+
+uint8_t sx127x::modemStatus() {
+    return readRegister(REG_MODEM_STAT_7X);
+}
+
+
+uint8_t sx127x::currentRssiRaw() {
+    uint8_t rssi = readRegister(REG_RSSI_VALUE_7X);
+    return rssi;
+}
+
+int ISR_VECT sx127x::currentRssi() {
+    int rssi = (int)readRegister(REG_RSSI_VALUE_7X) - RSSI_OFFSET;
+    if (_frequency < 820E6) rssi -= 7;
+    return rssi;
+}
+
+uint8_t sx127x::packetRssiRaw() {
+    uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE_7X);
+    return pkt_rssi_value;
+}
+
+int ISR_VECT sx127x::packetRssi() {
+    int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET;
+    int pkt_snr = packetSnr();
+
+    if (_frequency < 820E6) pkt_rssi -= 7;
+
+    if (pkt_snr < 0) {
+        pkt_rssi += pkt_snr;
+    } else {
+        // Slope correction is (16/15)*pkt_rssi,
+        // this estimation looses one floating point
+        // operation, and should be precise enough.
+        pkt_rssi = (int)(1.066 * pkt_rssi);
+    }
+    return pkt_rssi;
+}
+
+uint8_t ISR_VECT sx127x::packetSnrRaw() {
+    return readRegister(REG_PKT_SNR_VALUE_7X);
+}
+
+float ISR_VECT sx127x::packetSnr() {
+    return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25;
+}
+
+long sx127x::packetFrequencyError()
+{
+  int32_t freqError = 0;
+  freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB_7X) & B111);
+  freqError <<= 8L;
+  freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID_7X));
+  freqError <<= 8L;
+  freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB_7X));
+
+  if (readRegister(REG_FREQ_ERROR_MSB_7X) & B1000) { // Sign bit is on
+      freqError -= 524288; // B1000'0000'0000'0000'0000
+  }
+
+  const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
+  const float fError = ((static_cast<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f); // p. 37
+
+  return static_cast<long>(fError);
+}
+
+size_t sx127x::write(uint8_t byte)
+{
+  return write(&byte, sizeof(byte));
+}
+
+size_t sx127x::write(const uint8_t *buffer, size_t size)
+{
+    int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X);
+
+    // check size
+    if ((currentLength + size) > MAX_PKT_LENGTH) {
+        size = MAX_PKT_LENGTH - currentLength;
+    }
+
+    // write data
+    for (size_t i = 0; i < size; i++) {
+        writeRegister(REG_FIFO_7X, buffer[i]);
+    }
+
+    // update length
+    writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size);
+    return size;
+}
+
+int ISR_VECT sx127x::available()
+{
+    return (readRegister(REG_RX_NB_BYTES_7X) - _packetIndex);
+}
+
+int ISR_VECT sx127x::read()
+{
+  if (!available()) {
+    return -1;
+  }
+
+  _packetIndex++;
+  return readRegister(REG_FIFO_7X);
+}
+
+int sx127x::peek()
+{
+  if (!available()) {
+    return -1;
+  }
+
+  // store current FIFO address
+  int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X);
+
+  // read
+  uint8_t b = readRegister(REG_FIFO_7X);
+
+  // restore FIFO address
+  writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress);
+
+  return b;
+}
+
+void sx127x::flush()
+{
+}
+
+void sx127x::onReceive(void(*callback)(int))
+{
+  _onReceive = callback;
+
+  if (callback) {
+    pinMode(_dio0, INPUT);
+
+    writeRegister(REG_DIO_MAPPING_1_7X, 0x00);
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+    SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+    attachInterrupt(digitalPinToInterrupt(_dio0), sx127x::onDio0Rise, RISING);
+  } else {
+    detachInterrupt(digitalPinToInterrupt(_dio0));
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+    SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+  }
+}
+
+void sx127x::receive(int size)
+{
+    if (size > 0) {
+        implicitHeaderMode();
+
+        writeRegister(REG_PAYLOAD_LENGTH_7X, size & 0xff);
+    } else {
+        explicitHeaderMode();
+    }
+
+    writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_RX_CONTINUOUS_7X);
+}
+
+void sx127x::idle()
+{
+    writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_STDBY_7X);
+}
+
+void sx127x::sleep()
+{
+    writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_SLEEP_7X);
+}
+
+void sx127x::enableTCXO() {
+    uint8_t tcxo_reg = readRegister(REG_TCXO_7X);
+    writeRegister(REG_TCXO_7X, tcxo_reg | 0x10);
+}
+
+void sx127x::disableTCXO() {
+      uint8_t tcxo_reg = readRegister(REG_TCXO_7X);
+      writeRegister(REG_TCXO_7X, tcxo_reg & 0xEF);
+}
+
+void sx127x::setTxPower(int level, int outputPin) {
+    if (PA_OUTPUT_RFO_PIN == outputPin) {
+        // RFO
+        if (level < 0) {
+            level = 0;
+        } else if (level > 14) {
+            level = 14;
+        }
+
+        writeRegister(REG_PA_DAC_7X, 0x84);
+        writeRegister(REG_PA_CONFIG_7X, 0x70 | level);
+
+    } else {
+        // PA BOOST
+        if (level < 2) {
+            level = 2;
+        } else if (level > 17) {
+            level = 17;
+        }
+
+        writeRegister(REG_PA_DAC_7X, 0x84);
+        writeRegister(REG_PA_CONFIG_7X, PA_BOOST_7X | (level - 2));
+    }
+}
+
+uint8_t sx127x::getTxPower() {
+    byte txp = readRegister(REG_PA_CONFIG_7X);
+    return txp;
+}
+
+void sx127x::setFrequency(unsigned long frequency) {
+  _frequency = frequency;
+
+  uint32_t frf = ((uint64_t)frequency << 19) / 32000000;
+
+  writeRegister(REG_FRF_MSB_7X, (uint8_t)(frf >> 16));
+  writeRegister(REG_FRF_MID_7X, (uint8_t)(frf >> 8));
+  writeRegister(REG_FRF_LSB_7X, (uint8_t)(frf >> 0));
+
+  optimizeModemSensitivity();
+}
+
+uint32_t sx127x::getFrequency() {
+    uint8_t msb = readRegister(REG_FRF_MSB_7X);
+    uint8_t mid = readRegister(REG_FRF_MID_7X);
+    uint8_t lsb = readRegister(REG_FRF_LSB_7X);
+
+    uint32_t frf = ((uint32_t)msb << 16) | ((uint32_t)mid << 8) | (uint32_t)lsb;
+    uint64_t frm = (uint64_t)frf*32000000;
+    uint32_t frequency = (frm >> 19);
+
+    return frequency;
+}
+
+void sx127x::setSpreadingFactor(int sf)
+{
+  if (sf < 6) {
+    sf = 6;
+  } else if (sf > 12) {
+    sf = 12;
+  }
+
+  if (sf == 6) {
+      writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc5);
+      writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0c);
+  } else {
+      writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc3);
+      writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0a);
+  }
+
+  writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0));
+  handleLowDataRate();
+}
+
+long sx127x::getSignalBandwidth()
+{
+      byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4);
+      switch (bw) {
+        case 0: return 7.8E3;
+        case 1: return 10.4E3;
+        case 2: return 15.6E3;
+        case 3: return 20.8E3;
+        case 4: return 31.25E3;
+        case 5: return 41.7E3;
+        case 6: return 62.5E3;
+        case 7: return 125E3;
+        case 8: return 250E3;
+        case 9: return 500E3;
+      }
+  return 0;
+}
+
+void sx127x::handleLowDataRate(){
+    int sf = (readRegister(REG_MODEM_CONFIG_2_7X) >> 4);
+    if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) {
+        // set auto AGC and LowDataRateOptimize
+        writeRegister(REG_MODEM_CONFIG_3_7X, (1<<3)|(1<<2));
+    } else {
+        // set auto AGC
+        writeRegister(REG_MODEM_CONFIG_3_7X, (1<<2));
+    }
+}
+
+void sx127x::optimizeModemSensitivity(){
+    byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4);
+    uint32_t freq = getFrequency();
+
+    if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) {
+        writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02);
+        writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x7f);
+    } else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) {
+        writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02);
+        writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x64);
+    } else {
+        writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x03);
+    }
+}
+
+void sx127x::setSignalBandwidth(long sbw)
+{
+    int bw;
+
+    if (sbw <= 7.8E3) {
+        bw = 0;
+    } else if (sbw <= 10.4E3) {
+        bw = 1;
+    } else if (sbw <= 15.6E3) {
+        bw = 2;
+    } else if (sbw <= 20.8E3) {
+        bw = 3;
+    } else if (sbw <= 31.25E3) {
+        bw = 4;
+    } else if (sbw <= 41.7E3) {
+        bw = 5;
+    } else if (sbw <= 62.5E3) {
+        bw = 6;
+    } else if (sbw <= 125E3) {
+        bw = 7;
+    } else if (sbw <= 250E3) {
+        bw = 8;
+    } else /*if (sbw <= 250E3)*/ {
+        bw = 9;
+    }
+
+    writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0x0f) | (bw << 4));
+
+
+  handleLowDataRate();
+  optimizeModemSensitivity();
+}
+
+void sx127x::setCodingRate4(int denominator)
+{
+  if (denominator < 5) {
+    denominator = 5;
+  } else if (denominator > 8) {
+    denominator = 8;
+  }
+
+  int cr = denominator - 4;
+
+  writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1));
+}
+
+void sx127x::setPreambleLength(long length)
+{
+  writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8));
+  writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 0));
+}
+
+void sx127x::setSyncWord(int sw)
+{
+    writeRegister(REG_SYNC_WORD_7X, sw);
+}
+
+void sx127x::enableCrc()
+{
+    writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) | 0x04);
+}
+
+void sx127x::disableCrc()
+{
+    writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) & 0xfb);
+}
+
+byte sx127x::random()
+{
+    return readRegister(REG_RSSI_WIDEBAND_7X);
+}
+
+void sx127x::setPins(int ss, int reset, int dio0, int busy)
+{
+  _ss = ss;
+  _reset = reset;
+  _dio0 = dio0;
+  _busy = busy;
+}
+
+void sx127x::setSPIFrequency(uint32_t frequency)
+{
+  _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
+}
+
+void sx127x::dumpRegisters(Stream& out)
+{
+  for (int i = 0; i < 128; i++) {
+    out.print("0x");
+    out.print(i, HEX);
+    out.print(": 0x");
+    out.println(readRegister(i), HEX);
+  }
+}
+
+void sx127x::explicitHeaderMode()
+{
+  _implicitHeaderMode = 0;
+
+  writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) & 0xfe);
+}
+
+void sx127x::implicitHeaderMode()
+{
+  _implicitHeaderMode = 1;
+
+  writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) | 0x01);
+}
+
+
+void ISR_VECT sx127x::handleDio0Rise()
+{
+    int irqFlags = readRegister(REG_IRQ_FLAGS_7X);
+
+    // clear IRQ's
+    writeRegister(REG_IRQ_FLAGS_7X, irqFlags);
+    if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) {
+
+        // received a packet
+        _packetIndex = 0;
+
+        // read packet length
+        int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X);
+
+        // set FIFO address to current RX address
+        writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X));
+
+        if (_onReceive) {
+            _onReceive(packetLength);
+        }
+
+        // reset FIFO address
+        writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
+    }
+}
+
+void ISR_VECT sx127x::onDio0Rise()
+{
+  sx127x_modem.handleDio0Rise();
+}
+
+sx127x sx127x_modem;
diff --git a/sx127x.h b/sx127x.h
new file mode 100644 (file)
index 0000000..dca01c0
--- /dev/null
+++ b/sx127x.h
@@ -0,0 +1,117 @@
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2023 by Mark Qvist
+// Obviously still under the MIT license.
+
+#ifndef SX1276_H
+#define SX1276_H
+
+#include <Arduino.h>
+#include <SPI.h>
+#include "Modem.h"
+
+#define LORA_DEFAULT_SS_PIN    10
+#define LORA_DEFAULT_RESET_PIN 9
+#define LORA_DEFAULT_DIO0_PIN  2
+#define LORA_DEFAULT_BUSY_PIN  -1
+
+#define PA_OUTPUT_RFO_PIN      0
+#define PA_OUTPUT_PA_BOOST_PIN 1
+
+#define RSSI_OFFSET 157
+
+class sx127x : public Stream {
+public:
+  sx127x();
+
+  int begin(long frequency);
+  void end();
+
+  int beginPacket(int implicitHeader = false);
+  int endPacket();
+
+  int parsePacket(int size = 0);
+  int packetRssi();
+  int currentRssi();
+  uint8_t packetRssiRaw();
+  uint8_t currentRssiRaw();
+  uint8_t packetSnrRaw();
+  float packetSnr();
+  long packetFrequencyError();
+
+  // from Print
+  virtual size_t write(uint8_t byte);
+  virtual size_t write(const uint8_t *buffer, size_t size);
+
+  // from Stream
+  virtual int available();
+  virtual int read();
+  virtual int peek();
+  virtual void flush();
+
+  void onReceive(void(*callback)(int));
+
+  void receive(int size = 0);
+  void idle();
+  void sleep();
+
+  bool preInit();
+  uint8_t getTxPower();
+  void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+  uint32_t getFrequency();
+  void setFrequency(unsigned long frequency);
+  void setSpreadingFactor(int sf);
+  long getSignalBandwidth();
+  void setSignalBandwidth(long sbw);
+  void setCodingRate4(int denominator);
+  void setPreambleLength(long length);
+  void setSyncWord(int sw);
+  uint8_t modemStatus();
+  void enableCrc();
+  void disableCrc();
+  void enableTCXO();
+  void disableTCXO();
+
+  // deprecated
+  void crc() { enableCrc(); }
+  void noCrc() { disableCrc(); }
+
+  byte random();
+
+  void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN);
+  void setSPIFrequency(uint32_t frequency);
+
+  void dumpRegisters(Stream& out);
+
+private:
+  void explicitHeaderMode();
+  void implicitHeaderMode();
+
+  void handleDio0Rise();
+
+  uint8_t readRegister(uint8_t address);
+  void writeRegister(uint8_t address, uint8_t value);
+  uint8_t singleTransfer(uint8_t address, uint8_t value);
+
+  static void onDio0Rise();
+
+  void handleLowDataRate();
+  void optimizeModemSensitivity();
+
+private:
+  SPISettings _spiSettings;
+  int _ss;
+  int _reset;
+  int _dio0;
+  int _busy;
+  long _frequency;
+  int _packetIndex;
+  int _implicitHeaderMode;
+  bool _preinit_done;
+  void (*_onReceive)(int);
+};
+
+extern sx127x sx127x_modem;
+
+#endif
diff --git a/sx128x.cpp b/sx128x.cpp
new file mode 100644 (file)
index 0000000..baa1f31
--- /dev/null
@@ -0,0 +1,882 @@
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2023 by Mark Qvist
+// Obviously still under the MIT license.
+
+#include "sx128x.h"
+
+#define MCU_1284P 0x91
+#define MCU_2560  0x92
+#define MCU_ESP32 0x81
+#define MCU_NRF52 0x71
+#if defined(__AVR_ATmega1284P__)
+  #define PLATFORM PLATFORM_AVR
+  #define MCU_VARIANT MCU_1284P
+#elif defined(__AVR_ATmega2560__)
+  #define PLATFORM PLATFORM_AVR
+  #define MCU_VARIANT MCU_2560
+#elif defined(ESP32)
+  #define PLATFORM PLATFORM_ESP32
+  #define MCU_VARIANT MCU_ESP32
+#elif defined(NRF52840_XXAA)
+  #define PLATFORM PLATFORM_NRF52
+  #define MCU_VARIANT MCU_NRF52
+#endif
+
+#ifndef MCU_VARIANT
+  #error No MCU variant defined, cannot compile
+#endif
+
+#if MCU_VARIANT == MCU_ESP32
+  #include "soc/rtc_wdt.h"
+  #define ISR_VECT IRAM_ATTR
+#else
+  #define ISR_VECT
+#endif
+
+#define OP_RF_FREQ_8X               0x86
+#define OP_SLEEP_8X                 0x84
+#define OP_STANDBY_8X               0x80
+#define OP_TX_8X                    0x83
+#define OP_RX_8X                    0x82
+#define OP_SET_IRQ_FLAGS_8X         0x8D // also provides info such as
+                                         // preamble detection, etc for
+                                         // knowing when it's safe to switch
+                                         // antenna modes
+#define OP_CLEAR_IRQ_STATUS_8X      0x97
+#define OP_GET_IRQ_STATUS_8X        0x15
+#define OP_RX_BUFFER_STATUS_8X      0x17
+#define OP_PACKET_STATUS_8X         0x1D // get snr & rssi of last packet
+#define OP_CURRENT_RSSI_8X          0x1F
+#define OP_MODULATION_PARAMS_8X     0x8B // bw, sf, cr, etc.
+#define OP_PACKET_PARAMS_8X         0x8C // crc, preamble, payload length, etc.
+#define OP_STATUS_8X                0xC0
+#define OP_TX_PARAMS_8X             0x8E // set dbm, etc
+#define OP_PACKET_TYPE_8X           0x8A
+#define OP_BUFFER_BASE_ADDR_8X      0x8F
+#define OP_READ_REGISTER_8X         0x19
+#define OP_WRITE_REGISTER_8X        0x18
+#define IRQ_TX_DONE_MASK_8X         0x01
+#define IRQ_RX_DONE_MASK_8X         0x02
+#define IRQ_HEADER_DET_MASK_8X      0x10
+#define IRQ_HEADER_ERROR_MASK_8X    0x20
+#define IRQ_PAYLOAD_CRC_ERROR_MASK_8X 0x40
+
+#define MODE_LONG_RANGE_MODE_8X     0x01
+
+#define OP_FIFO_WRITE_8X            0x1A
+#define OP_FIFO_READ_8X             0x1B
+#define IRQ_PREAMBLE_DET_MASK_8X    0x80
+
+#define REG_PACKET_SIZE            0x901
+#define REG_FIRM_VER_MSB           0x154
+#define REG_FIRM_VER_LSB           0x153
+
+#define XTAL_FREQ_8X (double)52000000
+#define FREQ_DIV_8X (double)pow(2.0, 18.0)
+#define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X)
+
+extern SPIClass spiModem;
+#define SPI spiModem
+
+extern SPIClass SPI;
+
+#define MAX_PKT_LENGTH           255
+
+sx128x::sx128x() :
+  _spiSettings(8E6, MSBFIRST, SPI_MODE0),
+  _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN),
+  _frequency(0),
+  _txp(0),
+  _sf(0x50),
+  _bw(0x34),
+  _cr(0x01),
+  _packetIndex(0),
+  _preambleLength(18),
+  _implicitHeaderMode(0),
+  _payloadLength(255),
+  _crcMode(0),
+  _fifo_tx_addr_ptr(0),
+  _fifo_rx_addr_ptr(0),
+  _packet({0}),
+  _rxPacketLength(0),
+  _preinit_done(false),
+  _onReceive(NULL)
+{
+  // overide Stream timeout value
+  setTimeout(0);
+}
+
+bool sx128x::preInit() {
+  // setup pins
+  pinMode(_ss, OUTPUT);
+  // set SS high
+  digitalWrite(_ss, HIGH);
+  
+  SPI.begin();
+
+  // check version (retry for up to 2 seconds)
+  long start = millis();
+
+  uint8_t version_msb;
+  uint8_t version_lsb;
+
+  while (((millis() - start) < 2000) && (millis() >= start)) {
+
+      version_msb = readRegister(REG_FIRM_VER_MSB);
+      version_lsb = readRegister(REG_FIRM_VER_LSB);
+
+      if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) {
+          break;
+      }
+      delay(100);
+  }
+  if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) {
+      return false;
+  }
+
+  _preinit_done = true;
+  return true;
+}
+
+uint8_t ISR_VECT sx128x::readRegister(uint16_t address)
+{
+  return singleTransfer(OP_READ_REGISTER_8X, address, 0x00);
+}
+
+void sx128x::writeRegister(uint16_t address, uint8_t value)
+{
+    singleTransfer(OP_WRITE_REGISTER_8X, address, value);
+}
+
+uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value)
+{
+    waitOnBusy();
+
+    uint8_t response;
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(opcode);
+    SPI.transfer((address & 0xFF00) >> 8);
+    SPI.transfer(address & 0x00FF);
+    if (opcode == OP_READ_REGISTER_8X) {
+        SPI.transfer(0x00);
+    }
+    response = SPI.transfer(value);
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+
+    return response;
+}
+
+void sx128x::rxAntEnable()
+{
+    if (_txen != -1) {
+        digitalWrite(_txen, LOW);
+    }
+    if (_rxen != -1) {
+        digitalWrite(_rxen, HIGH);
+    }
+}
+
+void sx128x::txAntEnable()
+{
+    if (_txen != -1) {
+        digitalWrite(_txen, HIGH);
+    }
+    if (_rxen != -1) {
+        digitalWrite(_rxen, LOW);
+    }
+}
+
+void sx128x::loraMode() {
+    // enable lora mode on the SX1262 chip
+    uint8_t mode = MODE_LONG_RANGE_MODE_8X;
+    executeOpcode(OP_PACKET_TYPE_8X, &mode, 1);
+}
+
+void sx128x::waitOnBusy() {
+    unsigned long time = millis();
+    if (_busy != -1) {
+        while (digitalRead(_busy) == HIGH)
+        {
+            if (millis() >= (time + 300)) {
+                pinMode(LED_BLUE, OUTPUT);
+                digitalWrite(LED_BLUE, HIGH);
+                break;
+            }
+            // do nothing
+        }
+    }
+}
+
+void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(opcode);
+
+    for (int i = 0; i < size; i++)
+    {
+        SPI.transfer(buffer[i]);
+    }
+
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(opcode);
+    SPI.transfer(0x00);
+
+    for (int i = 0; i < size; i++)
+    {
+        buffer[i] = SPI.transfer(0x00);
+    }
+
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx128x::writeBuffer(const uint8_t* buffer, size_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(OP_FIFO_WRITE_8X);
+    SPI.transfer(_fifo_tx_addr_ptr);
+
+    for (int i = 0; i < size; i++)
+    {
+        SPI.transfer(buffer[i]);
+        _fifo_tx_addr_ptr++;
+    }
+
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx128x::readBuffer(uint8_t* buffer, size_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    SPI.beginTransaction(_spiSettings);
+    SPI.transfer(OP_FIFO_READ_8X);
+    SPI.transfer(_fifo_rx_addr_ptr);
+    SPI.transfer(0x00);
+
+    for (int i = 0; i < size; i++)
+    {
+        buffer[i] = SPI.transfer(0x00);
+    }
+
+    SPI.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) {
+  // because there is no access to these registers on the sx1280, we have
+  // to set all these parameters at once or not at all.
+  uint8_t buf[3];
+
+  buf[0] = sf;
+  buf[1] = bw;
+  buf[2] = cr; 
+  executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3);
+
+  if (sf <= 6) {
+      writeRegister(0x925, 0x1E);
+  } else if (sf <= 8) {
+      writeRegister(0x925, 0x37);
+  } else if (sf >= 9) {
+      writeRegister(0x925, 0x32);
+  }
+  writeRegister(0x093C, 0x1);
+}
+
+void sx128x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
+  // because there is no access to these registers on the sx1280, we have
+  // to set all these parameters at once or not at all.
+  uint8_t buf[7];
+
+  // calculate exponent and mantissa values for modem
+  uint8_t e = 1;
+  uint8_t m = 1;
+  uint32_t preamblelen;
+
+  for (e <= 15; e++;) {
+      for (m <= 15; m++;) {
+          preamblelen = m * (uint32_t(1) << e);
+          if (preamblelen >= preamble) break;
+      }
+      if (preamblelen >= preamble) break;
+  }
+
+  buf[0] = (e << 4) | m;
+  buf[1] = headermode;
+  buf[2] = length;
+  buf[3] = crc;
+  // standard IQ setting (no inversion)
+  buf[4] = 0x40; 
+  // unused params
+  buf[5] = 0x00; 
+  buf[6] = 0x00; 
+
+  executeOpcode(OP_PACKET_PARAMS_8X, buf, 7);
+}
+
+int sx128x::begin(unsigned long frequency)
+{
+  if (_reset != -1) {
+    pinMode(_reset, OUTPUT);
+
+    // perform reset
+    digitalWrite(_reset, LOW);
+    delay(10);
+    digitalWrite(_reset, HIGH);
+    delay(10);
+  }
+
+  if (_busy != -1) {
+      pinMode(_busy, INPUT);
+  }
+
+  if (!_preinit_done) {
+    if (!preInit()) {
+      return false;
+    }
+  }
+
+  idle();
+  loraMode();
+  rxAntEnable();
+
+  setFrequency(frequency);
+
+  // set LNA boost
+  // todo: implement this
+  //writeRegister(REG_LNA, 0x96);
+
+  setModulationParams(_sf, _bw, _cr);
+  setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+  // set output power to 2 dBm
+  setTxPower(2);
+
+  // set base addresses
+  uint8_t basebuf[2] = {0};
+  executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2);
+
+  return 1;
+}
+
+void sx128x::end()
+{
+  // put in sleep mode
+  sleep();
+
+  // stop SPI
+  SPI.end();
+
+  _preinit_done = false;
+}
+
+int sx128x::beginPacket(int implicitHeader)
+{
+  // put in standby mode
+  idle();
+
+  if (implicitHeader) {
+    implicitHeaderMode();
+  } else {
+    explicitHeaderMode();
+  }
+
+  _payloadLength = 0;
+  _fifo_tx_addr_ptr = 0;
+  setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+  return 1;
+}
+
+int sx128x::endPacket()
+{
+  setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+  txAntEnable();
+
+  // put in single TX mode
+  uint8_t timeout[3] = {0};
+  executeOpcode(OP_TX_8X, timeout, 3);
+
+  uint8_t buf[2];
+
+  buf[0] = 0x00;
+  buf[1] = 0x00;
+
+  executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
+
+  // wait for TX done
+  while ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0) {
+    buf[0] = 0x00;
+    buf[1] = 0x00;
+    executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
+    yield();
+  }
+
+  // clear IRQ's
+
+  uint8_t mask[2];
+  mask[0] = 0x00;
+  mask[1] = IRQ_TX_DONE_MASK_8X;
+  executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2);
+  return 1;
+}
+
+uint8_t sx128x::modemStatus() {
+    // imitate the register status from the sx1276 / 78
+    uint8_t buf[2] = {0};
+
+    // debug
+    executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
+
+    uint8_t clearbuf[2] = {0};
+
+    uint8_t byte = 0x00;
+
+    if (buf[0] & IRQ_PREAMBLE_DET_MASK_8X != 0) {
+        byte = byte | 0x01 | 0x04;
+        // clear register after reading
+        //clearbuf[0] = IRQ_PREAMBLE_DET_MASK_8X; 
+        clearbuf[0] = 0xFF;
+    }
+
+    if (buf[1] & IRQ_HEADER_DET_MASK_8X != 0) {
+        byte = byte | 0x02 | 0x04;
+        // clear register after reading
+        //clearbuf[1] = IRQ_HEADER_DET_MASK_8X; 
+        clearbuf[1] = 0xFF;
+    }
+
+    // debug
+    executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2);
+
+    return byte;
+}
+
+
+uint8_t sx128x::currentRssiRaw() {
+    uint8_t byte = 0;
+    executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1);
+    return byte;
+}
+
+int ISR_VECT sx128x::currentRssi() {
+    uint8_t byte = 0;
+    executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1);
+    int rssi = -byte / 2;
+    return rssi;
+}
+
+uint8_t sx128x::packetRssiRaw() {
+    uint8_t buf[5] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
+    return buf[0];
+}
+
+int ISR_VECT sx128x::packetRssi() {
+    // may need more calculations here
+    uint8_t buf[5] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
+    int pkt_rssi = -buf[0] / 2;
+    return pkt_rssi;
+}
+
+uint8_t ISR_VECT sx128x::packetSnrRaw() {
+    uint8_t buf[5] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
+    return buf[1];
+}
+
+float ISR_VECT sx128x::packetSnr() {
+    uint8_t buf[5] = {0};
+    executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 3);
+    return float(buf[1]) * 0.25;
+}
+
+long sx128x::packetFrequencyError()
+{
+  int32_t freqError = 0;
+  // todo: implement this, page 120 of sx1280 datasheet
+  const float fError = 0.0;
+  return static_cast<long>(fError);
+}
+
+size_t sx128x::write(uint8_t byte)
+{
+  return write(&byte, sizeof(byte));
+}
+
+size_t sx128x::write(const uint8_t *buffer, size_t size)
+{
+  if ((_payloadLength + size) > MAX_PKT_LENGTH) {
+      size = MAX_PKT_LENGTH - _payloadLength;
+  }
+
+  // write data
+  writeBuffer(buffer, size);
+  _payloadLength = _payloadLength + size;
+  return size;
+}
+
+int ISR_VECT sx128x::available()
+{
+    return _rxPacketLength - _packetIndex;
+}
+
+int ISR_VECT sx128x::read()
+{
+  if (!available()) {
+    return -1;
+  }
+
+  uint8_t byte = _packet[_packetIndex];
+  _packetIndex++;
+  return byte;
+}
+
+int sx128x::peek()
+{
+  if (!available()) {
+    return -1;
+  }
+
+  uint8_t b = _packet[_packetIndex];
+  return b;
+}
+
+void sx128x::flush()
+{
+}
+
+void sx128x::onReceive(void(*callback)(int))
+{
+  _onReceive = callback;
+
+  if (callback) {
+    pinMode(_dio0, INPUT);
+
+      // set preamble and header detection irqs, plus dio0 mask
+      uint8_t buf[8];
+
+      // set irq masks, enable all
+      buf[0] = 0xFF; 
+      buf[1] = 0xFF;
+
+      // set dio0 masks
+      buf[2] = 0x00;
+      buf[3] = IRQ_RX_DONE_MASK_8X; 
+
+      // set dio1 masks
+      buf[4] = 0x00; 
+      buf[5] = 0x00;
+
+      // set dio2 masks
+      buf[6] = 0x00; 
+      buf[7] = 0x00;
+
+      executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8);
+//#ifdef SPI_HAS_NOTUSINGINTERRUPT
+//    SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
+//#endif
+    attachInterrupt(digitalPinToInterrupt(_dio0), sx128x::onDio0Rise, RISING);
+  } else {
+    detachInterrupt(digitalPinToInterrupt(_dio0));
+//#ifdef SPI_HAS_NOTUSINGINTERRUPT
+//    SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
+//#endif
+  }
+}
+
+void sx128x::receive(int size)
+{
+  if (size > 0) {
+    implicitHeaderMode();
+
+    // tell radio payload length
+    _rxPacketLength = size;
+    //_payloadLength = size;
+    //setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+  } else {
+    explicitHeaderMode();
+  }
+
+  rxAntEnable();
+
+    uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
+    executeOpcode(OP_RX_8X, mode, 3);
+}
+
+void sx128x::idle()
+{
+      #if HAS_TCXO
+          // STDBY_XOSC
+          uint8_t byte = 0x01;
+      #else
+          // STDBY_RC
+          uint8_t byte = 0x00;
+      #endif
+      executeOpcode(OP_STANDBY_8X, &byte, 1); 
+}
+
+void sx128x::sleep()
+{
+    uint8_t byte = 0x00;
+    executeOpcode(OP_SLEEP_8X, &byte, 1);
+}
+
+void sx128x::enableTCXO() {
+    // todo: need to check how to implement on sx1280
+}
+
+void sx128x::disableTCXO() {
+    // todo: need to check how to implement on sx1280
+}
+
+void sx128x::setTxPower(int level, int outputPin) {
+    if (level > 13) {
+        level = 13;
+    } else if (level < -18) {
+        level = -18;
+    }
+
+    _txp = level;
+
+    level = level + 18;
+
+    uint8_t tx_buf[2];
+
+    tx_buf[0] = level;
+    tx_buf[1] = 0xE0; // ramping time - 20 microseconds
+
+    executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2);
+}
+
+uint8_t sx128x::getTxPower() {
+      return _txp;
+}
+
+void sx128x::setFrequency(unsigned long frequency) {
+  _frequency = frequency;
+
+  uint8_t buf[3];
+
+  uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X);
+
+  buf[0] = ((freq >> 16) & 0xFF);
+  buf[1] = ((freq >> 8) & 0xFF);
+  buf[2] = (freq & 0xFF);
+
+  executeOpcode(OP_RF_FREQ_8X, buf, 3);
+}
+
+uint32_t sx128x::getFrequency() {
+  // we can't read the frequency on the sx1280
+  uint32_t frequency = _frequency;
+
+  return frequency;
+}
+
+void sx128x::setSpreadingFactor(int sf)
+{
+  if (sf < 5) {
+      sf = 5;
+  } else if (sf > 12) {
+    sf = 12;
+  }
+
+  _sf = sf << 4;
+
+  setModulationParams(sf << 4, _bw, _cr);
+  handleLowDataRate();
+}
+
+long sx128x::getSignalBandwidth()
+{
+  int bw = _bw;
+  switch (bw) {
+      case 0x34: return 203.125E3;
+      case 0x26: return 406.25E3;
+      case 0x18: return 812.5E3;
+      case 0x0A: return 1625E3;
+  }
+  
+  return 0;
+}
+
+void sx128x::handleLowDataRate(){
+    // todo: do i need this??
+}
+
+void sx128x::optimizeModemSensitivity(){
+    // todo: check if there's anything the sx1280 can do here
+}
+
+void sx128x::setSignalBandwidth(long sbw)
+{
+      if (sbw <= 203.125E3) {
+          _bw = 0x34;
+      } else if (sbw <= 406.25E3) {
+          _bw = 0x26;
+      } else if (sbw <= 812.5E3) {
+          _bw = 0x18;
+      } else {
+          _bw = 0x0A;
+      }
+
+      setModulationParams(_sf, _bw, _cr);
+
+  handleLowDataRate();
+  optimizeModemSensitivity();
+}
+
+void sx128x::setCodingRate4(int denominator)
+{
+  if (denominator < 5) {
+    denominator = 5;
+  } else if (denominator > 8) {
+    denominator = 8;
+  }
+
+  _cr = denominator - 4;
+
+  // todo: add support for new interleaving scheme, see page 117 of sx1280
+  // datasheet
+
+  // update cr values for sx1280's use
+
+  setModulationParams(_sf, _bw, _cr);
+}
+
+void sx128x::setPreambleLength(long length)
+{
+  _preambleLength = length;
+  setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx128x::setSyncWord(int sw)
+{
+    // not implemented
+}
+
+void sx128x::enableCrc()
+{
+      _crcMode = 0x20;
+      setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx128x::disableCrc()
+{
+    _crcMode = 0;
+    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+byte sx128x::random()
+{
+    // todo: implement
+}
+
+void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen)
+{
+  _ss = ss;
+  _reset = reset;
+  _dio0 = dio0;
+  _busy = busy;
+  _rxen = rxen;
+  _txen = txen;
+}
+
+void sx128x::setSPIFrequency(uint32_t frequency)
+{
+  _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
+}
+
+void sx128x::dumpRegisters(Stream& out)
+{
+  for (int i = 0; i < 128; i++) {
+    out.print("0x");
+    out.print(i, HEX);
+    out.print(": 0x");
+    out.println(readRegister(i), HEX);
+  }
+}
+
+void sx128x::explicitHeaderMode()
+{
+  _implicitHeaderMode = 0;
+
+  setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx128x::implicitHeaderMode()
+{
+    _implicitHeaderMode = 0x80;
+    setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+
+void ISR_VECT sx128x::handleDio0Rise()
+{
+    uint8_t buf[2];
+
+    buf[0] = 0x00;
+    buf[1] = 0x00;
+
+    executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
+
+    executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2);
+
+    if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) {
+        // received a packet
+        _packetIndex = 0;
+
+        uint8_t rxbuf[2] = {0};
+        executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2);
+        _rxPacketLength = rxbuf[0];
+        _fifo_rx_addr_ptr = rxbuf[1];
+        readBuffer(_packet, _rxPacketLength);
+
+        if (_onReceive) {
+            _onReceive(_rxPacketLength);
+        }
+
+    }
+}
+
+void ISR_VECT sx128x::onDio0Rise()
+{
+  sx128x_modem.handleDio0Rise();
+}
+
+sx128x sx128x_modem;
diff --git a/sx128x.h b/sx128x.h
new file mode 100644 (file)
index 0000000..213fc48
--- /dev/null
+++ b/sx128x.h
@@ -0,0 +1,144 @@
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2023 by Mark Qvist
+// Obviously still under the MIT license.
+
+#ifndef SX128X_H
+#define SX128X_H
+
+#include <Arduino.h>
+#include <SPI.h>
+#include "Modem.h"
+
+#define LORA_DEFAULT_SS_PIN    10
+#define LORA_DEFAULT_RESET_PIN 9
+#define LORA_DEFAULT_DIO0_PIN  2
+#define LORA_DEFAULT_RXEN_PIN  -1
+#define LORA_DEFAULT_TXEN_PIN  -1
+#define LORA_DEFAULT_BUSY_PIN  -1
+
+#define PA_OUTPUT_RFO_PIN      0
+#define PA_OUTPUT_PA_BOOST_PIN 1
+
+#define RSSI_OFFSET 157
+
+class sx128x : public Stream {
+public:
+  sx128x();
+
+  int begin(unsigned long frequency);
+  void end();
+
+  int beginPacket(int implicitHeader = false);
+  int endPacket();
+
+  int parsePacket(int size = 0);
+  int packetRssi();
+  int currentRssi();
+  uint8_t packetRssiRaw();
+  uint8_t currentRssiRaw();
+  uint8_t packetSnrRaw();
+  float packetSnr();
+  long packetFrequencyError();
+
+  // from Print
+  virtual size_t write(uint8_t byte);
+  virtual size_t write(const uint8_t *buffer, size_t size);
+
+  // from Stream
+  virtual int available();
+  virtual int read();
+  virtual int peek();
+  virtual void flush();
+
+  void onReceive(void(*callback)(int));
+
+  void receive(int size = 0);
+  void idle();
+  void sleep();
+
+  bool preInit();
+  uint8_t getTxPower();
+  void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+  uint32_t getFrequency();
+  void setFrequency(unsigned long frequency);
+  void setSpreadingFactor(int sf);
+  long getSignalBandwidth();
+  void setSignalBandwidth(long sbw);
+  void setCodingRate4(int denominator);
+  void setPreambleLength(long length);
+  void setSyncWord(int sw);
+  uint8_t modemStatus();
+  void enableCrc();
+  void disableCrc();
+  void enableTCXO();
+  void disableTCXO();
+
+  void txAntEnable();
+  void rxAntEnable();
+  void loraMode();
+  void waitOnBusy();
+  void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
+  void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
+  void writeBuffer(const uint8_t* buffer, size_t size);
+  void readBuffer(uint8_t* buffer, size_t size);
+  void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
+  void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr);
+
+  // deprecated
+  void crc() { enableCrc(); }
+  void noCrc() { disableCrc(); }
+
+  byte random();
+
+  void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN);
+  void setSPIFrequency(uint32_t frequency);
+
+  void dumpRegisters(Stream& out);
+
+private:
+  void explicitHeaderMode();
+  void implicitHeaderMode();
+
+  void handleDio0Rise();
+
+  uint8_t readRegister(uint16_t address);
+  void writeRegister(uint16_t address, uint8_t value);
+  uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
+
+  static void onDio0Rise();
+
+  void handleLowDataRate();
+  void optimizeModemSensitivity();
+
+private:
+  SPISettings _spiSettings;
+  int _ss;
+  int _reset;
+  int _dio0;
+  int _rxen;
+  int _txen;
+  int _busy;
+  int _modem;
+  unsigned long _frequency;
+  int _txp;
+  uint8_t _sf;
+  uint8_t _bw;
+  uint8_t _cr;
+  int _packetIndex;
+  uint32_t _preambleLength;
+  int _implicitHeaderMode;
+  int _payloadLength;
+  int _crcMode;
+  int _fifo_tx_addr_ptr;
+  int _fifo_rx_addr_ptr;
+  uint8_t _packet[256];
+  bool _preinit_done;
+  int _rxPacketLength;
+  void (*_onReceive)(int);
+};
+
+extern sx128x sx128x_modem;
+
+#endif