]> git.tnoah.ca Git - lora-radio.git/commitdiff
Fix SPI semaphore lockup issue on ESP32
authorjacob.eva <x00010@disroot.org>
Tue, 6 Aug 2024 16:37:47 +0000 (17:37 +0100)
committerjacob.eva <x00010@disroot.org>
Tue, 6 Aug 2024 16:37:47 +0000 (17:37 +0100)
Boards.h
RNode_Firmware_CE.ino
Radio.cpp
Radio.h [deleted file]
Radio.hpp [new file with mode: 0644]
Utilities.h

index c8c4d9478b11738d6c23789cf5534e905b118a12..75f528469b6f253b9aab4d01ef21e3ae5378179a 100644 (file)
--- a/Boards.h
+++ b/Boards.h
       };
       #endif
 
-        #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;
     #endif
 
   #endif
-  #ifndef INTERFACE_SPI
-    // INTERFACE_SPI is only required on NRF52 platforms, as the SPI pins are set in the class constructor and not by a setter method.
-    // 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
 #endif
index c91ac311e2922b61b528a3bbda4b69cc36ebead2..dabe7c90b10c94d06ffc05dba754a65487532c9a 100644 (file)
 #include <SPI.h>
 #include "Utilities.h"
 
-#if BOARD_MODEL == BOARD_HELTEC32_V3
-// Default stack size for loop function on Heltec32 V3 is not large enough,
-// must be increased to 11kb to prevent crashes.
-SET_LOOP_TASK_STACK_SIZE(11 * 1024);  // 11KB
+#if MCU_VARIANT == MCU_NRF52 
+        #define INTERFACE_SPI
+        // Required because on RAK4631, non-default SPI pins must be initialised when class is declared.
+      SPIClass interface_spi[1] = {
+            // SX1262
+            SPIClass(
+                NRF_SPIM2, 
+                interface_pins[0][3], 
+                interface_pins[0][1], 
+                interface_pins[0][2]
+               )
+      };
+#endif
+
+#ifndef INTERFACE_SPI
+// INTERFACE_SPI is only required on NRF52 platforms, as the SPI pins are set in the class constructor and not by a setter method.
+// Even if custom SPI interfaces are not needed, the array must exist to prevent compilation errors.
+#define INTERFACE_SPI
+SPIClass interface_spi[1];
 #endif
 
 FIFOBuffer serialFIFO;
@@ -134,13 +149,13 @@ void setup() {
               sx126x* obj;
               // if default spi enabled
               if (interface_cfg[i][0]) {
-                obj = new sx126x(i, SPI, interface_cfg[i][1],
+                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],
+            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]);
@@ -157,12 +172,12 @@ void setup() {
               sx127x* obj;
               // if default spi enabled
               if (interface_cfg[i][0]) {
-            obj = new sx127x(i, SPI, interface_pins[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],
+            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]);
               }
@@ -177,13 +192,13 @@ void setup() {
               sx128x* obj;
               // if default spi enabled
               if (interface_cfg[i][0]) {
-            obj = new sx128x(i, SPI, interface_cfg[i][1],
+            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],
+            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]);
index 585cceb38ea1d688b8e5ac2597047b926bbbf3df..cb949c299130f3aec9497a2463b56d24b9d4fb06 100644 (file)
--- a/Radio.cpp
+++ b/Radio.cpp
@@ -4,7 +4,7 @@
 // Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
 // Obviously still under the MIT license.
 
-#include "Radio.h"
+#include "Radio.hpp"
 
 #if PLATFORM == PLATFORM_ESP32 
   #if defined(ESP32) and !defined(CONFIG_IDF_TARGET_ESP32S3)
@@ -106,7 +106,7 @@ void ISR_VECT onDio0Rise() {
     }
 }
 
-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) :
+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),
@@ -130,12 +130,12 @@ bool sx126x::preInit() {
   // 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);
+    _spiModem->begin(_sclk, _miso, _mosi, _ss);
   } else {
-    _spiModem.begin();
+    _spiModem->begin();
   }
   #else
