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 9fd613e..0000000 --- a/sx126x.cpp +++ /dev/null @@ -1,985 +0,0 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. -// Licensed under the MIT license. - -// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva -// Obviously still under the MIT license. - -#include "Boards.h" - -#if MODEM == SX1262 -#include "sx126x.h" - -#if MCU_VARIANT == MCU_ESP32 - #if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3) - #include "soc/rtc_wdt.h" - #endif - #define ISR_VECT IRAM_ATTR -#else - #define ISR_VECT -#endif - -#define OP_RF_FREQ_6X 0x86 -#define OP_SLEEP_6X 0x84 -#define OP_STANDBY_6X 0x80 -#define OP_TX_6X 0x83 -#define OP_RX_6X 0x82 -#define OP_PA_CONFIG_6X 0x95 -#define OP_SET_IRQ_FLAGS_6X 0x08 // also provides info such as - // preamble detection, etc for - // knowing when it's safe to switch - // antenna modes -#define OP_CLEAR_IRQ_STATUS_6X 0x02 -#define OP_GET_IRQ_STATUS_6X 0x12 -#define OP_RX_BUFFER_STATUS_6X 0x13 -#define OP_PACKET_STATUS_6X 0x14 // get snr & rssi of last packet -#define OP_CURRENT_RSSI_6X 0x15 -#define OP_MODULATION_PARAMS_6X 0x8B // bw, sf, cr, etc. -#define OP_PACKET_PARAMS_6X 0x8C // crc, preamble, payload length, etc. -#define OP_STATUS_6X 0xC0 -#define OP_TX_PARAMS_6X 0x8E // set dbm, etc -#define OP_PACKET_TYPE_6X 0x8A -#define OP_BUFFER_BASE_ADDR_6X 0x8F -#define OP_READ_REGISTER_6X 0x1D -#define OP_WRITE_REGISTER_6X 0x0D -#define OP_DIO3_TCXO_CTRL_6X 0x97 -#define OP_DIO2_RF_CTRL_6X 0x9D -#define OP_CAD_PARAMS 0x88 -#define OP_CALIBRATE_6X 0x89 -#define OP_RX_TX_FALLBACK_MODE_6X 0x93 -#define OP_REGULATOR_MODE_6X 0x96 -#define OP_CALIBRATE_IMAGE_6X 0x98 - -#define MASK_CALIBRATE_ALL 0x7f - -#define IRQ_TX_DONE_MASK_6X 0x01 -#define IRQ_RX_DONE_MASK_6X 0x02 -#define IRQ_HEADER_DET_MASK_6X 0x10 -#define IRQ_PREAMBLE_DET_MASK_6X 0x04 -#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40 -#define IRQ_ALL_MASK_6X 0b0100001111111111 - -#define MODE_LONG_RANGE_MODE_6X 0x01 - -#define OP_FIFO_WRITE_6X 0x0E -#define OP_FIFO_READ_6X 0x1E -#define REG_OCP_6X 0x08E7 -#define REG_LNA_6X 0x08AC // no agc in sx1262 -#define REG_SYNC_WORD_MSB_6X 0x0740 -#define REG_SYNC_WORD_LSB_6X 0x0741 -#define REG_PAYLOAD_LENGTH_6X 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98 -#define REG_RANDOM_GEN_6X 0x0819 - -#define MODE_TCXO_3_3V_6X 0x07 -#define MODE_TCXO_3_0V_6X 0x06 -#define MODE_TCXO_2_7V_6X 0x06 -#define MODE_TCXO_2_4V_6X 0x06 -#define MODE_TCXO_2_2V_6X 0x03 -#define MODE_TCXO_1_8V_6X 0x02 -#define MODE_TCXO_1_7V_6X 0x01 -#define MODE_TCXO_1_6V_6X 0x00 - -#define MODE_STDBY_RC_6X 0x00 -#define MODE_STDBY_XOSC_6X 0x01 -#define MODE_FALLBACK_STDBY_RC_6X 0x20 -#define MODE_IMPLICIT_HEADER 0x01 -#define MODE_EXPLICIT_HEADER 0x00 - -#define SYNC_WORD_6X 0x1424 - -#define XTAL_FREQ_6X (double)32000000 -#define FREQ_DIV_6X (double)pow(2.0, 25.0) -#define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X) - -#if defined(NRF52840_XXAA) - extern SPIClass spiModem; - #define SPI spiModem -#endif - -extern SPIClass SPI; - -#define MAX_PKT_LENGTH 255 - -sx126x::sx126x() : - _spiSettings(8E6, MSBFIRST, SPI_MODE0), - _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), - _frequency(0), - _txp(0), - _sf(0x07), - _bw(0x04), - _cr(0x01), - _ldro(0x00), - _packetIndex(0), - _preambleLength(18), - _implicitHeaderMode(0), - _payloadLength(255), - _crcMode(1), - _fifo_tx_addr_ptr(0), - _fifo_rx_addr_ptr(0), - _packet({0}), - _preinit_done(false), - _onReceive(NULL) -{ - // overide Stream timeout value - setTimeout(0); -} - -bool sx126x::preInit() { - pinMode(_ss, OUTPUT); - digitalWrite(_ss, HIGH); - - #if BOARD_MODEL == BOARD_RNODE_NG_22 || BOARD_MODEL == BOARD_HELTEC32_V3 - SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs); - #else - SPI.begin(); - #endif - - // check version (retry for up to 2 seconds) - // TODO: Actually read version registers, not syncwords - long start = millis(); - uint8_t syncmsb; - uint8_t synclsb; - while (((millis() - start) < 2000) && (millis() >= start)) { - syncmsb = readRegister(REG_SYNC_WORD_MSB_6X); - synclsb = readRegister(REG_SYNC_WORD_LSB_6X); - if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) { - break; - } - delay(100); - } - if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) { - return false; - } - - _preinit_done = true; - return true; -} - -uint8_t ISR_VECT sx126x::readRegister(uint16_t address) -{ - return singleTransfer(OP_READ_REGISTER_6X, address, 0x00); -} - -void sx126x::writeRegister(uint16_t address, uint8_t value) -{ - singleTransfer(OP_WRITE_REGISTER_6X, address, value); -} - -uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) -{ - waitOnBusy(); - - uint8_t response; - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - SPI.transfer((address & 0xFF00) >> 8); - SPI.transfer(address & 0x00FF); - if (opcode == OP_READ_REGISTER_6X) { - SPI.transfer(0x00); - } - response = SPI.transfer(value); - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); - - return response; -} - -void sx126x::rxAntEnable() -{ - if (_rxen != -1) { - digitalWrite(_rxen, HIGH); - } -} - -void sx126x::loraMode() { - // enable lora mode on the SX1262 chip - uint8_t mode = MODE_LONG_RANGE_MODE_6X; - executeOpcode(OP_PACKET_TYPE_6X, &mode, 1); -} - -void sx126x::waitOnBusy() { - unsigned long time = millis(); - while (digitalRead(_busy) == HIGH) - { - if (millis() >= (time + 100)) { - break; - } - // do nothing - } -} - -void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); -} - -void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); -} - -void sx126x::writeBuffer(const uint8_t* buffer, size_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(OP_FIFO_WRITE_6X); - SPI.transfer(_fifo_tx_addr_ptr); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - _fifo_tx_addr_ptr++; - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); -} - -void sx126x::readBuffer(uint8_t* buffer, size_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(OP_FIFO_READ_6X); - SPI.transfer(_fifo_rx_addr_ptr); - SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); -} - -void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) { - // because there is no access to these registers on the sx1262, we have - // to set all these parameters at once or not at all. - uint8_t buf[8]; - - buf[0] = sf; - buf[1] = bw; - buf[2] = cr; - // low data rate toggle - buf[3] = ldro; - // unused params in LoRa mode - buf[4] = 0x00; - buf[5] = 0x00; - buf[6] = 0x00; - buf[7] = 0x00; - - executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8); -} - -void sx126x::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) { - // because there is no access to these registers on the sx1262, we have - // to set all these parameters at once or not at all. - uint8_t buf[9]; - - buf[0] = uint8_t((preamble & 0xFF00) >> 8); - buf[1] = uint8_t((preamble & 0x00FF)); - buf[2] = headermode; - buf[3] = length; - buf[4] = crc; - // standard IQ setting (no inversion) - buf[5] = 0x00; - // unused params - buf[6] = 0x00; - buf[7] = 0x00; - buf[8] = 0x00; - - executeOpcode(OP_PACKET_PARAMS_6X, buf, 9); -} - -void sx126x::reset(void) { - if (_reset != -1) { - pinMode(_reset, OUTPUT); - - // perform reset - digitalWrite(_reset, LOW); - delay(10); - digitalWrite(_reset, HIGH); - delay(10); - } -} - -void sx126x::calibrate(void) { - // Put in STDBY_RC mode before calibration - uint8_t mode_byte = MODE_STDBY_RC_6X; - executeOpcode(OP_STANDBY_6X, &mode_byte, 1); - - // calibrate RC64k, RC13M, PLL, ADC and image - uint8_t calibrate = MASK_CALIBRATE_ALL; - executeOpcode(OP_CALIBRATE_6X, &calibrate, 1); - - delay(5); - waitOnBusy(); -} - -void sx126x::calibrate_image(long frequency) { - uint8_t image_freq[2] = {0}; - - if (frequency >= 430E6 && frequency <= 440E6) { - image_freq[0] = 0x6B; - image_freq[1] = 0x6F; - } - else if (frequency >= 470E6 && frequency <= 510E6) { - image_freq[0] = 0x75; - image_freq[1] = 0x81; - } - else if (frequency >= 779E6 && frequency <= 787E6) { - image_freq[0] = 0xC1; - image_freq[1] = 0xC5; - } - else if (frequency >= 863E6 && frequency <= 870E6) { - image_freq[0] = 0xD7; - image_freq[1] = 0xDB; - } - else if (frequency >= 902E6 && frequency <= 928E6) { - image_freq[0] = 0xE1; - image_freq[1] = 0xE9; - } - - executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2); - waitOnBusy(); -} - -int sx126x::begin(long frequency) -{ - reset(); - - if (_busy != -1) { - pinMode(_busy, INPUT); - } - - if (!_preinit_done) { - if (!preInit()) { - return false; - } - } - - if (_rxen != -1) { - pinMode(_rxen, OUTPUT); - } - - calibrate(); - calibrate_image(frequency); - - enableTCXO(); - - loraMode(); - standby(); - - // Set sync word - setSyncWord(SYNC_WORD_6X); - - #if DIO2_AS_RF_SWITCH - // enable dio2 rf switch - uint8_t byte = 0x01; - executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1); - #endif - - rxAntEnable(); - - setFrequency(frequency); - - // set output power to 2 dBm - setTxPower(2); - enableCrc(); - - // set LNA boost - writeRegister(REG_LNA_6X, 0x96); - - // set base addresses - uint8_t basebuf[2] = {0}; - executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2); - - setModulationParams(_sf, _bw, _cr, _ldro); - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); - - return 1; -} - -void sx126x::end() -{ - // put in sleep mode - sleep(); - - // stop SPI - SPI.end(); - - _preinit_done = false; -} - -int sx126x::beginPacket(int implicitHeader) -{ - standby(); - - if (implicitHeader) { - implicitHeaderMode(); - } else { - explicitHeaderMode(); - } - - _payloadLength = 0; - _fifo_tx_addr_ptr = 0; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); - - return 1; -} - -int sx126x::endPacket() -{ - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); - - // put in single TX mode - uint8_t timeout[3] = {0}; - executeOpcode(OP_TX_6X, timeout, 3); - - uint8_t buf[2]; - - buf[0] = 0x00; - buf[1] = 0x00; - - executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); - - // wait for TX done - while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) { - buf[0] = 0x00; - buf[1] = 0x00; - executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); - yield(); - } - - // clear IRQ's - - uint8_t mask[2]; - mask[0] = 0x00; - mask[1] = IRQ_TX_DONE_MASK_6X; - executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2); - return 1; -} - -uint8_t sx126x::modemStatus() { - // imitate the register status from the sx1276 / 78 - uint8_t buf[2] = {0}; - - executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2); - uint8_t clearbuf[2] = {0}; - uint8_t byte = 0x00; - - if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) { - byte = byte | 0x01 | 0x04; - // clear register after reading - clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X; - } - - if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) { - byte = byte | 0x02 | 0x04; - } - - executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2); - - return byte; -} - - -uint8_t sx126x::currentRssiRaw() { - uint8_t byte = 0; - executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); - return byte; -} - -int ISR_VECT sx126x::currentRssi() { - uint8_t byte = 0; - executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1); - int rssi = -(int(byte)) / 2; - return rssi; -} - -uint8_t sx126x::packetRssiRaw() { - uint8_t buf[3] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); - return buf[2]; -} - -int ISR_VECT sx126x::packetRssi() { - // may need more calculations here - uint8_t buf[3] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); - int pkt_rssi = -buf[0] / 2; - return pkt_rssi; -} - -uint8_t ISR_VECT sx126x::packetSnrRaw() { - uint8_t buf[3] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); - return buf[1]; -} - -float ISR_VECT sx126x::packetSnr() { - uint8_t buf[3] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3); - return float(buf[1]) * 0.25; -} - -long sx126x::packetFrequencyError() -{ - // todo: implement this, no idea how to check it on the sx1262 - const float fError = 0.0; - return static_cast(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 c3b0668..0000000 --- a/sx126x.h +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. -// Licensed under the MIT license. - -// Modifications and additions copyright 2023 by Mark Qvist -// Obviously still under the MIT license. - -#ifndef SX126X_H -#define SX126X_H - -#include -#include -#include "Interfaces.h" - -#define LORA_DEFAULT_SS_PIN 10 -#define LORA_DEFAULT_RESET_PIN 9 -#define LORA_DEFAULT_DIO0_PIN 2 -#define LORA_DEFAULT_RXEN_PIN -1 -#define LORA_DEFAULT_TXEN_PIN -1 -#define LORA_DEFAULT_BUSY_PIN 11 - -#define PA_OUTPUT_RFO_PIN 0 -#define PA_OUTPUT_PA_BOOST_PIN 1 - -#define RSSI_OFFSET 157 - -class sx126x : public Stream { -public: - sx126x(); - - int begin(long frequency); - void end(); - - int beginPacket(int implicitHeader = false); - int endPacket(); - - int parsePacket(int size = 0); - int packetRssi(); - int currentRssi(); - uint8_t packetRssiRaw(); - uint8_t currentRssiRaw(); - uint8_t packetSnrRaw(); - float packetSnr(); - long packetFrequencyError(); - - // from Print - virtual size_t write(uint8_t byte); - virtual size_t write(const uint8_t *buffer, size_t size); - - // from Stream - virtual int available(); - virtual int read(); - virtual int peek(); - virtual void flush(); - - void onReceive(void(*callback)(int)); - - void receive(int size = 0); - void standby(); - void sleep(); - - bool preInit(); - uint8_t getTxPower(); - void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); - uint32_t getFrequency(); - void setFrequency(long frequency); - void setSpreadingFactor(int sf); - long getSignalBandwidth(); - void setSignalBandwidth(long sbw); - void setCodingRate4(int denominator); - void setPreambleLength(long length); - void setSyncWord(uint16_t sw); - uint8_t modemStatus(); - void enableCrc(); - void disableCrc(); - void enableTCXO(); - void disableTCXO(); - - void rxAntEnable(); - void loraMode(); - void waitOnBusy(); - void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); - void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); - void writeBuffer(const uint8_t* buffer, size_t size); - void readBuffer(uint8_t* buffer, size_t size); - void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc); - - void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); - - // deprecated - void crc() { enableCrc(); } - void noCrc() { disableCrc(); } - - byte random(); - - void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN); - void setSPIFrequency(uint32_t frequency); - - void dumpRegisters(Stream& out); - -private: - void explicitHeaderMode(); - void implicitHeaderMode(); - - void handleDio0Rise(); - - uint8_t readRegister(uint16_t address); - void writeRegister(uint16_t address, uint8_t value); - uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); - - static void onDio0Rise(); - - void handleLowDataRate(); - void optimizeModemSensitivity(); - - void reset(void); - void calibrate(void); - void calibrate_image(long frequency); - -private: - SPISettings _spiSettings; - int _ss; - int _reset; - int _dio0; - int _rxen; - int _busy; - long _frequency; - int _txp; - uint8_t _sf; - uint8_t _bw; - uint8_t _cr; - uint8_t _ldro; - int _packetIndex; - int _preambleLength; - int _implicitHeaderMode; - int _payloadLength; - int _crcMode; - int _fifo_tx_addr_ptr; - int _fifo_rx_addr_ptr; - uint8_t _packet[255]; - bool _preinit_done; - void (*_onReceive)(int); - bool _tcxo; -}; - -extern sx126x sx126x_modem; - -#endif diff --git a/sx127x.cpp b/sx127x.cpp deleted file mode 100644 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 9c4a249..0000000 --- a/sx128x.cpp +++ /dev/null @@ -1,887 +0,0 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. -// Licensed under the MIT license. - -// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva -// Obviously still under the MIT license. - -#include "sx128x.h" -#include "Boards.h" - -#define MCU_1284P 0x91 -#define MCU_2560 0x92 -#define MCU_ESP32 0x81 -#define MCU_NRF52 0x71 -#if defined(__AVR_ATmega1284P__) - #define PLATFORM PLATFORM_AVR - #define MCU_VARIANT MCU_1284P -#elif defined(__AVR_ATmega2560__) - #define PLATFORM PLATFORM_AVR - #define MCU_VARIANT MCU_2560 -#elif defined(ESP32) - #define PLATFORM PLATFORM_ESP32 - #define MCU_VARIANT MCU_ESP32 -#elif defined(NRF52840_XXAA) - #define PLATFORM PLATFORM_NRF52 - #define MCU_VARIANT MCU_NRF52 -#endif - -#ifndef MCU_VARIANT - #error No MCU variant defined, cannot compile -#endif - -#if MCU_VARIANT == MCU_ESP32 - #if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3) - #include "soc/rtc_wdt.h" - #endif - #define ISR_VECT IRAM_ATTR -#else - #define ISR_VECT -#endif - -#define OP_RF_FREQ_8X 0x86 -#define OP_SLEEP_8X 0x84 -#define OP_STANDBY_8X 0x80 -#define OP_TX_8X 0x83 -#define OP_RX_8X 0x82 -#define OP_SET_IRQ_FLAGS_8X 0x8D // also provides info such as - // preamble detection, etc for - // knowing when it's safe to switch - // antenna modes -#define OP_CLEAR_IRQ_STATUS_8X 0x97 -#define OP_GET_IRQ_STATUS_8X 0x15 -#define OP_RX_BUFFER_STATUS_8X 0x17 -#define OP_PACKET_STATUS_8X 0x1D // get snr & rssi of last packet -#define OP_CURRENT_RSSI_8X 0x1F -#define OP_MODULATION_PARAMS_8X 0x8B // bw, sf, cr, etc. -#define OP_PACKET_PARAMS_8X 0x8C // crc, preamble, payload length, etc. -#define OP_STATUS_8X 0xC0 -#define OP_TX_PARAMS_8X 0x8E // set dbm, etc -#define OP_PACKET_TYPE_8X 0x8A -#define OP_BUFFER_BASE_ADDR_8X 0x8F -#define OP_READ_REGISTER_8X 0x19 -#define OP_WRITE_REGISTER_8X 0x18 -#define IRQ_TX_DONE_MASK_8X 0x01 -#define IRQ_RX_DONE_MASK_8X 0x02 -#define IRQ_HEADER_DET_MASK_8X 0x10 -#define IRQ_HEADER_ERROR_MASK_8X 0x20 -#define IRQ_PAYLOAD_CRC_ERROR_MASK_8X 0x40 - -#define MODE_LONG_RANGE_MODE_8X 0x01 - -#define OP_FIFO_WRITE_8X 0x1A -#define OP_FIFO_READ_8X 0x1B -#define IRQ_PREAMBLE_DET_MASK_8X 0x80 - -#define REG_PACKET_SIZE 0x901 -#define REG_FIRM_VER_MSB 0x154 -#define REG_FIRM_VER_LSB 0x153 - -#define XTAL_FREQ_8X (double)52000000 -#define FREQ_DIV_8X (double)pow(2.0, 18.0) -#define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X) - -#if defined(NRF52840_XXAA) - extern SPIClass spiModem; - #define SPI spiModem -#endif - -extern SPIClass SPI; - -#define MAX_PKT_LENGTH 255 - -sx128x::sx128x() : - _spiSettings(8E6, MSBFIRST, SPI_MODE0), - _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN), - _frequency(0), - _txp(0), - _sf(0x50), - _bw(0x34), - _cr(0x01), - _packetIndex(0), - _preambleLength(18), - _implicitHeaderMode(0), - _payloadLength(255), - _crcMode(0), - _fifo_tx_addr_ptr(0), - _fifo_rx_addr_ptr(0), - _packet({0}), - _rxPacketLength(0), - _preinit_done(false), - _onReceive(NULL) -{ - // overide Stream timeout value - setTimeout(0); -} - -bool sx128x::preInit() { - // setup pins - pinMode(_ss, OUTPUT); - // set SS high - digitalWrite(_ss, HIGH); - - SPI.begin(); - - // check version (retry for up to 2 seconds) - long start = millis(); - - uint8_t version_msb; - uint8_t version_lsb; - - while (((millis() - start) < 2000) && (millis() >= start)) { - - version_msb = readRegister(REG_FIRM_VER_MSB); - version_lsb = readRegister(REG_FIRM_VER_LSB); - - if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) { - break; - } - delay(100); - } - if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) { - return false; - } - - _preinit_done = true; - return true; -} - -uint8_t ISR_VECT sx128x::readRegister(uint16_t address) -{ - return singleTransfer(OP_READ_REGISTER_8X, address, 0x00); -} - -void sx128x::writeRegister(uint16_t address, uint8_t value) -{ - singleTransfer(OP_WRITE_REGISTER_8X, address, value); -} - -uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value) -{ - waitOnBusy(); - - uint8_t response; - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - SPI.transfer((address & 0xFF00) >> 8); - SPI.transfer(address & 0x00FF); - if (opcode == OP_READ_REGISTER_8X) { - SPI.transfer(0x00); - } - response = SPI.transfer(value); - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); - - return response; -} - -void sx128x::rxAntEnable() -{ - if (_txen != -1) { - digitalWrite(_txen, LOW); - } - if (_rxen != -1) { - digitalWrite(_rxen, HIGH); - } -} - -void sx128x::txAntEnable() -{ - if (_txen != -1) { - digitalWrite(_txen, HIGH); - } - if (_rxen != -1) { - digitalWrite(_rxen, LOW); - } -} - -void sx128x::loraMode() { - // enable lora mode on the SX1262 chip - uint8_t mode = MODE_LONG_RANGE_MODE_8X; - executeOpcode(OP_PACKET_TYPE_8X, &mode, 1); -} - -void sx128x::waitOnBusy() { - unsigned long time = millis(); - while (digitalRead(_busy) == HIGH) - { - if (millis() >= (time + 100)) { - break; - } - // do nothing - } -} - -void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); -} - -void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(opcode); - SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); -} - -void sx128x::writeBuffer(const uint8_t* buffer, size_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(OP_FIFO_WRITE_8X); - SPI.transfer(_fifo_tx_addr_ptr); - - for (int i = 0; i < size; i++) - { - SPI.transfer(buffer[i]); - _fifo_tx_addr_ptr++; - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); -} - -void sx128x::readBuffer(uint8_t* buffer, size_t size) -{ - waitOnBusy(); - - digitalWrite(_ss, LOW); - - SPI.beginTransaction(_spiSettings); - SPI.transfer(OP_FIFO_READ_8X); - SPI.transfer(_fifo_rx_addr_ptr); - SPI.transfer(0x00); - - for (int i = 0; i < size; i++) - { - buffer[i] = SPI.transfer(0x00); - } - - SPI.endTransaction(); - - digitalWrite(_ss, HIGH); -} - -void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) { - // because there is no access to these registers on the sx1280, we have - // to set all these parameters at once or not at all. - uint8_t buf[3]; - - buf[0] = sf; - buf[1] = bw; - buf[2] = cr; - executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3); - - if (sf <= 6) { - writeRegister(0x925, 0x1E); - } else if (sf <= 8) { - writeRegister(0x925, 0x37); - } else if (sf >= 9) { - writeRegister(0x925, 0x32); - } - writeRegister(0x093C, 0x1); -} - -void sx128x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) { - // because there is no access to these registers on the sx1280, we have - // to set all these parameters at once or not at all. - uint8_t buf[7]; - - // calculate exponent and mantissa values for modem - uint8_t e = 1; - uint8_t m = 1; - uint32_t preamblelen; - - for (e <= 15; e++;) { - for (m <= 15; m++;) { - preamblelen = m * (uint32_t(1) << e); - if (preamblelen >= preamble) break; - } - if (preamblelen >= preamble) break; - } - - buf[0] = (e << 4) | m; - buf[1] = headermode; - buf[2] = length; - buf[3] = crc; - // standard IQ setting (no inversion) - buf[4] = 0x40; - // unused params - buf[5] = 0x00; - buf[6] = 0x00; - - executeOpcode(OP_PACKET_PARAMS_8X, buf, 7); -} - -int sx128x::begin(unsigned long frequency) -{ - if (_reset != -1) { - pinMode(_reset, OUTPUT); - - // perform reset - digitalWrite(_reset, LOW); - delay(10); - digitalWrite(_reset, HIGH); - delay(10); - } - - if (_rxen != -1) { - pinMode(_rxen, OUTPUT); - } - - if (_txen != -1) { - pinMode(_txen, OUTPUT); - } - - if (_busy != -1) { - pinMode(_busy, INPUT); - } - - if (!_preinit_done) { - if (!preInit()) { - return false; - } - } - - idle(); - loraMode(); - rxAntEnable(); - - setFrequency(frequency); - - // set LNA boost - // todo: implement this - //writeRegister(REG_LNA, 0x96); - - setModulationParams(_sf, _bw, _cr); - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); - - // set output power to 2 dBm - setTxPower(2); - - // set base addresses - uint8_t basebuf[2] = {0}; - executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2); - - return 1; -} - -void sx128x::end() -{ - // put in sleep mode - sleep(); - - // stop SPI - SPI.end(); - - _preinit_done = false; -} - -int sx128x::beginPacket(int implicitHeader) -{ - // put in standby mode - idle(); - - if (implicitHeader) { - implicitHeaderMode(); - } else { - explicitHeaderMode(); - } - - _payloadLength = 0; - _fifo_tx_addr_ptr = 0; - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); - - return 1; -} - -int sx128x::endPacket() -{ - setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode); - - txAntEnable(); - - // put in single TX mode - uint8_t timeout[3] = {0}; - executeOpcode(OP_TX_8X, timeout, 3); - - uint8_t buf[2]; - - buf[0] = 0x00; - buf[1] = 0x00; - - executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); - - // wait for TX done - while ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0) { - buf[0] = 0x00; - buf[1] = 0x00; - executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); - yield(); - } - - // clear IRQ's - - uint8_t mask[2]; - mask[0] = 0x00; - mask[1] = IRQ_TX_DONE_MASK_8X; - executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2); - return 1; -} - -uint8_t sx128x::modemStatus() { - // imitate the register status from the sx1276 / 78 - uint8_t buf[2] = {0}; - - executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2); - - uint8_t clearbuf[2] = {0}; - - uint8_t byte = 0x00; - - if ((buf[0] & IRQ_PREAMBLE_DET_MASK_8X) != 0) { - byte = byte | 0x01 | 0x04; - // clear register after reading - clearbuf[0] = 0xFF; - } - - if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) { - byte = byte | 0x02 | 0x04; - // clear register after reading - clearbuf[1] = 0xFF; - } - - executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2); - - return byte; -} - - -uint8_t sx128x::currentRssiRaw() { - uint8_t byte = 0; - executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1); - return byte; -} - -int ISR_VECT sx128x::currentRssi() { - uint8_t byte = 0; - executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1); - int rssi = -byte / 2; - return rssi; -} - -uint8_t sx128x::packetRssiRaw() { - uint8_t buf[5] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); - return buf[0]; -} - -int ISR_VECT sx128x::packetRssi() { - // may need more calculations here - uint8_t buf[5] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); - int pkt_rssi = -buf[0] / 2; - return pkt_rssi; -} - -uint8_t ISR_VECT sx128x::packetSnrRaw() { - uint8_t buf[5] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5); - return buf[1]; -} - -float ISR_VECT sx128x::packetSnr() { - uint8_t buf[5] = {0}; - executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 3); - return float(buf[1]) * 0.25; -} - -long sx128x::packetFrequencyError() -{ - int32_t freqError = 0; - // todo: implement this, page 120 of sx1280 datasheet - const float fError = 0.0; - return static_cast(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 ad76d51..0000000 --- a/sx128x.h +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. -// Licensed under the MIT license. - -// Modifications and additions copyright 2023 by Mark Qvist -// Obviously still under the MIT license. - -#ifndef SX128X_H -#define SX128X_H - -#include -#include -#include "Interfaces.h" - -#define LORA_DEFAULT_SS_PIN 10 -#define LORA_DEFAULT_RESET_PIN 9 -#define LORA_DEFAULT_DIO0_PIN 2 -#define LORA_DEFAULT_RXEN_PIN -1 -#define LORA_DEFAULT_TXEN_PIN -1 -#define LORA_DEFAULT_BUSY_PIN 11 - -#define PA_OUTPUT_RFO_PIN 0 -#define PA_OUTPUT_PA_BOOST_PIN 1 - -#define RSSI_OFFSET 157 - -class sx128x : public Stream { -public: - sx128x(); - - int begin(unsigned long frequency); - void end(); - - int beginPacket(int implicitHeader = false); - int endPacket(); - - int parsePacket(int size = 0); - int packetRssi(); - int currentRssi(); - uint8_t packetRssiRaw(); - uint8_t currentRssiRaw(); - uint8_t packetSnrRaw(); - float packetSnr(); - long packetFrequencyError(); - - // from Print - virtual size_t write(uint8_t byte); - virtual size_t write(const uint8_t *buffer, size_t size); - - // from Stream - virtual int available(); - virtual int read(); - virtual int peek(); - virtual void flush(); - - void onReceive(void(*callback)(int)); - - void receive(int size = 0); - void idle(); - void sleep(); - - bool preInit(); - uint8_t getTxPower(); - void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); - uint32_t getFrequency(); - void setFrequency(unsigned long frequency); - void setSpreadingFactor(int sf); - long getSignalBandwidth(); - void setSignalBandwidth(long sbw); - void setCodingRate4(int denominator); - void setPreambleLength(long length); - void setSyncWord(int sw); - uint8_t modemStatus(); - void enableCrc(); - void disableCrc(); - void enableTCXO(); - void disableTCXO(); - - void txAntEnable(); - void rxAntEnable(); - void loraMode(); - void waitOnBusy(); - void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); - void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); - void writeBuffer(const uint8_t* buffer, size_t size); - void readBuffer(uint8_t* buffer, size_t size); - void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); - void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); - - // deprecated - void crc() { enableCrc(); } - void noCrc() { disableCrc(); } - - byte random(); - - void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN); - void setSPIFrequency(uint32_t frequency); - - void dumpRegisters(Stream& out); - -private: - void explicitHeaderMode(); - void implicitHeaderMode(); - - void handleDio0Rise(); - - uint8_t readRegister(uint16_t address); - void writeRegister(uint16_t address, uint8_t value); - uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); - - static void onDio0Rise(); - - void handleLowDataRate(); - void optimizeModemSensitivity(); - -private: - SPISettings _spiSettings; - int _ss; - int _reset; - int _dio0; - int _rxen; - int _txen; - int _busy; - int _modem; - unsigned long _frequency; - int _txp; - uint8_t _sf; - uint8_t _bw; - uint8_t _cr; - int _packetIndex; - uint32_t _preambleLength; - int _implicitHeaderMode; - int _payloadLength; - int _crcMode; - int _fifo_tx_addr_ptr; - int _fifo_rx_addr_ptr; - uint8_t _packet[256]; - bool _preinit_done; - int _rxPacketLength; - void (*_onReceive)(int); -}; - -extern sx128x sx128x_modem; - -#endif