]> git.tnoah.ca Git - lora-radio.git/commitdiff
Add multiple interface support
authorjacob.eva <x00010@disroot.org>
Mon, 10 Jun 2024 12:05:21 +0000 (13:05 +0100)
committerjacob.eva <x00010@disroot.org>
Mon, 10 Jun 2024 12:05:21 +0000 (13:05 +0100)
18 files changed:
Boards.h
Config.h
Display.h
Documentation/CONTRIBUTING.md
Framing.h
Graphics.h
Interfaces.h [new file with mode: 0644]
Modem.h [deleted file]
RNode_Firmware_CE.ino
Radio.cpp [new file with mode: 0644]
Radio.h [new file with mode: 0644]
Utilities.h
sx126x.cpp [deleted file]
sx126x.h [deleted file]
sx127x.cpp [deleted file]
sx127x.h [deleted file]
sx128x.cpp [deleted file]
sx128x.h [deleted file]

index 310543943387a126c9246a5614c05e2be7611ac0..7321269af93dac4fb9aaeecfc731135af4db7985 100644 (file)
--- a/Boards.h
+++ b/Boards.h
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-#include "Modem.h"
+#include "Interfaces.h"
 
 #ifndef BOARDS_H
   #define BOARDS_H
 
-  #define PLATFORM_AVR        0x90
   #define PLATFORM_ESP32      0x80
   #define PLATFORM_NRF52      0x70
 
-  #define MCU_1284P           0x91
-  #define MCU_2560            0x92
   #define MCU_ESP32           0x81
   #define MCU_NRF52           0x71
 
   #define EINK_BW 0x02
   #define EINK_3C 0x03
 
-  #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)
+  #if defined(ESP32)
     #define PLATFORM PLATFORM_ESP32
     #define MCU_VARIANT MCU_ESP32
   #elif defined(NRF52840_XXAA)
       #error "The firmware cannot be compiled for the selected MCU variant"
   #endif
 
-  #ifndef MODEM
-    #if BOARD_MODEL == BOARD_RAK4631
-      #define MODEM SX1262
-    #elif BOARD_MODEL == BOARD_GENERIC_NRF52
-      #define MODEM SX1262
-    #else
-      #define MODEM SX1276
-    #endif
-  #endif
-
   #define HAS_DISPLAY false
   #define HAS_BLUETOOTH false
   #define HAS_BLE false
       #define HAS_TCXO true
   #endif
 
-  #if MCU_VARIANT == MCU_1284P
-    const int pin_cs = 4;
-    const int pin_reset = 3;
-    const int pin_dio = 2;
-    const int pin_led_rx = 12;
-    const int pin_led_tx = 13;
-
-    #define BOARD_MODEL BOARD_RNODE
-    #define HAS_EEPROM true
-    #define CONFIG_UART_BUFFER_SIZE 6144
-    #define CONFIG_QUEUE_SIZE 6144
-    #define CONFIG_QUEUE_MAX_LENGTH 200
-    #define EEPROM_SIZE 4096
-    #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
-  
-  #elif MCU_VARIANT == MCU_2560
-    const int pin_cs = 5;
-    const int pin_reset = 4;
-    const int pin_dio = 2;
-    const int pin_led_rx = 12;
-    const int pin_led_tx = 13;
-
-    #define BOARD_MODEL BOARD_HMBRW
-    #define HAS_EEPROM true
-    #define CONFIG_UART_BUFFER_SIZE 768
-    #define CONFIG_QUEUE_SIZE 5120
-    #define CONFIG_QUEUE_MAX_LENGTH 24
-    #define EEPROM_SIZE 4096
-    #define EEPROM_OFFSET EEPROM_SIZE-EEPROM_RESERVED
-
-  #elif MCU_VARIANT == MCU_ESP32
+  #if MCU_VARIANT == MCU_ESP32
 
     // Board models for ESP32 based builds are
     // defined by the build target in the makefile.
       #define HAS_BLUETOOTH true
       #define HAS_CONSOLE true
       #define HAS_EEPROM true