-    _spiModem.begin();
+    _spiModem->begin();
   #endif
 
   // check version (retry for up to 2 seconds)
@@ -177,15 +177,15 @@ uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(opcode);
-    _spiModem.transfer((address & 0xFF00) >> 8);
-    _spiModem.transfer(address & 0x00FF);
+    _spiModem->beginTransaction(_spiSettings);
+    _spiModem->transfer(opcode);
+    _spiModem->transfer((address & 0xFF00) >> 8);
+    _spiModem->transfer(address & 0x00FF);
     if (opcode == OP_READ_REGISTER_6X) {
-        _spiModem.transfer(0x00);
+        _spiModem->transfer(0x00);
     }
-    response = _spiModem.transfer(value);
-    _spiModem.endTransaction();
+    response = _spiModem->transfer(value);
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 
@@ -222,15 +222,15 @@ void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(opcode);
+    _spiModem->beginTransaction(_spiSettings);
+    _spiModem->transfer(opcode);
 
     for (int i = 0; i < size; i++)
     {
-        _spiModem.transfer(buffer[i]);
+        _spiModem->transfer(buffer[i]);
     }
 
-    _spiModem.endTransaction();
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 }
@@ -241,16 +241,16 @@ void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(opcode);
-    _spiModem.transfer(0x00);
+    _spiModem->beginTransaction(_spiSettings);
+    _spiModem->transfer(opcode);
+    _spiModem->transfer(0x00);
 
     for (int i = 0; i < size; i++)
     {
-        buffer[i] = _spiModem.transfer(0x00);
+        buffer[i] = _spiModem->transfer(0x00);
     }
 
-    _spiModem.endTransaction();
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 }
@@ -261,17 +261,17 @@ void sx126x::writeBuffer(const uint8_t* buffer, size_t size)
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(OP_FIFO_WRITE_6X);
-    _spiModem.transfer(_fifo_tx_addr_ptr);
+    _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]);
+        _spiModem->transfer(buffer[i]);
         _fifo_tx_addr_ptr++;
     }
 
-    _spiModem.endTransaction();
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 }
@@ -282,17 +282,17 @@ void sx126x::readBuffer(uint8_t* buffer, size_t size)
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(OP_FIFO_READ_6X);
-    _spiModem.transfer(_fifo_rx_addr_ptr);
-    _spiModem.transfer(0x00);
+    _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);
+        buffer[i] = _spiModem->transfer(0x00);
     }
 
-    _spiModem.endTransaction();
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 }
@@ -451,7 +451,7 @@ void sx126x::end()
   sleep();
 
   // stop SPI
-  _spiModem.end();
+  _spiModem->end();
 
   _bitrate = 0;
 
@@ -676,7 +676,7 @@ void sx126x::onReceive(void(*callback)(uint8_t, int))
 
     executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8);
 #ifdef SPI_HAS_NOTUSINGINTERRUPT
-    _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0));
+    _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
 #endif
     // make function available
     extern void onDio0Rise();
@@ -685,7 +685,7 @@ void sx126x::onReceive(void(*callback)(uint8_t, int))
   } else {
     detachInterrupt(digitalPinToInterrupt(_dio0));
 #ifdef SPI_HAS_NOTUSINGINTERRUPT
-    _spiModem.notUsingInterrupt(digitalPinToInterrupt(_dio0));
+    _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
 #endif
   }
 }
@@ -1081,7 +1081,7 @@ void sx126x::clearIRQStatus() {
 
 #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) :
+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),
@@ -1116,12 +1116,12 @@ bool sx127x::preInit() {
   // 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);
+    _spiModem->begin(_sclk, _miso, _mosi, _ss);
   } else {
-    _spiModem.begin();
+    _spiModem->begin();
   }
   #else
-    _spiModem.begin();
+    _spiModem->begin();
   #endif
 
   // Check modem version
@@ -1143,10 +1143,10 @@ 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();
+  _spiModem->beginTransaction(_spiSettings);
+  _spiModem->transfer(address);
+  response = _spiModem->transfer(value);
+  _spiModem->endTransaction();
   digitalWrite(_ss, HIGH);
 
   return response;
