diff --git a/Boards.h b/Boards.h
index 3105439..7321269 100644
--- a/Boards.h
+++ b/Boards.h
@@ -13,17 +13,14 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-#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
@@ -47,13 +44,7 @@
#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)
@@ -64,16 +55,6 @@
#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
@@ -90,37 +71,7 @@
#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.
@@ -143,11 +94,33 @@
#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
@@ -161,32 +134,70 @@
#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
@@ -205,6 +216,33 @@
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
@@ -223,6 +261,33 @@
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
@@ -231,11 +296,30 @@
#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;
@@ -245,15 +329,43 @@
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;
@@ -262,6 +374,33 @@
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
@@ -273,6 +412,7 @@
#define HAS_SLEEP true
#define PIN_WAKEUP GPIO_NUM_0
#define WAKEUP_LEVEL 0
+ #define INTERFACE_COUNT 1
const int pin_btn_usr1 = 0;
@@ -284,20 +424,30 @@
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
@@ -320,6 +470,31 @@
#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
@@ -329,9 +504,6 @@
#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;
@@ -349,12 +521,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
@@ -373,16 +567,6 @@
// #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;
@@ -402,6 +586,31 @@
#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
@@ -410,17 +619,14 @@
#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
@@ -429,15 +635,45 @@
#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;
@@ -447,25 +683,15 @@
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
diff --git a/Config.h b/Config.h
index 047a3fb..f0bb09c 100644
--- 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
@@ -51,13 +51,10 @@
#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;
@@ -66,36 +63,15 @@
// 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;
@@ -106,7 +82,6 @@
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;
@@ -125,49 +100,6 @@
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
@@ -192,4 +124,10 @@
#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
diff --git a/Display.h b/Display.h
index d566a53..54f53b2 100644
--- 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)
diff --git a/Documentation/CONTRIBUTING.md b/Documentation/CONTRIBUTING.md
index 2882304..3a8d1ac 100644
--- a/Documentation/CONTRIBUTING.md
+++ b/Documentation/CONTRIBUTING.md
@@ -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
diff --git a/Framing.h b/Framing.h
index 0236aa9..0dfb1ce 100644
--- 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
@@ -75,6 +74,34 @@
#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
@@ -98,4 +125,4 @@
bool ESCAPE = false;
uint8_t command = CMD_UNKNOWN;
-#endif
\ No newline at end of file
+#endif
diff --git a/Graphics.h b/Graphics.h
index 7e92bd6..3b81690 100644
--- a/Graphics.h
+++ b/Graphics.h
@@ -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
index 0000000..1d8e6a4
--- /dev/null
+++ b/Interfaces.h
@@ -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
index 027e314..0000000
--- a/Modem.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#define SX1276 0x01
-#define SX1278 0x02
-#define SX1262 0x03
-#define SX1280 0x04
diff --git a/RNode_Firmware_CE.ino b/RNode_Firmware_CE.ino
index 1745a21..cc4630b 100644
--- a/RNode_Firmware_CE.ino
+++ b/RNode_Firmware_CE.ino
@@ -20,18 +20,22 @@
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_starts_buf, 0, sizeof(packet_starts_buf));
+ memset(packet_lengths_buf, 0, sizeof(packet_starts_buf));
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);
+ 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);
+ }
- // 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;
+ // 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;
+ radio->enableCrc();
- init_channel_stats();
+ radio->onReceive(receive_callback);
- setTXPower();
- setBandwidth();
- setSpreadingFactor();
- setCodingRate();
- getFrequency();
+ radio->updateBitrate();
+ sort_interfaces();
+ kiss_indicate_phy_stats(radio);
- LoRa->enableCrc();
-
- 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 (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[index]++;
+
+ fifo16_push(&packet_starts[index], s);
+ fifo16_push(&packet_lengths[index], l);
+ current_packet_start[index] = queue_cursor[index];
+ }
- if (s != e) {
- l = (s < e) ? e - s + 1 : CONFIG_QUEUE_SIZE - s + e + 1;
- } else {
- l = 1;
}
-
- if (l >= MIN_L) {
- queue_height++;
-
- fifo16_push(&packet_starts, s);
- fifo16_push(&packet_lengths, l);
-
- 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);
+ 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();
+ }
- updateModemStatus();
-
- if (!dcd) {
- dcd_waiting = false;
- flushQueue();
+ 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
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(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(readRegister(REG_FREQ_ERROR_MSB_7X) & B111);
+ freqError <<= 8L;
+ freqError += static_cast(readRegister(REG_FREQ_ERROR_MID_7X));
+ freqError <<= 8L;
+ freqError += static_cast(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(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f);
+
+ return static_cast(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< 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(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
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
+#include
+#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
diff --git a/Utilities.h b/Utilities.h
index 090e5d5..ef18e7f 100644
--- a/Utilities.h
+++ b/Utilities.h
@@ -13,8 +13,13 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
+#include "Radio.h"
#include "Config.h"
+// Included for sorting
+#include
+#include
+
#if HAS_EEPROM
#include
#elif PLATFORM == PLATFORM_NRF52
@@ -28,17 +33,6 @@
#endif
#include
-#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
- #include
-#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_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() {
- #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_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,76 +964,118 @@ 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);
+
+ 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) 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) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_C9) radio->setTxPower(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) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
+ if (model == MODEL_FF) 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_B3) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_B4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_B8) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_B9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
- if (model == MODEL_C4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_C9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
-
- if (model == MODEL_E4) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_E9) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_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_FE) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
- if (model == MODEL_FF) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
- }
-}
-
-
-void getBandwidth() {
- if (radio_online) {
- lora_bw = LoRa->getSignalBandwidth();
- }
- updateBitrate();
-}
-
-void setBandwidth() {
- if (radio_online) {
- LoRa->setSignalBandwidth(lora_bw);
- getBandwidth();
- }
-}
-
-void getFrequency() {
- if (radio_online) {
- lora_freq = LoRa->getFrequency();
- }
-}
-
-void setFrequency() {
- if (radio_online) {
- LoRa->setFrequency(lora_freq);
- getFrequency();
- }
-}
-
-uint8_t getRandom() {
- if (radio_online) {
- return LoRa->random();
+uint8_t getRandom(RadioInterface* radio) {
+ if (radio->getRadioOnline()) {
+ return radio->random();
} else {
return 0x00;
}
}
+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 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() {
promisc = true;
}
@@ -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());
- 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);
+ uint32_t bw = radio->getSignalBandwidth();
- 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);
+ 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);
+
+ 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
index 2ddd8ee..0000000
--- a/sx126x.cpp
+++ /dev/null
@@ -1,987 +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();
- if (_busy != -1) {
- 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(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
index 5e0ff4d..0000000
--- a/sx126x.h
+++ /dev/null
@@ -1,146 +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
-#include
-#include "Modem.h"
-
-#define LORA_DEFAULT_SS_PIN 10
-#define LORA_DEFAULT_RESET_PIN 9
-#define LORA_DEFAULT_DIO0_PIN 2
-#define LORA_DEFAULT_RXEN_PIN -1
-#define LORA_DEFAULT_TXEN_PIN -1
-#define LORA_DEFAULT_BUSY_PIN -1
-
-#define PA_OUTPUT_RFO_PIN 0
-#define PA_OUTPUT_PA_BOOST_PIN 1
-
-#define RSSI_OFFSET 157
-
-class sx126x : public Stream {
-public:
- sx126x();
-
- int begin(long frequency);
- void end();
-
- int beginPacket(int implicitHeader = false);
- int endPacket();
-
- int parsePacket(int size = 0);
- int packetRssi();
- int currentRssi();
- uint8_t packetRssiRaw();
- uint8_t currentRssiRaw();
- uint8_t packetSnrRaw();
- float packetSnr();
- long packetFrequencyError();
-
- // from Print
- virtual size_t write(uint8_t byte);
- virtual size_t write(const uint8_t *buffer, size_t size);
-
- // from Stream
- virtual int available();
- virtual int read();
- virtual int peek();
- virtual void flush();
-
- void onReceive(void(*callback)(int));
-
- void receive(int size = 0);
- void 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);
-};
-
-extern sx126x sx126x_modem;
-
-#endif
diff --git a/sx127x.cpp b/sx127x.cpp
deleted file mode 100644
index c5f6d40..0000000
--- a/sx127x.cpp
+++ /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(readRegister(REG_FREQ_ERROR_MSB_7X) & B111);
- freqError <<= 8L;
- freqError += static_cast(readRegister(REG_FREQ_ERROR_MID_7X));
- freqError <<= 8L;
- freqError += static_cast(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(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f);
-
- return static_cast(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< 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
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
-#include
-#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
index c632697..0000000
--- a/sx128x.cpp
+++ /dev/null
@@ -1,889 +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();
- if (_busy != -1) {
- 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(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
index 213fc48..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
-#include
-#include "Modem.h"
-
-#define LORA_DEFAULT_SS_PIN 10
-#define LORA_DEFAULT_RESET_PIN 9
-#define LORA_DEFAULT_DIO0_PIN 2
-#define LORA_DEFAULT_RXEN_PIN -1
-#define LORA_DEFAULT_TXEN_PIN -1
-#define LORA_DEFAULT_BUSY_PIN -1
-
-#define PA_OUTPUT_RFO_PIN 0
-#define PA_OUTPUT_PA_BOOST_PIN 1
-
-#define RSSI_OFFSET 157
-
-class sx128x : public Stream {
-public:
- sx128x();
-
- int begin(unsigned long frequency);
- void end();
-
- int beginPacket(int implicitHeader = false);
- int endPacket();
-
- int parsePacket(int size = 0);
- int packetRssi();
- int currentRssi();
- uint8_t packetRssiRaw();
- uint8_t currentRssiRaw();
- uint8_t packetSnrRaw();
- float packetSnr();
- long packetFrequencyError();
-
- // from Print
- virtual size_t write(uint8_t byte);
- virtual size_t write(const uint8_t *buffer, size_t size);
-
- // from Stream
- virtual int available();
- virtual int read();
- virtual int peek();
- virtual void flush();
-
- void onReceive(void(*callback)(int));
-
- void receive(int size = 0);
- void idle();
- void sleep();
-
- bool preInit();
- uint8_t getTxPower();
- void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
- uint32_t getFrequency();
- void setFrequency(unsigned long frequency);
- void setSpreadingFactor(int sf);
- long getSignalBandwidth();
- void setSignalBandwidth(long sbw);
- void setCodingRate4(int denominator);
- void setPreambleLength(long length);
- void setSyncWord(int sw);
- uint8_t modemStatus();
- void enableCrc();
- void disableCrc();
- void enableTCXO();
- void disableTCXO();
-
- void txAntEnable();
- void rxAntEnable();
- void loraMode();
- void waitOnBusy();
- void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
- void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
- void writeBuffer(const uint8_t* buffer, size_t size);
- void readBuffer(uint8_t* buffer, size_t size);
- void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
- void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr);
-
- // deprecated
- void crc() { enableCrc(); }
- void noCrc() { disableCrc(); }
-
- byte random();
-
- void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN);
- void setSPIFrequency(uint32_t frequency);
-
- void dumpRegisters(Stream& out);
-
-private:
- void explicitHeaderMode();
- void implicitHeaderMode();
-
- void handleDio0Rise();
-
- uint8_t readRegister(uint16_t address);
- void writeRegister(uint16_t address, uint8_t value);
- uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
-
- static void onDio0Rise();
-
- void handleLowDataRate();
- void optimizeModemSensitivity();
-
-private:
- SPISettings _spiSettings;
- int _ss;
- int _reset;
- int _dio0;
- int _rxen;
- int _txen;
- int _busy;
- int _modem;
- unsigned long _frequency;
- int _txp;
- uint8_t _sf;
- uint8_t _bw;
- uint8_t _cr;
- int _packetIndex;
- uint32_t _preambleLength;
- int _implicitHeaderMode;
- int _payloadLength;
- int _crcMode;
- int _fifo_tx_addr_ptr;
- int _fifo_rx_addr_ptr;
- uint8_t _packet[256];
- bool _preinit_done;
- int _rxPacketLength;
- void (*_onReceive)(int);
-};
-
-extern sx128x sx128x_modem;
-
-#endif