-      const int pin_cs = 4;
-      const int pin_reset = 36;
-      const int pin_dio = 39;
+      #define INTERFACE_COUNT 1
       const int pin_led_rx = 14;
       const int pin_led_tx = 32;
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX127X};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX127X
+          {
+              true, // DEFAULT_SPI
+              false, // HAS_TCXO
+              false  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX127X
+          {
+              4, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              -1, // pin_busy
+              39, // pin_dio
+              36, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
 
     #elif BOARD_MODEL == BOARD_TBEAM
       #define HAS_DISPLAY true
       #define I2C_SDA 21
       #define I2C_SCL 22
       #define PMU_IRQ 35
-      const int pin_cs = 18;
-      const int pin_reset = 23;
       const int pin_led_rx = 2;
       const int pin_led_tx = 4;
 
-      #if MODEM == SX1262
-        #define HAS_TCXO true
-        #define HAS_BUSY true
-        #define DIO2_AS_RF_SWITCH true
-        const int pin_busy = 32;
-        const int pin_dio = 33;
-        const int pin_tcxo_enable = -1;
-      #else
-        const int pin_dio = 26;
-      #endif
+      #define INTERFACE_COUNT 1
+
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX1262
+          {
+              true, // DEFAULT_SPI
+              true, // HAS_TCXO
+              true  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX1262
+          {
+              18, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              32, // pin_busy
+              33, // pin_dio
+              23, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
 
     #elif BOARD_MODEL == BOARD_HUZZAH32
       #define HAS_BLUETOOTH true
       #define HAS_CONSOLE true
       #define HAS_EEPROM true
-      const int pin_cs = 4;
-      const int pin_reset = 36;
-      const int pin_dio = 39;
       const int pin_led_rx = 14;
       const int pin_led_tx = 32;
 
+      #define INTERFACE_COUNT 1
+
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX127X};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX127X
+          {
+              true, // DEFAULT_SPI
+              false, // HAS_TCXO
+              false  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX1262
+          {
+               4, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              -1, // pin_busy
+              39, // pin_dio
+              36, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+
     #elif BOARD_MODEL == BOARD_LORA32_V1_0
       #define HAS_DISPLAY true
       #define DISPLAY OLED
         const int pin_led_tx = 2;
       #endif
 
+      #define INTERFACE_COUNT 1
+
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX1276};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX1276
+          {
+              true, // DEFAULT_SPI
+              false, // HAS_TCXO
+              false  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX1276
+          {
+              18, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              -1, // pin_busy
+              26, // pin_dio
+              14, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+
     #elif BOARD_MODEL == BOARD_LORA32_V2_0
       #define HAS_DISPLAY true
       #define DISPLAY OLED
         const int pin_led_tx = 22;
       #endif
 
+      #define INTERFACE_COUNT 1
+
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX127X};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX127X
+          {
+              true, // DEFAULT_SPI
+              false, // HAS_TCXO
+              false  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX127X
+          {
+              18, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              -1, // pin_busy
+              26, // pin_dio
+              14, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+
     #elif BOARD_MODEL == BOARD_LORA32_V2_1
       #define HAS_DISPLAY true
       #define DISPLAY OLED
       #define HAS_PMU true
       #define HAS_CONSOLE true
       #define HAS_EEPROM true
-      const int pin_cs = 18;
-      const int pin_reset = 23;
-      const int pin_dio = 26;
       #if HAS_TCXO == true
-        const int pin_tcxo_enable = 33;
+        const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                        // SX127X
+              {
+                  true, // DEFAULT_SPI
+                  true, // HAS_TCXO
+                  false  // DIO2_AS_RF_SWITCH
+              }, 
+        };
+      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX127X
+          {
+              18, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              -1, // pin_busy
+              26, // pin_dio
+              23, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              33  // pin_tcxo_enable
+          }
+      };
       #endif
       #if defined(EXTERNAL_LEDS)
         const int pin_led_rx = 15;
         const int pin_led_tx = 25;
       #endif
 
+      #define INTERFACE_COUNT 1
+
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX127X};
+
+      #if HAS_TCXO == false
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX127X
+          {
+              true, // DEFAULT_SPI
+              false, // HAS_TCXO
+              false  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+
+      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX127X
+          {
+              18, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              -1, // pin_busy
+              26, // pin_dio
+              23, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+      #endif
+
     #elif BOARD_MODEL == BOARD_HELTEC32_V2
       #define HAS_DISPLAY true
       #define DISPLAY OLED
       #define HAS_BLUETOOTH true
       #define HAS_CONSOLE true
       #define HAS_EEPROM true
-      const int pin_cs = 18;
-      const int pin_reset = 14;
-      const int pin_dio = 26;
       #if defined(EXTERNAL_LEDS)
         const int pin_led_rx = 36;
         const int pin_led_tx = 37;
         const int pin_led_tx = 25;
       #endif
 
+      #define INTERFACE_COUNT 1
+
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX127X};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX127X
+          {
+              true, // DEFAULT_SPI
+              false, // HAS_TCXO
+              false  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const int8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX127X
+          {
+              18, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              -1, // pin_busy
+              26, // pin_dio
+              14, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+
     #elif BOARD_MODEL == BOARD_HELTEC32_V3
       #define IS_ESP32S3 true
       #define HAS_DISPLAY true
       #define HAS_SLEEP true
       #define PIN_WAKEUP GPIO_NUM_0
       #define WAKEUP_LEVEL 0
+      #define INTERFACE_COUNT 1
 
       const int pin_btn_usr1 = 0;
 
         const int pin_led_tx = 35;
       #endif
 
-      #define MODEM SX1262
-      #define HAS_TCXO true
-      const int pin_tcxo_enable = -1;
-      #define HAS_BUSY true
-      #define DIO2_AS_RF_SWITCH true
-
-      // Following pins are for the SX1262
-      const int pin_cs = 8;
-      const int pin_busy = 13;
-      const int pin_dio = 14;
-      const int pin_reset = 12;
-      const int pin_mosi = 10;
-      const int pin_miso = 11;
-      const int pin_sclk = 9;
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX1262
+          {
+              false, // DEFAULT_SPI
+              true, // HAS_TCXO
+              true  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const uint8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX1262
+          {
+              8, // pin_ss
+              9, // pin_sclk
+              10, // pin_mosi
+              11, // pin_miso
+              13, // pin_busy
+              14, // pin_dio
+              12, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
 
     #elif BOARD_MODEL == BOARD_RNODE_NG_20
       #define HAS_DISPLAY true
         #endif
       #endif
 
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX1276};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX1276
+          {
+              false, // DEFAULT_SPI
+              true, // HAS_TCXO
+              true  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const uint8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX1276
+          {
+              8, // pin_ss
+              9, // pin_sclk
+              10, // pin_mosi
+              11, // pin_miso
+              13, // pin_busy
+              14, // pin_dio
+              12, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+
     #elif BOARD_MODEL == BOARD_RNODE_NG_21
       #define HAS_DISPLAY true
       #define DISPLAY OLED
       #define HAS_NP true
       #define HAS_SD false
       #define HAS_EEPROM true
-      const int pin_cs = 18;
-      const int pin_reset = 23;
-      const int pin_dio = 26;
       const int pin_np = 12;
       const int pin_dac = 25;
       const int pin_adc = 34;
         #endif
       #endif
 
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX127X};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX127X
+          {
+              true, // DEFAULT_SPI
+              false, // HAS_TCXO
+              false  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const uint8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX127X
+          {
+              18, // pin_ss
+              -1, // pin_sclk
+              -1, // pin_mosi
+              -1, // pin_miso
+              -1, // pin_busy
+              26, // pin_dio
+              23, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+
     #elif BOARD_MODEL == BOARD_RNODE_NG_22
       #define IS_ESP32S3 true
       #define MODEM SX1262
-      #define DIO2_AS_RF_SWITCH true
-      #define HAS_BUSY true
-      #define HAS_TCXO true
 
       #define HAS_DISPLAY true
       #define DISPLAY OLED
       // #define PIN_DISP_SLEEP 21
       // #define DISP_SLEEP_LEVEL HIGH
       const int pin_btn_usr1 = 0;
-
-      const int pin_cs = 7;
-      const int pin_reset = 8;
-      const int pin_sclk = 5;
-      const int pin_mosi = 6;
-      const int pin_miso = 3;
-      const int pin_tcxo_enable = -1;
-
-      const int pin_dio = 33;
-      const int pin_busy = 34;
       
       const int pin_np = 38;
       const int pin_dac = 25;
         #endif
       #endif
 
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX1262
+          {
+              false, // DEFAULT_SPI
+              true, // HAS_TCXO
+              true  // DIO2_AS_RF_SWITCH
+          }, 
+      };
+      const uint8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX1262
+          {
+               7, // pin_ss
+               5, // pin_sclk
+               6, // pin_mosi
+               3, // pin_miso
+              34, // pin_busy
+              33, // pin_dio
+               8, // pin_reset
+              -1, // pin_txen
+              -1, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+
     #else
       #error An unsupported ESP32 board was selected. Cannot compile RNode firmware.
     #endif
     #if BOARD_MODEL == BOARD_RAK4631
       #define HAS_EEPROM false
       #define HAS_DISPLAY true
-      #define DISPLAY EINK_3C
+      #define DISPLAY EINK_BW
       #define HAS_BLUETOOTH false
       #define HAS_BLE true
       #define HAS_CONSOLE false
       #define HAS_PMU true
       #define HAS_NP false
       #define HAS_SD false
-      #define HAS_TCXO true
       #define HAS_RF_SWITCH_RX_TX true
-      #define HAS_BUSY true
-      #define DIO2_AS_RF_SWITCH true
       #define CONFIG_UART_BUFFER_SIZE 6144
       #define CONFIG_QUEUE_SIZE 6144
       #define CONFIG_QUEUE_MAX_LENGTH 200
       #define BLE_MANUFACTURER "RAK Wireless"
       #define BLE_MODEL "RAK4640"
 
-      // Following pins are for the sx1262
-      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;
+      #define INTERFACE_COUNT 1
+
+      // first interface in list is the primary
+      const uint8_t interfaces[INTERFACE_COUNT] = {SX126X};
+      const bool interface_cfg[INTERFACE_COUNT][3] = { 
+                    // SX1262
+          {
+              false, // DEFAULT_SPI
+              true, // HAS_TCXO
+              true  // DIO2_AS_RF_SWITCH
+          }
+      };
+      const uint8_t interface_pins[INTERFACE_COUNT][10] = { 
+                  // SX1262
+          {
+              42, // pin_ss
+              43, // pin_sclk
+              44, // pin_mosi
+              45, // pin_miso
+              46, // pin_busy
+              47, // pin_dio
+              38, // pin_reset
+              -1, // pin_txen
+              37, // pin_rxen
+              -1  // pin_tcxo_enable
+          }
+      };
+
+        #define INTERFACE_SPI
+        // Required because on RAK4631, non-default SPI pins must be initialised when class is declared.
+      const SPIClass interface_spi[1] = {
+            // SX1262
+            SPIClass(
+                NRF_SPIM2, 
+                interface_pins[0][3], 
+                interface_pins[0][1], 
+                interface_pins[0][2]
+               )
+      };
 
       const int pin_disp_cs = SS;
       const int pin_disp_dc = WB_IO1;
 
       const int pin_led_rx = LED_BLUE;
       const int pin_led_tx = LED_GREEN;
-      const int pin_tcxo_enable = -1;
 
     #else
       #error An unsupported nRF board was selected. Cannot compile RNode firmware.
     #endif
 
   #endif
-
-  #ifndef HAS_RF_SWITCH_RX_TX
-    const int pin_rxen = -1;
-    const int pin_txen = -1;
+  #ifndef INTERFACE_SPI
+    // Even if custom SPI interfaces are not needed, the array must exist to prevent compilation errors.
+    #define INTERFACE_SPI
+    const SPIClass interface_spi[1];
   #endif
-
-  #ifndef HAS_BUSY
-    const int pin_busy = -1;
-  #endif
-
-  #ifndef DIO2_AS_RF_SWITCH
-    #define DIO2_AS_RF_SWITCH false
-  #endif
-
 #endif
index 047a3fb0ecb148932d4995959a801b02345dd37e..f0bb09c01234542de148f906df3738825f14c1d0 100644 (file)
--- a/Config.h
+++ b/Config.h
@@ -43,7 +43,7 @@
        #define M_FRQ_S 27388122
        #define M_FRQ_R 27388061
        bool console_active = false;
-       bool modem_installed = false;
+       bool modems_installed = false;
 
        #define MTU        508
        #define SINGLE_MTU 255
        #define MIN_L      1
        #define CMD_L      64
 
-    bool mw_radio_online = false;
-
        #define eeprom_addr(a) (a+EEPROM_OFFSET)
 
-    #if (MODEM == SX1262 || MODEM == SX1280) && defined(NRF52840_XXAA)
-        SPIClass spiModem(NRF_SPIM2, pin_miso, pin_sclk, pin_mosi);
-    #endif
+    #define PA_OUTPUT_RFO_PIN      0
+    #define PA_OUTPUT_PA_BOOST_PIN 1
 
        // MCU independent configuration parameters
        const long serial_baudrate  = 115200;
        // packet RSSI register
        const int  rssi_offset = 157;
 
-       // Default LoRa settings
-       const int lora_rx_turnaround_ms = 66;
-       const int lora_post_tx_yield_slots = 6;
-       uint32_t post_tx_yield_timeout = 0;
-       #define LORA_PREAMBLE_SYMBOLS_HW  4
-       #define LORA_PREAMBLE_SYMBOLS_MIN 18
-       #define LORA_PREAMBLE_TARGET_MS   15
-       #define LORA_CAD_SYMBOLS 3
-       int csma_slot_ms = 50;
-       float csma_p_min = 0.1;
-       float csma_p_max = 0.8;
-       uint8_t csma_p = 0;
-
-       int  lora_sf               = 0;
-       int  lora_cr               = 5;
-       int  lora_txp              = 0xFF;
-       uint32_t lora_bw           = 0;
-       uint32_t lora_freq         = 0;
-       uint32_t lora_bitrate      = 0;
-       long lora_preamble_symbols = 6;
-       float lora_symbol_time_ms  = 0.0;
-       float lora_symbol_rate     = 0.0;
-       float lora_us_per_byte     = 0.0;
+
+    // Default LoRa settings
+    const int lora_rx_turnaround_ms = 66;
+    const int lora_post_tx_yield_slots = 6;
+    #define LORA_CAD_SYMBOLS 3
 
        // Operational variables
-       bool radio_locked  = true;
-       bool radio_online  = false;
        bool community_fw  = true;
        bool hw_ready      = false;
-       bool radio_error   = false;
        bool disp_ready    = false;
        bool pmu_ready     = false;
        bool promisc       = false;
        uint8_t model     = 0x00;
        uint8_t hwrev     = 0x00;
 
-    int     current_rssi    = -292;
        int             last_rssi               = -292;
        uint8_t last_rssi_raw   = 0x00;
        uint8_t last_snr_raw    = 0x80;
        uint32_t stat_rx                = 0;
        uint32_t stat_tx                = 0;
 
-       #define STATUS_INTERVAL_MS 3
-       #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-         #define DCD_SAMPLES 2500
-               #define UTIL_UPDATE_INTERVAL_MS 1000
-               #define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS)
-               #define AIRTIME_LONGTERM 3600
-               #define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000)
-               #define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES)
-               #define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS)
-               bool util_samples[DCD_SAMPLES];
-               uint16_t airtime_bins[AIRTIME_BINS];
-               float longterm_bins[AIRTIME_BINS];
-               int dcd_sample = 0;
-               float local_channel_util = 0.0;
-               float total_channel_util = 0.0;
-               float longterm_channel_util = 0.0;
-               float airtime = 0.0;
-               float longterm_airtime = 0.0;
-               #define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS
-       #endif
-       float st_airtime_limit = 0.0;
-       float lt_airtime_limit = 0.0;
-       bool airtime_lock = false;
-
-       bool stat_signal_detected   = false;
-       bool stat_signal_synced     = false;
-       bool stat_rx_ongoing        = false;
-       bool dcd                    = false;
-       bool dcd_led                = false;
-       bool dcd_waiting            = false;
-       long dcd_wait_until         = 0;
-       uint16_t dcd_count          = 0;
-       uint16_t dcd_threshold      = 2;
-
-       uint32_t status_interval_ms = STATUS_INTERVAL_MS;
-       uint32_t last_status_update = 0;
-       uint32_t last_dcd = 0;
-
-       // Status flags
-       const uint8_t SIG_DETECT = 0x01;
-       const uint8_t SIG_SYNCED = 0x02;
-       const uint8_t RX_ONGOING = 0x04;
-
     // Power management
     #define BATTERY_STATE_DISCHARGING 0x01
     #define BATTERY_STATE_CHARGING 0x02
        #define START_FROM_BROWNOUT 0x03
        #define START_FROM_JTAG 0x04
 
+    // Subinterfaces
+    // select interface 0 by default
+    uint8_t interface = 0;
+    RadioInterface* selected_radio;
+    RadioInterface* interface_obj[INTERFACE_COUNT];
+    RadioInterface* interface_obj_sorted[INTERFACE_COUNT];
 #endif
index d566a53a967cf9f573f063c8acf05d5e73c06d2d..54f53b291a60d69b73cfeefb3085123c9276c54e 100644 (file)
--- a/Display.h
+++ b/Display.h
@@ -130,10 +130,19 @@ unsigned char fb[512];
 uint32_t last_disp_update = 0;
 int disp_update_interval = 1000/disp_target_fps;
 uint32_t last_page_flip = 0;
+uint32_t last_interface_page_flip = 0;
 int page_interval = 4000;
 bool device_signatures_ok();
 bool device_firmware_ok();
 
+bool stat_area_initialised = false;
+bool radio_online = false;
+
+#define START_PAGE 0
+const uint8_t pages = 3;
+uint8_t disp_page = START_PAGE;
+uint8_t interface_page = START_PAGE;
+
 #if DISPLAY == OLED
 #define WATERFALL_SIZE 46
 #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
@@ -142,8 +151,8 @@ bool device_firmware_ok();
 // add more eink compatible boards here
 #endif
 
-int waterfall[WATERFALL_SIZE];
-int waterfall_head = 0;
+int waterfall[INTERFACE_COUNT][WATERFALL_SIZE] = {0};
+int waterfall_head[INTERFACE_COUNT] = {0};
 
 int p_ad_x = 0;
 int p_ad_y = 0;
@@ -286,9 +295,6 @@ bool display_init() {
       #endif
 
       update_area_positions();
-      for (int i = 0; i < WATERFALL_SIZE; i++) {
-        waterfall[i] = 0;
-      }
 
       last_page_flip = millis();
 
@@ -359,34 +365,63 @@ void draw_bt_icon(int px, int py) {
   }
 }
 
-void draw_lora_icon(int px, int py) {
+void draw_lora_icon(RadioInterface* radio, int px, int py) {
+  // todo: make display show other interfaces
   if (radio_online) {
-    #if DISPLAY == OLED
-        stat_area.drawBitmap(px, py, bm_rf+1*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
-    #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
-        stat_area.drawBitmap(px, py, bm_rf+1*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
-    #endif
-  } else {
-    #if DISPLAY == OLED
-        stat_area.drawBitmap(px, py, bm_rf+0*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
-    #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
-        stat_area.drawBitmap(px, py, bm_rf+0*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
-    #endif
-  }
+        #if DISPLAY == OLED
+                if (interface_page == radio->getIndex()) {
+                    stat_area.drawBitmap(px - 2, py - 2, bm_dot_sqr, 18, 18, GxEPD_WHITE, GxEPD_BLACK);
+
+                    // redraw stat area on next refresh
+                    stat_area_initialised = false;
+                }
+                  if (radio->getRadioOnline()) {
+                stat_area.drawBitmap(px, py, bm_rf+1*32, 16, 16, GxEPD_WHITE, GxEPD_BLACK);
+                  } else {
+                stat_area.drawBitmap(px, py, bm_rf+0*32, 16, 16, GxEPD_WHITE, GxEPD_BLACK);
+                  }
+        #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
+                if (interface_page == radio->getIndex()) {
+                    stat_area.drawBitmap(px - 2, py - 2, bm_dot_sqr, 34, 36, GxEPD_WHITE, GxEPD_BLACK);
+
+                    // redraw stat area on next refresh
+                    stat_area_initialised = false;
+                }
+                  if (radio->getRadioOnline()) {
+                stat_area.drawBitmap(px, py, bm_rf+1*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
+                  } else {
+                stat_area.drawBitmap(px, py, bm_rf+0*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
+                  }
+        #endif
+      } else {
+        #if DISPLAY == OLED
+            stat_area.drawBitmap(px, py, bm_rf+0*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
+        #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
+            stat_area.drawBitmap(px, py, bm_rf+0*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
+        #endif
+      }
 }
 
 void draw_mw_icon(int px, int py) {
-  if (mw_radio_online) {
-    #if DISPLAY == OLED
-        stat_area.drawBitmap(px, py, bm_rf+3*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
-    #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
-        stat_area.drawBitmap(px, py, bm_rf+3*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
-    #endif
+  if (INTERFACE_COUNT >= 2) {
+      if (interface_obj[1]->getRadioOnline()) {
+        #if DISPLAY == OLED
+            stat_area.drawBitmap(px, py, bm_rf+3*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
+        #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
+            stat_area.drawBitmap(px, py, bm_rf+3*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
+        #endif
+      } else {
+        #if DISPLAY == OLED
+            stat_area.drawBitmap(px, py, bm_rf+2*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
+        #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
+            stat_area.drawBitmap(px, py, bm_rf+2*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
+        #endif
+      }
   } else {
     #if DISPLAY == OLED
-        stat_area.drawBitmap(px, py, bm_rf+2*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
+      stat_area.drawBitmap(px, py, bm_rf+2*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
     #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
-        stat_area.drawBitmap(px, py, bm_rf+2*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
+      stat_area.drawBitmap(px, py, bm_rf+2*128, 30, 32, GxEPD_WHITE, GxEPD_BLACK);
     #endif
   }
 }
@@ -479,7 +514,7 @@ void draw_battery_bars(int px, int py) {
 void draw_quality_bars(int px, int py) {
   signed char t_snr = (signed int)last_snr_raw;
   int snr_int = (int)t_snr;
-  float snr_min = Q_SNR_MIN_BASE-(int)lora_sf*Q_SNR_STEP;
+  float snr_min = Q_SNR_MIN_BASE-(int)interface_obj[interface_page]->getSpreadingFactor()*Q_SNR_STEP;
   float snr_span = (Q_SNR_MAX-snr_min);
   float snr = ((int)snr_int) * 0.25;
   float quality = ((snr-snr_min)/(snr_span))*100;
@@ -593,13 +628,13 @@ void draw_signal_bars(int px, int py) {
 #define WF_PIXEL_WIDTH 22
 #endif
 void draw_waterfall(int px, int py) {
-  int rssi_val = current_rssi;
+  int rssi_val = interface_obj[interface_page]->currentRssi();
   if (rssi_val < WF_RSSI_MIN) rssi_val = WF_RSSI_MIN;
   if (rssi_val > WF_RSSI_MAX) rssi_val = WF_RSSI_MAX;
   int rssi_normalised = ((rssi_val - WF_RSSI_MIN)*(1.0/WF_RSSI_SPAN))*WF_PIXEL_WIDTH;
 
-  waterfall[waterfall_head++] = rssi_normalised;
-  if (waterfall_head >= WATERFALL_SIZE) waterfall_head = 0;
+  waterfall[interface_page][waterfall_head[interface_page]++] = rssi_normalised;
+  if (waterfall_head[interface_page] >= WATERFALL_SIZE) waterfall_head[interface_page] = 0;
 
   #if DISPLAY == OLED
   stat_area.fillRect(px,py,WF_PIXEL_WIDTH, WATERFALL_SIZE, SSD1306_BLACK);
@@ -607,8 +642,8 @@ void draw_waterfall(int px, int py) {
   stat_area.fillRect(px,py,WF_PIXEL_WIDTH, WATERFALL_SIZE, GxEPD_BLACK);
   #endif
   for (int i = 0; i < WATERFALL_SIZE; i++){
-    int wi = (waterfall_head+i)%WATERFALL_SIZE;
-    int ws = waterfall[wi];
+    int wi = (waterfall_head[interface_page]+i)%WATERFALL_SIZE;
+    int ws = waterfall[interface_page][wi];
     if (ws > 0) {
       #if DISPLAY == OLED
         stat_area.drawLine(px, py+i, px+ws-1, py+i, SSD1306_WHITE);
@@ -619,7 +654,6 @@ void draw_waterfall(int px, int py) {
   }
 }
 
-bool stat_area_initialised = false;
 void draw_stat_area() {
   if (device_init_done) {
     if (!stat_area_initialised) {
@@ -631,19 +665,51 @@ void draw_stat_area() {
       stat_area_initialised = true;
     }
 
+    if (millis()-last_interface_page_flip >= page_interval) {
+      int online_interfaces = 0;
+      for (int i = 0; i < INTERFACE_COUNT; i++) {
+          if (interface_obj[i]->getRadioOnline()) {
+              online_interfaces++;
+          }
+      }
+
+      // cap at two for now, as only two boxes to symbolise interfaces
+      // available on display
+      if (online_interfaces > 2) {
+          online_interfaces = 2;
+      }
+      interface_page = (++interface_page%online_interfaces);
+      last_interface_page_flip = millis();
+    }
+
     #if DISPLAY == OLED
     draw_cable_icon(3, 8);
     draw_bt_icon(3, 30);
-    draw_lora_icon(45, 8);
-    draw_mw_icon(45, 30);
+    draw_lora_icon(interface_obj[0], 45, 8);
+
+    // todo, expand support to show more than two interfaces on screen
+    if (INTERFACE_COUNT > 1) {
+        draw_lora_icon(interface_obj[1], 45, 30);
+    }
     draw_battery_bars(4, 58);
     #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
     draw_cable_icon(6, 18);
     draw_bt_icon(6, 60);
-    draw_lora_icon(86, 18);
-    draw_mw_icon(86, 60);
+    draw_lora_icon(interface_obj[0], 86, 18);
+
+    // todo, expand support to show more than two interfaces on screen
+    if (INTERFACE_COUNT > 1) {
+        draw_lora_icon(interface_obj[1], 86, 60);
+    }
     draw_battery_bars(8, 113);
     #endif
+    radio_online = false;
+    for (int i = 0; i < INTERFACE_COUNT; i++) {
+        if (interface_obj[i]->getRadioOnline()) {
+            radio_online = true;
+            break;
+        }
+    }
     if (radio_online) {
       #if DISPLAY == OLED
       draw_quality_bars(28, 56);
@@ -701,9 +767,6 @@ void update_stat_area() {
   }
 }
 
-#define START_PAGE 0
-const uint8_t pages = 3;
-uint8_t disp_page = START_PAGE;
 void draw_disp_area() {
   if (!device_init_done || firmware_update_mode) {
     uint8_t p_by = 37;
@@ -726,6 +789,7 @@ void draw_disp_area() {
     if (!disp_ext_fb or bt_ssp_pin != 0) {
       if (radio_online && display_diagnostics) {
         #if DISPLAY == OLED
+          selected_radio = interface_obj[interface_page];
           disp_area.fillRect(0,8,disp_area.width(),37, SSD1306_BLACK); disp_area.fillRect(0,37,disp_area.width(),27, SSD1306_WHITE);
           disp_area.setFont(SMALL_FONT); disp_area.setTextWrap(false); disp_area.setTextColor(SSD1306_WHITE);
 
@@ -734,26 +798,26 @@ void draw_disp_area() {
           disp_area.setCursor(14, 13);
           disp_area.print("@");
           disp_area.setCursor(21, 13);
-          disp_area.printf("%.1fKbps", (float)lora_bitrate/1000.0);
+          disp_area.printf("%.1fKbps", (float)(selected_radio->getBitrate())/1000.0);
 
           disp_area.setCursor(2, 23-1);
           disp_area.print("Airtime:");
 
           disp_area.setCursor(11, 33-1);
-          if (total_channel_util < 0.099) {
-            disp_area.printf("%.1f%%", airtime*100.0);
+          if (selected_radio->getTotalChannelUtil() < 0.099) {
+            disp_area.printf("%.1f%%", selected_radio->getAirtime()*100.0);
           } else {
-            disp_area.printf("%.0f%%", airtime*100.0);
+            disp_area.printf("%.0f%%", selected_radio->getAirtime()*100.0);
           }
 
           disp_area.drawBitmap(2, 26-1, bm_hg_low, 5, 9, SSD1306_WHITE, SSD1306_BLACK);
 
           disp_area.setCursor(32+11, 33-1);
 
-          if (longterm_channel_util < 0.099) {
-            disp_area.printf("%.1f%%", longterm_airtime*100.0);
+          if (selected_radio->getLongtermChannelUtil() < 0.099) {
+            disp_area.printf("%.1f%%", selected_radio->getLongtermAirtime()*100.0);
           } else {
-            disp_area.printf("%.0f%%", longterm_airtime*100.0);
+            disp_area.printf("%.0f%%", selected_radio->getLongtermAirtime()*100.0);
           }
           disp_area.drawBitmap(32+2, 26-1, bm_hg_high, 5, 9, SSD1306_WHITE, SSD1306_BLACK);
 
@@ -765,22 +829,23 @@ void draw_disp_area() {
           disp_area.print("Load:");
 
           disp_area.setCursor(11, 57);
-          if (total_channel_util < 0.099) {
-            disp_area.printf("%.1f%%", total_channel_util*100.0);
+          if (selected_radio->getTotalChannelUtil() < 0.099) {
+            disp_area.printf("%.1f%%", selected_radio->getTotalChannelUtil()*100.0);
           } else {
-            disp_area.printf("%.0f%%", total_channel_util*100.0);
+            disp_area.printf("%.0f%%", selected_radio->getTotalChannelUtil()*100.0);
           }
           disp_area.drawBitmap(2, 50, bm_hg_low, 5, 9, SSD1306_BLACK, SSD1306_WHITE);
         
           disp_area.setCursor(32+11, 57);
-          if (longterm_channel_util < 0.099) {
-            disp_area.printf("%.1f%%", longterm_channel_util*100.0);
+          if (selected_radio->getLongtermChannelUtil() < 0.099) {
+            disp_area.printf("%.1f%%", selected_radio->getLongtermChannelUtil()*100.0);
           } else {
-            disp_area.printf("%.0f%%", longterm_channel_util*100.0);
+            disp_area.printf("%.0f%%", selected_radio->getLongtermChannelUtil()*100.0);
           }
           disp_area.drawBitmap(32+2, 50, bm_hg_high, 5, 9, SSD1306_BLACK, SSD1306_WHITE);
 
         #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
+          selected_radio = interface_obj[interface_page];
           disp_area.fillRect(0,12,disp_area.width(),57, GxEPD_BLACK); disp_area.fillRect(0,69,disp_area.width(),56, GxEPD_WHITE);
           disp_area.setFont(SMALL_FONT); disp_area.setTextWrap(false); disp_area.setTextColor(GxEPD_WHITE);
           disp_area.setTextSize(2); // scale text 2x
@@ -790,25 +855,25 @@ void draw_disp_area() {
           disp_area.setCursor(14*2, 22);
           disp_area.print("@");
           disp_area.setCursor(21*2, 22);
-          disp_area.printf("%.1fKbps", (float)lora_bitrate/1000.0);
+          disp_area.printf("%.1fKbps", (float)(selected_radio->getBitrate())/1000.0);
 
           disp_area.setCursor(2, 36);
           disp_area.print("Airtime:");
 
           disp_area.setCursor(7+12, 53);
-          if (total_channel_util < 0.099) {
-            disp_area.printf("%.1f%%", airtime*100.0);
+          if (selected_radio->getTotalChannelUtil() < 0.099) {
+            disp_area.printf("%.1f%%", selected_radio->getAirtime()*100.0);
           } else {
-            disp_area.printf("%.0f%%", airtime*100.0);
+            disp_area.printf("%.0f%%", selected_radio->getAirtime()*100.0);
           }
 
           disp_area.drawBitmap(2, 41, bm_hg_low, 10, 18, GxEPD_WHITE, GxEPD_BLACK);
 
           disp_area.setCursor(64+17, 53);
-          if (longterm_channel_util < 0.099) {
-            disp_area.printf("%.1f%%", longterm_airtime*100.0);
+          if (selected_radio->getLongtermChannelUtil() < 0.099) {
+            disp_area.printf("%.1f%%", selected_radio->getLongtermAirtime()*100.0);
           } else {
-            disp_area.printf("%.0f%%", longterm_airtime*100.0);
+            disp_area.printf("%.0f%%", selected_radio->getLongtermAirtime()*100.0);
           }
           disp_area.drawBitmap(64, 41, bm_hg_high, 10, 18, GxEPD_WHITE, GxEPD_BLACK);
 
@@ -820,18 +885,18 @@ void draw_disp_area() {
           disp_area.print("Load:");
         
           disp_area.setCursor(7+12, 110);
-          if (total_channel_util < 0.099) {
-            disp_area.printf("%.1f%%", total_channel_util*100.0);
+          if (selected_radio->getTotalChannelUtil() < 0.099) {
+            disp_area.printf("%.1f%%", selected_radio->getTotalChannelUtil()*100.0);
           } else {
-            disp_area.printf("%.0f%%", total_channel_util*100.0);
+            disp_area.printf("%.0f%%", selected_radio->getTotalChannelUtil()*100.0);
           }
           disp_area.drawBitmap(2, 98, bm_hg_low, 10, 18, GxEPD_BLACK, GxEPD_WHITE);
 
           disp_area.setCursor(64+17, 110);
-          if (longterm_channel_util < 0.099) {
-            disp_area.printf("%.1f%%", longterm_channel_util*100.0);
+          if (selected_radio->getLongtermChannelUtil() < 0.099) {
+            disp_area.printf("%.1f%%", selected_radio->getLongtermChannelUtil()*100.0);
           } else {
-            disp_area.printf("%.0f%%", longterm_channel_util*100.0);
+            disp_area.printf("%.0f%%", selected_radio->getLongtermChannelUtil()*100.0);
           }
           disp_area.drawBitmap(64, 98, bm_hg_high, 10, 18, GxEPD_BLACK, GxEPD_WHITE);
         #endif
@@ -852,7 +917,7 @@ void draw_disp_area() {
         }
       }
 
-      if (!hw_ready || radio_error || !device_firmware_ok()) {
+      if (!hw_ready || !device_firmware_ok()) {
         if (!device_firmware_ok()) {
           #if DISPLAY == OLED
             disp_area.drawBitmap(0, 37, bm_fw_corrupt, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
@@ -860,7 +925,7 @@ void draw_disp_area() {
             disp_area.drawBitmap(0, 71, bm_fw_corrupt, disp_area.width(), 54, GxEPD_WHITE, GxEPD_BLACK);
           #endif
         } else {
-          if (!modem_installed) {
+          if (!modems_installed) {
             #if DISPLAY == OLED
               disp_area.drawBitmap(0, 37, bm_no_radio, disp_area.width(), 27, SSD1306_WHITE, SSD1306_BLACK);
             #elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
index 28823044291bce926e4d809b65288531ea462df7..3a8d1ac16eee5af694bbdea231416ac0b56b6428 100644 (file)
@@ -10,6 +10,11 @@ This entry should include, at a minimum, the following:
 * whether the modem has a busy pin
 * RX and TX leds (preferably LEDs of different colours)
 
+# Check this area...
+see https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-spi.h#L39
+Effectively, there are multiple SPI buses we can map to pins on these
+devices (including the hardware SPI bus)
+
 An example of a minimal entry can be seen below:
 ```
 #elif BOARD_MODEL == BOARD_MY_WICKED_BOARD
index 0236aa96fb9e26ede8f4c31a1aa83302bd0debc9..0dfb1ce8d82c3f8649a2edbba22eedb8e4955140 100644 (file)
--- a/Framing.h
+++ b/Framing.h
@@ -22,7 +22,6 @@
   #define TFESC           0xDD
 
   #define CMD_UNKNOWN     0xFE
-  #define CMD_DATA        0x00
   #define CMD_FREQUENCY   0x01
   #define CMD_BANDWIDTH   0x02
   #define CMD_TXPOWER     0x03
   #define CMD_RESET       0x55
   #define CMD_RESET_BYTE  0xF8
 
+  #define CMD_INTERFACES  0x64
+
+  #define CMD_INT0_DATA   0x00
+  #define CMD_INT1_DATA   0x10
+  #define CMD_INT2_DATA   0x20
+  #define CMD_INT3_DATA   0x70
+  #define CMD_INT4_DATA   0x80
+  #define CMD_INT5_DATA   0x90
+  #define CMD_INT6_DATA   0xA0
+  #define CMD_INT7_DATA   0xB0
+  #define CMD_INT8_DATA   0xC0
+  #define CMD_INT9_DATA   0xD0
+  #define CMD_INT10_DATA  0xE0
+  #define CMD_INT11_DATA  0xF0
+
+  #define CMD_SEL_INT0    0x1E
+  #define CMD_SEL_INT1    0x1F
+  #define CMD_SEL_INT2    0x2F
+  #define CMD_SEL_INT3    0x7F
+  #define CMD_SEL_INT4    0x8F
+  #define CMD_SEL_INT5    0x9F
+  #define CMD_SEL_INT6    0xAF
+  #define CMD_SEL_INT7    0xBF
+  #define CMD_SEL_INT8    0xCF
+  #define CMD_SEL_INT9    0xDF
+  #define CMD_SEL_INT10   0xEF
+  #define CMD_SEL_INT11   0xFF
+
   #define DETECT_REQ      0x73
   #define DETECT_RESP     0x46
 
   bool ESCAPE = false;
   uint8_t command = CMD_UNKNOWN;
 
-#endif
\ No newline at end of file
+#endif
index 7e92bd663ff10586652328602dd8261bf51a8def..3b81690428a8ff8e8397d53150d195d9c5a21916 100644 (file)
@@ -401,6 +401,13 @@ const unsigned char bm_n_uh [] PROGMEM = {
    0xe7, 0xe7
 };
 
+const unsigned char bm_dot_sqr [] PROGMEM = {
+       0xdb, 0x36, 0xc0, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x80, 0x00, 0x40, 0x00, 
+       0x00, 0x00, 0x80, 0x00, 0x40, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 
+       0x40, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x40, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 
+       0x80, 0x00, 0x40, 0xdb, 0x36, 0xc0
+};
+
 #elif DISP_H == 122
 // use 122px wide graphics
 
@@ -1688,6 +1695,21 @@ const unsigned char bm_n_uh [] PROGMEM = {
        0xfc, 0x00, 0xfc, 0x00 // 9
 };
 
+const unsigned char bm_dot_sqr [] PROGMEM = {
+       0xee, 0xee, 0xdd, 0xdd, 0xc0, 0xee, 0xee, 0xdd, 0xdd, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 
+       0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 
+       0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 
+       0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 
+       0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 
+       0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 
+       0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 
+       0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 
+       0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 
+       0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0xc0, 
+       0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xee, 0xee, 0xdd, 0xdd, 0xc0, 0xee, 
+       0xee, 0xdd, 0xdd, 0xc0
+};
+
 #endif
 
 
diff --git a/Interfaces.h b/Interfaces.h
new file mode 100644 (file)
index 0000000..1d8e6a4
--- /dev/null
@@ -0,0 +1,7 @@
+#define SX127X 0x00
+#define SX1276 0x01
+#define SX1278 0x02
+#define SX126X 0x10
+#define SX1262 0x11
+#define SX128X 0x20
+#define SX1280 0x21
diff --git a/Modem.h b/Modem.h
deleted file mode 100644 (file)
index 027e314..0000000
--- a/Modem.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#define SX1276 0x01
-#define SX1278 0x02
-#define SX1262 0x03
-#define SX1280 0x04
index 1745a21141c0c5767452825cfaa748d3cd4f4b9c..cc4630b6f6fa0b76a5d3e7846c5a43ad71f685da 100644 (file)
 FIFOBuffer serialFIFO;
 uint8_t serialBuffer[CONFIG_UART_BUFFER_SIZE+1];
 
-FIFOBuffer16 packet_starts;
-uint16_t packet_starts_buf[CONFIG_QUEUE_MAX_LENGTH+1];
+uint16_t packet_starts_buf[(CONFIG_QUEUE_MAX_LENGTH/INTERFACE_COUNT)+1];
 
-FIFOBuffer16 packet_lengths;
-uint16_t packet_lengths_buf[CONFIG_QUEUE_MAX_LENGTH+1];
+uint16_t packet_lengths_buf[(CONFIG_QUEUE_MAX_LENGTH/INTERFACE_COUNT)+1];
 
-uint8_t packet_queue[CONFIG_QUEUE_SIZE];
+FIFOBuffer16 packet_starts[INTERFACE_COUNT];
+FIFOBuffer16 packet_lengths[INTERFACE_COUNT];
 
-volatile uint8_t queue_height = 0;
-volatile uint16_t queued_bytes = 0;
-volatile uint16_t queue_cursor = 0;
-volatile uint16_t current_packet_start = 0;
+// The total queue size is split evenly between all the interfaces.
+// todo, bias in size may be needed here for interfaces with higher data rates.
+uint8_t packet_queue[INTERFACE_COUNT][CONFIG_QUEUE_SIZE/INTERFACE_COUNT];
+
+volatile uint8_t queue_height[INTERFACE_COUNT] = {0};
+volatile uint16_t queued_bytes[INTERFACE_COUNT] = {0};
+
+volatile uint16_t queue_cursor[INTERFACE_COUNT] = {0};
+volatile uint16_t current_packet_start[INTERFACE_COUNT] = {0};
 volatile bool serial_buffering = false;
 #if HAS_BLUETOOTH || HAS_BLE == true
   bool bt_init_ran = false;
@@ -43,9 +47,8 @@ volatile bool serial_buffering = false;
 
 char sbuf[128];
 
-#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-  bool packet_ready = false;
-#endif
+bool packet_ready = false;
+uint8_t packet_interface;
 
 void setup() {
   #if MCU_VARIANT == MCU_ESP32
@@ -86,8 +89,6 @@ void setup() {
     while (!Serial);
   #endif
 
-  serial_interrupt_init();
-
   // Configure input and output pins
   #if HAS_INPUT
     input_init();
@@ -98,64 +99,140 @@ void setup() {
     pinMode(pin_led_tx, OUTPUT);
   #endif
 
-  #if HAS_TCXO == true
-    if (pin_tcxo_enable != -1) {
-        pinMode(pin_tcxo_enable, OUTPUT);
-        digitalWrite(pin_tcxo_enable, HIGH);
+  for (int i = 0; i < INTERFACE_COUNT; i++) {
+    if (interface_pins[i][9] != -1) {
+        pinMode(interface_pins[i][9], OUTPUT);
+        digitalWrite(interface_pins[i][9], HIGH);
     }
-  #endif
+  }
 
   // Initialise buffers
   memset(pbuf, 0, sizeof(pbuf));
   memset(cmdbuf, 0, sizeof(cmdbuf));
   
-  memset(packet_queue, 0, sizeof(packet_queue));
-
   memset(packet_starts_buf, 0, sizeof(packet_starts_buf));
-  fifo16_init(&packet_starts, packet_starts_buf, CONFIG_QUEUE_MAX_LENGTH);
-  
   memset(packet_lengths_buf, 0, sizeof(packet_starts_buf));
-  fifo16_init(&packet_lengths, packet_lengths_buf, CONFIG_QUEUE_MAX_LENGTH);
-
-  // Set chip select, reset and interrupt
-  // pins for the LoRa module
-  #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()) {
-      modem_installed = true;
-      uint32_t lfr = LoRa->getFrequency();
-      if (lfr == 0) {
-        // Normal boot
-      } else if (lfr == M_FRQ_R) {
-        // Quick reboot
-        #if HAS_CONSOLE
-          if (rtc_get_reset_reason(0) == POWERON_RESET) {
-            console_active = true;
+  memset(packet_queue, 0, sizeof(packet_queue));
+
+  for (int i = 0; i < INTERFACE_COUNT; i++) {
+      fifo16_init(&packet_starts[i], packet_starts_buf, CONFIG_QUEUE_MAX_LENGTH/INTERFACE_COUNT);
+      fifo16_init(&packet_lengths[i], packet_lengths_buf, CONFIG_QUEUE_MAX_LENGTH/INTERFACE_COUNT);
+  }
+
+  // Create and configure interface objects
+  for (uint8_t i = 0; i < INTERFACE_COUNT; i++) {
+      switch (interfaces[i]) {
+          case SX126X:
+          case SX1262:
+          {
+              sx126x* obj;
+              // if default spi enabled
+              if (interface_cfg[i][0]) {
+                obj = new sx126x(i, SPI, interface_cfg[i][1],
+                interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1],
+                interface_pins[i][2], interface_pins[i][3], interface_pins[i][6],
+                interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]);
+              }
+              else {
+            obj = new sx126x(i, interface_spi[i], interface_cfg[i][1],
+            interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1],
+            interface_pins[i][2], interface_pins[i][3], interface_pins[i][6],
+            interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]);
+              }
+            interface_obj[i] = obj;
+            interface_obj_sorted[i] = obj;
+            break;
           }
-        #endif
-      } else {
-        // Unknown boot
+
+          case SX127X:
+          case SX1276:
+          case SX1278:
+          {
+              sx127x* obj;
+              // if default spi enabled
+              if (interface_cfg[i][0]) {
+            obj = new sx127x(i, SPI, interface_pins[i][0],
+            interface_pins[i][1], interface_pins[i][2], interface_pins[i][3],
+            interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]);
+              }
+              else {
+            obj = new sx127x(i, interface_spi[i], interface_pins[i][0],
+            interface_pins[i][1], interface_pins[i][2], interface_pins[i][3],
+            interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]);
+              }
+            interface_obj[i] = obj;
+            interface_obj_sorted[i] = obj;
+            break;
+          }
+
+          case SX128X:
+          case SX1280:
+          {
+              sx128x* obj;
+              // if default spi enabled
+              if (interface_cfg[i][0]) {
+            obj = new sx128x(i, SPI, interface_cfg[i][1],
+            interface_pins[i][0], interface_pins[i][1], interface_pins[i][2],
+            interface_pins[i][3], interface_pins[i][6], interface_pins[i][5],
+            interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]);
+            }
+            else {
+            obj = new sx128x(i, interface_spi[i], interface_cfg[i][1],
+            interface_pins[i][0], interface_pins[i][1], interface_pins[i][2],
+            interface_pins[i][3], interface_pins[i][6], interface_pins[i][5],
+            interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]);
+            }
+            interface_obj[i] = obj;
+            interface_obj_sorted[i] = obj;
+            break;
+          }
+          
+          default:
+            break;
       }
-      LoRa->setFrequency(M_FRQ_S);
-    } else {
-      modem_installed = false;
+  }
+
+    // Check installed transceiver chip(s) and probe boot parameters. If any of
+    // the configured modems cannot be initialised, do not boot
+    for (int i = 0; i < INTERFACE_COUNT; i++) {
+        switch (interfaces[i]) {
+            case SX126X:
+            case SX1262:
+            case SX127X:
+            case SX1276:
+            case SX1278:
+            case SX128X:
+            case SX1280:
+                selected_radio = interface_obj[i];
+                break;
+
+            default:
+                modems_installed = false;
+                break;
+        }
+        if (selected_radio->preInit()) {
+          modems_installed = true;
+          uint32_t lfr = selected_radio->getFrequency();
+          if (lfr == 0) {
+            // Normal boot
+          } else if (lfr == M_FRQ_R) {
+            // Quick reboot
+            #if HAS_CONSOLE
+              if (rtc_get_reset_reason(0) == POWERON_RESET) {
+                console_active = true;
+              }
+            #endif
+          } else {
+            // Unknown boot
+          }
+          selected_radio->setFrequency(M_FRQ_S);
+        } else {
+          modems_installed = false;
+        }
+        if (!modems_installed) {
+            break;
+        }
     }
-  #else
-    // Older variants only came with SX1276/78 chips,
-    // so assume that to be the case for now.
-    modem_installed = true;
-  #endif
 
   #if HAS_DISPLAY
     #if HAS_EEPROM
@@ -175,7 +252,6 @@ void setup() {
     update_display();
   #endif
 
-  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
     #if HAS_PMU == true
       pmu_ready = init_pmu();
     #endif
@@ -194,25 +270,28 @@ void setup() {
     } else {
       kiss_indicate_reset();
     }
-  #endif
 
   // Validate board health, EEPROM and config
   validate_status();
-
-  if (op_mode != MODE_TNC) LoRa->setFrequency(0);
 }
 
-void lora_receive() {
+void lora_receive(RadioInterface* radio) {
   if (!implicit) {
-    LoRa->receive();
+    radio->receive();
   } else {
-    LoRa->receive(implicit_l);
+    radio->receive(implicit_l);
   }
 }
 
-inline void kiss_write_packet() {
+inline void kiss_write_packet(int index) {
+  // We need to convert the interface index to the command byte representation
+  uint8_t cmd_byte = getInterfaceCommandByte(index);
+
   serial_write(FEND);
-  serial_write(CMD_DATA);
+
+  // Add index of interface the packet came from
+  serial_write(cmd_byte);
+
   for (uint16_t i = 0; i < read_len; i++) {
     uint8_t byte = pbuf[i];
     if (byte == FEND) { serial_write(FESC); byte = TFEND; }
@@ -221,25 +300,25 @@ inline void kiss_write_packet() {
   }
   serial_write(FEND);
   read_len = 0;
-  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-    packet_ready = false;
-  #endif
+  packet_ready = false;
 }
 
-inline void getPacketData(uint16_t len) {
+inline void getPacketData(RadioInterface* radio, uint16_t len) {
   while (len-- && read_len < MTU) {
-    pbuf[read_len++] = LoRa->read();
+    pbuf[read_len++] = radio->read();
   }
 }
 
-void ISR_VECT receive_callback(int packet_size) {
+void ISR_VECT receive_callback(uint8_t index, int packet_size) {
   if (!promisc) {
+    selected_radio = interface_obj[index];
+
     // The standard operating mode allows large
     // packets with a payload up to 500 bytes,
     // 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   = selected_radio->read(); packet_size--;
     uint8_t sequence = packetSequence(header);
     bool    ready    = false;
 
@@ -250,25 +329,14 @@ void ISR_VECT receive_callback(int packet_size) {
       read_len = 0;
       seq = sequence;
 
-      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        last_rssi = LoRa->packetRssi();
-        last_snr_raw = LoRa->packetSnrRaw();
-      #endif
-
-      getPacketData(packet_size);
+      getPacketData(selected_radio, packet_size);
 
     } else if (isSplitPacket(header) && seq == sequence) {
       // This is the second part of a split
       // packet, so we add it to the buffer
       // and set the ready flag.
       
-
-      #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;
-      #endif
-
-      getPacketData(packet_size);
+      getPacketData(selected_radio, packet_size);
 
       seq = SEQ_UNSET;
       ready = true;
@@ -281,12 +349,7 @@ void ISR_VECT receive_callback(int packet_size) {
       read_len = 0;
       seq = sequence;
 
-      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        last_rssi = LoRa->packetRssi();
-        last_snr_raw = LoRa->packetSnrRaw();
-      #endif
-
-      getPacketData(packet_size);
+      getPacketData(selected_radio, packet_size);
 
     } else if (!isSplitPacket(header)) {
       // This is not a split packet, so we
@@ -300,85 +363,49 @@ void ISR_VECT receive_callback(int packet_size) {
         seq = SEQ_UNSET;
       }
 
-      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        last_rssi = LoRa->packetRssi();
-        last_snr_raw = LoRa->packetSnrRaw();
-      #endif
-
-      getPacketData(packet_size);
+      getPacketData(selected_radio, packet_size);
       ready = true;
     }
 
     if (ready) {
-      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        // We first signal the RSSI of the
-        // recieved packet to the host.
-        kiss_indicate_stat_rssi();
-        kiss_indicate_stat_snr();
-
-        // And then write the entire packet
-        kiss_write_packet();
-      #else
         packet_ready = true;
-      #endif
     }  
   } else {
     // In promiscuous mode, raw packets are
     // output directly to the host
     read_len = 0;
 
-    #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-      last_rssi = LoRa->packetRssi();
-      last_snr_raw = LoRa->packetSnrRaw();
-      getPacketData(packet_size);
-
-      // We first signal the RSSI of the
-      // recieved packet to the host.
-      kiss_indicate_stat_rssi();
-      kiss_indicate_stat_snr();
-
-      // And then write the entire packet
-      kiss_write_packet();
-
-    #else
-      getPacketData(packet_size);
-      packet_ready = true;
-    #endif
+    getPacketData(selected_radio, packet_size);
+    packet_ready = true;
   }
 }
 
-bool startRadio() {
-  update_radio_lock();
-  if (!radio_online && !console_active) {
-    if (!radio_locked && hw_ready) {
-      if (!LoRa->begin(lora_freq)) {
+bool startRadio(RadioInterface* radio) {
+  update_radio_lock(radio);
+  
+  if (modems_installed && !console_active) {
+    if (!radio->getRadioLock() && hw_ready) {
+      if (!radio->begin()) {
         // The radio could not be started.
         // Indicate this failure over both the
         // serial port and with the onboard LEDs
-        radio_error = true;
         kiss_indicate_error(ERROR_INITRADIO);
         led_indicate_error(0);
         return false;
       } else {
-        radio_online = true;
-
-        init_channel_stats();
+        radio->enableCrc();
 
-        setTXPower();
-        setBandwidth();
-        setSpreadingFactor();
-        setCodingRate();
-        getFrequency();
+        radio->onReceive(receive_callback);
 
-        LoRa->enableCrc();
+        radio->updateBitrate();
+        sort_interfaces();
+        kiss_indicate_phy_stats(radio);
 
-        LoRa->onReceive(receive_callback);
-
-        lora_receive();
+        lora_receive(radio);
 
         // Flash an info pattern to indicate
         // that the radio is now on
-        kiss_indicate_radiostate();
+        kiss_indicate_radiostate(radio);
         led_indicate_info(3);
         return true;
       }
@@ -387,121 +414,77 @@ bool startRadio() {
       // Flash a warning pattern to indicate
       // that the radio was locked, and thus
       // not started
-      radio_online = false;
-      kiss_indicate_radiostate();
+      kiss_indicate_radiostate(radio);
       led_indicate_warning(3);
       return false;
     }
   } else {
     // If radio is already on, we silently
     // ignore the request.
-    kiss_indicate_radiostate();
+    kiss_indicate_radiostate(radio);
     return true;
   }
 }
 
-void stopRadio() {
-  LoRa->end();
-  radio_online = false;
+void stopRadio(RadioInterface* radio) {
+  radio->end();
+  sort_interfaces();
 }
 
-void update_radio_lock() {
-  if (lora_freq != 0 && lora_bw != 0 && lora_txp != 0xFF && lora_sf != 0) {
-    radio_locked = false;
+void update_radio_lock(RadioInterface* radio) {
+  if (radio->getFrequency() != 0 && radio->getSignalBandwidth() != 0 && radio->getTxPower() != 0xFF && radio->getSpreadingFactor() != 0) {
+    radio->setRadioLock(false);
   } else {
-    radio_locked = true;
+    radio->setRadioLock(true);
   }
 }
 
-bool queueFull() {
-  return (queue_height >= CONFIG_QUEUE_MAX_LENGTH || queued_bytes >= CONFIG_QUEUE_SIZE);
+// Check if the queue is full for the selected radio.
+// Returns true if full, false if not
+bool queueFull(RadioInterface* radio) {
+  return (queue_height[radio->getIndex()] >= (CONFIG_QUEUE_MAX_LENGTH/INTERFACE_COUNT) || queued_bytes[radio->getIndex()] >= (CONFIG_QUEUE_SIZE/INTERFACE_COUNT));
 }
 
 volatile bool queue_flushing = false;
-void flushQueue(void) {
+
+// Flushes all packets for the interface
+void flushQueue(RadioInterface* radio) {
+    uint8_t index = radio->getIndex();
   if (!queue_flushing) {
     queue_flushing = true;
 
     led_tx_on();
     uint16_t processed = 0;
+    uint8_t data_byte;
 
-    #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-    while (!fifo16_isempty(&packet_starts)) {
-    #else
-    while (!fifo16_isempty_locked(&packet_starts)) {
-    #endif
-
-      uint16_t start = fifo16_pop(&packet_starts);
-      uint16_t length = fifo16_pop(&packet_lengths);
+    while (!fifo16_isempty(&packet_starts[index])) {
+      uint16_t start = fifo16_pop(&packet_starts[index]);
+      uint16_t length = fifo16_pop(&packet_lengths[index]);
 
       if (length >= MIN_L && length <= MTU) {
         for (uint16_t i = 0; i < length; i++) {
-          uint16_t pos = (start+i)%CONFIG_QUEUE_SIZE;
-          tbuf[i] = packet_queue[pos];
+          uint16_t pos = (start+i)%(CONFIG_QUEUE_SIZE/INTERFACE_COUNT);
+          tbuf[i] = packet_queue[index][pos];
         }
-
-        transmit(length);
+        transmit(radio, length);
         processed++;
       }
     }
 
-    lora_receive();
+    lora_receive(radio);
     led_tx_off();
-    post_tx_yield_timeout = millis()+(lora_post_tx_yield_slots*csma_slot_ms);
+
+    radio->setPostTxYieldTimeout(millis()+(lora_post_tx_yield_slots*selected_radio->getCSMASlotMS()));
   }
 
-  queue_height = 0;
-  queued_bytes = 0;
-  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-    update_airtime();
-  #endif
+  queue_height[index] = 0;
+  queued_bytes[index] = 0;
+  selected_radio->updateAirtime();
   queue_flushing = false;
 }
 
-#define PHY_HEADER_LORA_SYMBOLS 8
-void add_airtime(uint16_t written) {
-  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-    float packet_cost_ms = 0.0;
-    float payload_cost_ms = ((float)written * lora_us_per_byte)/1000.0;
-    packet_cost_ms += payload_cost_ms;
-    packet_cost_ms += (lora_preamble_symbols+4.25)*lora_symbol_time_ms;
-    packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * lora_symbol_time_ms;
-    uint16_t cb = current_airtime_bin();
-    uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
-    airtime_bins[cb] += packet_cost_ms;
-    airtime_bins[nb] = 0;
-  #endif
-}
-
-void update_airtime() {
-  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-    uint16_t cb = current_airtime_bin();
-    uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; }
-    uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
-    airtime_bins[nb] = 0;
-    airtime = (float)(airtime_bins[cb]+airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS);
-
-    uint32_t longterm_airtime_sum = 0;
-    for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) {
-      longterm_airtime_sum += airtime_bins[bin];
-    }
-    longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS;
-
-    float longterm_channel_util_sum = 0.0;
-    for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) {
-      longterm_channel_util_sum += longterm_bins[bin];
-    }
-    longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS;
-
-    #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-      update_csma_p();
-    #endif
-    kiss_indicate_channel_stats();
-  #endif
-}
-
-void transmit(uint16_t size) {
-  if (radio_online) {
+void transmit(RadioInterface* radio, uint16_t size) {
+  if (radio->getRadioOnline()) { 
     if (!promisc) {
       uint16_t  written = 0;
       uint8_t header  = random(256) & 0xF0;
@@ -510,23 +493,24 @@ void transmit(uint16_t size) {
         header = header | FLAG_SPLIT;
       }
 
-      LoRa->beginPacket();
-      LoRa->write(header); written++;
+
+      radio->beginPacket();
+      radio->write(header); written++;
 
       for (uint16_t i=0; i < size; i++) {
-        LoRa->write(tbuf[i]);
+        radio->write(tbuf[i]);
 
         written++;
 
         if (written == 255) {
-          LoRa->endPacket(); add_airtime(written);
-          LoRa->beginPacket();
-          LoRa->write(header);
+          radio->endPacket(); radio->addAirtime(written);
+          radio->beginPacket();
+          radio->write(header);
           written = 1;
         }
       }
 
-      LoRa->endPacket(); add_airtime(written);
+      radio->endPacket(); radio->addAirtime(written);
     } else {
       // In promiscuous mode, we only send out
       // plain raw LoRa packets with a maximum
@@ -542,17 +526,17 @@ void transmit(uint16_t size) {
       // If implicit header mode has been set,
       // set packet length to payload data length
       if (!implicit) {
-        LoRa->beginPacket();
+        radio->beginPacket();
       } else {
-        LoRa->beginPacket(size);
+        radio->beginPacket(size);
       }
 
       for (uint16_t i=0; i < size; i++) {
-        LoRa->write(tbuf[i]);
+        radio->write(tbuf[i]);
 
         written++;
       }
-      LoRa->endPacket(); add_airtime(written);
+      radio->endPacket(); radio->addAirtime(written);
     }
   } else {
     kiss_indicate_error(ERROR_TXFAILED);
@@ -561,29 +545,43 @@ void transmit(uint16_t size) {
 }
 
 void serialCallback(uint8_t sbyte) {
-  if (IN_FRAME && sbyte == FEND && command == CMD_DATA) {
+  if (IN_FRAME && sbyte == FEND && 
+            (command == CMD_INT0_DATA
+          || command == CMD_INT1_DATA
+          || command == CMD_INT2_DATA
+          || command == CMD_INT3_DATA
+          || command == CMD_INT4_DATA
+          || command == CMD_INT5_DATA
+          || command == CMD_INT6_DATA
+          || command == CMD_INT7_DATA
+          || command == CMD_INT8_DATA
+          || command == CMD_INT9_DATA
+          || command == CMD_INT10_DATA 
+          || command == CMD_INT11_DATA)) {
     IN_FRAME = false;
 
-    if (!fifo16_isfull(&packet_starts) && queued_bytes < CONFIG_QUEUE_SIZE) {
-        uint16_t s = current_packet_start;
-        int16_t e = queue_cursor-1; if (e == -1) e = CONFIG_QUEUE_SIZE-1;
-        uint16_t l;
-
-        if (s != e) {
-            l = (s < e) ? e - s + 1 : CONFIG_QUEUE_SIZE - s + e + 1;
-        } else {
-            l = 1;
-        }
+    if (getInterfaceIndex(command) < INTERFACE_COUNT) {
+            uint8_t index = getInterfaceIndex(command);
+        if (!fifo16_isfull(&packet_starts[index]) && queued_bytes[index] < (CONFIG_QUEUE_SIZE/INTERFACE_COUNT)) {
+            uint16_t s = current_packet_start[index];
+            int16_t e = queue_cursor[index]-1; if (e == -1) e = (CONFIG_QUEUE_SIZE/INTERFACE_COUNT)-1;
+            uint16_t l;
+
+            if (s != e) {
+                l = (s < e) ? e - s + 1: (CONFIG_QUEUE_SIZE/INTERFACE_COUNT) - s + e + 1;
+            } else {
+                l = 1;
+            }
 
-        if (l >= MIN_L) {
-            queue_height++;
+            if (l >= MIN_L) {
+                queue_height[index]++;
 
-            fifo16_push(&packet_starts, s);
-            fifo16_push(&packet_lengths, l);
+                fifo16_push(&packet_starts[index], s);
+                fifo16_push(&packet_lengths[index], l);
+                current_packet_start[index] = queue_cursor[index];
+            }
 
-            current_packet_start = queue_cursor;
         }
-
     }
 
   } else if (sbyte == FEND) {
@@ -594,7 +592,33 @@ void serialCallback(uint8_t sbyte) {
     // Have a look at the command byte first
     if (frame_len == 0 && command == CMD_UNKNOWN) {
         command = sbyte;
-    } else if (command == CMD_DATA) {
+        if  (command == CMD_SEL_INT0 
+                 || command == CMD_SEL_INT1 
+                 || command == CMD_SEL_INT2 
+                 || command == CMD_SEL_INT3 
+                 || command == CMD_SEL_INT4 
+                 || command == CMD_SEL_INT5 
+                 || command == CMD_SEL_INT6 
+                 || command == CMD_SEL_INT7 
+                 || command == CMD_SEL_INT8 
+                 || command == CMD_SEL_INT9 
+                 || command == CMD_SEL_INT10 
+                 || command == CMD_SEL_INT11) {
+            interface = getInterfaceIndex(command);
+        }
+
+    } else if  (command == CMD_INT0_DATA 
+             || command == CMD_INT1_DATA 
+             || command == CMD_INT2_DATA 
+             || command == CMD_INT3_DATA 
+             || command == CMD_INT4_DATA 
+             || command == CMD_INT5_DATA 
+             || command == CMD_INT6_DATA 
+             || command == CMD_INT7_DATA 
+             || command == CMD_INT8_DATA 
+             || command == CMD_INT9_DATA 
+             || command == CMD_INT10_DATA 
+             || command == CMD_INT11_DATA) {
         if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
         if (sbyte == FESC) {
             ESCAPE = true;
@@ -604,12 +628,20 @@ void serialCallback(uint8_t sbyte) {
                 if (sbyte == TFESC) sbyte = FESC;
                 ESCAPE = false;
             }
-            if (queue_height < CONFIG_QUEUE_MAX_LENGTH && queued_bytes < CONFIG_QUEUE_SIZE) {
-              queued_bytes++;
-              packet_queue[queue_cursor++] = sbyte;
-              if (queue_cursor == CONFIG_QUEUE_SIZE) queue_cursor = 0;
+
+            if (getInterfaceIndex(command) < INTERFACE_COUNT) {
+                    uint8_t index = getInterfaceIndex(command);
+                if (queue_height[index] < (CONFIG_QUEUE_MAX_LENGTH/INTERFACE_COUNT) && queued_bytes[index] < (CONFIG_QUEUE_SIZE/INTERFACE_COUNT)) {
+                  queued_bytes[index]++;
+                  packet_queue[index][queue_cursor[index]++] = sbyte;
+                  if (queue_cursor[index] == (CONFIG_QUEUE_SIZE/INTERFACE_COUNT)) queue_cursor[index] = 0;
+                }
             }
         }
+    } else if (command == CMD_INTERFACES) {
+        for (int i = 0; i < INTERFACE_COUNT; i++) {
+            kiss_indicate_interface(i);
+        }
     } else if (command == CMD_FREQUENCY) {
       if (sbyte == FESC) {
             ESCAPE = true;
@@ -625,13 +657,14 @@ void serialCallback(uint8_t sbyte) {
         if (frame_len == 4) {
           uint32_t freq = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
 
+          selected_radio = interface_obj[interface];
           if (freq == 0) {
-            kiss_indicate_frequency();
+            kiss_indicate_frequency(selected_radio);
           } else {
-            lora_freq = freq;
-            if (op_mode == MODE_HOST) setFrequency();
-            kiss_indicate_frequency();
+            if (op_mode == MODE_HOST) selected_radio->setFrequency(freq);
+            kiss_indicate_frequency(selected_radio);
           }
+          interface = 0;
         }
     } else if (command == CMD_BANDWIDTH) {
       if (sbyte == FESC) {
@@ -648,76 +681,90 @@ void serialCallback(uint8_t sbyte) {
         if (frame_len == 4) {
           uint32_t bw = (uint32_t)cmdbuf[0] << 24 | (uint32_t)cmdbuf[1] << 16 | (uint32_t)cmdbuf[2] << 8 | (uint32_t)cmdbuf[3];
 
+          selected_radio = interface_obj[interface];
+
           if (bw == 0) {
-            kiss_indicate_bandwidth();
+            kiss_indicate_bandwidth(selected_radio);
           } else {
-            lora_bw = bw;
-            if (op_mode == MODE_HOST) setBandwidth();
-            kiss_indicate_bandwidth();
+            if (op_mode == MODE_HOST) selected_radio->setSignalBandwidth(bw);
+            selected_radio->updateBitrate();
+            sort_interfaces();
+            kiss_indicate_phy_stats(selected_radio);
+            kiss_indicate_bandwidth(selected_radio);
           }
+          interface = 0;
         }
     } else if (command == CMD_TXPOWER) {
+      selected_radio = interface_obj[interface];
+
       if (sbyte == 0xFF) {
-        kiss_indicate_txpower();
+        kiss_indicate_txpower(selected_radio);
       } else {
         int txp = sbyte;
-        #if MODEM == SX1262
-          if (txp > 22) txp = 22;
-        #else
-          if (txp > 17) txp = 17;
-        #endif
 
-        lora_txp = txp;
-        if (op_mode == MODE_HOST) setTXPower();
-        kiss_indicate_txpower();
+        if (op_mode == MODE_HOST) setTXPower(selected_radio, txp);
+        kiss_indicate_txpower(selected_radio);
       }
+      interface = 0;
     } else if (command == CMD_SF) {
+      selected_radio = interface_obj[interface];
+
       if (sbyte == 0xFF) {
-        kiss_indicate_spreadingfactor();
+        kiss_indicate_spreadingfactor(selected_radio);
       } else {
         int sf = sbyte;
         if (sf < 5) sf = 5;
         if (sf > 12) sf = 12;
 
-        lora_sf = sf;
-        if (op_mode == MODE_HOST) setSpreadingFactor();
-        kiss_indicate_spreadingfactor();
+        if (op_mode == MODE_HOST) selected_radio->setSpreadingFactor(sf);
+        selected_radio->updateBitrate();
+        sort_interfaces();
+        kiss_indicate_phy_stats(selected_radio);
+        kiss_indicate_spreadingfactor(selected_radio);
       }
+      interface = 0;
     } else if (command == CMD_CR) {
+      selected_radio = interface_obj[interface];
       if (sbyte == 0xFF) {
-        kiss_indicate_codingrate();
+        kiss_indicate_codingrate(selected_radio);
       } else {
         int cr = sbyte;
         if (cr < 5) cr = 5;
         if (cr > 8) cr = 8;
 
-        lora_cr = cr;
-        if (op_mode == MODE_HOST) setCodingRate();
-        kiss_indicate_codingrate();
+        if (op_mode == MODE_HOST) selected_radio->setCodingRate4(cr);
+        selected_radio->updateBitrate();
+        sort_interfaces();
+        kiss_indicate_phy_stats(selected_radio);
+        kiss_indicate_codingrate(selected_radio);
       }
+      interface = 0;
     } else if (command == CMD_IMPLICIT) {
       set_implicit_length(sbyte);
       kiss_indicate_implicit_length();
     } else if (command == CMD_LEAVE) {
       if (sbyte == 0xFF) {
         cable_state   = CABLE_STATE_DISCONNECTED;
-        current_rssi  = -292;
+        //current_rssi  = -292;
         last_rssi     = -292;
         last_rssi_raw = 0x00;
         last_snr_raw  = 0x80;
       }
     } else if (command == CMD_RADIO_STATE) {
+      selected_radio = interface_obj[interface];
       if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
       if (sbyte == 0xFF) {
-        kiss_indicate_radiostate();
+        kiss_indicate_radiostate(selected_radio);
       } else if (sbyte == 0x00) {
-        stopRadio();
-        kiss_indicate_radiostate();
+        stopRadio(selected_radio);
+        kiss_indicate_radiostate(selected_radio);
       } else if (sbyte == 0x01) {
-        startRadio();
-        kiss_indicate_radiostate();
+        startRadio(selected_radio);
+        kiss_indicate_radiostate(selected_radio);
       }
+      interface = 0;
     } else if (command == CMD_ST_ALOCK) {
+      selected_radio = interface_obj[interface];
       if (sbyte == FESC) {
             ESCAPE = true;
         } else {
@@ -733,14 +780,17 @@ void serialCallback(uint8_t sbyte) {
           uint16_t at = (uint16_t)cmdbuf[0] << 8 | (uint16_t)cmdbuf[1];
 
           if (at == 0) {
-            st_airtime_limit = 0.0;
+            selected_radio->setSTALock(0.0);
           } else {
-            st_airtime_limit = (float)at/(100.0*100.0);
+            int st_airtime_limit = (float)at/(100.0*100.0);
             if (st_airtime_limit >= 1.0) { st_airtime_limit = 0.0; }
+            selected_radio->setSTALock(st_airtime_limit);
           }
-          kiss_indicate_st_alock();
+          kiss_indicate_st_alock(selected_radio);
         }
+        interface = 0;
     } else if (command == CMD_LT_ALOCK) {
+      selected_radio = interface_obj[interface];
       if (sbyte == FESC) {
             ESCAPE = true;
         } else {
@@ -756,13 +806,15 @@ void serialCallback(uint8_t sbyte) {
           uint16_t at = (uint16_t)cmdbuf[0] << 8 | (uint16_t)cmdbuf[1];
 
           if (at == 0) {
-            lt_airtime_limit = 0.0;
+            selected_radio->setLTALock(0.0);
           } else {
-            lt_airtime_limit = (float)at/(100.0*100.0);
+            int lt_airtime_limit = (float)at/(100.0*100.0);
             if (lt_airtime_limit >= 1.0) { lt_airtime_limit = 0.0; }
+            selected_radio->setLTALock(lt_airtime_limit);
           }
-          kiss_indicate_lt_alock();
+          kiss_indicate_lt_alock(selected_radio);
         }
+        interface = 0;
     } else if (command == CMD_STAT_RX) {
       kiss_indicate_stat_rx();
     } else if (command == CMD_STAT_TX) {
@@ -770,12 +822,18 @@ void serialCallback(uint8_t sbyte) {
     } else if (command == CMD_STAT_RSSI) {
       kiss_indicate_stat_rssi();
     } else if (command == CMD_RADIO_LOCK) {
-      update_radio_lock();
-      kiss_indicate_radio_lock();
+      selected_radio = interface_obj[interface];
+      update_radio_lock(selected_radio);
+      kiss_indicate_radio_lock(selected_radio);
+      interface = 0;
     } else if (command == CMD_BLINK) {
       led_indicate_info(sbyte);
     } else if (command == CMD_RANDOM) {
-      kiss_indicate_random(getRandom());
+      // pick an interface at random to get data from
+      int int_index = random(INTERFACE_COUNT);
+      selected_radio = interface_obj[int_index];
+      kiss_indicate_random(getRandom(selected_radio));
+      interface = 0;
     } else if (command == CMD_DETECT) {
       if (sbyte == DETECT_REQ) {
         if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
@@ -789,7 +847,8 @@ void serialCallback(uint8_t sbyte) {
       }
       kiss_indicate_promisc();
     } else if (command == CMD_READY) {
-      if (!queueFull()) {
+      selected_radio = interface_obj[interface];
+      if (!queueFull(selected_radio)) {
         kiss_indicate_ready();
       } else {
         kiss_indicate_not_ready();
@@ -828,7 +887,8 @@ void serialCallback(uint8_t sbyte) {
     } else if (command == CMD_BOARD) {
       kiss_indicate_board();
     } else if (command == CMD_CONF_SAVE) {
-      eeprom_conf_save();
+        // todo: add extra space in EEPROM so this isn't hardcoded
+      eeprom_conf_save(interface_obj[0]);
     } else if (command == CMD_CONF_DELETE) {
       eeprom_conf_delete();
     } else if (command == CMD_FB_EXT) {
@@ -867,13 +927,10 @@ void serialCallback(uint8_t sbyte) {
         kiss_indicate_fb();
       }
     } else if (command == CMD_DEV_HASH) {
-      #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
         if (sbyte != 0x00) {
           kiss_indicate_device_hash();
         }
-      #endif
     } else if (command == CMD_DEV_SIG) {
-      #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
         if (sbyte == FESC) {
               ESCAPE = true;
           } else {
@@ -889,7 +946,6 @@ void serialCallback(uint8_t sbyte) {
             memcpy(dev_sig, cmdbuf, DEV_SIG_LEN);
             device_save_signature();
           }
-      #endif
     } else if (command == CMD_FW_UPD) {
       if (sbyte == 0x01) {
         firmware_update_mode = true;
@@ -897,7 +953,6 @@ void serialCallback(uint8_t sbyte) {
         firmware_update_mode = false;
       }
     } else if (command == CMD_HASHES) {
-      #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
         if (sbyte == 0x01) {
           kiss_indicate_target_fw_hash();
         } else if (sbyte == 0x02) {
@@ -907,9 +962,7 @@ void serialCallback(uint8_t sbyte) {
         } else if (sbyte == 0x04) {
           kiss_indicate_partition_table_hash();
         }
-      #endif
     } else if (command == CMD_FW_HASH) {
-      #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
         if (sbyte == FESC) {
               ESCAPE = true;
           } else {
@@ -925,7 +978,6 @@ void serialCallback(uint8_t sbyte) {
             memcpy(dev_firmware_hash_target, cmdbuf, DEV_HASH_LEN);
             device_save_firmware_hash();
           }
-      #endif
     } else if (command == CMD_BT_CTRL) {
       #if HAS_BLUETOOTH || HAS_BLE
         if (sbyte == 0x00) {
@@ -976,106 +1028,8 @@ void serialCallback(uint8_t sbyte) {
   portMUX_TYPE update_lock = portMUX_INITIALIZER_UNLOCKED;
 #endif
 
-void updateModemStatus() {
-  #if MCU_VARIANT == MCU_ESP32
-    portENTER_CRITICAL(&update_lock);
-  #elif MCU_VARIANT == MCU_NRF52
-    portENTER_CRITICAL();
-  #endif
-
-  uint8_t status = LoRa->modemStatus();
-  current_rssi = LoRa->currentRssi();
-  last_status_update = millis();
-
-  #if MCU_VARIANT == MCU_ESP32
-    portEXIT_CRITICAL(&update_lock);
-  #elif MCU_VARIANT == MCU_NRF52
-    portEXIT_CRITICAL();
-  #endif
-
-  if ((status & SIG_DETECT) == SIG_DETECT) { stat_signal_detected = true; } else { stat_signal_detected = false; }
-  if ((status & SIG_SYNCED) == SIG_SYNCED) { stat_signal_synced = true; } else { stat_signal_synced = false; }
-  if ((status & RX_ONGOING) == RX_ONGOING) { stat_rx_ongoing = true; } else { stat_rx_ongoing = false; }
-
-  // if (stat_signal_detected || stat_signal_synced || stat_rx_ongoing) {
-  if (stat_signal_detected || stat_signal_synced) {
-    if (stat_rx_ongoing) {
-      if (dcd_count < dcd_threshold) {
-        dcd_count++;
-      } else {
-        last_dcd = last_status_update;
-        dcd_led = true;
-        dcd = true;
-      }
-    }
-  } else {
-    #define DCD_LED_STEP_D 3
-    if (dcd_count == 0) {
-      dcd_led = false;
-    } else if (dcd_count > DCD_LED_STEP_D) {
-      dcd_count -= DCD_LED_STEP_D;
-    } else {
-      dcd_count = 0;
-    }
-
-    if (last_status_update > last_dcd+csma_slot_ms) {
-      dcd = false;
-      dcd_led = false;
-      dcd_count = 0;
-    }
-  }
-
-  if (dcd_led) {
-    led_rx_on();
-  } else {
-    if (airtime_lock) {
-      led_indicate_airtime_lock();
-    } else {
-      led_rx_off();
-    }
-  }
-}
-
-void checkModemStatus() {
-  if (millis()-last_status_update >= status_interval_ms) {
-    updateModemStatus();
-
-    #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-      util_samples[dcd_sample] = dcd;
-      dcd_sample = (dcd_sample+1)%DCD_SAMPLES;
-      if (dcd_sample % UTIL_UPDATE_INTERVAL == 0) {
-        int util_count = 0;
-        for (int ui = 0; ui < DCD_SAMPLES; ui++) {
-          if (util_samples[ui]) util_count++;
-        }
-        local_channel_util = (float)util_count / (float)DCD_SAMPLES;
-        total_channel_util = local_channel_util + airtime;
-        if (total_channel_util > 1.0) total_channel_util = 1.0;
-
-        int16_t cb = current_airtime_bin();
-        uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
-        if (total_channel_util > longterm_bins[cb]) longterm_bins[cb] = total_channel_util;
-        longterm_bins[nb] = 0.0;
-
-        update_airtime();
-      }
-    #endif
-  }
-}
-
 void validate_status() {
-  #if MCU_VARIANT == MCU_1284P
-      uint8_t boot_flags = OPTIBOOT_MCUSR;
-      uint8_t F_POR = PORF;
-      uint8_t F_BOR = BORF;
-      uint8_t F_WDR = WDRF;
-  #elif MCU_VARIANT == MCU_2560
-      uint8_t boot_flags = OPTIBOOT_MCUSR;
-      if (boot_flags == 0x00) boot_flags = 0x03;
-      uint8_t F_POR = PORF;
-      uint8_t F_BOR = BORF;
-      uint8_t F_WDR = WDRF;
-  #elif MCU_VARIANT == MCU_ESP32
+  #if MCU_VARIANT == MCU_ESP32
       // TODO: Get ESP32 boot flags
       uint8_t boot_flags = 0x02;
       uint8_t F_POR = 0x00;
@@ -1123,16 +1077,12 @@ void validate_status() {
       if (eeprom_product_valid() && eeprom_model_valid() && eeprom_hwrev_valid()) {
         if (eeprom_checksum_valid()) {
           eeprom_ok = true;
-          if (modem_installed) {
-            #if PLATFORM == PLATFORM_ESP32 || PLATFORM == PLATFORM_NRF52
+          if (modems_installed) {
               if (device_init()) {
                 hw_ready = true;
               } else {
                 hw_ready = false;
               }
-            #else
-              hw_ready = true;
-            #endif
           } else {
             hw_ready = false;
             Serial.write("No valid radio module found\r\n");
@@ -1143,12 +1093,6 @@ void validate_status() {
               }
             #endif
           }
-          
-          if (hw_ready && eeprom_have_conf()) {
-            eeprom_conf_load();
-            op_mode = MODE_TNC;
-            startRadio();
-          }
         } else {
           hw_ready = false;
           #if HAS_DISPLAY
@@ -1189,90 +1133,90 @@ void validate_status() {
   }
 }
 
-#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-  #define _e 2.71828183
-  #define _S 10.0
-  float csma_slope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); }
-  void update_csma_p() {
-      csma_p = (uint8_t)((1.0-(csma_p_min+(csma_p_max-csma_p_min)*csma_slope(airtime)))*255.0);
-}
-#endif
 
 void loop() {
-  if (radio_online) {
+    bool ready = false;
+  for (int i = 0; i < INTERFACE_COUNT; i++) {
+      selected_radio = interface_obj[i];
+      if (selected_radio->getRadioOnline()) {
+          selected_radio->checkModemStatus();
+          ready = true;
+      }
+  }
+
+  // if at least one radio is online then we can continue
+  if (ready) {
     #if MCU_VARIANT == MCU_ESP32
       if (packet_ready) {
+        selected_radio = interface_obj[packet_interface];
         portENTER_CRITICAL(&update_lock);
-        last_rssi = LoRa->packetRssi();
-        last_snr_raw = LoRa->packetSnrRaw();
+        last_rssi = selected_radio->packetRssi();
+        last_snr_raw = selected_radio->packetSnrRaw();
         portEXIT_CRITICAL(&update_lock);
         kiss_indicate_stat_rssi();
         kiss_indicate_stat_snr();
-        kiss_write_packet();
+        kiss_write_packet(packet_interface);
       }
 
-      airtime_lock = false;
-      if (st_airtime_limit != 0.0 && airtime >= st_airtime_limit) airtime_lock = true;
-      if (lt_airtime_limit != 0.0 && longterm_airtime >= lt_airtime_limit) airtime_lock = true;
-
     #elif MCU_VARIANT == MCU_NRF52
       if (packet_ready) {
+        selected_radio = interface_obj[packet_interface];
         portENTER_CRITICAL();
-        last_rssi = LoRa->packetRssi();
-        last_snr_raw = LoRa->packetSnrRaw();
+        last_rssi = selected_radio->packetRssi();
+        last_snr_raw = selected_radio->packetSnrRaw();
         portEXIT_CRITICAL();
         kiss_indicate_stat_rssi();
         kiss_indicate_stat_snr();
-        kiss_write_packet();
+        kiss_write_packet(packet_interface);
       }
-
-      airtime_lock = false;
-      if (st_airtime_limit != 0.0 && airtime >= st_airtime_limit) airtime_lock = true;
-      if (lt_airtime_limit != 0.0 && longterm_airtime >= lt_airtime_limit) airtime_lock = true;
     #endif
 
-    checkModemStatus();
-    if (!airtime_lock) {
-      if (queue_height > 0) {
-        #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-          long check_time = millis();
-          if (check_time > post_tx_yield_timeout) {
-            if (dcd_waiting && (check_time >= dcd_wait_until)) { dcd_waiting = false; }
-            if (!dcd_waiting) {
-              for (uint8_t dcd_i = 0; dcd_i < dcd_threshold*2; dcd_i++) {
-                delay(STATUS_INTERVAL_MS); updateModemStatus();
-              }
+    for (int i = 0; i < INTERFACE_COUNT; i++) {
+        selected_radio = interface_obj_sorted[i];
 
-              if (!dcd) {
-                uint8_t csma_r = (uint8_t)random(256);
-                if (csma_p >= csma_r) {
-                  flushQueue();
-                } else {
-                  dcd_waiting = true;
-                  dcd_wait_until = millis()+csma_slot_ms;
+        if (selected_radio->calculateALock() || !selected_radio->getRadioOnline()) {
+            // skip this interface
+            continue;
+        }
+
+        // If a higher data rate interface has received a packet after its
+        // loop, it still needs to be the first to transmit, so check if this
+        // is the case.
+        if (i != 0) {
+            for (int j = 0; j < INTERFACE_COUNT; j++) {
+                if (!interface_obj_sorted[j]->calculateALock() || interface_obj_sorted[j]->getRadioOnline()) {
+                    if (interface_obj_sorted[j]->getBitrate() > selected_radio->getBitrate()) {
+                        if (queue_height[interface_obj_sorted[j]->getIndex()] > 0) {
+                            selected_radio = interface_obj_sorted[j];
+                        }
+                    }
                 }
-              }
             }
-          }
-          
-        #else
-          if (!dcd_waiting) updateModemStatus();
-
-          if (!dcd && !dcd_led) {
-            if (dcd_waiting) delay(lora_rx_turnaround_ms);
-
-            updateModemStatus();
+        }
 
-            if (!dcd) {
-              dcd_waiting = false;
-              flushQueue();
+        if (queue_height[selected_radio->getIndex()] > 0) {
+            long check_time = millis();
+            if (check_time > selected_radio->getPostTxYieldTimeout()) {
+                if (selected_radio->getDCDWaiting() && (check_time >= selected_radio->getDCDWaitUntil())) { selected_radio->setDCDWaiting(false); }
+                if (!selected_radio->getDCDWaiting()) {
+                    // todo, will the delay here slow down transmission with
+                    // multiple interfaces? needs investigation
+                    for (uint8_t dcd_i = 0; dcd_i < DCD_THRESHOLD*2; dcd_i++) {
+                        delay(STATUS_INTERVAL_MS); selected_radio->updateModemStatus();
+                    }
+
+                    if (!selected_radio->getDCD()) {
+                        uint8_t csma_r = (uint8_t)random(256);
+                        if (selected_radio->getCSMAp() >= csma_r) {
+                            flushQueue(selected_radio);
+                        } else {
+                            selected_radio->setDCDWaiting(true);
+                            selected_radio->setDCDWaitUntil(millis()+selected_radio->getCSMASlotMS());
+                        }
+                    }
+                }
             }
-
-          } else {
-            dcd_waiting = true;
-          }
-        #endif
-      }
+        }
     }
   
   } else {
@@ -1285,18 +1229,16 @@ void loop() {
         led_indicate_standby();
       }
     } else {
-
       led_indicate_not_ready();
-      stopRadio();
+      // shut down all radio interfaces
+      for (int i = 0; i < INTERFACE_COUNT; i++) {
+          stopRadio(interface_obj[i]);
+      }
     }
   }
 
-  #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-      buffer_serial();
-      if (!fifo_isempty(&serialFIFO)) serial_poll();
-  #else
-    if (!fifo_isempty_locked(&serialFIFO)) serial_poll();
-  #endif
+  buffer_serial();
+  if (!fifo_isempty(&serialFIFO)) serial_poll();
 
   #if HAS_DISPLAY
     if (disp_ready) update_display();
@@ -1345,11 +1287,7 @@ volatile bool serial_polling = false;
 void serial_poll() {
   serial_polling = true;
 
-  #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-  while (!fifo_isempty_locked(&serialFIFO)) {
-  #else
   while (!fifo_isempty(&serialFIFO)) {
-  #endif
     char sbyte = fifo_pop(&serialFIFO);
     serialCallback(sbyte);
   }
@@ -1357,11 +1295,8 @@ void serial_poll() {
   serial_polling = false;
 }
 
-#if MCU_VARIANT != MCU_ESP32
-  #define MAX_CYCLES 20
-#else
-  #define MAX_CYCLES 10
-#endif
+#define MAX_CYCLES 20
+
 void buffer_serial() {
   if (!serial_buffering) {
     serial_buffering = true;
@@ -1379,11 +1314,7 @@ void buffer_serial() {
     {
       c++;
 
-      #if MCU_VARIANT != MCU_ESP32 && MCU_VARIANT != MCU_NRF52
-        if (!fifo_isfull_locked(&serialFIFO)) {
-          fifo_push_locked(&serialFIFO, Serial.read());
-        }
-      #elif HAS_BLUETOOTH || HAS_BLE == true
+      #if HAS_BLUETOOTH || HAS_BLE == true
         if (bt_state == BT_STATE_CONNECTED) {
           if (!fifo_isfull(&serialFIFO)) {
             fifo_push(&serialFIFO, SerialBT.read());
@@ -1403,41 +1334,3 @@ void buffer_serial() {
     serial_buffering = false;
   }
 }
-
-void serial_interrupt_init() {
-  #if MCU_VARIANT == MCU_1284P
-      TCCR3A = 0;
-      TCCR3B = _BV(CS10) |
-               _BV(WGM33)|
-               _BV(WGM32);
-
-      // Buffer incoming frames every 1ms
-      ICR3 = 16000;
-
-      TIMSK3 = _BV(ICIE3);
-
-  #elif MCU_VARIANT == MCU_2560
-      // TODO: This should probably be updated for
-      // atmega2560 support. Might be source of
-      // reported issues from snh.
-      TCCR3A = 0;
-      TCCR3B = _BV(CS10) |
-               _BV(WGM33)|
-               _BV(WGM32);
-
-      // Buffer incoming frames every 1ms
-      ICR3 = 16000;
-
-      TIMSK3 = _BV(ICIE3);
-
-  #elif MCU_VARIANT == MCU_ESP32
-      // No interrupt-based polling on ESP32
-  #endif
-
-}
-
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-  ISR(TIMER3_CAPT_vect) {
-    buffer_serial();
-  }
-#endif
diff --git a/Radio.cpp b/Radio.cpp
new file mode 100644 (file)
index 0000000..4fb40fd
--- /dev/null
+++ b/Radio.cpp
@@ -0,0 +1,2405 @@
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
+// Obviously still under the MIT license.
+
+#include "Radio.h"
+
+#if PLATFORM == PLATFORM_ESP32 
+  #if defined(ESP32) and !defined(CONFIG_IDF_TARGET_ESP32S3)
+    #include "soc/rtc_wdt.h"
+  #endif
+  #define ISR_VECT IRAM_ATTR
+#else
+  #define ISR_VECT
+#endif
+
+#define MAX_PKT_LENGTH                255
+
+// SX126x registers
+#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_CAD_PARAMS               0x88
+#define OP_CALIBRATE_6X             0x89
+#define OP_RX_TX_FALLBACK_MODE_6X   0x93
+#define OP_REGULATOR_MODE_6X        0x96
+#define OP_CALIBRATE_IMAGE_6X       0x98
+
+#define MASK_CALIBRATE_ALL          0x7f
+
+#define IRQ_TX_DONE_MASK_6X         0x01
+#define IRQ_RX_DONE_MASK_6X         0x02
+#define IRQ_HEADER_DET_MASK_6X      0x10
+#define IRQ_PREAMBLE_DET_MASK_6X    0x04
+#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40
+#define IRQ_ALL_MASK_6X             0b0100001111111111
+
+#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 MODE_TCXO_3_0V_6X           0x06
+#define MODE_TCXO_2_7V_6X           0x06
+#define MODE_TCXO_2_4V_6X           0x06
+#define MODE_TCXO_2_2V_6X           0x03
+#define MODE_TCXO_1_8V_6X           0x02
+#define MODE_TCXO_1_7V_6X           0x01
+#define MODE_TCXO_1_6V_6X           0x00
+
+#define MODE_STDBY_RC_6X            0x00
+#define MODE_STDBY_XOSC_6X          0x01
+#define MODE_FALLBACK_STDBY_RC_6X   0x20
+#define MODE_IMPLICIT_HEADER        0x01
+#define MODE_EXPLICIT_HEADER        0x00
+
+#define SYNC_WORD_6X              0x1424
+
+#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 int packet_interface;
+extern RadioInterface* interface_obj[];
+
+// ISRs cannot provide parameters to the functions they call. Since we have
+// multiple radio objects, we have to read each dio0 pin for each one and see
+// which one is high. We can then use the index of this pin in the 2D array to
+// call the correct object.
+void onDio0Rise() {
+    for (int i = 0; i < INTERFACE_COUNT; i++) {
+        if (digitalRead(interface_pins[i][5]) == HIGH) {
+            packet_interface = i;
+            RadioInterface* obj = interface_obj[i];
+            obj->handleDio0Rise();
+            break;
+        }
+    }
+}
+
+sx126x::sx126x(uint8_t index, SPIClass spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen) :
+  RadioInterface(index),
+    _spiSettings(8E6, MSBFIRST, SPI_MODE0),
+    _spiModem(spi),
+  _ss(ss), _sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0),
+  _busy(busy), _rxen(rxen), _frequency(0), _txp(0), _sf(0x07), _bw(0x04),
+  _cr(0x01), _ldro(0x00), _packetIndex(0), _implicitHeaderMode(0),
+  _payloadLength(255), _crcMode(1), _fifo_tx_addr_ptr(0), _fifo_rx_addr_ptr(0),
+  _packet({0}), _preinit_done(false), _tcxo(tcxo),
+  _dio2_as_rf_switch(dio2_as_rf_switch)
+{
+  // overide Stream timeout value
+  setTimeout(0);
+  // TODO, figure out why this has to be done. Using the index to reference the
+  // interface_obj list causes a crash otherwise
+  _index = getIndex();
+}
+
+bool sx126x::preInit() {
+  pinMode(_ss, OUTPUT);
+  digitalWrite(_ss, HIGH);
+  
+  // todo: check if this change causes issues on any platforms
+  #if MCU_VARIANT == MCU_ESP32
+  if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
+    _spiModem.begin(_sclk, _miso, _mosi, _ss);
+  } else {
+    _spiModem.begin();
+  }
+  #else
+    _spiModem.begin();
+  #endif
+
+  // check version (retry for up to 2 seconds)
+  // TODO: Actually read version registers, not syncwords
+  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);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(opcode);
+    _spiModem.transfer((address & 0xFF00) >> 8);
+    _spiModem.transfer(address & 0x00FF);
+    if (opcode == OP_READ_REGISTER_6X) {
+        _spiModem.transfer(0x00);
+    }
+    response = _spiModem.transfer(value);
+    _spiModem.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+
+    return response;
+}
+
+void sx126x::rxAntEnable()
+{
+  if (_rxen != -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() {
+    unsigned long time = millis();
+    while (digitalRead(_busy) == HIGH)
+    {
+        if (millis() >= (time + 100)) {
+            break;
+        }
+        // do nothing
+    }
+}
+
+void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(opcode);
+
+    for (int i = 0; i < size; i++)
+    {
+        _spiModem.transfer(buffer[i]);
+    }
+
+    _spiModem.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(opcode);
+    _spiModem.transfer(0x00);
+
+    for (int i = 0; i < size; i++)
+    {
+        buffer[i] = _spiModem.transfer(0x00);
+    }
+
+    _spiModem.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx126x::writeBuffer(const uint8_t* buffer, size_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(OP_FIFO_WRITE_6X);
+    _spiModem.transfer(_fifo_tx_addr_ptr);
+
+    for (int i = 0; i < size; i++)
+    {
+        _spiModem.transfer(buffer[i]);
+        _fifo_tx_addr_ptr++;
+    }
+
+    _spiModem.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx126x::readBuffer(uint8_t* buffer, size_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(OP_FIFO_READ_6X);
+    _spiModem.transfer(_fifo_rx_addr_ptr);
+    _spiModem.transfer(0x00);
+
+    for (int i = 0; i < size; i++)
+    {
+        buffer[i] = _spiModem.transfer(0x00);
+    }
+
+    _spiModem.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(uint32_t 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);
+}
+
+void sx126x::reset(void) {
+  if (_reset != -1) {
+    pinMode(_reset, OUTPUT);
+
+    // perform reset
+    digitalWrite(_reset, LOW);
+    delay(10);
+    digitalWrite(_reset, HIGH);
+    delay(10);
+  }
+}
+
+void sx126x::calibrate(void) {
+  // Put in STDBY_RC mode before calibration
+  uint8_t mode_byte = MODE_STDBY_RC_6X;
+  executeOpcode(OP_STANDBY_6X, &mode_byte, 1);
+
+  // calibrate RC64k, RC13M, PLL, ADC and image
+  uint8_t calibrate = MASK_CALIBRATE_ALL;
+  executeOpcode(OP_CALIBRATE_6X, &calibrate, 1);
+
+  delay(5);
+  waitOnBusy();
+}
+
+void sx126x::calibrate_image(uint32_t frequency) {
+  uint8_t image_freq[2] = {0};
+
+  if (frequency >= 430E6 && frequency <= 440E6) {
+    image_freq[0] = 0x6B;
+    image_freq[1] = 0x6F;
+  }
+  else if (frequency >= 470E6 && frequency <= 510E6) {
+    image_freq[0] = 0x75;
+    image_freq[1] = 0x81;
+  }
+  else if (frequency >= 779E6 && frequency <= 787E6) {
+    image_freq[0] = 0xC1;
+    image_freq[1] = 0xC5;
+  }
+  else if (frequency >= 863E6 && frequency <= 870E6) {
+    image_freq[0] = 0xD7;
+    image_freq[1] = 0xDB;
+  }
+  else if (frequency >= 902E6 && frequency <= 928E6) {
+    image_freq[0] = 0xE1;
+    image_freq[1] = 0xE9;
+  }
+
+  executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2);
+  waitOnBusy();
+}
+
+int sx126x::begin()
+{
+  reset();
+
+  if (_busy != -1) {
+      pinMode(_busy, INPUT);
+  }
+
+  if (!_preinit_done) {
+    if (!preInit()) {
+      return false;
+    }
+  }
+
+  if (_rxen != -1) {
+      pinMode(_rxen, OUTPUT);
+  }
+
+  calibrate();
+  calibrate_image(_frequency);
+
+  enableTCXO();
+
+  loraMode();
+  standby();
+
+  // Set sync word
+  setSyncWord(SYNC_WORD_6X);
+
+  if (_dio2_as_rf_switch) {
+    // enable dio2 rf switch
+    uint8_t byte = 0x01;
+    executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1);
+  }
+
+  rxAntEnable();
+
+  setFrequency(_frequency);
+
+  setTxPower(_txp);
+  enableCrc();
+
+  // 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);
+
+  _radio_online = true;
+  return 1;
+}
+
+void sx126x::end()
+{
+  // put in sleep mode
+  sleep();
+
+  // stop SPI
+  _spiModem.end();
+
+  _bitrate = 0;
+
+  _radio_online = false;
+
+  _preinit_done = false;
+}
+
+int sx126x::beginPacket(int implicitHeader)
+{
+  standby();
+
+  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;
+    }
+
+    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;
+}
+
+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;
+}
+
+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)(uint8_t, 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
+    _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0));
+#endif
+    // make function available
+    extern void onDio0Rise();
+
+    attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
+  } else {
+    detachInterrupt(digitalPinToInterrupt(_dio0));
+#ifdef SPI_HAS_NOTUSINGINTERRUPT
+    _spiModem.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::standby()
+{
+  uint8_t byte;
+  if (_tcxo) {
+  // STDBY_XOSC
+  byte = MODE_STDBY_XOSC_6X;
+  } else {
+  // STDBY_RC
+  byte = MODE_STDBY_RC_6X;
+  }
+  executeOpcode(OP_STANDBY_6X, &byte, 1); 
+}
+
+void sx126x::sleep()
+{
+    uint8_t byte = 0x00;
+    executeOpcode(OP_SLEEP_6X, &byte, 1);
+}
+
+void sx126x::enableTCXO() {
+  if (_tcxo) {
+    #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3
+      uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF};
+    #elif BOARD_MODEL == BOARD_TBEAM
+      uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
+    #elif BOARD_MODEL == BOARD_RNODE_NG_22
+      uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
+    #else
+      uint8_t buf[4] = {0};
+    #endif
+    executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4);
+  }
+}
+
+// TODO: Once enabled, SX1262 needs a complete reset to disable TCXO
+void sx126x::disableTCXO() { }
+
+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; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs
+    pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262
+    pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261)
+    pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet)
+
+    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; }
+
+    _txp = level;
+
+    writeRegister(REG_OCP_6X, 0x38); // 160mA limit, overcurrent protection
+
+    uint8_t tx_buf[2];
+
+    tx_buf[0] = level;
+    tx_buf[1] = 0x02; // PA ramping time - 40 microseconds
+    
+    executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2);
+}
+
+uint8_t sx126x::getTxPower() {
+    return _txp;
+}
+
+void sx126x::setFrequency(uint32_t 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;
+
+  handleLowDataRate();
+  setModulationParams(sf, _bw, _cr, _ldro);
+}
+
+uint8_t sx126x::getSpreadingFactor()
+{
+    return _sf;
+}
+
+uint32_t 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(){
+  if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16) {
+    _ldro = 0x01;
+  } else {
+    _ldro = 0x00;
+  }
+}
+
+void sx126x::optimizeModemSensitivity(){
+    // todo: check if there's anything the sx1262 can do here
+}
+
+void sx126x::setSignalBandwidth(uint32_t 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;
+  }
+
+  handleLowDataRate();
+  setModulationParams(_sf, _bw, _cr, _ldro);
+
+  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);
+}
+
+uint8_t sx126x::getCodingRate4()
+{
+    return _cr + 4;
+}
+
+void sx126x::setPreambleLength(long length)
+{
+  _preambleLength = length;
+  setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
+}
+
+void sx126x::setSyncWord(uint16_t sw)
+{
+  // TODO: Fix
+    // writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8);
+    // writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF);
+    writeRegister(REG_SYNC_WORD_MSB_6X, 0x14);
+    writeRegister(REG_SYNC_WORD_LSB_6X, 0x24);
+}
+
+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::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(_index, packetLength);
+        }
+    }
+    // else {
+    //   Serial.println("CRCE");
+    //   Serial.println(buf[0]);
+    //   Serial.println(buf[1]);
+    // }
+}
+
+void sx126x::updateBitrate() {
+    if (_radio_online) {
+        _lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
+        _lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
+        _bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
+        _lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
+        //_csma_slot_ms = _lora_symbol_time_ms*10;
+        float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
+        if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
+            target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
+        } else {
+            target_preamble_symbols = ceil(target_preamble_symbols);
+        }
+        _preambleLength = (long)target_preamble_symbols;
+        setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+    } else {
+        _bitrate = 0;
+    }
+}
+
+// SX127x 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_RX_DONE_MASK_7X           0x40
+#define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20
+
+#define SYNC_WORD_7X                  0x12
+
+sx127x::sx127x(uint8_t index, SPIClass spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy) :
+  RadioInterface(index),
+    _spiSettings(8E6, MSBFIRST, SPI_MODE0),
+    _spiModem(spi),
+  _ss(ss), _sclk(sclk), _mosi(mosi), _miso(miso),  _reset(reset), _dio0(dio0),
+  _busy(busy), _frequency(0), _packetIndex(0), _preinit_done(false)
+{ 
+    setTimeout(0); 
+    // TODO, figure out why this has to be done. Using the index to reference the
+    // interface_obj list causes a crash otherwise
+    _index = getIndex();
+}
+
+void sx127x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
+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); }
+void sx127x::standby() { 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); }
+uint8_t sx127x::modemStatus() { return readRegister(REG_MODEM_STAT_7X); }
+void sx127x::setSyncWord(uint8_t 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); }
+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::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); }
+byte sx127x::random() { return readRegister(REG_RSSI_WIDEBAND_7X); }
+void sx127x::flush() { }
+
+bool sx127x::preInit() {
+  pinMode(_ss, OUTPUT);
+  digitalWrite(_ss, HIGH);
+  // todo: check if this change causes issues on any platforms
+  #if MCU_VARIANT == MCU_ESP32
+  if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
+    _spiModem.begin(_sclk, _miso, _mosi, _ss);
+  } else {
+    _spiModem.begin();
+  }
+  #else
+    _spiModem.begin();
+  #endif
+
+  // Check modem version
+  uint8_t version;
+  long start = millis();
+  while (((millis() - start) < 500) && (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::singleTransfer(uint8_t address, uint8_t value) {
+  uint8_t response;
+
+  digitalWrite(_ss, LOW);
+  _spiModem.beginTransaction(_spiSettings);
+  _spiModem.transfer(address);
+  response = _spiModem.transfer(value);
+  _spiModem.endTransaction();
+  digitalWrite(_ss, HIGH);
+
+  return response;
+}
+
+int sx127x::begin() {
+  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; }
+  }
+
+  sleep();
+  setFrequency(_frequency);
+
+  // set base addresses
+  writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0);
+  writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0);
+
+  // set LNA boost and auto AGC
+  writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03);
+  writeRegister(REG_MODEM_CONFIG_3_7X, 0x04);
+
+  setSyncWord(SYNC_WORD_7X);
+  enableCrc();
+  setTxPower(2);
+
+  standby();
+
+  _radio_online = true;
+
+  return 1;
+}
+
+void sx127x::end() {
+  sleep();
+  _spiModem.end();
+  _bitrate = 0;
+  _radio_online = false;
+  _preinit_done = false;
+}
+
+int sx127x::beginPacket(int implicitHeader) {
+  standby();
+
+  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() {
+  // Enter TX mode
+  writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X);
+
+  // Wait for TX completion
+  while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) {
+    yield();
+  }
+
+  // Clear TX complete IRQ
+  writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X);
+  return 1;
+}
+
+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);
+
+  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);
+    if ((currentLength + size) > MAX_PKT_LENGTH) {
+        size = MAX_PKT_LENGTH - currentLength;
+    }
+
+    for (size_t i = 0; i < size; i++) {
+        writeRegister(REG_FIFO_7X, buffer[i]);
+    }
+
+    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; }
+
+  // Remember current FIFO address, read, and then reset address
+  int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X);
+  uint8_t b = readRegister(REG_FIFO_7X);
+  writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress);
+
+  return b;
+}
+
+void sx127x::onReceive(void(*callback)(uint8_t, int)) {
+  _onReceive = callback;
+
+  if (callback) {
+    pinMode(_dio0, INPUT);
+    writeRegister(REG_DIO_MAPPING_1_7X, 0x00);
+    
+    #ifdef SPI_HAS_NOTUSINGINTERRUPT
+      _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0));
+    #endif
+    
+    // make function available
+    extern void onDio0Rise();
+
+    attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
+  
+  } else {
+    detachInterrupt(digitalPinToInterrupt(_dio0));
+    
+    #ifdef SPI_HAS_NOTUSINGINTERRUPT
+      _spiModem.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::setTxPower(int level, int outputPin) {
+  // Setup according to RFO or PA_BOOST output pin
+  if (PA_OUTPUT_RFO_PIN == outputPin) {
+    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 {
+    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(uint32_t 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);
+  }
+
+  _sf = sf;
+
+  writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0));
+  handleLowDataRate();
+}
+
+uint8_t sx127x::getSpreadingFactor()
+{
+    return _sf;
+}
+
+uint32_t 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::setSignalBandwidth(uint32_t 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;
+  _cr = cr;
+  writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1));
+}
+
+uint8_t sx127x::getCodingRate4()
+{
+    return _cr + 4;
+}
+
+void sx127x::setPreambleLength(long length) { 
+  _preambleLength = length;
+  writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8));
+  writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 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 {
+    // Only 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 ISR_VECT sx127x::handleDio0Rise() {
+  int irqFlags = readRegister(REG_IRQ_FLAGS_7X);
+
+  // Clear IRQs
+  writeRegister(REG_IRQ_FLAGS_7X, irqFlags);
+  if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) {
+    _packetIndex = 0;
+    int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X);
+    writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X));
+    if (_onReceive) { 
+        _onReceive(_index, packetLength); 
+    }
+    writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
+  }
+}
+
+void sx127x::updateBitrate() {
+    if (_radio_online) {
+        _lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
+        _lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
+        _bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
+        _lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
+        //_csma_slot_ms = _lora_symbol_time_ms*10;
+        float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
+        if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
+            target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
+        } else {
+            target_preamble_symbols = ceil(target_preamble_symbols);
+        }
+        _preambleLength = (long)target_preamble_symbols;
+    } else {
+        _bitrate = 0;
+    }
+}
+
+// SX128x registers
+#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)
+
+sx128x::sx128x(uint8_t index, SPIClass spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen) :
+  RadioInterface(index),
+    _spiSettings(8E6, MSBFIRST, SPI_MODE0),
+    _spiModem(spi),
+  _ss(ss), _sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0),
+  _busy(busy), _rxen(rxen), _txen(txen), _frequency(0), _txp(0), _sf(0x50),
+  _bw(0x34), _cr(0x01), _packetIndex(0), _implicitHeaderMode(0),
+  _payloadLength(255), _crcMode(0), _fifo_tx_addr_ptr(0), _fifo_rx_addr_ptr(0),
+  _packet({0}), _rxPacketLength(0), _preinit_done(false),
+  _tcxo(tcxo)
+{
+  // overide Stream timeout value
+  setTimeout(0);
+  // TODO, figure out why this has to be done. Using the index to reference the
+  // interface_obj list causes a crash otherwise
+  _index = getIndex();
+}
+
+bool sx128x::preInit() {
+  // setup pins
+  pinMode(_ss, OUTPUT);
+  // set SS high
+  digitalWrite(_ss, HIGH);
+  
+  // todo: check if this change causes issues on any platforms
+  #if MCU_VARIANT == MCU_ESP32
+  if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) {
+    _spiModem.begin(_sclk, _miso, _mosi, _ss);
+  } else {
+    _spiModem.begin();
+  }
+  #else
+    _spiModem.begin();
+  #endif
+
+  // 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);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(opcode);
+    _spiModem.transfer((address & 0xFF00) >> 8);
+    _spiModem.transfer(address & 0x00FF);
+    if (opcode == OP_READ_REGISTER_8X) {
+        _spiModem.transfer(0x00);
+    }
+    response = _spiModem.transfer(value);
+    _spiModem.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();
+    while (digitalRead(_busy) == HIGH)
+    {
+        if (millis() >= (time + 100)) {
+            break;
+        }
+        // do nothing
+    }
+}
+
+void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(opcode);
+
+    for (int i = 0; i < size; i++)
+    {
+        _spiModem.transfer(buffer[i]);
+    }
+
+    _spiModem.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(opcode);
+    _spiModem.transfer(0x00);
+
+    for (int i = 0; i < size; i++)
+    {
+        buffer[i] = _spiModem.transfer(0x00);
+    }
+
+    _spiModem.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx128x::writeBuffer(const uint8_t* buffer, size_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(OP_FIFO_WRITE_8X);
+    _spiModem.transfer(_fifo_tx_addr_ptr);
+
+    for (int i = 0; i < size; i++)
+    {
+        _spiModem.transfer(buffer[i]);
+        _fifo_tx_addr_ptr++;
+    }
+
+    _spiModem.endTransaction();
+
+    digitalWrite(_ss, HIGH);
+}
+
+void sx128x::readBuffer(uint8_t* buffer, size_t size)
+{
+    waitOnBusy();
+
+    digitalWrite(_ss, LOW);
+
+    _spiModem.beginTransaction(_spiSettings);
+    _spiModem.transfer(OP_FIFO_READ_8X);
+    _spiModem.transfer(_fifo_rx_addr_ptr);
+    _spiModem.transfer(0x00);
+
+    for (int i = 0; i < size; i++)
+    {
+        buffer[i] = _spiModem.transfer(0x00);
+    }
+
+    _spiModem.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 << 4;
+  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()
+{
+  if (_reset != -1) {
+    pinMode(_reset, OUTPUT);
+
+    // perform reset
+    digitalWrite(_reset, LOW);
+    delay(10);
+    digitalWrite(_reset, HIGH);
+    delay(10);
+  }
+
+  if (_rxen != -1) {
+      pinMode(_rxen, OUTPUT);
+  }
+
+  if (_txen != -1) {
+      pinMode(_txen, OUTPUT);
+  }
+
+  if (_busy != -1) {
+      pinMode(_busy, INPUT);
+  }
+
+  if (!_preinit_done) {
+    if (!preInit()) {
+      return false;
+    }
+  }
+
+  standby();
+  loraMode();
+  rxAntEnable();
+
+  setFrequency(_frequency);
+
+  // set LNA boost
+  // todo: implement this
+  //writeRegister(REG_LNA, 0x96);
+
+  setModulationParams(_sf, _bw, _cr);
+  setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+
+  setTxPower(_txp);
+
+  // set base addresses
+  uint8_t basebuf[2] = {0};
+  executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2);
+
+  _radio_online = true;
+  return 1;
+}
+
+void sx128x::end()
+{
+  // put in sleep mode
+  sleep();
+
+  // stop SPI
+  _spiModem.end();
+
+  _bitrate = 0;
+
+  _radio_online = false;
+  _preinit_done = false;
+}
+
+int sx128x::beginPacket(int implicitHeader)
+{
+  // put in standby mode
+  standby();
+
+  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};
+
+    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;
+    }
+
+    if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) {
+        byte = byte | 0x02 | 0x04;
+    }
+
+    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)(uint8_t, 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
+//    _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0));
+//#endif
+
+    // make function available
+    extern void onDio0Rise();
+
+    attachInterrupt(digitalPinToInterrupt(_dio0), onDio0Rise, RISING);
+  } else {
+    detachInterrupt(digitalPinToInterrupt(_dio0));
+//#ifdef SPI_HAS_NOTUSINGINTERRUPT
+//    _spiModem.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::standby()
+{
+    uint8_t byte;
+    if (_tcxo) {
+          // STDBY_XOSC
+          byte = 0x01;
+    } else {
+          // STDBY_RC
+          byte = 0x00;
+    }
+      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(uint32_t 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;
+
+  setModulationParams(sf, _bw, _cr);
+  handleLowDataRate();
+}
+
+uint8_t sx128x::getSpreadingFactor()
+{
+    return _sf;
+}
+
+uint32_t 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(uint32_t 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);
+}
+
+uint8_t sx128x::getCodingRate4()
+{
+    return _cr + 4;
+}
+
+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::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(_index, _rxPacketLength);
+        }
+
+    }
+}
+
+void sx128x::updateBitrate() {
+    if (_radio_online) {
+        _lora_symbol_rate = (float)getSignalBandwidth()/(float)(pow(2, _sf));
+        _lora_symbol_time_ms = (1.0/_lora_symbol_rate)*1000.0;
+        _bitrate = (uint32_t)(_sf * ( (4.0/(float)(_cr+4)) / ((float)(pow(2, _sf))/((float)getSignalBandwidth()/1000.0)) ) * 1000.0);
+        _lora_us_per_byte = 1000000.0/((float)_bitrate/8.0);
+        //_csma_slot_ms = _lora_symbol_time_ms*10;
+        float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/_lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
+        if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
+            target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
+        } else {
+            target_preamble_symbols = ceil(target_preamble_symbols);
+        }
+        _preambleLength = (long)target_preamble_symbols;
+        setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
+    } else {
+        _bitrate = 0;
+    }
+}
diff --git a/Radio.h b/Radio.h
new file mode 100644 (file)
index 0000000..94e47bc
--- /dev/null
+++ b/Radio.h
@@ -0,0 +1,656 @@
+// Copyright (c) Sandeep Mistry. All rights reserved.
+// Licensed under the MIT license.
+
+// Modifications and additions copyright 2023 by Mark Qvist & Jacob Eva
+// Obviously still under the MIT license.
+
+#ifndef RADIO_H
+#define RADIO_H
+
+#include <Arduino.h>
+#include <SPI.h>
+#include "Interfaces.h"
+#include "Boards.h"
+
+// TX
+#define PA_OUTPUT_RFO_PIN 0
+#define PA_OUTPUT_PA_BOOST_PIN 1
+
+// DCD
+#define STATUS_INTERVAL_MS 3
+#define DCD_SAMPLES 2500
+#define UTIL_UPDATE_INTERVAL_MS 1000
+#define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS)
+#define AIRTIME_LONGTERM 3600
+#define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000)
+#define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES)
+#define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS)
+#define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS
+#define DCD_THRESHOLD 2
+#define DCD_LED_STEP_D 3
+#define LORA_PREAMBLE_SYMBOLS_HW  4
+#define LORA_PREAMBLE_SYMBOLS_MIN 18
+#define LORA_PREAMBLE_TARGET_MS   15
+
+#define RSSI_OFFSET 157
+
+#define PHY_HEADER_LORA_SYMBOLS 8
+
+#define _e 2.71828183
+#define _S 10.0
+
+// Status flags
+const uint8_t SIG_DETECT = 0x01;
+const uint8_t SIG_SYNCED = 0x02;
+const uint8_t RX_ONGOING = 0x04;
+
+// forward declare Utilities.h LED functions
+void led_rx_on();
+void led_rx_off();
+void led_indicate_airtime_lock();
+
+#if PLATFORM == PLATFORM_ESP32
+// get update_lock for ESP32
+extern portMUX_TYPE update_lock;
+#endif
+
+class RadioInterface : public Stream {
+public:
+    // todo: in the future define _spiModem and _spiSettings from here for inheritence by child classes
+    RadioInterface(uint8_t index) : _index(index), _radio_locked(false),
+    _radio_online(false), _st_airtime_limit(0.0), _lt_airtime_limit(0.0),
+    _airtime_lock(false), _airtime(0.0), _longterm_airtime(0.0),
+    _local_channel_util(0.0), _total_channel_util(0.0),
+    _longterm_channel_util(0.0), _last_status_update(0), _last_dcd(0),
+    _stat_rx_ongoing(false), _stat_signal_detected(false),
+    _stat_signal_synced(false), _dcd_count(0), _dcd(false), _dcd_led(false),
+    _dcd_waiting(false), _dcd_wait_until(0), _dcd_sample(0),
+    _post_tx_yield_timeout(0), _csma_slot_ms(50), _csma_p_min(0.1),
+    _csma_p_max(0.8), _preambleLength(6), _lora_symbol_time_ms(0.0),
+    _lora_symbol_rate(0.0), _lora_us_per_byte(0.0), _bitrate(0),
+    _onReceive(NULL) {};
+    virtual int begin();
+    virtual void end();
+
+    virtual int beginPacket(int implicitHeader = false);
+    virtual int endPacket();
+
+    virtual int packetRssi();
+    virtual int currentRssi();
+    virtual uint8_t packetRssiRaw();
+    virtual uint8_t currentRssiRaw();
+    virtual uint8_t packetSnrRaw();
+    virtual float packetSnr();
+    virtual 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();
+
+    virtual void onReceive(void(*callback)(uint8_t, int));
+
+    virtual void receive(int size = 0);
+    virtual void standby();
+    virtual void sleep();
+
+    virtual bool preInit();
+    virtual uint8_t getTxPower();
+    virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+    virtual uint32_t getFrequency();
+    virtual void setFrequency(uint32_t frequency);
+    virtual void setSpreadingFactor(int sf);
+    virtual uint8_t getSpreadingFactor();
+    virtual uint32_t getSignalBandwidth();
+    virtual void setSignalBandwidth(uint32_t sbw);
+    virtual void setCodingRate4(int denominator);
+    virtual uint8_t getCodingRate4();
+    virtual void setPreambleLength(long length);
+    virtual uint8_t modemStatus();
+    virtual void enableCrc();
+    virtual void disableCrc();
+    virtual void enableTCXO();
+    virtual void disableTCXO();
+
+    virtual byte random();
+
+    virtual void setSPIFrequency(uint32_t frequency);
+
+    virtual void updateBitrate();
+    virtual void handleDio0Rise();
+    uint32_t getBitrate() { return _bitrate; };
+    uint8_t getIndex() { return _index; };
+    void setRadioLock(bool lock) { _radio_locked = lock; };
+    bool getRadioLock() { return _radio_locked; };
+    void setRadioOnline(bool online) { _radio_online = online; };
+    bool getRadioOnline() { return _radio_online; };
+    void setSTALock(float at) { _st_airtime_limit = at; };
+    float getSTALock() { return _st_airtime_limit; };
+    void setLTALock(float at) { _lt_airtime_limit = at; };
+    float getLTALock() { return _lt_airtime_limit; };
+    bool calculateALock() {
+      _airtime_lock = false;
+      if (_st_airtime_limit != 0.0 && _airtime >= _st_airtime_limit) {
+          _airtime_lock = true;
+      }
+      if (_lt_airtime_limit != 0.0 && _longterm_airtime >= _lt_airtime_limit) {
+          _airtime_lock = true;
+      }
+      return _airtime_lock;
+    };
+    void updateAirtime() {
+        uint16_t cb = current_airtime_bin();
+        uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; }
+        uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
+        _airtime_bins[nb] = 0;
+        _airtime = (float)(_airtime_bins[cb]+_airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS);
+    
+        uint32_t longterm_airtime_sum = 0;
+        for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) {
+          longterm_airtime_sum += _airtime_bins[bin];
+        }
+        _longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS;
+    
+        float longterm_channel_util_sum = 0.0;
+        for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) {
+          longterm_channel_util_sum += _longterm_bins[bin];
+        }
+        _longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS;
+    
+          updateCSMAp();
+
+          //kiss_indicate_channel_stats(); // todo: enable me!
+    };
+    void addAirtime(uint16_t written) {
+        float packet_cost_ms = 0.0;
+        float payload_cost_ms = ((float)written * _lora_us_per_byte)/1000.0;
+        packet_cost_ms += payload_cost_ms;
+        packet_cost_ms += (_preambleLength+4.25)*_lora_symbol_time_ms;
+        packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * _lora_symbol_time_ms;
+        uint16_t cb = current_airtime_bin();
+        uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
+        _airtime_bins[cb] += packet_cost_ms;
+        _airtime_bins[nb] = 0;
+    };
+    void checkModemStatus() {
+      if (millis()-_last_status_update >= STATUS_INTERVAL_MS) {
+        updateModemStatus();
+    
+          _util_samples[_dcd_sample] = _dcd;
+          _dcd_sample = (_dcd_sample+1)%DCD_SAMPLES;
+          if (_dcd_sample % UTIL_UPDATE_INTERVAL == 0) {
+            int util_count = 0;
+            for (int ui = 0; ui < DCD_SAMPLES; ui++) {
+              if (_util_samples[ui]) util_count++;
+            }
+            _local_channel_util = (float)util_count / (float)DCD_SAMPLES;
+            _total_channel_util = _local_channel_util + _airtime;
+            if (_total_channel_util > 1.0) _total_channel_util = 1.0;
+    
+            int16_t cb = current_airtime_bin();
+            uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
+            if (_total_channel_util > _longterm_bins[cb]) _longterm_bins[cb] = _total_channel_util;
+            _longterm_bins[nb] = 0.0;
+    
+            updateAirtime();
+          }
+      }
+    };
+      void updateModemStatus() {
+          #if PLATFORM == PLATFORM_ESP32 
+          portENTER_CRITICAL(&update_lock);
+          #elif PLATFORM == PLATFORM_NRF52 
+          portENTER_CRITICAL();
+          #endif
+    
+          uint8_t status = modemStatus();
+    
+          _last_status_update = millis();
+    
+          #if PLATFORM == PLATFORM_ESP32 
+          portEXIT_CRITICAL(&update_lock);
+          #elif PLATFORM == PLATFORM_NRF52 
+          portEXIT_CRITICAL();
+          #endif
+    
+          if ((status & SIG_DETECT) == SIG_DETECT) { _stat_signal_detected = true; } else { _stat_signal_detected = false; }
+          if ((status & SIG_SYNCED) == SIG_SYNCED) { _stat_signal_synced = true; } else { _stat_signal_synced = false; }
+          if ((status & RX_ONGOING) == RX_ONGOING) { _stat_rx_ongoing = true; } else { _stat_rx_ongoing = false; }
+    
+          // if (stat_signal_detected || stat_signal_synced || stat_rx_ongoing) {
+          if (_stat_signal_detected || _stat_signal_synced) {
+              if (_stat_rx_ongoing) {
+                  if (_dcd_count < DCD_THRESHOLD) {
+                      _dcd_count++;
+                  } else {
+                      _last_dcd = _last_status_update;
+                      _dcd_led = true;
+                      _dcd = true;
+                  }
+              }
+          } else {
+              if (_dcd_count == 0) {
+                  _dcd_led = false;
+              } else if (_dcd_count > DCD_LED_STEP_D) {
+                  _dcd_count -= DCD_LED_STEP_D;
+              } else {
+                  _dcd_count = 0;
+              }
+    
+              if (_last_status_update > _last_dcd+_csma_slot_ms) {
+                  _dcd = false;
+                  _dcd_led = false;
+                  _dcd_count = 0;
+              }
+          }
+    
+          if (_dcd_led) {
+              led_rx_on();
+          } else {
+              if (_airtime_lock) {
+                  led_indicate_airtime_lock();
+              } else {
+                  led_rx_off();
+              }
+          }
+    };
+    void setPostTxYieldTimeout(uint32_t timeout) { _post_tx_yield_timeout = timeout; };
+    uint32_t getPostTxYieldTimeout() { return _post_tx_yield_timeout; };
+    void setDCD(bool dcd) { _dcd = dcd; };
+    bool getDCD() { return _dcd; };
+    void setDCDWaiting(bool dcd_waiting) { _dcd_waiting = dcd_waiting; };
+    bool getDCDWaiting() { return _dcd_waiting; };
+    void setDCDWaitUntil(uint32_t dcd_wait_until) { _dcd_wait_until = dcd_wait_until; };
+    bool getDCDWaitUntil() { return _dcd_wait_until; };
+    float getAirtime() { return _airtime; };
+    float getLongtermAirtime() { return _longterm_airtime; };
+    float getTotalChannelUtil() { return _total_channel_util; };
+    float getLongtermChannelUtil() { return _longterm_channel_util; };
+    float CSMASlope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); };
+    void updateCSMAp() {
+      _csma_p = (uint8_t)((1.0-(_csma_p_min+(_csma_p_max-_csma_p_min)*CSMASlope(_airtime)))*255.0);
+    };
+    uint8_t getCSMAp() { return _csma_p; };
+    void setCSMASlotMS(int slot_size) { _csma_slot_ms = slot_size; };
+    int getCSMASlotMS() { return _csma_slot_ms; };
+    float getSymbolTime() { return _lora_symbol_time_ms; };
+    float getSymbolRate() { return _lora_symbol_rate; };
+    long getPreambleLength() { return _preambleLength; };
+protected:
+    virtual void explicitHeaderMode();
+    virtual void implicitHeaderMode();
+
+    uint8_t _index;
+    bool _radio_locked;
+    bool _radio_online;
+    float _st_airtime_limit;
+    float _lt_airtime_limit;
+    bool _airtime_lock;
+    uint16_t _airtime_bins[AIRTIME_BINS] = {0};
+    uint16_t _longterm_bins[AIRTIME_BINS] = {0};
+    float _airtime;
+    float _longterm_airtime;
+    float _local_channel_util;
+    float _total_channel_util;
+    float _longterm_channel_util;
+    uint32_t _last_status_update;
+    bool _stat_signal_detected;
+    bool _stat_signal_synced;
+    bool _stat_rx_ongoing;
+    uint32_t _last_dcd;
+    uint16_t _dcd_count;
+    bool _dcd;
+    bool _dcd_led;
+    bool _dcd_waiting;
+    long _dcd_wait_until;
+       bool _util_samples[DCD_SAMPLES] = {false};
+       int _dcd_sample;
+    uint32_t _post_tx_yield_timeout;
+    uint8_t _csma_p;
+    int _csma_slot_ms;
+    float _csma_p_min;
+    float _csma_p_max;
+    long _preambleLength;
+    float _lora_symbol_time_ms;
+    float _lora_symbol_rate;
+    float _lora_us_per_byte;
+    uint32_t _bitrate;
+    void (*_onReceive)(uint8_t, int);
+};
+
+class sx126x : public RadioInterface {
+public:
+  sx126x(uint8_t index, SPIClass spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int
+          dio0, int busy, int rxen);
+
+  int begin();
+  void end();
+
+  int beginPacket(int implicitHeader = false);
+  int endPacket();
+
+  int packetRssi();
+  int currentRssi();
+  uint8_t packetRssiRaw();
+  uint8_t currentRssiRaw();
+  uint8_t packetSnrRaw();
+  float packetSnr();
+  long packetFrequencyError();
+
+  // from Print
+  size_t write(uint8_t byte);
+  size_t write(const uint8_t *buffer, size_t size);
+
+  // from Stream
+  int available();
+  int read();
+  int peek();
+  void flush();
+
+  void onReceive(void(*callback)(uint8_t, int));
+
+  void receive(int size = 0);
+  void standby();
+  void sleep();
+
+  bool preInit();
+  uint8_t getTxPower();
+  void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+  uint32_t getFrequency();
+  void setFrequency(uint32_t frequency);
+  void setSpreadingFactor(int sf);
+  uint8_t getSpreadingFactor();
+  uint32_t getSignalBandwidth();
+  void setSignalBandwidth(uint32_t sbw);
+  void setCodingRate4(int denominator);
+  uint8_t getCodingRate4();
+  void setPreambleLength(long length);
+  uint8_t modemStatus();
+  void enableCrc();
+  void disableCrc();
+  void enableTCXO();
+  void disableTCXO();
+
+
+  byte random();
+
+  void setSPIFrequency(uint32_t frequency);
+
+  void dumpRegisters(Stream& out);
+
+  void updateBitrate();
+
+  void handleDio0Rise();
+private:
+  void writeBuffer(const uint8_t* buffer, size_t size);
+  void readBuffer(uint8_t* buffer, size_t size);
+  void loraMode();
+  void rxAntEnable();
+  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, int ldro);
+  void setSyncWord(uint16_t sw);
+  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 explicitHeaderMode();
+  void implicitHeaderMode();
+
+
+  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();
+
+  void reset(void);
+  void calibrate(void);
+  void calibrate_image(uint32_t frequency);
+
+private:
+  SPISettings _spiSettings;
+  SPIClass _spiModem;
+  int _ss;
+  int _sclk;
+  int _mosi;
+  int _miso;
+  int _reset;
+  int _dio0;
+  int _rxen;
+  int _busy;
+  uint32_t _frequency;
+  int _txp;
+  uint8_t _sf;
+  uint8_t _bw;
+  uint8_t _cr;
+  uint8_t _ldro;
+  int _packetIndex;
+  int _implicitHeaderMode;
+  int _payloadLength;
+  int _crcMode;
+  int _fifo_tx_addr_ptr;
+  int _fifo_rx_addr_ptr;
+  uint8_t _packet[255];
+  bool _preinit_done;
+  uint8_t _index;
+  bool _tcxo;
+  bool _dio2_as_rf_switch;
+};
+
+class sx127x : public RadioInterface {
+public:
+  sx127x(uint8_t index, SPIClass spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy);
+
+  int begin();
+  void end();
+
+  int beginPacket(int implicitHeader = false);
+  int endPacket();
+
+  int packetRssi();
+  int currentRssi();
+  uint8_t packetRssiRaw();
+  uint8_t currentRssiRaw();
+  uint8_t packetSnrRaw();
+  float packetSnr();
+  long packetFrequencyError();
+
+  // from Print
+  size_t write(uint8_t byte);
+  size_t write(const uint8_t *buffer, size_t size);
+
+  // from Stream
+  int available();
+  int read();
+  int peek();
+  void flush();
+
+  void onReceive(void(*callback)(uint8_t, int));
+
+  void receive(int size = 0);
+  void standby();
+  void sleep();
+
+  bool preInit();
+  uint8_t getTxPower();
+  void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+  uint32_t getFrequency();
+  void setFrequency(uint32_t frequency);
+  void setSpreadingFactor(int sf);
+  uint8_t getSpreadingFactor();
+  uint32_t getSignalBandwidth();
+  void setSignalBandwidth(uint32_t sbw);
+  void setCodingRate4(int denominator);
+  uint8_t getCodingRate4();
+  void setPreambleLength(long length);
+  uint8_t modemStatus();
+  void enableCrc();
+  void disableCrc();
+  void enableTCXO();
+  void disableTCXO();
+
+  byte random();
+
+  void setSPIFrequency(uint32_t frequency);
+
+  void updateBitrate();
+
+  void handleDio0Rise();
+private:
+  void setSyncWord(uint8_t sw);
+  void explicitHeaderMode();
+  void implicitHeaderMode();
+
+
+  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;
+  SPIClass _spiModem;
+  int _ss;
+  int _sclk;
+  int _mosi;
+  int _miso;
+  int _reset;
+  int _dio0;
+  int _busy;
+  uint32_t _frequency;
+  int _packetIndex;
+  int _implicitHeaderMode;
+  bool _preinit_done;
+  uint8_t _index;
+  uint8_t _sf;
+  uint8_t _cr;
+};
+
+class sx128x : public RadioInterface {
+public:
+  sx128x(uint8_t index, SPIClass spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen);
+
+  int begin();
+  void end();
+
+  int beginPacket(int implicitHeader = false);
+  int endPacket();
+
+  int packetRssi();
+  int currentRssi();
+  uint8_t packetRssiRaw();
+  uint8_t currentRssiRaw();
+  uint8_t packetSnrRaw();
+  float packetSnr();
+  long packetFrequencyError();
+
+  // from Print
+  size_t write(uint8_t byte);
+  size_t write(const uint8_t *buffer, size_t size);
+
+  // from Stream
+  int available();
+  int read();
+  int peek();
+  void flush();
+
+  void onReceive(void(*callback)(uint8_t, int));
+
+  void receive(int size = 0);
+  void standby();
+  void sleep();
+
+  bool preInit();
+  uint8_t getTxPower();
+  void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
+  uint32_t getFrequency();
+  void setFrequency(uint32_t frequency);
+  void setSpreadingFactor(int sf);
+  uint8_t getSpreadingFactor();
+  uint32_t getSignalBandwidth();
+  void setSignalBandwidth(uint32_t sbw);
+  void setCodingRate4(int denominator);
+  uint8_t getCodingRate4();
+  void setPreambleLength(long length);
+  uint8_t modemStatus();
+  void enableCrc();
+  void disableCrc();
+  void enableTCXO();
+  void disableTCXO();
+
+  byte random();
+
+  void setSPIFrequency(uint32_t frequency);
+
+  void dumpRegisters(Stream& out);
+
+  void updateBitrate();
+
+  void handleDio0Rise();
+private:
+  void writeBuffer(const uint8_t* buffer, size_t size);
+  void readBuffer(uint8_t* buffer, size_t size);
+  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 setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
+  void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr);
+  void setSyncWord(int sw);
+  void explicitHeaderMode();
+  void implicitHeaderMode();
+
+
+  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;
+  SPIClass _spiModem;
+  int _ss;
+  int _sclk;
+  int _mosi;
+  int _miso;
+  int _reset;
+  int _dio0;
+  int _rxen;
+  int _txen;
+  int _busy;
+  int _modem;
+  uint32_t _frequency;
+  int _txp;
+  uint8_t _sf;
+  uint8_t _bw;
+  uint8_t _cr;
+  int _packetIndex;
+  int _implicitHeaderMode;
+  int _payloadLength;
+  int _crcMode;
+  int _fifo_tx_addr_ptr;
+  int _fifo_rx_addr_ptr;
+  uint8_t _packet[255];
+  bool _preinit_done;
+  int _rxPacketLength;
+  uint8_t _index;
+  bool _tcxo;
+};
+#endif
index 090e5d5ffa984629e3bb0a9974a9ec8c37aeb19c..ef18e7fea480c4238cca49ebed7a345aa9fa6457 100644 (file)
 // You should have received a copy of the GNU General Public License
 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
+#include "Radio.h"
 #include "Config.h"
 
+// Included for sorting
+#include <algorithm>
+#include <iterator>
+
 #if HAS_EEPROM 
     #include <EEPROM.h>
 #elif PLATFORM == PLATFORM_NRF52
 #endif
 #include <stddef.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"
@@ -81,20 +75,9 @@ uint8_t eeprom_read(uint32_t mapped_addr);
   #define ISR_VECT
 #endif
 
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-       #include <avr/wdt.h>
-       #include <util/atomic.h>
-#endif
-
 uint8_t boot_vector = 0x00;
 
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-       uint8_t OPTIBOOT_MCUSR __attribute__ ((section(".noinit")));
-       void resetFlagsInit(void) __attribute__ ((naked)) __attribute__ ((used)) __attribute__ ((section (".init0")));
-       void resetFlagsInit(void) {
-           __asm__ __volatile__ ("sts %0, r2\n" : "=m" (OPTIBOOT_MCUSR) :);
-       }
-#elif MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_ESP32
        // TODO: Get ESP32 boot flags
 #elif MCU_VARIANT == MCU_NRF52
        // TODO: Get NRF52 boot flags
@@ -138,12 +121,7 @@ uint8_t boot_vector = 0x00;
   void boot_seq() { }
 #endif
 
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-       void led_rx_on()  { digitalWrite(pin_led_rx, HIGH); }
-       void led_rx_off() {     digitalWrite(pin_led_rx, LOW); }
-       void led_tx_on()  { digitalWrite(pin_led_tx, HIGH); }
-       void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
-#elif MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_ESP32
        #if HAS_NP == true
                void led_rx_on()  { npset(0, 0, 0xFF); }
                void led_rx_off() {     npset(0, 0, 0); }
@@ -236,12 +214,7 @@ uint8_t boot_vector = 0x00;
 #endif
 
 void hard_reset(void) {
-       #if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-               wdt_enable(WDTO_15MS);
-               while(true) {
-                       led_tx_on(); led_rx_off();
-               }
-       #elif MCU_VARIANT == MCU_ESP32
+       #if MCU_VARIANT == MCU_ESP32
                ESP.restart();
        #elif MCU_VARIANT == MCU_NRF52
         NVIC_SystemReset();
@@ -332,20 +305,7 @@ void led_indicate_warning(int cycles) {
 }
 
 // LED Indication: Info
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-       void led_indicate_info(int cycles) {
-               bool forever = (cycles == 0) ? true : false;
-               cycles = forever ? 1 : cycles;
-               while(cycles > 0) {
-           led_rx_off();
-           delay(100);
-           led_rx_on();
-           delay(100);
-           if (!forever) cycles--;
-         }
-         led_rx_off();
-       }
-#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
+#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
        #if HAS_NP == true
                void led_indicate_info(int cycles) {
                        bool forever = (cycles == 0) ? true : false;
@@ -403,12 +363,7 @@ void led_indicate_warning(int cycles) {
 
 
 unsigned long led_standby_ticks = 0;
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-       uint8_t led_standby_min = 1;
-       uint8_t led_standby_max = 40;
-       unsigned long led_standby_wait = 11000;
-
-#elif MCU_VARIANT == MCU_ESP32
+#if MCU_VARIANT == MCU_ESP32
 
        #if HAS_NP == true
                int led_standby_lng = 100;
@@ -451,23 +406,7 @@ unsigned long led_standby_ticks = 0;
 unsigned long led_standby_value = led_standby_min;
 int8_t  led_standby_direction = 0;
 
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-       void led_indicate_standby() {
-               led_standby_ticks++;
-               if (led_standby_ticks > led_standby_wait) {
-                       led_standby_ticks = 0;
-                       if (led_standby_value <= led_standby_min) {
-                               led_standby_direction = 1;
-                       } else if (led_standby_value >= led_standby_max) {
-                               led_standby_direction = -1;
-                       }
-                       led_standby_value += led_standby_direction;
-                       analogWrite(pin_led_rx, led_standby_value);
-                       led_tx_off();
-               }
-       }
-
-#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
+#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
        #if HAS_NP == true
                void led_indicate_standby() {
                        led_standby_ticks++;
@@ -560,22 +499,7 @@ int8_t  led_standby_direction = 0;
   #endif
 #endif
 
-#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-       void led_indicate_not_ready() {
-               led_standby_ticks++;
-               if (led_standby_ticks > led_standby_wait) {
-                       led_standby_ticks = 0;
-                       if (led_standby_value <= led_standby_min) {
-                               led_standby_direction = 1;
-                       } else if (led_standby_value >= led_standby_max) {
-                               led_standby_direction = -1;
-                       }
-                       led_standby_value += led_standby_direction;
-                       analogWrite(pin_led_tx, led_standby_value);
-                       led_rx_off();
-               }
-       }
-#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
+#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
        #if HAS_NP == true
     void led_indicate_not_ready() {
        led_standby_ticks++;
@@ -636,6 +560,18 @@ int8_t  led_standby_direction = 0;
        #endif
 #endif
 
+
+bool interface_bitrate_cmp(RadioInterface* p, RadioInterface* q) {
+    long p_bitrate = p->getBitrate();
+    long q_bitrate = q->getBitrate();
+    return p_bitrate > q_bitrate;
+}
+
+// Sort interfaces in descending order according to bitrate.
+void sort_interfaces() {
+    std::sort(std::begin(interface_obj_sorted), std::end(interface_obj_sorted), interface_bitrate_cmp);
+}
+
 void serial_write(uint8_t byte) {
        #if HAS_BLUETOOTH || HAS_BLE == true
                if (bt_state != BT_STATE_CONNECTED) {
@@ -668,31 +604,33 @@ void kiss_indicate_error(uint8_t error_code) {
        serial_write(FEND);
 }
 
-void kiss_indicate_radiostate() {
+void kiss_indicate_radiostate(RadioInterface* radio) {
        serial_write(FEND);
        serial_write(CMD_RADIO_STATE);
-       serial_write(radio_online);
+       serial_write(radio->getRadioOnline());
        serial_write(FEND);
 }
 
 void kiss_indicate_stat_rx() {
-       serial_write(FEND);
-       serial_write(CMD_STAT_RX);
-       escaped_serial_write(stat_rx>>24);
-       escaped_serial_write(stat_rx>>16);
-       escaped_serial_write(stat_rx>>8);
-       escaped_serial_write(stat_rx);
-       serial_write(FEND);
+    // todo, implement
+       //serial_write(FEND);
+       //serial_write(CMD_STAT_RX);
+       //escaped_serial_write(stat_rx>>24);
+       //escaped_serial_write(stat_rx>>16);
+       //escaped_serial_write(stat_rx>>8);
+       //escaped_serial_write(stat_rx);
+       //serial_write(FEND);
 }
 
 void kiss_indicate_stat_tx() {
-       serial_write(FEND);
-       serial_write(CMD_STAT_TX);
-       escaped_serial_write(stat_tx>>24);
-       escaped_serial_write(stat_tx>>16);
-       escaped_serial_write(stat_tx>>8);
-       escaped_serial_write(stat_tx);
-       serial_write(FEND);
+    // todo, implement
+       //serial_write(FEND);
+       //serial_write(CMD_STAT_TX);
+       //escaped_serial_write(stat_tx>>24);
+       //escaped_serial_write(stat_tx>>16);
+       //escaped_serial_write(stat_tx>>8);
+       //escaped_serial_write(stat_tx);
+       //serial_write(FEND);
 }
 
 void kiss_indicate_stat_rssi() {
@@ -710,24 +648,24 @@ void kiss_indicate_stat_snr() {
        serial_write(FEND);
 }
 
-void kiss_indicate_radio_lock() {
+void kiss_indicate_radio_lock(RadioInterface* radio) {
        serial_write(FEND);
        serial_write(CMD_RADIO_LOCK);
-       serial_write(radio_locked);
+       serial_write(radio->getRadioLock());
        serial_write(FEND);
 }
 
-void kiss_indicate_spreadingfactor() {
+void kiss_indicate_spreadingfactor(RadioInterface* radio) {
        serial_write(FEND);
        serial_write(CMD_SF);
-       serial_write((uint8_t)lora_sf);
+       serial_write(radio->getSpreadingFactor());
        serial_write(FEND);
 }
 
-void kiss_indicate_codingrate() {
+void kiss_indicate_codingrate(RadioInterface* radio) {
        serial_write(FEND);
        serial_write(CMD_CR);
-       serial_write((uint8_t)lora_cr);
+       serial_write(radio->getCodingRate4());
        serial_write(FEND);
 }
 
@@ -738,35 +676,47 @@ void kiss_indicate_implicit_length() {
        serial_write(FEND);
 }
 
-void kiss_indicate_txpower() {
+void kiss_indicate_txpower(RadioInterface* radio) {
+    uint8_t txp = radio->getTxPower();
        serial_write(FEND);
        serial_write(CMD_TXPOWER);
-       serial_write((uint8_t)lora_txp);
+       serial_write(txp);
        serial_write(FEND);
 }
 
-void kiss_indicate_bandwidth() {
+void kiss_indicate_bandwidth(RadioInterface* radio) {
+    uint32_t bw = radio->getSignalBandwidth();
        serial_write(FEND);
        serial_write(CMD_BANDWIDTH);
-       escaped_serial_write(lora_bw>>24);
-       escaped_serial_write(lora_bw>>16);
-       escaped_serial_write(lora_bw>>8);
-       escaped_serial_write(lora_bw);
+       escaped_serial_write(bw>>24);
+       escaped_serial_write(bw>>16);
+       escaped_serial_write(bw>>8);
+       escaped_serial_write(bw);
        serial_write(FEND);
 }
 
-void kiss_indicate_frequency() {
+void kiss_indicate_frequency(RadioInterface* radio) {
+    uint32_t freq = radio->getFrequency();
        serial_write(FEND);
        serial_write(CMD_FREQUENCY);
-       escaped_serial_write(lora_freq>>24);
-       escaped_serial_write(lora_freq>>16);
-       escaped_serial_write(lora_freq>>8);
-       escaped_serial_write(lora_freq);
+       escaped_serial_write(freq>>24);
+       escaped_serial_write(freq>>16);
+       escaped_serial_write(freq>>8);
+       escaped_serial_write(freq);
        serial_write(FEND);
 }
 
-void kiss_indicate_st_alock() {
-       uint16_t at = (uint16_t)(st_airtime_limit*100*100);
+void kiss_indicate_interface(int index) {
+    serial_write(FEND);
+    serial_write(CMD_INTERFACES);
+    // print the index to the interface and the interface type
+    serial_write(index);
+    serial_write(interfaces[index]);
+       serial_write(FEND);
+}
+
+void kiss_indicate_st_alock(RadioInterface* radio) {
+       uint16_t at = (uint16_t)(radio->getSTALock()*100*100);
        serial_write(FEND);
        serial_write(CMD_ST_ALOCK);
        escaped_serial_write(at>>8);
@@ -774,8 +724,8 @@ void kiss_indicate_st_alock() {
        serial_write(FEND);
 }
 
-void kiss_indicate_lt_alock() {
-       uint16_t at = (uint16_t)(lt_airtime_limit*100*100);
+void kiss_indicate_lt_alock(RadioInterface* radio) {
+       uint16_t at = (uint16_t)(radio->getLTALock()*100*100);
        serial_write(FEND);
        serial_write(CMD_LT_ALOCK);
        escaped_serial_write(at>>8);
@@ -783,47 +733,43 @@ void kiss_indicate_lt_alock() {
        serial_write(FEND);
 }
 
-void kiss_indicate_channel_stats() {
-       #if MCU_VARIANT == MCU_ESP32
-               uint16_t ats = (uint16_t)(airtime*100*100);
-               uint16_t atl = (uint16_t)(longterm_airtime*100*100);
-               uint16_t cls = (uint16_t)(total_channel_util*100*100);
-               uint16_t cll = (uint16_t)(longterm_channel_util*100*100);
-               serial_write(FEND);
-               serial_write(CMD_STAT_CHTM);
-               escaped_serial_write(ats>>8);
-               escaped_serial_write(ats);
-               escaped_serial_write(atl>>8);
-               escaped_serial_write(atl);
-               escaped_serial_write(cls>>8);
-               escaped_serial_write(cls);
-               escaped_serial_write(cll>>8);
-               escaped_serial_write(cll);
-               serial_write(FEND);
-       #endif
-}
-
-void kiss_indicate_phy_stats() {
-       #if MCU_VARIANT == MCU_ESP32
-               uint16_t lst = (uint16_t)(lora_symbol_time_ms*1000);
-               uint16_t lsr = (uint16_t)(lora_symbol_rate);
-               uint16_t prs = (uint16_t)(lora_preamble_symbols+4);
-               uint16_t prt = (uint16_t)((lora_preamble_symbols+4)*lora_symbol_time_ms);
-               uint16_t cst = (uint16_t)(csma_slot_ms);
-               serial_write(FEND);
-               serial_write(CMD_STAT_PHYPRM);
-               escaped_serial_write(lst>>8);
-               escaped_serial_write(lst);
-               escaped_serial_write(lsr>>8);
-               escaped_serial_write(lsr);
-               escaped_serial_write(prs>>8);
-               escaped_serial_write(prs);
-               escaped_serial_write(prt>>8);
-               escaped_serial_write(prt);
-               escaped_serial_write(cst>>8);
-               escaped_serial_write(cst);
-               serial_write(FEND);
-       #endif
+void kiss_indicate_channel_stats(RadioInterface* radio) {
+    uint16_t ats = (uint16_t)(radio->getAirtime()*100*100);
+    uint16_t atl = (uint16_t)(radio->getLongtermAirtime()*100*100);
+    uint16_t cls = (uint16_t)(radio->getTotalChannelUtil()*100*100);
+    uint16_t cll = (uint16_t)(radio->getLongtermChannelUtil()*100*100);
+    serial_write(FEND);
+    serial_write(CMD_STAT_CHTM);
+    escaped_serial_write(ats>>8);
+    escaped_serial_write(ats);
+    escaped_serial_write(atl>>8);
+    escaped_serial_write(atl);
+    escaped_serial_write(cls>>8);
+    escaped_serial_write(cls);
+    escaped_serial_write(cll>>8);
+    escaped_serial_write(cll);
+    serial_write(FEND);
+}
+
+void kiss_indicate_phy_stats(RadioInterface* radio) {
+    uint16_t lst = (uint16_t)(radio->getSymbolTime()*1000);
+    uint16_t lsr = (uint16_t)(radio->getSymbolRate());
+    uint16_t prs = (uint16_t)(radio->getPreambleLength()+4);
+    uint16_t prt = (uint16_t)((radio->getPreambleLength()+4)*radio->getSymbolTime());
+    uint16_t cst = (uint16_t)(radio->getCSMASlotMS());
+    serial_write(FEND);
+    serial_write(CMD_STAT_PHYPRM);
+    escaped_serial_write(lst>>8);
+    escaped_serial_write(lst);
+    escaped_serial_write(lsr>>8);
+    escaped_serial_write(lsr);
+    escaped_serial_write(prs>>8);
+    escaped_serial_write(prs);
+    escaped_serial_write(prt>>8);
+    escaped_serial_write(prt);
+    escaped_serial_write(cst>>8);
+    escaped_serial_write(cst);
+    serial_write(FEND);
 }
 
 void kiss_indicate_battery() {
@@ -1009,43 +955,6 @@ inline uint8_t packetSequence(uint8_t header) {
        return header >> 4;
 }
 
-void setPreamble() {
-       if (radio_online) LoRa->setPreambleLength(lora_preamble_symbols);
-       kiss_indicate_phy_stats();
-}
-
-void updateBitrate() {
-       #if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
-               if (radio_online) {
-                       lora_symbol_rate = (float)lora_bw/(float)(pow(2, lora_sf));
-                       lora_symbol_time_ms = (1.0/lora_symbol_rate)*1000.0;
-                       lora_bitrate = (uint32_t)(lora_sf * ( (4.0/(float)lora_cr) / ((float)(pow(2, lora_sf))/((float)lora_bw/1000.0)) ) * 1000.0);
-                       lora_us_per_byte = 1000000.0/((float)lora_bitrate/8.0);
-                       // csma_slot_ms = lora_symbol_time_ms*10;
-                       float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/lora_symbol_time_ms)-LORA_PREAMBLE_SYMBOLS_HW;
-                       if (target_preamble_symbols < LORA_PREAMBLE_SYMBOLS_MIN) {
-                               target_preamble_symbols = LORA_PREAMBLE_SYMBOLS_MIN;
-                       } else {
-                               target_preamble_symbols = ceil(target_preamble_symbols);
-                       }
-                       lora_preamble_symbols = (long)target_preamble_symbols;
-                       setPreamble();
-               } else {
-                       lora_bitrate = 0;
-               }
-       #endif
-}
-
-void setSpreadingFactor() {
-       if (radio_online) LoRa->setSpreadingFactor(lora_sf);
-       updateBitrate();
-}
-
-void setCodingRate() {
-       if (radio_online) LoRa->setCodingRate4(lora_cr);
-       updateBitrate();
-}
-
 void set_implicit_length(uint8_t len) {
        implicit_l = len;
        if (implicit_l != 0) {
@@ -1055,74 +964,116 @@ void set_implicit_length(uint8_t len) {
        }
 }
 
-int getTxPower() {
-       uint8_t txp = LoRa->getTxPower();
-       return (int)txp;
-}
+void setTXPower(RadioInterface* radio, int txp) {
+    if (model == MODEL_11) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+    if (model == MODEL_12) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
 
-void setTXPower() {
-       if (radio_online) {
-               if (model == MODEL_A1) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_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_A6) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_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_A1) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_A2) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_A3) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+    if (model == MODEL_A4) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
+    if (model == MODEL_A6) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_A7) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_A8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_A9) radio->setTxPower(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_B3) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_B4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_B8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_B9) radio->setTxPower(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_C4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_C9) radio->setTxPower(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_E3) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-               if (model == MODEL_E8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_E4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_E9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_E3) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_E8) radio->setTxPower(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_FE) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+    if (model == MODEL_FF) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
 }
 
-
-void getBandwidth() {
-       if (radio_online) {
-                       lora_bw = LoRa->getSignalBandwidth();
-       }
-       updateBitrate();
-}
-
-void setBandwidth() {
-       if (radio_online) {
-               LoRa->setSignalBandwidth(lora_bw);
-               getBandwidth();
-       }
-}
-
-void getFrequency() {
-       if (radio_online) {
-               lora_freq = LoRa->getFrequency();
+uint8_t getRandom(RadioInterface* radio) {
+       if (radio->getRadioOnline()) {
+               return radio->random();
+       } else {
+               return 0x00;
        }
 }
 
-void setFrequency() {
-       if (radio_online) {
-               LoRa->setFrequency(lora_freq);
-               getFrequency();
-       }
+uint8_t getInterfaceIndex(uint8_t byte) {
+    switch (byte) {
+        case CMD_INT0_DATA:
+        case CMD_SEL_INT0:
+            return 0;
+        case CMD_INT1_DATA:
+        case CMD_SEL_INT1:
+            return 1;
+        case CMD_INT2_DATA:
+        case CMD_SEL_INT2:
+            return 2;
+        case CMD_INT3_DATA:
+        case CMD_SEL_INT3:
+            return 3;
+        case CMD_INT4_DATA:
+        case CMD_SEL_INT4:
+            return 4;
+        case CMD_INT5_DATA:
+        case CMD_SEL_INT5:
+            return 5;
+        case CMD_INT6_DATA:
+        case CMD_SEL_INT6:
+            return 6;
+        case CMD_INT7_DATA:
+        case CMD_SEL_INT7:
+            return 7;
+        case CMD_INT8_DATA:
+        case CMD_SEL_INT8:
+            return 8;
+        case CMD_INT9_DATA:
+        case CMD_SEL_INT9:
+            return 9;
+        case CMD_INT10_DATA:
+        case CMD_SEL_INT10:
+            return 10;
+        case CMD_INT11_DATA:
+        case CMD_SEL_INT11:
+            return 11;
+        default:
+            return 0;
+    }
 }
 
-uint8_t getRandom() {
-       if (radio_online) {
-               return LoRa->random();
-       } else {
-               return 0x00;
-       }
+uint8_t getInterfaceCommandByte(uint8_t index) {
+    switch (index) {
+        case 0:
+            return CMD_INT0_DATA;
+        case 1:
+            return CMD_INT1_DATA;
+        case 2:
+            return CMD_INT2_DATA;
+        case 3:
+            return CMD_INT3_DATA;
+        case 4:
+            return CMD_INT4_DATA;
+        case 5:
+            return CMD_INT5_DATA;
+        case 6:
+            return CMD_INT6_DATA;
+        case 7:
+            return CMD_INT7_DATA;
+        case 8:
+            return CMD_INT8_DATA;
+        case 9:
+            return CMD_INT9_DATA;
+        case 10:
+            return CMD_INT10_DATA;
+        case 11:
+            return CMD_INT11_DATA;
+        default:
+            return 0;
+    }
 }
 
 void promisc_enable() {
@@ -1228,9 +1179,7 @@ void eeprom_flush() {
 #endif
 
 void eeprom_update(int mapped_addr, uint8_t byte) {
-       #if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
-               EEPROM.update(mapped_addr, byte);
-       #elif MCU_VARIANT == MCU_ESP32
+       #if MCU_VARIANT == MCU_ESP32
                if (EEPROM.read(mapped_addr) != byte) {
                        EEPROM.write(mapped_addr, byte);
                        EEPROM.commit();
@@ -1297,9 +1246,7 @@ bool eeprom_product_valid() {
            uint8_t rval = eeprom_read(eeprom_addr(ADDR_PRODUCT));
     #endif
 
-       #if PLATFORM == PLATFORM_AVR
-       if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW) {
-       #elif PLATFORM == PLATFORM_ESP32
+       #if PLATFORM == PLATFORM_ESP32
        if (rval == PRODUCT_RNODE || rval == BOARD_RNODE_NG_20 || rval == BOARD_RNODE_NG_21 || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_10 || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21 || rval == PRODUCT_H32_V2 || rval == PRODUCT_H32_V3) {
        #elif PLATFORM == PLATFORM_NRF52
        if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HMBRW) {
@@ -1434,39 +1381,51 @@ bool eeprom_have_conf() {
        }
 }
 
-void eeprom_conf_load() {
+void eeprom_conf_load(RadioInterface* radio) {
        if (eeprom_have_conf()) {
+        if (!(radio->getRadioOnline())) {
         #if HAS_EEPROM
-            lora_sf = EEPROM.read(eeprom_addr(ADDR_CONF_SF));
-            lora_cr = EEPROM.read(eeprom_addr(ADDR_CONF_CR));
-            lora_txp = EEPROM.read(eeprom_addr(ADDR_CONF_TXP));
-            lora_freq = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
-            lora_bw = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x03);
+            uint8_t sf = EEPROM.read(eeprom_addr(ADDR_CONF_SF));
+            uint8_t cr = EEPROM.read(eeprom_addr(ADDR_CONF_CR));
+            uint8_t txp = EEPROM.read(eeprom_addr(ADDR_CONF_TXP));
+            uint32_t freq = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
+            uint32_t bw = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x03);
         #elif MCU_VARIANT == MCU_NRF52
-            lora_sf = eeprom_read(eeprom_addr(ADDR_CONF_SF));
-            lora_cr = eeprom_read(eeprom_addr(ADDR_CONF_CR));
-            lora_txp = eeprom_read(eeprom_addr(ADDR_CONF_TXP));
-            lora_freq = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
-            lora_bw = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x03);
+            uint8_t sf = eeprom_read(eeprom_addr(ADDR_CONF_SF));
+            uint8_t cr = eeprom_read(eeprom_addr(ADDR_CONF_CR));
+            uint8_t txp = eeprom_read(eeprom_addr(ADDR_CONF_TXP));
+            uint32_t freq = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
+            uint32_t bw = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x03);
         #endif
+            radio->setSpreadingFactor(sf);
+            radio->setCodingRate4(cr);
+            setTXPower(radio, txp);
+            radio->setFrequency(freq);
+            radio->setSignalBandwidth(bw);
+            radio->updateBitrate();
+        }
        }
 }
 
-void eeprom_conf_save() {
-       if (hw_ready && radio_online) {
-               eeprom_update(eeprom_addr(ADDR_CONF_SF), lora_sf);
-               eeprom_update(eeprom_addr(ADDR_CONF_CR), lora_cr);
-               eeprom_update(eeprom_addr(ADDR_CONF_TXP), lora_txp);
+void eeprom_conf_save(RadioInterface* radio) {
+       if (hw_ready && radio->getRadioOnline()) {
+               eeprom_update(eeprom_addr(ADDR_CONF_SF), radio->getSpreadingFactor());
+               eeprom_update(eeprom_addr(ADDR_CONF_CR), radio->getCodingRate4());
+               eeprom_update(eeprom_addr(ADDR_CONF_TXP), radio->getTxPower());
+
+        uint32_t bw = radio->getSignalBandwidth();
 
-               eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x00, lora_bw>>24);
-               eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x01, lora_bw>>16);
-               eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x02, lora_bw>>8);
-               eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x03, lora_bw);
+               eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x00, bw>>24);
+               eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x01, bw>>16);
+               eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x02, bw>>8);
+               eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x03, bw);
 
-               eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x00, lora_freq>>24);
-               eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x01, lora_freq>>16);
-               eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x02, lora_freq>>8);
-               eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x03, lora_freq);
+        uint32_t freq = radio->getFrequency();
+
+               eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x00, freq>>24);
+               eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x01, freq>>16);
+               eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x02, freq>>8);
+               eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x03, freq);
 
                eeprom_update(eeprom_addr(ADDR_CONF_OK), CONF_OK_BYTE);
                led_indicate_info(10);
@@ -1484,18 +1443,6 @@ void unlock_rom() {
        eeprom_erase();
 }
 
-void init_channel_stats() {
-       #if MCU_VARIANT == MCU_ESP32
-               for (uint16_t ai = 0; ai < DCD_SAMPLES; ai++) { util_samples[ai] = false; }
-               for (uint16_t ai = 0; ai < AIRTIME_BINS; ai++) { airtime_bins[ai] = 0; }
-               for (uint16_t ai = 0; ai < AIRTIME_BINS; ai++) { longterm_bins[ai] = 0.0; }
-               local_channel_util = 0.0;
-               total_channel_util = 0.0;
-               airtime = 0.0;
-               longterm_airtime = 0.0;
-       #endif
-}
-
 typedef struct FIFOBuffer
 {
   unsigned char *begin;
diff --git a/sx126x.cpp b/sx126x.cpp
deleted file mode 100644 (file)
index 9fd613e..0000000
+++ /dev/null
@@ -1,985 +0,0 @@
-// Copyright (c) Sandeep Mistry. All rights reserved.
-// Licensed under the MIT license.
-
-// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
-// Obviously still under the MIT license.
-
-#include "Boards.h"
-
-#if MODEM == SX1262
-#include "sx126x.h"
-
-#if MCU_VARIANT == MCU_ESP32
-  #if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
-    #include "soc/rtc_wdt.h"
-  #endif
-  #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_CAD_PARAMS               0x88
-#define OP_CALIBRATE_6X             0x89
-#define OP_RX_TX_FALLBACK_MODE_6X   0x93
-#define OP_REGULATOR_MODE_6X        0x96
-#define OP_CALIBRATE_IMAGE_6X       0x98
-
-#define MASK_CALIBRATE_ALL          0x7f
-
-#define IRQ_TX_DONE_MASK_6X         0x01
-#define IRQ_RX_DONE_MASK_6X         0x02
-#define IRQ_HEADER_DET_MASK_6X      0x10
-#define IRQ_PREAMBLE_DET_MASK_6X    0x04
-#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40
-#define IRQ_ALL_MASK_6X             0b0100001111111111
-
-#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 MODE_TCXO_3_0V_6X           0x06
-#define MODE_TCXO_2_7V_6X           0x06
-#define MODE_TCXO_2_4V_6X           0x06
-#define MODE_TCXO_2_2V_6X           0x03
-#define MODE_TCXO_1_8V_6X           0x02
-#define MODE_TCXO_1_7V_6X           0x01
-#define MODE_TCXO_1_6V_6X           0x00
-
-#define MODE_STDBY_RC_6X            0x00
-#define MODE_STDBY_XOSC_6X          0x01
-#define MODE_FALLBACK_STDBY_RC_6X   0x20
-#define MODE_IMPLICIT_HEADER        0x01
-#define MODE_EXPLICIT_HEADER        0x00
-
-#define SYNC_WORD_6X              0x1424
-
-#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)
-
-#if defined(NRF52840_XXAA)
-  extern SPIClass spiModem;
-  #define SPI spiModem
-#endif
-
-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() {
-  pinMode(_ss, OUTPUT);
-  digitalWrite(_ss, HIGH);
-  
-  #if BOARD_MODEL == BOARD_RNODE_NG_22 || BOARD_MODEL == BOARD_HELTEC32_V3
-    SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs);
-  #else
-    SPI.begin();
-  #endif
-
-  // check version (retry for up to 2 seconds)
-  // TODO: Actually read version registers, not syncwords
-  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()
-{
-  if (_rxen != -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() {
-    unsigned long time = millis();
-    while (digitalRead(_busy) == HIGH)
-    {
-        if (millis() >= (time + 100)) {
-            break;
-        }
-        // 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);
-}
-
-void sx126x::reset(void) {
-  if (_reset != -1) {
-    pinMode(_reset, OUTPUT);
-
-    // perform reset
-    digitalWrite(_reset, LOW);
-    delay(10);
-    digitalWrite(_reset, HIGH);
-    delay(10);
-  }
-}
-
-void sx126x::calibrate(void) {
-  // Put in STDBY_RC mode before calibration
-  uint8_t mode_byte = MODE_STDBY_RC_6X;
-  executeOpcode(OP_STANDBY_6X, &mode_byte, 1);
-
-  // calibrate RC64k, RC13M, PLL, ADC and image
-  uint8_t calibrate = MASK_CALIBRATE_ALL;
-  executeOpcode(OP_CALIBRATE_6X, &calibrate, 1);
-
-  delay(5);
-  waitOnBusy();
-}
-
-void sx126x::calibrate_image(long frequency) {
-  uint8_t image_freq[2] = {0};
-
-  if (frequency >= 430E6 && frequency <= 440E6) {
-    image_freq[0] = 0x6B;
-    image_freq[1] = 0x6F;
-  }
-  else if (frequency >= 470E6 && frequency <= 510E6) {
-    image_freq[0] = 0x75;
-    image_freq[1] = 0x81;
-  }
-  else if (frequency >= 779E6 && frequency <= 787E6) {
-    image_freq[0] = 0xC1;
-    image_freq[1] = 0xC5;
-  }
-  else if (frequency >= 863E6 && frequency <= 870E6) {
-    image_freq[0] = 0xD7;
-    image_freq[1] = 0xDB;
-  }
-  else if (frequency >= 902E6 && frequency <= 928E6) {
-    image_freq[0] = 0xE1;
-    image_freq[1] = 0xE9;
-  }
-
-  executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2);
-  waitOnBusy();
-}
-
-int sx126x::begin(long frequency)
-{
-  reset();
-
-  if (_busy != -1) {
-      pinMode(_busy, INPUT);
-  }
-
-  if (!_preinit_done) {
-    if (!preInit()) {
-      return false;
-    }
-  }
-
-  if (_rxen != -1) {
-      pinMode(_rxen, OUTPUT);
-  }
-
-  calibrate();
-  calibrate_image(frequency);
-
-  enableTCXO();
-
-  loraMode();
-  standby();
-
-  // Set sync word
-  setSyncWord(SYNC_WORD_6X);
-
-  #if DIO2_AS_RF_SWITCH
-    // enable dio2 rf switch
-    uint8_t byte = 0x01;
-    executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1);
-  #endif
-
-  rxAntEnable();
-
-  setFrequency(frequency);
-
-  // set output power to 2 dBm
-  setTxPower(2);
-  enableCrc();
-
-  // 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)
-{
-  standby();
-
-  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;
-    }
-
-    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;
-}
-
-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;
-}
-
-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::standby()
-{
-  // STDBY_XOSC
-  uint8_t byte = MODE_STDBY_XOSC_6X;
-  // STDBY_RC
-  // uint8_t byte = MODE_STDBY_RC_6X;
-  executeOpcode(OP_STANDBY_6X, &byte, 1); 
-}
-
-void sx126x::sleep()
-{
-    uint8_t byte = 0x00;
-    executeOpcode(OP_SLEEP_6X, &byte, 1);
-}
-
-void sx126x::enableTCXO() {
-  #if HAS_TCXO
-    #if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3
-      uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF};
-    #elif BOARD_MODEL == BOARD_TBEAM
-      uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
-    #elif BOARD_MODEL == BOARD_RNODE_NG_22
-      uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
-    #endif
-    executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4);
-  #endif
-}
-
-// TODO: Once enabled, SX1262 needs a complete reset to disable TCXO
-void sx126x::disableTCXO() { }
-
-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; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs
-    pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262
-    pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261)
-    pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet)
-
-    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; // PA 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;
-
-  handleLowDataRate();
-  setModulationParams(sf, _bw, _cr, _ldro);
-}
-
-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(){
-  if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16) {
-    _ldro = 0x01;
-  } else {
-    _ldro = 0x00;
-  }
-}
-
-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;
-  }
-
-  handleLowDataRate();
-  setModulationParams(_sf, _bw, _cr, _ldro);
-
-  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)
-{
-  _preambleLength = length;
-  setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
-}
-
-void sx126x::setSyncWord(uint16_t sw)
-{
-  // TODO: Fix
-    // writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8);
-    // writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF);
-    writeRegister(REG_SYNC_WORD_MSB_6X, 0x14);
-    writeRegister(REG_SYNC_WORD_LSB_6X, 0x24);
-}
-
-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);
-        }
-    }
-    // else {
-    //   Serial.println("CRCE");
-    //   Serial.println(buf[0]);
-    //   Serial.println(buf[1]);
-    // }
-}
-
-void ISR_VECT sx126x::onDio0Rise()
-{
-    sx126x_modem.handleDio0Rise();
-}
-
-sx126x sx126x_modem;
-
-#endif
diff --git a/sx126x.h b/sx126x.h
deleted file mode 100644 (file)
index c3b0668..0000000
--- a/sx126x.h
+++ /dev/null
@@ -1,147 +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 SX126X_H
-#define SX126X_H
-
-#include <Arduino.h>
-#include <SPI.h>
-#include "Interfaces.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  11
-
-#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 standby();
-  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(uint16_t 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();
-
-  void reset(void);
-  void calibrate(void);
-  void calibrate_image(long frequency);
-
-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);
-  bool _tcxo;
-};
-
-extern sx126x sx126x_modem;
-
-#endif
diff --git a/sx127x.cpp b/sx127x.cpp
deleted file mode 100644 (file)
index c5f6d40..0000000
+++ /dev/null
@@ -1,498 +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"
-
-#if MODEM == SX1276
-#include "sx127x.h"
-
-#if MCU_VARIANT == MCU_ESP32
-  #if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
-    #include "soc/rtc_wdt.h"
-  #endif
-  #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_RX_DONE_MASK_7X           0x40
-#define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20
-
-#define SYNC_WORD_7X                  0x12
-#define MAX_PKT_LENGTH                255
-
-extern SPIClass SPI;
-
-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) { setTimeout(0); }
-
-void sx127x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
-void sx127x::setPins(int ss, int reset, int dio0, int busy) { _ss = ss; _reset = reset; _dio0 = dio0; _busy = busy; }
-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); }
-void sx127x::standby() { 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); }
-uint8_t sx127x::modemStatus() { return readRegister(REG_MODEM_STAT_7X); }
-void sx127x::setSyncWord(uint8_t 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); }
-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::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); }
-byte sx127x::random() { return readRegister(REG_RSSI_WIDEBAND_7X); }
-void sx127x::flush() { }
-
-bool sx127x::preInit() {
-  pinMode(_ss, OUTPUT);
-  digitalWrite(_ss, HIGH);
-  SPI.begin();
-
-  // Check modem version
-  uint8_t version;
-  long start = millis();
-  while (((millis() - start) < 500) && (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::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; }
-  }
-
-  sleep();
-  setFrequency(frequency);
-
-  // set base addresses
-  writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0);
-  writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0);
-
-  // set LNA boost and auto AGC
-  writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03);
-  writeRegister(REG_MODEM_CONFIG_3_7X, 0x04);
-
-  setSyncWord(SYNC_WORD_7X);
-  enableCrc();
-  setTxPower(2);
-
-  standby();
-
-  return 1;
-}
-
-void sx127x::end() {
-  sleep();
-  SPI.end();
-  _preinit_done = false;
-}
-
-int sx127x::beginPacket(int implicitHeader) {
-  standby();
-
-  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() {
-  // Enter TX mode
-  writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X);
-
-  // Wait for TX completion
-  while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) {
-    yield();
-  }
-
-  // Clear TX complete IRQ
-  writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X);
-  return 1;
-}
-
-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);
-
-  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);
-    if ((currentLength + size) > MAX_PKT_LENGTH) {
-        size = MAX_PKT_LENGTH - currentLength;
-    }
-
-    for (size_t i = 0; i < size; i++) {
-        writeRegister(REG_FIFO_7X, buffer[i]);
-    }
-
-    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; }
-
-  // Remember current FIFO address, read, and then reset address
-  int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X);
-  uint8_t b = readRegister(REG_FIFO_7X);
-  writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress);
-
-  return b;
-}
-
-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::setTxPower(int level, int outputPin) {
-  // Setup according to RFO or PA_BOOST output pin
-  if (PA_OUTPUT_RFO_PIN == outputPin) {
-    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 {
-    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::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::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 {
-    // Only 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 ISR_VECT sx127x::handleDio0Rise() {
-  int irqFlags = readRegister(REG_IRQ_FLAGS_7X);
-
-  // Clear IRQs
-  writeRegister(REG_IRQ_FLAGS_7X, irqFlags);
-  if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) {
-    _packetIndex = 0;
-    int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X);
-    writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X));
-    if (_onReceive) { _onReceive(packetLength); }
-    writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
-  }
-}
-
-void ISR_VECT sx127x::onDio0Rise() { sx127x_modem.handleDio0Rise(); }
-
-sx127x sx127x_modem;
-
-#endif
\ No newline at end of file
diff --git a/sx127x.h b/sx127x.h
deleted file mode 100644 (file)
index f154b44..0000000
--- a/sx127x.h
+++ /dev/null
@@ -1,111 +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 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 standby();
-  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(uint8_t sw);
-  uint8_t modemStatus();
-  void enableCrc();
-  void disableCrc();
-  void enableTCXO();
-  void disableTCXO();
-
-  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);
-
-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
deleted file mode 100644 (file)
index 9c4a249..0000000
+++ /dev/null
@@ -1,887 +0,0 @@
-// Copyright (c) Sandeep Mistry. All rights reserved.
-// Licensed under the MIT license.
-
-// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
-// Obviously still under the MIT license.
-
-#include "sx128x.h"
-#include "Boards.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 MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
-    #include "soc/rtc_wdt.h"
-  #endif
-  #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)
-
-#if defined(NRF52840_XXAA)
-  extern SPIClass spiModem;
-  #define SPI spiModem
-#endif
-
-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();
-    while (digitalRead(_busy) == HIGH)
-    {
-        if (millis() >= (time + 100)) {
-            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 (_rxen != -1) {
-      pinMode(_rxen, OUTPUT);
-  }
-
-  if (_txen != -1) {
-      pinMode(_txen, OUTPUT);
-  }
-
-  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};
-
-    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] = 0xFF;
-    }
-
-    if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) {
-        byte = byte | 0x02 | 0x04;
-        // clear register after reading
-        clearbuf[1] = 0xFF;
-    }
-
-    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
deleted file mode 100644 (file)
index ad76d51..0000000
--- a/sx128x.h
+++ /dev/null
@@ -1,144 +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 SX128X_H
-#define SX128X_H
-
-#include <Arduino.h>
-#include <SPI.h>
-#include "Interfaces.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  11
-
-#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