@@ -1193,7 +1193,7 @@ int sx127x::begin() {
 
 void sx127x::end() {
   sleep();
-  _spiModem.end();
+  _spiModem->end();
   _bitrate = 0;
   _radio_online = false;
   _preinit_done = false;
@@ -1331,7 +1331,7 @@ void sx127x::onReceive(void(*callback)(uint8_t, int)) {
     writeRegister(REG_DIO_MAPPING_1_7X, 0x00);
     
     #ifdef SPI_HAS_NOTUSINGINTERRUPT
-      _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0));
+      _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
     #endif
     
     // make function available
@@ -1343,7 +1343,7 @@ void sx127x::onReceive(void(*callback)(uint8_t, int)) {
     detachInterrupt(digitalPinToInterrupt(_dio0));
     
     #ifdef SPI_HAS_NOTUSINGINTERRUPT
-      _spiModem.notUsingInterrupt(digitalPinToInterrupt(_dio0));
+      _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
     #endif
   }
 }
@@ -1599,7 +1599,7 @@ void sx127x::clearIRQStatus() {
 #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) :
+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),
@@ -1626,12 +1626,12 @@ bool sx128x::preInit() {
   // 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);
+    _spiModem->begin(_sclk, _miso, _mosi, _ss);
   } else {
-    _spiModem.begin();
+    _spiModem->begin();
   }
   #else
-    _spiModem.begin();
+    _spiModem->begin();
   #endif
 
   // check version (retry for up to 2 seconds)
@@ -1676,15 +1676,15 @@ uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(opcode);
-    _spiModem.transfer((address & 0xFF00) >> 8);
-    _spiModem.transfer(address & 0x00FF);
+    _spiModem->beginTransaction(_spiSettings);
+    _spiModem->transfer(opcode);
+    _spiModem->transfer((address & 0xFF00) >> 8);
+    _spiModem->transfer(address & 0x00FF);
     if (opcode == OP_READ_REGISTER_8X) {
-        _spiModem.transfer(0x00);
+        _spiModem->transfer(0x00);
     }
-    response = _spiModem.transfer(value);
-    _spiModem.endTransaction();
+    response = _spiModem->transfer(value);
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 
@@ -1734,15 +1734,15 @@ void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(opcode);
+    _spiModem->beginTransaction(_spiSettings);
+    _spiModem->transfer(opcode);
 
     for (int i = 0; i < size; i++)
     {
-        _spiModem.transfer(buffer[i]);
+        _spiModem->transfer(buffer[i]);
     }
 
-    _spiModem.endTransaction();
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 }
@@ -1753,16 +1753,16 @@ void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(opcode);
-    _spiModem.transfer(0x00);
+    _spiModem->beginTransaction(_spiSettings);
+    _spiModem->transfer(opcode);
+    _spiModem->transfer(0x00);
 
     for (int i = 0; i < size; i++)
     {
-        buffer[i] = _spiModem.transfer(0x00);
+        buffer[i] = _spiModem->transfer(0x00);
     }
 
-    _spiModem.endTransaction();
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 }
@@ -1773,17 +1773,17 @@ void sx128x::writeBuffer(const uint8_t* buffer, size_t size)
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(OP_FIFO_WRITE_8X);
-    _spiModem.transfer(_fifo_tx_addr_ptr);
+    _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]);
+        _spiModem->transfer(buffer[i]);
         _fifo_tx_addr_ptr++;
     }
 
-    _spiModem.endTransaction();
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 }
@@ -1794,17 +1794,17 @@ void sx128x::readBuffer(uint8_t* buffer, size_t size)
 
     digitalWrite(_ss, LOW);
 
-    _spiModem.beginTransaction(_spiSettings);
-    _spiModem.transfer(OP_FIFO_READ_8X);
-    _spiModem.transfer(_fifo_rx_addr_ptr);
-    _spiModem.transfer(0x00);
+    _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);
+        buffer[i] = _spiModem->transfer(0x00);
     }
 
-    _spiModem.endTransaction();
+    _spiModem->endTransaction();
 
     digitalWrite(_ss, HIGH);
 }
@@ -1921,7 +1921,7 @@ void sx128x::end()
   sleep();
 
   // stop SPI
-  _spiModem.end();
+  _spiModem->end();
 
   _bitrate = 0;
 
@@ -2130,7 +2130,7 @@ void sx128x::onReceive(void(*callback)(uint8_t, int))
 
       executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8);
 //#ifdef SPI_HAS_NOTUSINGINTERRUPT
-//    _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0));
+//    _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0));
 //#endif
 
     // make function available
@@ -2140,7 +2140,7 @@ void sx128x::onReceive(void(*callback)(uint8_t, int))
   } else {
     detachInterrupt(digitalPinToInterrupt(_dio0));
 //#ifdef SPI_HAS_NOTUSINGINTERRUPT
-//    _spiModem.notUsingInterrupt(digitalPinToInterrupt(_dio0));
+//    _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0));
 //#endif
   }
 }
diff --git a/Radio.h b/Radio.h
deleted file mode 100644 (file)
index 13bb0da..0000000
--- a/Radio.h
+++ /dev/null
@@ -1,666 +0,0 @@
-// 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"
-
-#define MAX_PKT_LENGTH                255
-
-// 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 LORA_PREAMBLE_FAST_TARGET_MS 1
-#define LORA_FAST_BITRATE_THRESHOLD 40000
-
-#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),
-     _stat_signal_detected(false), _stat_signal_synced(false),_stat_rx_ongoing(false), _last_dcd(0), 
-     _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),
-     _packet{0}, _onReceive(NULL) {};
-    virtual int begin() = 0;
-    virtual void end() = 0;
-
-    virtual int beginPacket(int implicitHeader = false) = 0;
-    virtual int endPacket() = 0;
-
-    virtual int packetRssi() = 0;
-    virtual int currentRssi() = 0;
-    virtual uint8_t packetRssiRaw() = 0;
-    virtual uint8_t currentRssiRaw() = 0;
-    virtual uint8_t packetSnrRaw() = 0;
-    virtual float packetSnr() = 0;
-    virtual long packetFrequencyError() = 0;
-
-    // from Print
-    virtual size_t write(uint8_t byte) = 0;
-    virtual size_t write(const uint8_t *buffer, size_t size) = 0;
-
-    // from Stream
-    virtual int available() = 0;
-    virtual int read() = 0;
-    virtual int peek() = 0;
-    virtual void flush() = 0;
-
-    virtual void onReceive(void(*callback)(uint8_t, int)) = 0;
-
-    virtual void receive(int size = 0) = 0;
-    virtual void standby() = 0;
-    virtual void sleep() = 0;
-
-    virtual bool preInit() = 0;
-    virtual uint8_t getTxPower() = 0;
-    virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN) = 0;
-    virtual uint32_t getFrequency() = 0;
-    virtual void setFrequency(uint32_t frequency) = 0;
-    virtual void setSpreadingFactor(int sf) = 0;
-    virtual uint8_t getSpreadingFactor() = 0;
-    virtual uint32_t getSignalBandwidth() = 0;
-    virtual void setSignalBandwidth(uint32_t sbw) = 0;
-    virtual void setCodingRate4(int denominator) = 0;
-    virtual uint8_t getCodingRate4() = 0;
-    virtual void setPreambleLength(long length) = 0;
-    virtual uint8_t modemStatus() = 0;
-    virtual void enableCrc() = 0;
-    virtual void disableCrc() = 0;
-    virtual void enableTCXO() = 0;
-    virtual void disableTCXO() = 0;
-
-    virtual byte random() = 0;
-
-    virtual void setSPIFrequency(uint32_t frequency) = 0;
-
-    virtual void updateBitrate() = 0;
-    virtual void handleDio0Rise() = 0;
-    virtual void clearIRQStatus() = 0;
-    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() = 0;
-    virtual void implicitHeaderMode() = 0;
-
-    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;
-  uint8_t _packet[255];
-    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);
-  void clearIRQStatus();
-
-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;
-  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();
-  void clearIRQStatus();
-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();
-
-  void clearIRQStatus();
-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;
-  bool _preinit_done;
-  int _rxPacketLength;
-  uint8_t _index;
-  bool _tcxo;
-};
-#endif
diff --git a/Radio.hpp b/Radio.hpp
new file mode 100644 (file)
index 0000000..fa42abd
--- /dev/null
+++ b/Radio.hpp
@@ -0,0 +1,666 @@
+// 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"
+
+#define MAX_PKT_LENGTH                255
+
+// 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 LORA_PREAMBLE_FAST_TARGET_MS 1
+#define LORA_FAST_BITRATE_THRESHOLD 40000
+
+#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),
+     _stat_signal_detected(false), _stat_signal_synced(false),_stat_rx_ongoing(false), _last_dcd(0), 
+     _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),
+     _packet{0}, _onReceive(NULL) {};
+    virtual int begin() = 0;
+    virtual void end() = 0;
+
+    virtual int beginPacket(int implicitHeader = false) = 0;
+    virtual int endPacket() = 0;
+
+    virtual int packetRssi() = 0;
+    virtual int currentRssi() = 0;
+    virtual uint8_t packetRssiRaw() = 0;
+    virtual uint8_t currentRssiRaw() = 0;
+    virtual uint8_t packetSnrRaw() = 0;
+    virtual float packetSnr() = 0;
+    virtual long packetFrequencyError() = 0;
+
+    // from Print
+    virtual size_t write(uint8_t byte) = 0;
+    virtual size_t write(const uint8_t *buffer, size_t size) = 0;
+
+    // from Stream
+    virtual int available() = 0;
+    virtual int read() = 0;
+    virtual int peek() = 0;
+    virtual void flush() = 0;
+
+    virtual void onReceive(void(*callback)(uint8_t, int)) = 0;
+
+    virtual void receive(int size = 0) = 0;
+    virtual void standby() = 0;
+    virtual void sleep() = 0;
+
+    virtual bool preInit() = 0;
+    virtual uint8_t getTxPower() = 0;
+    virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN) = 0;
+    virtual uint32_t getFrequency() = 0;
+    virtual void setFrequency(uint32_t frequency) = 0;
+    virtual void setSpreadingFactor(int sf) = 0;
+    virtual uint8_t getSpreadingFactor() = 0;
+    virtual uint32_t getSignalBandwidth() = 0;
+    virtual void setSignalBandwidth(uint32_t sbw) = 0;
+    virtual void setCodingRate4(int denominator) = 0;
+    virtual uint8_t getCodingRate4() = 0;
+    virtual void setPreambleLength(long length) = 0;
+    virtual uint8_t modemStatus() = 0;
+    virtual void enableCrc() = 0;
+    virtual void disableCrc() = 0;
+    virtual void enableTCXO() = 0;
+    virtual void disableTCXO() = 0;
+
+    virtual byte random() = 0;
+
+    virtual void setSPIFrequency(uint32_t frequency) = 0;
+
+    virtual void updateBitrate() = 0;
+    virtual void handleDio0Rise() = 0;
+    virtual void clearIRQStatus() = 0;
+    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() = 0;
+    virtual void implicitHeaderMode() = 0;
+
+    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;
+  uint8_t _packet[255];
+    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);
+  void clearIRQStatus();
+
+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;
+  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();
+  void clearIRQStatus();
+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();
+
+  void clearIRQStatus();
+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;
+  bool _preinit_done;
+  int _rxPacketLength;
+  uint8_t _index;
+  bool _tcxo;
+};
+#endif
index 3f0a8b88677004d1fcb232bfa0ea89ee6945afdf..2d82b152b5a8e51bda09b36a5ea7a9dc00839478 100644 (file)
@@ -13,7 +13,7 @@
 // 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 "Radio.hpp"
 #include "Config.h"
 
 // Included for sorting