Add multiple interface support

master
jacob.eva 9 months ago
parent ebdec7f3e0
commit 3f8d012457
No known key found for this signature in database
GPG Key ID: B92E083BBCCAA1E
  1. 494
      Boards.h
  2. 82
      Config.h
  3. 165
      Display.h
  4. 5
      Documentation/CONTRIBUTING.md
  5. 29
      Framing.h
  6. 22
      Graphics.h
  7. 7
      Interfaces.h
  8. 4
      Modem.h
  9. 905
      RNode_Firmware_CE.ino
  10. 2405
      Radio.cpp
  11. 656
      Radio.h
  12. 501
      Utilities.h
  13. 985
      sx126x.cpp
  14. 147
      sx126x.h
  15. 498
      sx127x.cpp
  16. 111
      sx127x.h
  17. 887
      sx128x.cpp
  18. 144
      sx128x.h

@ -13,17 +13,14 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Modem.h"
#include "Interfaces.h"
#ifndef BOARDS_H
#define BOARDS_H
#define PLATFORM_AVR 0x90
#define PLATFORM_ESP32 0x80
#define PLATFORM_NRF52 0x70
#define MCU_1284P 0x91
#define MCU_2560 0x92
#define MCU_ESP32 0x81
#define MCU_NRF52 0x71
@ -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
@ -374,16 +568,6 @@
// #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;
const int pin_adc = 1;
@ -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;
#endif
#ifndef HAS_BUSY
const int pin_busy = -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 DIO2_AS_RF_SWITCH
#define DIO2_AS_RF_SWITCH false
#endif
#endif

@ -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;
// 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

@ -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,12 +365,33 @@ 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);
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
@ -376,7 +403,8 @@ void draw_lora_icon(int px, int py) {
}
void draw_mw_icon(int px, int py) {
if (mw_radio_online) {
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)
@ -389,6 +417,13 @@ void draw_mw_icon(int px, int py) {
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);
#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
}
}
uint8_t charge_tick = 0;
@ -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)

@ -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

@ -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

@ -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

@ -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

@ -1,4 +0,0 @@
#define SX1276 0x01
#define SX1278 0x02
#define SX1262 0x03
#define SX1280 0x04

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,656 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2023 by Mark Qvist & Jacob Eva
// Obviously still under the MIT license.
#ifndef RADIO_H
#define RADIO_H
#include <Arduino.h>
#include <SPI.h>
#include "Interfaces.h"
#include "Boards.h"
// TX
#define PA_OUTPUT_RFO_PIN 0
#define PA_OUTPUT_PA_BOOST_PIN 1
// DCD
#define STATUS_INTERVAL_MS 3
#define DCD_SAMPLES 2500
#define UTIL_UPDATE_INTERVAL_MS 1000
#define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS)
#define AIRTIME_LONGTERM 3600
#define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000)
#define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES)
#define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS)
#define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS
#define DCD_THRESHOLD 2
#define DCD_LED_STEP_D 3
#define LORA_PREAMBLE_SYMBOLS_HW 4
#define LORA_PREAMBLE_SYMBOLS_MIN 18
#define LORA_PREAMBLE_TARGET_MS 15
#define RSSI_OFFSET 157
#define PHY_HEADER_LORA_SYMBOLS 8
#define _e 2.71828183
#define _S 10.0
// Status flags
const uint8_t SIG_DETECT = 0x01;
const uint8_t SIG_SYNCED = 0x02;
const uint8_t RX_ONGOING = 0x04;
// forward declare Utilities.h LED functions
void led_rx_on();
void led_rx_off();
void led_indicate_airtime_lock();
#if PLATFORM == PLATFORM_ESP32
// get update_lock for ESP32
extern portMUX_TYPE update_lock;
#endif
class RadioInterface : public Stream {
public:
// todo: in the future define _spiModem and _spiSettings from here for inheritence by child classes
RadioInterface(uint8_t index) : _index(index), _radio_locked(false),
_radio_online(false), _st_airtime_limit(0.0), _lt_airtime_limit(0.0),
_airtime_lock(false), _airtime(0.0), _longterm_airtime(0.0),
_local_channel_util(0.0), _total_channel_util(0.0),
_longterm_channel_util(0.0), _last_status_update(0), _last_dcd(0),
_stat_rx_ongoing(false), _stat_signal_detected(false),
_stat_signal_synced(false), _dcd_count(0), _dcd(false), _dcd_led(false),
_dcd_waiting(false), _dcd_wait_until(0), _dcd_sample(0),
_post_tx_yield_timeout(0), _csma_slot_ms(50), _csma_p_min(0.1),
_csma_p_max(0.8), _preambleLength(6), _lora_symbol_time_ms(0.0),
_lora_symbol_rate(0.0), _lora_us_per_byte(0.0), _bitrate(0),
_onReceive(NULL) {};
virtual int begin();
virtual void end();
virtual int beginPacket(int implicitHeader = false);
virtual int endPacket();
virtual int packetRssi();
virtual int currentRssi();
virtual uint8_t packetRssiRaw();
virtual uint8_t currentRssiRaw();
virtual uint8_t packetSnrRaw();
virtual float packetSnr();
virtual long packetFrequencyError();
// from Print
virtual size_t write(uint8_t byte);
virtual size_t write(const uint8_t *buffer, size_t size);
// from Stream
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
virtual void onReceive(void(*callback)(uint8_t, int));
virtual void receive(int size = 0);
virtual void standby();
virtual void sleep();
virtual bool preInit();
virtual uint8_t getTxPower();
virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
virtual uint32_t getFrequency();
virtual void setFrequency(uint32_t frequency);
virtual void setSpreadingFactor(int sf);
virtual uint8_t getSpreadingFactor();
virtual uint32_t getSignalBandwidth();
virtual void setSignalBandwidth(uint32_t sbw);
virtual void setCodingRate4(int denominator);
virtual uint8_t getCodingRate4();
virtual void setPreambleLength(long length);
virtual uint8_t modemStatus();
virtual void enableCrc();
virtual void disableCrc();
virtual void enableTCXO();
virtual void disableTCXO();
virtual byte random();
virtual void setSPIFrequency(uint32_t frequency);
virtual void updateBitrate();
virtual void handleDio0Rise();
uint32_t getBitrate() { return _bitrate; };
uint8_t getIndex() { return _index; };
void setRadioLock(bool lock) { _radio_locked = lock; };
bool getRadioLock() { return _radio_locked; };
void setRadioOnline(bool online) { _radio_online = online; };
bool getRadioOnline() { return _radio_online; };
void setSTALock(float at) { _st_airtime_limit = at; };
float getSTALock() { return _st_airtime_limit; };
void setLTALock(float at) { _lt_airtime_limit = at; };
float getLTALock() { return _lt_airtime_limit; };
bool calculateALock() {
_airtime_lock = false;
if (_st_airtime_limit != 0.0 && _airtime >= _st_airtime_limit) {
_airtime_lock = true;
}
if (_lt_airtime_limit != 0.0 && _longterm_airtime >= _lt_airtime_limit) {
_airtime_lock = true;
}
return _airtime_lock;
};
void updateAirtime() {
uint16_t cb = current_airtime_bin();
uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; }
uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
_airtime_bins[nb] = 0;
_airtime = (float)(_airtime_bins[cb]+_airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS);
uint32_t longterm_airtime_sum = 0;
for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) {
longterm_airtime_sum += _airtime_bins[bin];
}
_longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS;
float longterm_channel_util_sum = 0.0;
for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) {
longterm_channel_util_sum += _longterm_bins[bin];
}
_longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS;
updateCSMAp();
//kiss_indicate_channel_stats(); // todo: enable me!
};
void addAirtime(uint16_t written) {
float packet_cost_ms = 0.0;
float payload_cost_ms = ((float)written * _lora_us_per_byte)/1000.0;
packet_cost_ms += payload_cost_ms;
packet_cost_ms += (_preambleLength+4.25)*_lora_symbol_time_ms;
packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * _lora_symbol_time_ms;
uint16_t cb = current_airtime_bin();
uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
_airtime_bins[cb] += packet_cost_ms;
_airtime_bins[nb] = 0;
};
void checkModemStatus() {
if (millis()-_last_status_update >= STATUS_INTERVAL_MS) {
updateModemStatus();
_util_samples[_dcd_sample] = _dcd;
_dcd_sample = (_dcd_sample+1)%DCD_SAMPLES;
if (_dcd_sample % UTIL_UPDATE_INTERVAL == 0) {
int util_count = 0;
for (int ui = 0; ui < DCD_SAMPLES; ui++) {
if (_util_samples[ui]) util_count++;
}
_local_channel_util = (float)util_count / (float)DCD_SAMPLES;
_total_channel_util = _local_channel_util + _airtime;
if (_total_channel_util > 1.0) _total_channel_util = 1.0;
int16_t cb = current_airtime_bin();
uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
if (_total_channel_util > _longterm_bins[cb]) _longterm_bins[cb] = _total_channel_util;
_longterm_bins[nb] = 0.0;
updateAirtime();
}
}
};
void updateModemStatus() {
#if PLATFORM == PLATFORM_ESP32
portENTER_CRITICAL(&update_lock);
#elif PLATFORM == PLATFORM_NRF52
portENTER_CRITICAL();
#endif
uint8_t status = modemStatus();
_last_status_update = millis();
#if PLATFORM == PLATFORM_ESP32
portEXIT_CRITICAL(&update_lock);
#elif PLATFORM == PLATFORM_NRF52
portEXIT_CRITICAL();
#endif
if ((status & SIG_DETECT) == SIG_DETECT) { _stat_signal_detected = true; } else { _stat_signal_detected = false; }
if ((status & SIG_SYNCED) == SIG_SYNCED) { _stat_signal_synced = true; } else { _stat_signal_synced = false; }
if ((status & RX_ONGOING) == RX_ONGOING) { _stat_rx_ongoing = true; } else { _stat_rx_ongoing = false; }
// if (stat_signal_detected || stat_signal_synced || stat_rx_ongoing) {
if (_stat_signal_detected || _stat_signal_synced) {
if (_stat_rx_ongoing) {
if (_dcd_count < DCD_THRESHOLD) {
_dcd_count++;
} else {
_last_dcd = _last_status_update;
_dcd_led = true;
_dcd = true;
}
}
} else {
if (_dcd_count == 0) {
_dcd_led = false;
} else if (_dcd_count > DCD_LED_STEP_D) {
_dcd_count -= DCD_LED_STEP_D;
} else {
_dcd_count = 0;
}
if (_last_status_update > _last_dcd+_csma_slot_ms) {
_dcd = false;
_dcd_led = false;
_dcd_count = 0;
}
}
if (_dcd_led) {
led_rx_on();
} else {
if (_airtime_lock) {
led_indicate_airtime_lock();
} else {
led_rx_off();
}
}
};
void setPostTxYieldTimeout(uint32_t timeout) { _post_tx_yield_timeout = timeout; };
uint32_t getPostTxYieldTimeout() { return _post_tx_yield_timeout; };
void setDCD(bool dcd) { _dcd = dcd; };
bool getDCD() { return _dcd; };
void setDCDWaiting(bool dcd_waiting) { _dcd_waiting = dcd_waiting; };
bool getDCDWaiting() { return _dcd_waiting; };
void setDCDWaitUntil(uint32_t dcd_wait_until) { _dcd_wait_until = dcd_wait_until; };
bool getDCDWaitUntil() { return _dcd_wait_until; };
float getAirtime() { return _airtime; };
float getLongtermAirtime() { return _longterm_airtime; };
float getTotalChannelUtil() { return _total_channel_util; };
float getLongtermChannelUtil() { return _longterm_channel_util; };
float CSMASlope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); };
void updateCSMAp() {
_csma_p = (uint8_t)((1.0-(_csma_p_min+(_csma_p_max-_csma_p_min)*CSMASlope(_airtime)))*255.0);
};
uint8_t getCSMAp() { return _csma_p; };
void setCSMASlotMS(int slot_size) { _csma_slot_ms = slot_size; };
int getCSMASlotMS() { return _csma_slot_ms; };
float getSymbolTime() { return _lora_symbol_time_ms; };
float getSymbolRate() { return _lora_symbol_rate; };
long getPreambleLength() { return _preambleLength; };
protected:
virtual void explicitHeaderMode();
virtual void implicitHeaderMode();
uint8_t _index;
bool _radio_locked;
bool _radio_online;
float _st_airtime_limit;
float _lt_airtime_limit;
bool _airtime_lock;
uint16_t _airtime_bins[AIRTIME_BINS] = {0};
uint16_t _longterm_bins[AIRTIME_BINS] = {0};
float _airtime;
float _longterm_airtime;
float _local_channel_util;
float _total_channel_util;
float _longterm_channel_util;
uint32_t _last_status_update;
bool _stat_signal_detected;
bool _stat_signal_synced;
bool _stat_rx_ongoing;
uint32_t _last_dcd;
uint16_t _dcd_count;
bool _dcd;
bool _dcd_led;
bool _dcd_waiting;
long _dcd_wait_until;
bool _util_samples[DCD_SAMPLES] = {false};
int _dcd_sample;
uint32_t _post_tx_yield_timeout;
uint8_t _csma_p;
int _csma_slot_ms;
float _csma_p_min;
float _csma_p_max;
long _preambleLength;
float _lora_symbol_time_ms;
float _lora_symbol_rate;
float _lora_us_per_byte;
uint32_t _bitrate;
void (*_onReceive)(uint8_t, int);
};
class sx126x : public RadioInterface {
public:
sx126x(uint8_t index, SPIClass spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int
dio0, int busy, int rxen);
int begin();
void end();
int beginPacket(int implicitHeader = false);
int endPacket();
int packetRssi();
int currentRssi();
uint8_t packetRssiRaw();
uint8_t currentRssiRaw();
uint8_t packetSnrRaw();
float packetSnr();
long packetFrequencyError();
// from Print
size_t write(uint8_t byte);
size_t write(const uint8_t *buffer, size_t size);
// from Stream
int available();
int read();
int peek();
void flush();
void onReceive(void(*callback)(uint8_t, int));
void receive(int size = 0);
void standby();
void sleep();
bool preInit();
uint8_t getTxPower();
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
uint32_t getFrequency();
void setFrequency(uint32_t frequency);
void setSpreadingFactor(int sf);
uint8_t getSpreadingFactor();
uint32_t getSignalBandwidth();
void setSignalBandwidth(uint32_t sbw);
void setCodingRate4(int denominator);
uint8_t getCodingRate4();
void setPreambleLength(long length);
uint8_t modemStatus();
void enableCrc();
void disableCrc();
void enableTCXO();
void disableTCXO();
byte random();
void setSPIFrequency(uint32_t frequency);
void dumpRegisters(Stream& out);
void updateBitrate();
void handleDio0Rise();
private:
void writeBuffer(const uint8_t* buffer, size_t size);
void readBuffer(uint8_t* buffer, size_t size);
void loraMode();
void rxAntEnable();
void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro);
void setSyncWord(uint16_t sw);
void waitOnBusy();
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
void explicitHeaderMode();
void implicitHeaderMode();
uint8_t readRegister(uint16_t address);
void writeRegister(uint16_t address, uint8_t value);
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
static void onDio0Rise();
void handleLowDataRate();
void optimizeModemSensitivity();
void reset(void);
void calibrate(void);
void calibrate_image(uint32_t frequency);
private:
SPISettings _spiSettings;
SPIClass _spiModem;
int _ss;
int _sclk;
int _mosi;
int _miso;
int _reset;
int _dio0;
int _rxen;
int _busy;
uint32_t _frequency;
int _txp;
uint8_t _sf;
uint8_t _bw;
uint8_t _cr;
uint8_t _ldro;
int _packetIndex;
int _implicitHeaderMode;
int _payloadLength;
int _crcMode;
int _fifo_tx_addr_ptr;
int _fifo_rx_addr_ptr;
uint8_t _packet[255];
bool _preinit_done;
uint8_t _index;
bool _tcxo;
bool _dio2_as_rf_switch;
};
class sx127x : public RadioInterface {
public:
sx127x(uint8_t index, SPIClass spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy);
int begin();
void end();
int beginPacket(int implicitHeader = false);
int endPacket();
int packetRssi();
int currentRssi();
uint8_t packetRssiRaw();
uint8_t currentRssiRaw();
uint8_t packetSnrRaw();
float packetSnr();
long packetFrequencyError();
// from Print
size_t write(uint8_t byte);
size_t write(const uint8_t *buffer, size_t size);
// from Stream
int available();
int read();
int peek();
void flush();
void onReceive(void(*callback)(uint8_t, int));
void receive(int size = 0);
void standby();
void sleep();
bool preInit();
uint8_t getTxPower();
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
uint32_t getFrequency();
void setFrequency(uint32_t frequency);
void setSpreadingFactor(int sf);
uint8_t getSpreadingFactor();
uint32_t getSignalBandwidth();
void setSignalBandwidth(uint32_t sbw);
void setCodingRate4(int denominator);
uint8_t getCodingRate4();
void setPreambleLength(long length);
uint8_t modemStatus();
void enableCrc();
void disableCrc();
void enableTCXO();
void disableTCXO();
byte random();
void setSPIFrequency(uint32_t frequency);
void updateBitrate();
void handleDio0Rise();
private:
void setSyncWord(uint8_t sw);
void explicitHeaderMode();
void implicitHeaderMode();
uint8_t readRegister(uint8_t address);
void writeRegister(uint8_t address, uint8_t value);
uint8_t singleTransfer(uint8_t address, uint8_t value);
static void onDio0Rise();
void handleLowDataRate();
void optimizeModemSensitivity();
private:
SPISettings _spiSettings;
SPIClass _spiModem;
int _ss;
int _sclk;
int _mosi;
int _miso;
int _reset;
int _dio0;
int _busy;
uint32_t _frequency;
int _packetIndex;
int _implicitHeaderMode;
bool _preinit_done;
uint8_t _index;
uint8_t _sf;
uint8_t _cr;
};
class sx128x : public RadioInterface {
public:
sx128x(uint8_t index, SPIClass spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen);
int begin();
void end();
int beginPacket(int implicitHeader = false);
int endPacket();
int packetRssi();
int currentRssi();
uint8_t packetRssiRaw();
uint8_t currentRssiRaw();
uint8_t packetSnrRaw();
float packetSnr();
long packetFrequencyError();
// from Print
size_t write(uint8_t byte);
size_t write(const uint8_t *buffer, size_t size);
// from Stream
int available();
int read();
int peek();
void flush();
void onReceive(void(*callback)(uint8_t, int));
void receive(int size = 0);
void standby();
void sleep();
bool preInit();
uint8_t getTxPower();
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
uint32_t getFrequency();
void setFrequency(uint32_t frequency);
void setSpreadingFactor(int sf);
uint8_t getSpreadingFactor();
uint32_t getSignalBandwidth();
void setSignalBandwidth(uint32_t sbw);
void setCodingRate4(int denominator);
uint8_t getCodingRate4();
void setPreambleLength(long length);
uint8_t modemStatus();
void enableCrc();
void disableCrc();
void enableTCXO();
void disableTCXO();
byte random();
void setSPIFrequency(uint32_t frequency);
void dumpRegisters(Stream& out);
void updateBitrate();
void handleDio0Rise();
private:
void writeBuffer(const uint8_t* buffer, size_t size);
void readBuffer(uint8_t* buffer, size_t size);
void txAntEnable();
void rxAntEnable();
void loraMode();
void waitOnBusy();
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr);
void setSyncWord(int sw);
void explicitHeaderMode();
void implicitHeaderMode();
uint8_t readRegister(uint16_t address);
void writeRegister(uint16_t address, uint8_t value);
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
static void onDio0Rise();
void handleLowDataRate();
void optimizeModemSensitivity();
private:
SPISettings _spiSettings;
SPIClass _spiModem;
int _ss;
int _sclk;
int _mosi;
int _miso;
int _reset;
int _dio0;
int _rxen;
int _txen;
int _busy;
int _modem;
uint32_t _frequency;
int _txp;
uint8_t _sf;
uint8_t _bw;
uint8_t _cr;
int _packetIndex;
int _implicitHeaderMode;
int _payloadLength;
int _crcMode;
int _fifo_tx_addr_ptr;
int _fifo_rx_addr_ptr;
uint8_t _packet[255];
bool _preinit_done;
int _rxPacketLength;
uint8_t _index;
bool _tcxo;
};
#endif

@ -13,8 +13,13 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Radio.h"
#include "Config.h"
// Included for sorting
#include <algorithm>
#include <iterator>
#if HAS_EEPROM
#include <EEPROM.h>
#elif PLATFORM == PLATFORM_NRF52
@ -28,17 +33,6 @@
#endif
#include <stddef.h>
#if MODEM == SX1262
#include "sx126x.h"
sx126x *LoRa = &sx126x_modem;
#elif MODEM == SX1276 || MODEM == SX1278
#include "sx127x.h"
sx127x *LoRa = &sx127x_modem;
#elif MODEM == SX1280
#include "sx128x.h"
sx128x *LoRa = &sx128x_modem;
#endif
#include "ROM.h"
#include "Framing.h"
#include "MD5.h"
@ -81,20 +75,9 @@ uint8_t eeprom_read(uint32_t mapped_addr);
#define ISR_VECT
#endif
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
#include <avr/wdt.h>
#include <util/atomic.h>
#endif
uint8_t boot_vector = 0x00;
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
uint8_t OPTIBOOT_MCUSR __attribute__ ((section(".noinit")));
void resetFlagsInit(void) __attribute__ ((naked)) __attribute__ ((used)) __attribute__ ((section (".init0")));
void resetFlagsInit(void) {
__asm__ __volatile__ ("sts %0, r2\n" : "=m" (OPTIBOOT_MCUSR) :);
}
#elif MCU_VARIANT == MCU_ESP32
#if MCU_VARIANT == MCU_ESP32
// TODO: Get ESP32 boot flags
#elif MCU_VARIANT == MCU_NRF52
// TODO: Get NRF52 boot flags
@ -138,12 +121,7 @@ uint8_t boot_vector = 0x00;
void boot_seq() { }
#endif
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
void led_rx_on() { digitalWrite(pin_led_rx, HIGH); }
void led_rx_off() { digitalWrite(pin_led_rx, LOW); }
void led_tx_on() { digitalWrite(pin_led_tx, HIGH); }
void led_tx_off() { digitalWrite(pin_led_tx, LOW); }
#elif MCU_VARIANT == MCU_ESP32
#if MCU_VARIANT == MCU_ESP32
#if HAS_NP == true
void led_rx_on() { npset(0, 0, 0xFF); }
void led_rx_off() { npset(0, 0, 0); }
@ -236,12 +214,7 @@ uint8_t boot_vector = 0x00;
#endif
void hard_reset(void) {
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
wdt_enable(WDTO_15MS);
while(true) {
led_tx_on(); led_rx_off();
}
#elif MCU_VARIANT == MCU_ESP32
#if MCU_VARIANT == MCU_ESP32
ESP.restart();
#elif MCU_VARIANT == MCU_NRF52
NVIC_SystemReset();
@ -332,20 +305,7 @@ void led_indicate_warning(int cycles) {
}
// LED Indication: Info
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
void led_indicate_info(int cycles) {
bool forever = (cycles == 0) ? true : false;
cycles = forever ? 1 : cycles;
while(cycles > 0) {
led_rx_off();
delay(100);
led_rx_on();
delay(100);
if (!forever) cycles--;
}
led_rx_off();
}
#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
#if HAS_NP == true
void led_indicate_info(int cycles) {
bool forever = (cycles == 0) ? true : false;
@ -403,12 +363,7 @@ void led_indicate_warning(int cycles) {
unsigned long led_standby_ticks = 0;
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
uint8_t led_standby_min = 1;
uint8_t led_standby_max = 40;
unsigned long led_standby_wait = 11000;
#elif MCU_VARIANT == MCU_ESP32
#if MCU_VARIANT == MCU_ESP32
#if HAS_NP == true
int led_standby_lng = 100;
@ -451,23 +406,7 @@ unsigned long led_standby_ticks = 0;
unsigned long led_standby_value = led_standby_min;
int8_t led_standby_direction = 0;
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
void led_indicate_standby() {
led_standby_ticks++;
if (led_standby_ticks > led_standby_wait) {
led_standby_ticks = 0;
if (led_standby_value <= led_standby_min) {
led_standby_direction = 1;
} else if (led_standby_value >= led_standby_max) {
led_standby_direction = -1;
}
led_standby_value += led_standby_direction;
analogWrite(pin_led_rx, led_standby_value);
led_tx_off();
}
}
#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
#if HAS_NP == true
void led_indicate_standby() {
led_standby_ticks++;
@ -560,22 +499,7 @@ int8_t led_standby_direction = 0;
#endif
#endif
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
void led_indicate_not_ready() {
led_standby_ticks++;
if (led_standby_ticks > led_standby_wait) {
led_standby_ticks = 0;
if (led_standby_value <= led_standby_min) {
led_standby_direction = 1;
} else if (led_standby_value >= led_standby_max) {
led_standby_direction = -1;
}
led_standby_value += led_standby_direction;
analogWrite(pin_led_tx, led_standby_value);
led_rx_off();
}
}
#elif MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
#if HAS_NP == true
void led_indicate_not_ready() {
led_standby_ticks++;
@ -636,6 +560,18 @@ int8_t led_standby_direction = 0;
#endif
#endif
bool interface_bitrate_cmp(RadioInterface* p, RadioInterface* q) {
long p_bitrate = p->getBitrate();
long q_bitrate = q->getBitrate();
return p_bitrate > q_bitrate;
}
// Sort interfaces in descending order according to bitrate.
void sort_interfaces() {
std::sort(std::begin(interface_obj_sorted), std::end(interface_obj_sorted), interface_bitrate_cmp);
}
void serial_write(uint8_t byte) {
#if HAS_BLUETOOTH || HAS_BLE == true
if (bt_state != BT_STATE_CONNECTED) {
@ -668,31 +604,33 @@ void kiss_indicate_error(uint8_t error_code) {
serial_write(FEND);
}
void kiss_indicate_radiostate() {
void kiss_indicate_radiostate(RadioInterface* radio) {
serial_write(FEND);
serial_write(CMD_RADIO_STATE);
serial_write(radio_online);
serial_write(radio->getRadioOnline());
serial_write(FEND);
}
void kiss_indicate_stat_rx() {
serial_write(FEND);
serial_write(CMD_STAT_RX);
escaped_serial_write(stat_rx>>24);
escaped_serial_write(stat_rx>>16);
escaped_serial_write(stat_rx>>8);
escaped_serial_write(stat_rx);
serial_write(FEND);
// todo, implement
//serial_write(FEND);
//serial_write(CMD_STAT_RX);
//escaped_serial_write(stat_rx>>24);
//escaped_serial_write(stat_rx>>16);
//escaped_serial_write(stat_rx>>8);
//escaped_serial_write(stat_rx);
//serial_write(FEND);
}
void kiss_indicate_stat_tx() {
serial_write(FEND);
serial_write(CMD_STAT_TX);
escaped_serial_write(stat_tx>>24);
escaped_serial_write(stat_tx>>16);
escaped_serial_write(stat_tx>>8);
escaped_serial_write(stat_tx);
serial_write(FEND);
// todo, implement
//serial_write(FEND);
//serial_write(CMD_STAT_TX);
//escaped_serial_write(stat_tx>>24);
//escaped_serial_write(stat_tx>>16);
//escaped_serial_write(stat_tx>>8);
//escaped_serial_write(stat_tx);
//serial_write(FEND);
}
void kiss_indicate_stat_rssi() {
@ -710,24 +648,24 @@ void kiss_indicate_stat_snr() {
serial_write(FEND);
}
void kiss_indicate_radio_lock() {
void kiss_indicate_radio_lock(RadioInterface* radio) {
serial_write(FEND);
serial_write(CMD_RADIO_LOCK);
serial_write(radio_locked);
serial_write(radio->getRadioLock());
serial_write(FEND);
}
void kiss_indicate_spreadingfactor() {
void kiss_indicate_spreadingfactor(RadioInterface* radio) {
serial_write(FEND);
serial_write(CMD_SF);
serial_write((uint8_t)lora_sf);
serial_write(radio->getSpreadingFactor());
serial_write(FEND);
}
void kiss_indicate_codingrate() {
void kiss_indicate_codingrate(RadioInterface* radio) {
serial_write(FEND);
serial_write(CMD_CR);
serial_write((uint8_t)lora_cr);
serial_write(radio->getCodingRate4());
serial_write(FEND);
}
@ -738,35 +676,47 @@ void kiss_indicate_implicit_length() {
serial_write(FEND);
}
void kiss_indicate_txpower() {
void kiss_indicate_txpower(RadioInterface* radio) {
uint8_t txp = radio->getTxPower();
serial_write(FEND);
serial_write(CMD_TXPOWER);
serial_write((uint8_t)lora_txp);
serial_write(txp);
serial_write(FEND);
}
void kiss_indicate_bandwidth() {
void kiss_indicate_bandwidth(RadioInterface* radio) {
uint32_t bw = radio->getSignalBandwidth();
serial_write(FEND);
serial_write(CMD_BANDWIDTH);
escaped_serial_write(lora_bw>>24);
escaped_serial_write(lora_bw>>16);
escaped_serial_write(lora_bw>>8);
escaped_serial_write(lora_bw);
escaped_serial_write(bw>>24);
escaped_serial_write(bw>>16);
escaped_serial_write(bw>>8);
escaped_serial_write(bw);
serial_write(FEND);
}
void kiss_indicate_frequency() {
void kiss_indicate_frequency(RadioInterface* radio) {
uint32_t freq = radio->getFrequency();
serial_write(FEND);
serial_write(CMD_FREQUENCY);
escaped_serial_write(lora_freq>>24);
escaped_serial_write(lora_freq>>16);
escaped_serial_write(lora_freq>>8);
escaped_serial_write(lora_freq);
escaped_serial_write(freq>>24);
escaped_serial_write(freq>>16);
escaped_serial_write(freq>>8);
escaped_serial_write(freq);
serial_write(FEND);
}
void kiss_indicate_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() {
uint16_t at = (uint16_t)(st_airtime_limit*100*100);
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,12 +733,11 @@ 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);
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);
@ -800,16 +749,14 @@ void kiss_indicate_channel_stats() {
escaped_serial_write(cll>>8);
escaped_serial_write(cll);
serial_write(FEND);
#endif
}
void kiss_indicate_phy_stats() {
#if MCU_VARIANT == MCU_ESP32
uint16_t lst = (uint16_t)(lora_symbol_time_ms*1000);
uint16_t lsr = (uint16_t)(lora_symbol_rate);
uint16_t prs = (uint16_t)(lora_preamble_symbols+4);
uint16_t prt = (uint16_t)((lora_preamble_symbols+4)*lora_symbol_time_ms);
uint16_t cst = (uint16_t)(csma_slot_ms);
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);
@ -823,7 +770,6 @@ void kiss_indicate_phy_stats() {
escaped_serial_write(cst>>8);
escaped_serial_write(cst);
serial_write(FEND);
#endif
}
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,73 +964,115 @@ void set_implicit_length(uint8_t len) {
}
}
int getTxPower() {
uint8_t txp = LoRa->getTxPower();
return (int)txp;
}
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);
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_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_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_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_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_FE) LoRa->setTxPower(lora_txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_FF) LoRa->setTxPower(lora_txp, PA_OUTPUT_RFO_PIN);
}
}
if (model == MODEL_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);
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();
}
if (model == MODEL_FE) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_FF) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
}
void setFrequency() {
if (radio_online) {
LoRa->setFrequency(lora_freq);
getFrequency();
uint8_t getRandom(RadioInterface* radio) {
if (radio->getRadioOnline()) {
return radio->random();
} else {
return 0x00;
}
}
uint8_t getRandom() {
if (radio_online) {
return LoRa->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;
}
}
@ -1228,9 +1179,7 @@ void eeprom_flush() {
#endif
void eeprom_update(int mapped_addr, uint8_t byte) {
#if MCU_VARIANT == MCU_1284P || MCU_VARIANT == MCU_2560
EEPROM.update(mapped_addr, byte);
#elif MCU_VARIANT == MCU_ESP32
#if MCU_VARIANT == MCU_ESP32
if (EEPROM.read(mapped_addr) != byte) {
EEPROM.write(mapped_addr, byte);
EEPROM.commit();
@ -1297,9 +1246,7 @@ bool eeprom_product_valid() {
uint8_t rval = eeprom_read(eeprom_addr(ADDR_PRODUCT));
#endif
#if PLATFORM == PLATFORM_AVR
if (rval == PRODUCT_RNODE || rval == PRODUCT_HMBRW) {
#elif PLATFORM == PLATFORM_ESP32
#if PLATFORM == PLATFORM_ESP32
if (rval == PRODUCT_RNODE || rval == BOARD_RNODE_NG_20 || rval == BOARD_RNODE_NG_21 || rval == PRODUCT_HMBRW || rval == PRODUCT_TBEAM || rval == PRODUCT_T32_10 || rval == PRODUCT_T32_20 || rval == PRODUCT_T32_21 || rval == PRODUCT_H32_V2 || rval == PRODUCT_H32_V3) {
#elif PLATFORM == PLATFORM_NRF52
if (rval == PRODUCT_RAK4631 || rval == PRODUCT_HMBRW) {
@ -1434,39 +1381,51 @@ bool eeprom_have_conf() {
}
}
void eeprom_conf_load() {
void eeprom_conf_load(RadioInterface* radio) {
if (eeprom_have_conf()) {
if (!(radio->getRadioOnline())) {
#if HAS_EEPROM
lora_sf = EEPROM.read(eeprom_addr(ADDR_CONF_SF));
lora_cr = EEPROM.read(eeprom_addr(ADDR_CONF_CR));
lora_txp = EEPROM.read(eeprom_addr(ADDR_CONF_TXP));
lora_freq = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
lora_bw = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x03);
uint8_t sf = EEPROM.read(eeprom_addr(ADDR_CONF_SF));
uint8_t cr = EEPROM.read(eeprom_addr(ADDR_CONF_CR));
uint8_t txp = EEPROM.read(eeprom_addr(ADDR_CONF_TXP));
uint32_t freq = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
uint32_t bw = (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)EEPROM.read(eeprom_addr(ADDR_CONF_BW)+0x03);
#elif MCU_VARIANT == MCU_NRF52
lora_sf = eeprom_read(eeprom_addr(ADDR_CONF_SF));
lora_cr = eeprom_read(eeprom_addr(ADDR_CONF_CR));
lora_txp = eeprom_read(eeprom_addr(ADDR_CONF_TXP));
lora_freq = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
lora_bw = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x03);
uint8_t sf = eeprom_read(eeprom_addr(ADDR_CONF_SF));
uint8_t cr = eeprom_read(eeprom_addr(ADDR_CONF_CR));
uint8_t txp = eeprom_read(eeprom_addr(ADDR_CONF_TXP));
uint32_t freq = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_FREQ)+0x03);
uint32_t bw = (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x00) << 24 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x01) << 16 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x02) << 8 | (uint32_t)eeprom_read(eeprom_addr(ADDR_CONF_BW)+0x03);
#endif
radio->setSpreadingFactor(sf);
radio->setCodingRate4(cr);
setTXPower(radio, txp);
radio->setFrequency(freq);
radio->setSignalBandwidth(bw);
radio->updateBitrate();
}
}
}
void eeprom_conf_save() {
if (hw_ready && radio_online) {
eeprom_update(eeprom_addr(ADDR_CONF_SF), lora_sf);
eeprom_update(eeprom_addr(ADDR_CONF_CR), lora_cr);
eeprom_update(eeprom_addr(ADDR_CONF_TXP), lora_txp);
void eeprom_conf_save(RadioInterface* radio) {
if (hw_ready && radio->getRadioOnline()) {
eeprom_update(eeprom_addr(ADDR_CONF_SF), radio->getSpreadingFactor());
eeprom_update(eeprom_addr(ADDR_CONF_CR), radio->getCodingRate4());
eeprom_update(eeprom_addr(ADDR_CONF_TXP), radio->getTxPower());
uint32_t bw = radio->getSignalBandwidth();
eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x00, lora_bw>>24);
eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x01, lora_bw>>16);
eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x02, lora_bw>>8);
eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x03, lora_bw);
eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x00, bw>>24);
eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x01, bw>>16);
eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x02, bw>>8);
eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x03, bw);
eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x00, lora_freq>>24);
eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x01, lora_freq>>16);
eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x02, lora_freq>>8);
eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x03, lora_freq);
uint32_t freq = radio->getFrequency();
eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x00, freq>>24);
eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x01, freq>>16);
eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x02, freq>>8);
eeprom_update(eeprom_addr(ADDR_CONF_FREQ)+0x03, freq);
eeprom_update(eeprom_addr(ADDR_CONF_OK), CONF_OK_BYTE);
led_indicate_info(10);
@ -1484,18 +1443,6 @@ void unlock_rom() {
eeprom_erase();
}
void init_channel_stats() {
#if MCU_VARIANT == MCU_ESP32
for (uint16_t ai = 0; ai < DCD_SAMPLES; ai++) { util_samples[ai] = false; }
for (uint16_t ai = 0; ai < AIRTIME_BINS; ai++) { airtime_bins[ai] = 0; }
for (uint16_t ai = 0; ai < AIRTIME_BINS; ai++) { longterm_bins[ai] = 0.0; }
local_channel_util = 0.0;
total_channel_util = 0.0;
airtime = 0.0;
longterm_airtime = 0.0;
#endif
}
typedef struct FIFOBuffer
{
unsigned char *begin;

@ -1,985 +0,0 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
// Obviously still under the MIT license.
#include "Boards.h"
#if MODEM == SX1262
#include "sx126x.h"
#if MCU_VARIANT == MCU_ESP32
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
#include "soc/rtc_wdt.h"
#endif
#define ISR_VECT IRAM_ATTR
#else
#define ISR_VECT
#endif
#define OP_RF_FREQ_6X 0x86
#define OP_SLEEP_6X 0x84
#define OP_STANDBY_6X 0x80
#define OP_TX_6X 0x83
#define OP_RX_6X 0x82
#define OP_PA_CONFIG_6X 0x95
#define OP_SET_IRQ_FLAGS_6X 0x08 // also provides info such as
// preamble detection, etc for
// knowing when it's safe to switch
// antenna modes
#define OP_CLEAR_IRQ_STATUS_6X 0x02
#define OP_GET_IRQ_STATUS_6X 0x12
#define OP_RX_BUFFER_STATUS_6X 0x13
#define OP_PACKET_STATUS_6X 0x14 // get snr & rssi of last packet
#define OP_CURRENT_RSSI_6X 0x15
#define OP_MODULATION_PARAMS_6X 0x8B // bw, sf, cr, etc.
#define OP_PACKET_PARAMS_6X 0x8C // crc, preamble, payload length, etc.
#define OP_STATUS_6X 0xC0
#define OP_TX_PARAMS_6X 0x8E // set dbm, etc
#define OP_PACKET_TYPE_6X 0x8A
#define OP_BUFFER_BASE_ADDR_6X 0x8F
#define OP_READ_REGISTER_6X 0x1D
#define OP_WRITE_REGISTER_6X 0x0D
#define OP_DIO3_TCXO_CTRL_6X 0x97
#define OP_DIO2_RF_CTRL_6X 0x9D
#define OP_CAD_PARAMS 0x88
#define OP_CALIBRATE_6X 0x89
#define OP_RX_TX_FALLBACK_MODE_6X 0x93
#define OP_REGULATOR_MODE_6X 0x96
#define OP_CALIBRATE_IMAGE_6X 0x98
#define MASK_CALIBRATE_ALL 0x7f
#define IRQ_TX_DONE_MASK_6X 0x01
#define IRQ_RX_DONE_MASK_6X 0x02
#define IRQ_HEADER_DET_MASK_6X 0x10
#define IRQ_PREAMBLE_DET_MASK_6X 0x04
#define IRQ_PAYLOAD_CRC_ERROR_MASK_6X 0x40
#define IRQ_ALL_MASK_6X 0b0100001111111111
#define MODE_LONG_RANGE_MODE_6X 0x01
#define OP_FIFO_WRITE_6X 0x0E
#define OP_FIFO_READ_6X 0x1E
#define REG_OCP_6X 0x08E7
#define REG_LNA_6X 0x08AC // no agc in sx1262
#define REG_SYNC_WORD_MSB_6X 0x0740
#define REG_SYNC_WORD_LSB_6X 0x0741
#define REG_PAYLOAD_LENGTH_6X 0x0702 // https://github.com/beegee-tokyo/SX126x-Arduino/blob/master/src/radio/sx126x/sx126x.h#L98
#define REG_RANDOM_GEN_6X 0x0819
#define MODE_TCXO_3_3V_6X 0x07
#define MODE_TCXO_3_0V_6X 0x06
#define MODE_TCXO_2_7V_6X 0x06
#define MODE_TCXO_2_4V_6X 0x06
#define MODE_TCXO_2_2V_6X 0x03
#define MODE_TCXO_1_8V_6X 0x02
#define MODE_TCXO_1_7V_6X 0x01
#define MODE_TCXO_1_6V_6X 0x00
#define MODE_STDBY_RC_6X 0x00
#define MODE_STDBY_XOSC_6X 0x01
#define MODE_FALLBACK_STDBY_RC_6X 0x20
#define MODE_IMPLICIT_HEADER 0x01
#define MODE_EXPLICIT_HEADER 0x00
#define SYNC_WORD_6X 0x1424
#define XTAL_FREQ_6X (double)32000000
#define FREQ_DIV_6X (double)pow(2.0, 25.0)
#define FREQ_STEP_6X (double)(XTAL_FREQ_6X / FREQ_DIV_6X)
#if defined(NRF52840_XXAA)
extern SPIClass spiModem;
#define SPI spiModem
#endif
extern SPIClass SPI;
#define MAX_PKT_LENGTH 255
sx126x::sx126x() :
_spiSettings(8E6, MSBFIRST, SPI_MODE0),
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _busy(LORA_DEFAULT_BUSY_PIN), _rxen(LORA_DEFAULT_RXEN_PIN),
_frequency(0),
_txp(0),
_sf(0x07),
_bw(0x04),
_cr(0x01),
_ldro(0x00),
_packetIndex(0),
_preambleLength(18),
_implicitHeaderMode(0),
_payloadLength(255),
_crcMode(1),
_fifo_tx_addr_ptr(0),
_fifo_rx_addr_ptr(0),
_packet({0}),
_preinit_done(false),
_onReceive(NULL)
{
// overide Stream timeout value
setTimeout(0);
}
bool sx126x::preInit() {
pinMode(_ss, OUTPUT);
digitalWrite(_ss, HIGH);
#if BOARD_MODEL == BOARD_RNODE_NG_22 || BOARD_MODEL == BOARD_HELTEC32_V3
SPI.begin(pin_sclk, pin_miso, pin_mosi, pin_cs);
#else
SPI.begin();
#endif
// check version (retry for up to 2 seconds)
// TODO: Actually read version registers, not syncwords
long start = millis();
uint8_t syncmsb;
uint8_t synclsb;
while (((millis() - start) < 2000) && (millis() >= start)) {
syncmsb = readRegister(REG_SYNC_WORD_MSB_6X);
synclsb = readRegister(REG_SYNC_WORD_LSB_6X);
if ( uint16_t(syncmsb << 8 | synclsb) == 0x1424 || uint16_t(syncmsb << 8 | synclsb) == 0x4434) {
break;
}
delay(100);
}
if ( uint16_t(syncmsb << 8 | synclsb) != 0x1424 && uint16_t(syncmsb << 8 | synclsb) != 0x4434) {
return false;
}
_preinit_done = true;
return true;
}
uint8_t ISR_VECT sx126x::readRegister(uint16_t address)
{
return singleTransfer(OP_READ_REGISTER_6X, address, 0x00);
}
void sx126x::writeRegister(uint16_t address, uint8_t value)
{
singleTransfer(OP_WRITE_REGISTER_6X, address, value);
}
uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value)
{
waitOnBusy();
uint8_t response;
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
SPI.transfer((address & 0xFF00) >> 8);
SPI.transfer(address & 0x00FF);
if (opcode == OP_READ_REGISTER_6X) {
SPI.transfer(0x00);
}
response = SPI.transfer(value);
SPI.endTransaction();
digitalWrite(_ss, HIGH);
return response;
}
void sx126x::rxAntEnable()
{
if (_rxen != -1) {
digitalWrite(_rxen, HIGH);
}
}
void sx126x::loraMode() {
// enable lora mode on the SX1262 chip
uint8_t mode = MODE_LONG_RANGE_MODE_6X;
executeOpcode(OP_PACKET_TYPE_6X, &mode, 1);
}
void sx126x::waitOnBusy() {
unsigned long time = millis();
while (digitalRead(_busy) == HIGH)
{
if (millis() >= (time + 100)) {
break;
}
// do nothing
}
}
void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
{
waitOnBusy();
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
for (int i = 0; i < size; i++)
{
SPI.transfer(buffer[i]);
}
SPI.endTransaction();
digitalWrite(_ss, HIGH);
}
void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
{
waitOnBusy();
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
SPI.transfer(0x00);
for (int i = 0; i < size; i++)
{
buffer[i] = SPI.transfer(0x00);
}
SPI.endTransaction();
digitalWrite(_ss, HIGH);
}
void sx126x::writeBuffer(const uint8_t* buffer, size_t size)
{
waitOnBusy();
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(OP_FIFO_WRITE_6X);
SPI.transfer(_fifo_tx_addr_ptr);
for (int i = 0; i < size; i++)
{
SPI.transfer(buffer[i]);
_fifo_tx_addr_ptr++;
}
SPI.endTransaction();
digitalWrite(_ss, HIGH);
}
void sx126x::readBuffer(uint8_t* buffer, size_t size)
{
waitOnBusy();
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(OP_FIFO_READ_6X);
SPI.transfer(_fifo_rx_addr_ptr);
SPI.transfer(0x00);
for (int i = 0; i < size; i++)
{
buffer[i] = SPI.transfer(0x00);
}
SPI.endTransaction();
digitalWrite(_ss, HIGH);
}
void sx126x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro) {
// because there is no access to these registers on the sx1262, we have
// to set all these parameters at once or not at all.
uint8_t buf[8];
buf[0] = sf;
buf[1] = bw;
buf[2] = cr;
// low data rate toggle
buf[3] = ldro;
// unused params in LoRa mode
buf[4] = 0x00;
buf[5] = 0x00;
buf[6] = 0x00;
buf[7] = 0x00;
executeOpcode(OP_MODULATION_PARAMS_6X, buf, 8);
}
void sx126x::setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
// because there is no access to these registers on the sx1262, we have
// to set all these parameters at once or not at all.
uint8_t buf[9];
buf[0] = uint8_t((preamble & 0xFF00) >> 8);
buf[1] = uint8_t((preamble & 0x00FF));
buf[2] = headermode;
buf[3] = length;
buf[4] = crc;
// standard IQ setting (no inversion)
buf[5] = 0x00;
// unused params
buf[6] = 0x00;
buf[7] = 0x00;
buf[8] = 0x00;
executeOpcode(OP_PACKET_PARAMS_6X, buf, 9);
}
void sx126x::reset(void) {
if (_reset != -1) {
pinMode(_reset, OUTPUT);
// perform reset
digitalWrite(_reset, LOW);
delay(10);
digitalWrite(_reset, HIGH);
delay(10);
}
}
void sx126x::calibrate(void) {
// Put in STDBY_RC mode before calibration
uint8_t mode_byte = MODE_STDBY_RC_6X;
executeOpcode(OP_STANDBY_6X, &mode_byte, 1);
// calibrate RC64k, RC13M, PLL, ADC and image
uint8_t calibrate = MASK_CALIBRATE_ALL;
executeOpcode(OP_CALIBRATE_6X, &calibrate, 1);
delay(5);
waitOnBusy();
}
void sx126x::calibrate_image(long frequency) {
uint8_t image_freq[2] = {0};
if (frequency >= 430E6 && frequency <= 440E6) {
image_freq[0] = 0x6B;
image_freq[1] = 0x6F;
}
else if (frequency >= 470E6 && frequency <= 510E6) {
image_freq[0] = 0x75;
image_freq[1] = 0x81;
}
else if (frequency >= 779E6 && frequency <= 787E6) {
image_freq[0] = 0xC1;
image_freq[1] = 0xC5;
}
else if (frequency >= 863E6 && frequency <= 870E6) {
image_freq[0] = 0xD7;
image_freq[1] = 0xDB;
}
else if (frequency >= 902E6 && frequency <= 928E6) {
image_freq[0] = 0xE1;
image_freq[1] = 0xE9;
}
executeOpcode(OP_CALIBRATE_IMAGE_6X, image_freq, 2);
waitOnBusy();
}
int sx126x::begin(long frequency)
{
reset();
if (_busy != -1) {
pinMode(_busy, INPUT);
}
if (!_preinit_done) {
if (!preInit()) {
return false;
}
}
if (_rxen != -1) {
pinMode(_rxen, OUTPUT);
}
calibrate();
calibrate_image(frequency);
enableTCXO();
loraMode();
standby();
// Set sync word
setSyncWord(SYNC_WORD_6X);
#if DIO2_AS_RF_SWITCH
// enable dio2 rf switch
uint8_t byte = 0x01;
executeOpcode(OP_DIO2_RF_CTRL_6X, &byte, 1);
#endif
rxAntEnable();
setFrequency(frequency);
// set output power to 2 dBm
setTxPower(2);
enableCrc();
// set LNA boost
writeRegister(REG_LNA_6X, 0x96);
// set base addresses
uint8_t basebuf[2] = {0};
executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2);
setModulationParams(_sf, _bw, _cr, _ldro);
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
return 1;
}
void sx126x::end()
{
// put in sleep mode
sleep();
// stop SPI
SPI.end();
_preinit_done = false;
}
int sx126x::beginPacket(int implicitHeader)
{
standby();
if (implicitHeader) {
implicitHeaderMode();
} else {
explicitHeaderMode();
}
_payloadLength = 0;
_fifo_tx_addr_ptr = 0;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
return 1;
}
int sx126x::endPacket()
{
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
// put in single TX mode
uint8_t timeout[3] = {0};
executeOpcode(OP_TX_6X, timeout, 3);
uint8_t buf[2];
buf[0] = 0x00;
buf[1] = 0x00;
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
// wait for TX done
while ((buf[1] & IRQ_TX_DONE_MASK_6X) == 0) {
buf[0] = 0x00;
buf[1] = 0x00;
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
yield();
}
// clear IRQ's
uint8_t mask[2];
mask[0] = 0x00;
mask[1] = IRQ_TX_DONE_MASK_6X;
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, mask, 2);
return 1;
}
uint8_t sx126x::modemStatus() {
// imitate the register status from the sx1276 / 78
uint8_t buf[2] = {0};
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
uint8_t clearbuf[2] = {0};
uint8_t byte = 0x00;
if ((buf[1] & IRQ_PREAMBLE_DET_MASK_6X) != 0) {
byte = byte | 0x01 | 0x04;
// clear register after reading
clearbuf[1] = IRQ_PREAMBLE_DET_MASK_6X;
}
if ((buf[1] & IRQ_HEADER_DET_MASK_6X) != 0) {
byte = byte | 0x02 | 0x04;
}
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, clearbuf, 2);
return byte;
}
uint8_t sx126x::currentRssiRaw() {
uint8_t byte = 0;
executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
return byte;
}
int ISR_VECT sx126x::currentRssi() {
uint8_t byte = 0;
executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
int rssi = -(int(byte)) / 2;
return rssi;
}
uint8_t sx126x::packetRssiRaw() {
uint8_t buf[3] = {0};
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
return buf[2];
}
int ISR_VECT sx126x::packetRssi() {
// may need more calculations here
uint8_t buf[3] = {0};
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
int pkt_rssi = -buf[0] / 2;
return pkt_rssi;
}
uint8_t ISR_VECT sx126x::packetSnrRaw() {
uint8_t buf[3] = {0};
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
return buf[1];
}
float ISR_VECT sx126x::packetSnr() {
uint8_t buf[3] = {0};
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
return float(buf[1]) * 0.25;
}
long sx126x::packetFrequencyError()
{
// todo: implement this, no idea how to check it on the sx1262
const float fError = 0.0;
return static_cast<long>(fError);
}
size_t sx126x::write(uint8_t byte)
{
return write(&byte, sizeof(byte));
}
size_t sx126x::write(const uint8_t *buffer, size_t size)
{
if ((_payloadLength + size) > MAX_PKT_LENGTH) {
size = MAX_PKT_LENGTH - _payloadLength;
}
// write data
writeBuffer(buffer, size);
_payloadLength = _payloadLength + size;
return size;
}
int ISR_VECT sx126x::available()
{
uint8_t buf[2] = {0};
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, buf, 2);
return buf[0] - _packetIndex;
}
int ISR_VECT sx126x::read()
{
if (!available()) {
return -1;
}
// if received new packet
if (_packetIndex == 0) {
uint8_t rxbuf[2] = {0};
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
int size = rxbuf[0];
_fifo_rx_addr_ptr = rxbuf[1];
readBuffer(_packet, size);
}
uint8_t byte = _packet[_packetIndex];
_packetIndex++;
return byte;
}
int sx126x::peek()
{
if (!available()) {
return -1;
}
// if received new packet
if (_packetIndex == 0) {
uint8_t rxbuf[2] = {0};
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
int size = rxbuf[0];
_fifo_rx_addr_ptr = rxbuf[1];
readBuffer(_packet, size);
}
uint8_t b = _packet[_packetIndex];
return b;
}
void sx126x::flush()
{
}
void sx126x::onReceive(void(*callback)(int))
{
_onReceive = callback;
if (callback) {
pinMode(_dio0, INPUT);
// set preamble and header detection irqs, plus dio0 mask
uint8_t buf[8];
// set irq masks, enable all
buf[0] = 0xFF;
buf[1] = 0xFF;
// set dio0 masks
buf[2] = 0x00;
buf[3] = IRQ_RX_DONE_MASK_6X;
// set dio1 masks
buf[4] = 0x00;
buf[5] = 0x00;
// set dio2 masks
buf[6] = 0x00;
buf[7] = 0x00;
executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8);
#ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
#endif
attachInterrupt(digitalPinToInterrupt(_dio0), sx126x::onDio0Rise, RISING);
} else {
detachInterrupt(digitalPinToInterrupt(_dio0));
#ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
#endif
}
}
void sx126x::receive(int size)
{
if (size > 0) {
implicitHeaderMode();
// tell radio payload length
_payloadLength = size;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
} else {
explicitHeaderMode();
}
if (_rxen != -1) {
rxAntEnable();
}
uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
executeOpcode(OP_RX_6X, mode, 3);
}
void sx126x::standby()
{
// STDBY_XOSC
uint8_t byte = MODE_STDBY_XOSC_6X;
// STDBY_RC
// uint8_t byte = MODE_STDBY_RC_6X;
executeOpcode(OP_STANDBY_6X, &byte, 1);
}
void sx126x::sleep()
{
uint8_t byte = 0x00;
executeOpcode(OP_SLEEP_6X, &byte, 1);
}
void sx126x::enableTCXO() {
#if HAS_TCXO
#if BOARD_MODEL == BOARD_RAK4631 || BOARD_MODEL == BOARD_HELTEC32_V3
uint8_t buf[4] = {MODE_TCXO_3_3V_6X, 0x00, 0x00, 0xFF};
#elif BOARD_MODEL == BOARD_TBEAM
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
#elif BOARD_MODEL == BOARD_RNODE_NG_22
uint8_t buf[4] = {MODE_TCXO_1_8V_6X, 0x00, 0x00, 0xFF};
#endif
executeOpcode(OP_DIO3_TCXO_CTRL_6X, buf, 4);
#endif
}
// TODO: Once enabled, SX1262 needs a complete reset to disable TCXO
void sx126x::disableTCXO() { }
void sx126x::setTxPower(int level, int outputPin) {
// currently no low power mode for SX1262 implemented, assuming PA boost
// WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2
// RegTxClampConfig = @address 0x08D8
writeRegister(0x08D8, readRegister(0x08D8) | (0x0F << 1));
uint8_t pa_buf[4];
pa_buf[0] = 0x04; // PADutyCycle needs to be 0x04 to achieve 22dBm output, but can be lowered for better efficiency at lower outputs
pa_buf[1] = 0x07; // HPMax at 0x07 is maximum supported for SX1262
pa_buf[2] = 0x00; // DeviceSel 0x00 for SX1262 (0x01 for SX1261)
pa_buf[3] = 0x01; // PALut always 0x01 (reserved according to datasheet)
executeOpcode(OP_PA_CONFIG_6X, pa_buf, 4); // set pa_config for high power
if (level > 22) { level = 22; }
else if (level < -9) { level = -9; }
writeRegister(REG_OCP_6X, 0x38); // 160mA limit, overcurrent protection
uint8_t tx_buf[2];
tx_buf[0] = level;
tx_buf[1] = 0x02; // PA ramping time - 40 microseconds
executeOpcode(OP_TX_PARAMS_6X, tx_buf, 2);
_txp = level;
}
uint8_t sx126x::getTxPower() {
return _txp;
}
void sx126x::setFrequency(long frequency) {
_frequency = frequency;
uint8_t buf[4];
uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_6X);
buf[0] = ((freq >> 24) & 0xFF);
buf[1] = ((freq >> 16) & 0xFF);
buf[2] = ((freq >> 8) & 0xFF);
buf[3] = (freq & 0xFF);
executeOpcode(OP_RF_FREQ_6X, buf, 4);
}
uint32_t sx126x::getFrequency() {
// we can't read the frequency on the sx1262 / 80
uint32_t frequency = _frequency;
return frequency;
}
void sx126x::setSpreadingFactor(int sf)
{
if (sf < 5) {
sf = 5;
} else if (sf > 12) {
sf = 12;
}
_sf = sf;
handleLowDataRate();
setModulationParams(sf, _bw, _cr, _ldro);
}
long sx126x::getSignalBandwidth()
{
int bw = _bw;
switch (bw) {
case 0x00: return 7.8E3;
case 0x01: return 15.6E3;
case 0x02: return 31.25E3;
case 0x03: return 62.5E3;
case 0x04: return 125E3;
case 0x05: return 250E3;
case 0x06: return 500E3;
case 0x08: return 10.4E3;
case 0x09: return 20.8E3;
case 0x0A: return 41.7E3;
}
return 0;
}
void sx126x::handleLowDataRate(){
if ( long( (1<<_sf) / (getSignalBandwidth()/1000)) > 16) {
_ldro = 0x01;
} else {
_ldro = 0x00;
}
}
void sx126x::optimizeModemSensitivity(){
// todo: check if there's anything the sx1262 can do here
}
void sx126x::setSignalBandwidth(long sbw)
{
if (sbw <= 7.8E3) {
_bw = 0x00;
} else if (sbw <= 10.4E3) {
_bw = 0x08;
} else if (sbw <= 15.6E3) {
_bw = 0x01;
} else if (sbw <= 20.8E3) {
_bw = 0x09;
} else if (sbw <= 31.25E3) {
_bw = 0x02;
} else if (sbw <= 41.7E3) {
_bw = 0x0A;
} else if (sbw <= 62.5E3) {
_bw = 0x03;
} else if (sbw <= 125E3) {
_bw = 0x04;
} else if (sbw <= 250E3) {
_bw = 0x05;
} else /*if (sbw <= 250E3)*/ {
_bw = 0x06;
}
handleLowDataRate();
setModulationParams(_sf, _bw, _cr, _ldro);
optimizeModemSensitivity();
}
void sx126x::setCodingRate4(int denominator)
{
if (denominator < 5) {
denominator = 5;
} else if (denominator > 8) {
denominator = 8;
}
int cr = denominator - 4;
_cr = cr;
setModulationParams(_sf, _bw, cr, _ldro);
}
void sx126x::setPreambleLength(long length)
{
_preambleLength = length;
setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
}
void sx126x::setSyncWord(uint16_t sw)
{
// TODO: Fix
// writeRegister(REG_SYNC_WORD_MSB_6X, (sw & 0xFF00) >> 8);
// writeRegister(REG_SYNC_WORD_LSB_6X, sw & 0x00FF);
writeRegister(REG_SYNC_WORD_MSB_6X, 0x14);
writeRegister(REG_SYNC_WORD_LSB_6X, 0x24);
}
void sx126x::enableCrc()
{
_crcMode = 1;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
}
void sx126x::disableCrc()
{
_crcMode = 0;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
}
byte sx126x::random()
{
return readRegister(REG_RANDOM_GEN_6X);
}
void sx126x::setPins(int ss, int reset, int dio0, int busy, int rxen)
{
_ss = ss;
_reset = reset;
_dio0 = dio0;
_busy = busy;
_rxen = rxen;
}
void sx126x::setSPIFrequency(uint32_t frequency)
{
_spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
}
void sx126x::dumpRegisters(Stream& out)
{
for (int i = 0; i < 128; i++) {
out.print("0x");
out.print(i, HEX);
out.print(": 0x");
out.println(readRegister(i), HEX);
}
}
void sx126x::explicitHeaderMode()
{
_implicitHeaderMode = 0;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
}
void sx126x::implicitHeaderMode()
{
_implicitHeaderMode = 1;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
}
void ISR_VECT sx126x::handleDio0Rise()
{
uint8_t buf[2];
buf[0] = 0x00;
buf[1] = 0x00;
executeOpcodeRead(OP_GET_IRQ_STATUS_6X, buf, 2);
executeOpcode(OP_CLEAR_IRQ_STATUS_6X, buf, 2);
if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_6X) == 0) {
// received a packet
_packetIndex = 0;
// read packet length
uint8_t rxbuf[2] = {0};
executeOpcodeRead(OP_RX_BUFFER_STATUS_6X, rxbuf, 2);
int packetLength = rxbuf[0];
if (_onReceive) {
_onReceive(packetLength);
}
}
// else {
// Serial.println("CRCE");
// Serial.println(buf[0]);
// Serial.println(buf[1]);
// }
}
void ISR_VECT sx126x::onDio0Rise()
{
sx126x_modem.handleDio0Rise();
}
sx126x sx126x_modem;
#endif

@ -1,147 +0,0 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2023 by Mark Qvist
// Obviously still under the MIT license.
#ifndef SX126X_H
#define SX126X_H
#include <Arduino.h>
#include <SPI.h>
#include "Interfaces.h"
#define LORA_DEFAULT_SS_PIN 10
#define LORA_DEFAULT_RESET_PIN 9
#define LORA_DEFAULT_DIO0_PIN 2
#define LORA_DEFAULT_RXEN_PIN -1
#define LORA_DEFAULT_TXEN_PIN -1
#define LORA_DEFAULT_BUSY_PIN 11
#define PA_OUTPUT_RFO_PIN 0
#define PA_OUTPUT_PA_BOOST_PIN 1
#define RSSI_OFFSET 157
class sx126x : public Stream {
public:
sx126x();
int begin(long frequency);
void end();
int beginPacket(int implicitHeader = false);
int endPacket();
int parsePacket(int size = 0);
int packetRssi();
int currentRssi();
uint8_t packetRssiRaw();
uint8_t currentRssiRaw();
uint8_t packetSnrRaw();
float packetSnr();
long packetFrequencyError();
// from Print
virtual size_t write(uint8_t byte);
virtual size_t write(const uint8_t *buffer, size_t size);
// from Stream
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
void onReceive(void(*callback)(int));
void receive(int size = 0);
void standby();
void sleep();
bool preInit();
uint8_t getTxPower();
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
uint32_t getFrequency();
void setFrequency(long frequency);
void setSpreadingFactor(int sf);
long getSignalBandwidth();
void setSignalBandwidth(long sbw);
void setCodingRate4(int denominator);
void setPreambleLength(long length);
void setSyncWord(uint16_t sw);
uint8_t modemStatus();
void enableCrc();
void disableCrc();
void enableTCXO();
void disableTCXO();
void rxAntEnable();
void loraMode();
void waitOnBusy();
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
void writeBuffer(const uint8_t* buffer, size_t size);
void readBuffer(uint8_t* buffer, size_t size);
void setPacketParams(long preamble, uint8_t headermode, uint8_t length, uint8_t crc);
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro);
// deprecated
void crc() { enableCrc(); }
void noCrc() { disableCrc(); }
byte random();
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN);
void setSPIFrequency(uint32_t frequency);
void dumpRegisters(Stream& out);
private:
void explicitHeaderMode();
void implicitHeaderMode();
void handleDio0Rise();
uint8_t readRegister(uint16_t address);
void writeRegister(uint16_t address, uint8_t value);
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
static void onDio0Rise();
void handleLowDataRate();
void optimizeModemSensitivity();
void reset(void);
void calibrate(void);
void calibrate_image(long frequency);
private:
SPISettings _spiSettings;
int _ss;
int _reset;
int _dio0;
int _rxen;
int _busy;
long _frequency;
int _txp;
uint8_t _sf;
uint8_t _bw;
uint8_t _cr;
uint8_t _ldro;
int _packetIndex;
int _preambleLength;
int _implicitHeaderMode;
int _payloadLength;
int _crcMode;
int _fifo_tx_addr_ptr;
int _fifo_rx_addr_ptr;
uint8_t _packet[255];
bool _preinit_done;
void (*_onReceive)(int);
bool _tcxo;
};
extern sx126x sx126x_modem;
#endif

@ -1,498 +0,0 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2023 by Mark Qvist
// Obviously still under the MIT license.
#include "Boards.h"
#if MODEM == SX1276
#include "sx127x.h"
#if MCU_VARIANT == MCU_ESP32
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
#include "soc/rtc_wdt.h"
#endif
#define ISR_VECT IRAM_ATTR
#else
#define ISR_VECT
#endif
// Registers
#define REG_FIFO_7X 0x00
#define REG_OP_MODE_7X 0x01
#define REG_FRF_MSB_7X 0x06
#define REG_FRF_MID_7X 0x07
#define REG_FRF_LSB_7X 0x08
#define REG_PA_CONFIG_7X 0x09
#define REG_OCP_7X 0x0b
#define REG_LNA_7X 0x0c
#define REG_FIFO_ADDR_PTR_7X 0x0d
#define REG_FIFO_TX_BASE_ADDR_7X 0x0e
#define REG_FIFO_RX_BASE_ADDR_7X 0x0f
#define REG_FIFO_RX_CURRENT_ADDR_7X 0x10
#define REG_IRQ_FLAGS_7X 0x12
#define REG_RX_NB_BYTES_7X 0x13
#define REG_MODEM_STAT_7X 0x18
#define REG_PKT_SNR_VALUE_7X 0x19
#define REG_PKT_RSSI_VALUE_7X 0x1a
#define REG_RSSI_VALUE_7X 0x1b
#define REG_MODEM_CONFIG_1_7X 0x1d
#define REG_MODEM_CONFIG_2_7X 0x1e
#define REG_PREAMBLE_MSB_7X 0x20
#define REG_PREAMBLE_LSB_7X 0x21
#define REG_PAYLOAD_LENGTH_7X 0x22
#define REG_MODEM_CONFIG_3_7X 0x26
#define REG_FREQ_ERROR_MSB_7X 0x28
#define REG_FREQ_ERROR_MID_7X 0x29
#define REG_FREQ_ERROR_LSB_7X 0x2a
#define REG_RSSI_WIDEBAND_7X 0x2c
#define REG_DETECTION_OPTIMIZE_7X 0x31
#define REG_HIGH_BW_OPTIMIZE_1_7X 0x36
#define REG_DETECTION_THRESHOLD_7X 0x37
#define REG_SYNC_WORD_7X 0x39
#define REG_HIGH_BW_OPTIMIZE_2_7X 0x3a
#define REG_DIO_MAPPING_1_7X 0x40
#define REG_VERSION_7X 0x42
#define REG_TCXO_7X 0x4b
#define REG_PA_DAC_7X 0x4d
// Modes
#define MODE_LONG_RANGE_MODE_7X 0x80
#define MODE_SLEEP_7X 0x00
#define MODE_STDBY_7X 0x01
#define MODE_TX_7X 0x03
#define MODE_RX_CONTINUOUS_7X 0x05
#define MODE_RX_SINGLE_7X 0x06
// PA config
#define PA_BOOST_7X 0x80
// IRQ masks
#define IRQ_TX_DONE_MASK_7X 0x08
#define IRQ_RX_DONE_MASK_7X 0x40
#define IRQ_PAYLOAD_CRC_ERROR_MASK_7X 0x20
#define SYNC_WORD_7X 0x12
#define MAX_PKT_LENGTH 255
extern SPIClass SPI;
sx127x::sx127x() :
_spiSettings(8E6, MSBFIRST, SPI_MODE0),
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
_frequency(0),
_packetIndex(0),
_preinit_done(false),
_onReceive(NULL) { setTimeout(0); }
void sx127x::setSPIFrequency(uint32_t frequency) { _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0); }
void sx127x::setPins(int ss, int reset, int dio0, int busy) { _ss = ss; _reset = reset; _dio0 = dio0; _busy = busy; }
uint8_t ISR_VECT sx127x::readRegister(uint8_t address) { return singleTransfer(address & 0x7f, 0x00); }
void sx127x::writeRegister(uint8_t address, uint8_t value) { singleTransfer(address | 0x80, value); }
void sx127x::standby() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_STDBY_7X); }
void sx127x::sleep() { writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_SLEEP_7X); }
uint8_t sx127x::modemStatus() { return readRegister(REG_MODEM_STAT_7X); }
void sx127x::setSyncWord(uint8_t sw) { writeRegister(REG_SYNC_WORD_7X, sw); }
void sx127x::enableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) | 0x04); }
void sx127x::disableCrc() { writeRegister(REG_MODEM_CONFIG_2_7X, readRegister(REG_MODEM_CONFIG_2_7X) & 0xfb); }
void sx127x::enableTCXO() { uint8_t tcxo_reg = readRegister(REG_TCXO_7X); writeRegister(REG_TCXO_7X, tcxo_reg | 0x10); }
void sx127x::disableTCXO() { uint8_t tcxo_reg = readRegister(REG_TCXO_7X); writeRegister(REG_TCXO_7X, tcxo_reg & 0xEF); }
void sx127x::explicitHeaderMode() { _implicitHeaderMode = 0; writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) & 0xfe); }
void sx127x::implicitHeaderMode() { _implicitHeaderMode = 1; writeRegister(REG_MODEM_CONFIG_1_7X, readRegister(REG_MODEM_CONFIG_1_7X) | 0x01); }
byte sx127x::random() { return readRegister(REG_RSSI_WIDEBAND_7X); }
void sx127x::flush() { }
bool sx127x::preInit() {
pinMode(_ss, OUTPUT);
digitalWrite(_ss, HIGH);
SPI.begin();
// Check modem version
uint8_t version;
long start = millis();
while (((millis() - start) < 500) && (millis() >= start)) {
version = readRegister(REG_VERSION_7X);
if (version == 0x12) { break; }
delay(100);
}
if (version != 0x12) { return false; }
_preinit_done = true;
return true;
}
uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value) {
uint8_t response;
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(address);
response = SPI.transfer(value);
SPI.endTransaction();
digitalWrite(_ss, HIGH);
return response;
}
int sx127x::begin(long frequency) {
if (_reset != -1) {
pinMode(_reset, OUTPUT);
// Perform reset
digitalWrite(_reset, LOW);
delay(10);
digitalWrite(_reset, HIGH);
delay(10);
}
if (_busy != -1) { pinMode(_busy, INPUT); }
if (!_preinit_done) {
if (!preInit()) { return false; }
}
sleep();
setFrequency(frequency);
// set base addresses
writeRegister(REG_FIFO_TX_BASE_ADDR_7X, 0);
writeRegister(REG_FIFO_RX_BASE_ADDR_7X, 0);
// set LNA boost and auto AGC
writeRegister(REG_LNA_7X, readRegister(REG_LNA_7X) | 0x03);
writeRegister(REG_MODEM_CONFIG_3_7X, 0x04);
setSyncWord(SYNC_WORD_7X);
enableCrc();
setTxPower(2);
standby();
return 1;
}
void sx127x::end() {
sleep();
SPI.end();
_preinit_done = false;
}
int sx127x::beginPacket(int implicitHeader) {
standby();
if (implicitHeader) {
implicitHeaderMode();
} else {
explicitHeaderMode();
}
// Reset FIFO address and payload length
writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
writeRegister(REG_PAYLOAD_LENGTH_7X, 0);
return 1;
}
int sx127x::endPacket() {
// Enter TX mode
writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_TX_7X);
// Wait for TX completion
while ((readRegister(REG_IRQ_FLAGS_7X) & IRQ_TX_DONE_MASK_7X) == 0) {
yield();
}
// Clear TX complete IRQ
writeRegister(REG_IRQ_FLAGS_7X, IRQ_TX_DONE_MASK_7X);
return 1;
}
uint8_t sx127x::currentRssiRaw() {
uint8_t rssi = readRegister(REG_RSSI_VALUE_7X);
return rssi;
}
int ISR_VECT sx127x::currentRssi() {
int rssi = (int)readRegister(REG_RSSI_VALUE_7X) - RSSI_OFFSET;
if (_frequency < 820E6) rssi -= 7;
return rssi;
}
uint8_t sx127x::packetRssiRaw() {
uint8_t pkt_rssi_value = readRegister(REG_PKT_RSSI_VALUE_7X);
return pkt_rssi_value;
}
int ISR_VECT sx127x::packetRssi() {
int pkt_rssi = (int)readRegister(REG_PKT_RSSI_VALUE_7X) - RSSI_OFFSET;
int pkt_snr = packetSnr();
if (_frequency < 820E6) pkt_rssi -= 7;
if (pkt_snr < 0) {
pkt_rssi += pkt_snr;
} else {
// Slope correction is (16/15)*pkt_rssi,
// this estimation looses one floating point
// operation, and should be precise enough.
pkt_rssi = (int)(1.066 * pkt_rssi);
}
return pkt_rssi;
}
uint8_t ISR_VECT sx127x::packetSnrRaw() {
return readRegister(REG_PKT_SNR_VALUE_7X);
}
float ISR_VECT sx127x::packetSnr() {
return ((int8_t)readRegister(REG_PKT_SNR_VALUE_7X)) * 0.25;
}
long sx127x::packetFrequencyError() {
int32_t freqError = 0;
freqError = static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MSB_7X) & B111);
freqError <<= 8L;
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_MID_7X));
freqError <<= 8L;
freqError += static_cast<int32_t>(readRegister(REG_FREQ_ERROR_LSB_7X));
if (readRegister(REG_FREQ_ERROR_MSB_7X) & B1000) { // Sign bit is on
freqError -= 524288; // B1000'0000'0000'0000'0000
}
const float fXtal = 32E6; // FXOSC: crystal oscillator (XTAL) frequency (2.5. Chip Specification, p. 14)
const float fError = ((static_cast<float>(freqError) * (1L << 24)) / fXtal) * (getSignalBandwidth() / 500000.0f);
return static_cast<long>(fError);
}
size_t sx127x::write(uint8_t byte) { return write(&byte, sizeof(byte)); }
size_t sx127x::write(const uint8_t *buffer, size_t size) {
int currentLength = readRegister(REG_PAYLOAD_LENGTH_7X);
if ((currentLength + size) > MAX_PKT_LENGTH) {
size = MAX_PKT_LENGTH - currentLength;
}
for (size_t i = 0; i < size; i++) {
writeRegister(REG_FIFO_7X, buffer[i]);
}
writeRegister(REG_PAYLOAD_LENGTH_7X, currentLength + size);
return size;
}
int ISR_VECT sx127x::available() { return (readRegister(REG_RX_NB_BYTES_7X) - _packetIndex); }
int ISR_VECT sx127x::read() {
if (!available()) { return -1; }
_packetIndex++;
return readRegister(REG_FIFO_7X);
}
int sx127x::peek() {
if (!available()) { return -1; }
// Remember current FIFO address, read, and then reset address
int currentAddress = readRegister(REG_FIFO_ADDR_PTR_7X);
uint8_t b = readRegister(REG_FIFO_7X);
writeRegister(REG_FIFO_ADDR_PTR_7X, currentAddress);
return b;
}
void sx127x::onReceive(void(*callback)(int)) {
_onReceive = callback;
if (callback) {
pinMode(_dio0, INPUT);
writeRegister(REG_DIO_MAPPING_1_7X, 0x00);
#ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
#endif
attachInterrupt(digitalPinToInterrupt(_dio0), sx127x::onDio0Rise, RISING);
} else {
detachInterrupt(digitalPinToInterrupt(_dio0));
#ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
#endif
}
}
void sx127x::receive(int size) {
if (size > 0) {
implicitHeaderMode();
writeRegister(REG_PAYLOAD_LENGTH_7X, size & 0xff);
} else { explicitHeaderMode(); }
writeRegister(REG_OP_MODE_7X, MODE_LONG_RANGE_MODE_7X | MODE_RX_CONTINUOUS_7X);
}
void sx127x::setTxPower(int level, int outputPin) {
// Setup according to RFO or PA_BOOST output pin
if (PA_OUTPUT_RFO_PIN == outputPin) {
if (level < 0) { level = 0; }
else if (level > 14) { level = 14; }
writeRegister(REG_PA_DAC_7X, 0x84);
writeRegister(REG_PA_CONFIG_7X, 0x70 | level);
} else {
if (level < 2) { level = 2; }
else if (level > 17) { level = 17; }
writeRegister(REG_PA_DAC_7X, 0x84);
writeRegister(REG_PA_CONFIG_7X, PA_BOOST_7X | (level - 2));
}
}
uint8_t sx127x::getTxPower() { byte txp = readRegister(REG_PA_CONFIG_7X); return txp; }
void sx127x::setFrequency(unsigned long frequency) {
_frequency = frequency;
uint32_t frf = ((uint64_t)frequency << 19) / 32000000;
writeRegister(REG_FRF_MSB_7X, (uint8_t)(frf >> 16));
writeRegister(REG_FRF_MID_7X, (uint8_t)(frf >> 8));
writeRegister(REG_FRF_LSB_7X, (uint8_t)(frf >> 0));
optimizeModemSensitivity();
}
uint32_t sx127x::getFrequency() {
uint8_t msb = readRegister(REG_FRF_MSB_7X);
uint8_t mid = readRegister(REG_FRF_MID_7X);
uint8_t lsb = readRegister(REG_FRF_LSB_7X);
uint32_t frf = ((uint32_t)msb << 16) | ((uint32_t)mid << 8) | (uint32_t)lsb;
uint64_t frm = (uint64_t)frf*32000000;
uint32_t frequency = (frm >> 19);
return frequency;
}
void sx127x::setSpreadingFactor(int sf) {
if (sf < 6) { sf = 6; }
else if (sf > 12) { sf = 12; }
if (sf == 6) {
writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc5);
writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0c);
} else {
writeRegister(REG_DETECTION_OPTIMIZE_7X, 0xc3);
writeRegister(REG_DETECTION_THRESHOLD_7X, 0x0a);
}
writeRegister(REG_MODEM_CONFIG_2_7X, (readRegister(REG_MODEM_CONFIG_2_7X) & 0x0f) | ((sf << 4) & 0xf0));
handleLowDataRate();
}
long sx127x::getSignalBandwidth() {
byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4);
switch (bw) {
case 0: return 7.8E3;
case 1: return 10.4E3;
case 2: return 15.6E3;
case 3: return 20.8E3;
case 4: return 31.25E3;
case 5: return 41.7E3;
case 6: return 62.5E3;
case 7: return 125E3;
case 8: return 250E3;
case 9: return 500E3; }
return 0;
}
void sx127x::setSignalBandwidth(long sbw) {
int bw;
if (sbw <= 7.8E3) {
bw = 0;
} else if (sbw <= 10.4E3) {
bw = 1;
} else if (sbw <= 15.6E3) {
bw = 2;
} else if (sbw <= 20.8E3) {
bw = 3;
} else if (sbw <= 31.25E3) {
bw = 4;
} else if (sbw <= 41.7E3) {
bw = 5;
} else if (sbw <= 62.5E3) {
bw = 6;
} else if (sbw <= 125E3) {
bw = 7;
} else if (sbw <= 250E3) {
bw = 8;
} else /*if (sbw <= 250E3)*/ {
bw = 9;
}
writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0x0f) | (bw << 4));
handleLowDataRate();
optimizeModemSensitivity();
}
void sx127x::setCodingRate4(int denominator) {
if (denominator < 5) { denominator = 5; }
else if (denominator > 8) { denominator = 8; }
int cr = denominator - 4;
writeRegister(REG_MODEM_CONFIG_1_7X, (readRegister(REG_MODEM_CONFIG_1_7X) & 0xf1) | (cr << 1));
}
void sx127x::setPreambleLength(long length) {
writeRegister(REG_PREAMBLE_MSB_7X, (uint8_t)(length >> 8));
writeRegister(REG_PREAMBLE_LSB_7X, (uint8_t)(length >> 0));
}
void sx127x::handleLowDataRate() {
int sf = (readRegister(REG_MODEM_CONFIG_2_7X) >> 4);
if ( long( (1<<sf) / (getSignalBandwidth()/1000)) > 16) {
// Set auto AGC and LowDataRateOptimize
writeRegister(REG_MODEM_CONFIG_3_7X, (1<<3)|(1<<2));
} else {
// Only set auto AGC
writeRegister(REG_MODEM_CONFIG_3_7X, (1<<2));
}
}
void sx127x::optimizeModemSensitivity() {
byte bw = (readRegister(REG_MODEM_CONFIG_1_7X) >> 4);
uint32_t freq = getFrequency();
if (bw == 9 && (410E6 <= freq) && (freq <= 525E6)) {
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02);
writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x7f);
} else if (bw == 9 && (820E6 <= freq) && (freq <= 1020E6)) {
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x02);
writeRegister(REG_HIGH_BW_OPTIMIZE_2_7X, 0x64);
} else {
writeRegister(REG_HIGH_BW_OPTIMIZE_1_7X, 0x03);
}
}
void ISR_VECT sx127x::handleDio0Rise() {
int irqFlags = readRegister(REG_IRQ_FLAGS_7X);
// Clear IRQs
writeRegister(REG_IRQ_FLAGS_7X, irqFlags);
if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK_7X) == 0) {
_packetIndex = 0;
int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH_7X) : readRegister(REG_RX_NB_BYTES_7X);
writeRegister(REG_FIFO_ADDR_PTR_7X, readRegister(REG_FIFO_RX_CURRENT_ADDR_7X));
if (_onReceive) { _onReceive(packetLength); }
writeRegister(REG_FIFO_ADDR_PTR_7X, 0);
}
}
void ISR_VECT sx127x::onDio0Rise() { sx127x_modem.handleDio0Rise(); }
sx127x sx127x_modem;
#endif

@ -1,111 +0,0 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2023 by Mark Qvist
// Obviously still under the MIT license.
#ifndef SX1276_H
#define SX1276_H
#include <Arduino.h>
#include <SPI.h>
#include "Modem.h"
#define LORA_DEFAULT_SS_PIN 10
#define LORA_DEFAULT_RESET_PIN 9
#define LORA_DEFAULT_DIO0_PIN 2
#define LORA_DEFAULT_BUSY_PIN -1
#define PA_OUTPUT_RFO_PIN 0
#define PA_OUTPUT_PA_BOOST_PIN 1
#define RSSI_OFFSET 157
class sx127x : public Stream {
public:
sx127x();
int begin(long frequency);
void end();
int beginPacket(int implicitHeader = false);
int endPacket();
int parsePacket(int size = 0);
int packetRssi();
int currentRssi();
uint8_t packetRssiRaw();
uint8_t currentRssiRaw();
uint8_t packetSnrRaw();
float packetSnr();
long packetFrequencyError();
// from Print
virtual size_t write(uint8_t byte);
virtual size_t write(const uint8_t *buffer, size_t size);
// from Stream
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
void onReceive(void(*callback)(int));
void receive(int size = 0);
void standby();
void sleep();
bool preInit();
uint8_t getTxPower();
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
uint32_t getFrequency();
void setFrequency(unsigned long frequency);
void setSpreadingFactor(int sf);
long getSignalBandwidth();
void setSignalBandwidth(long sbw);
void setCodingRate4(int denominator);
void setPreambleLength(long length);
void setSyncWord(uint8_t sw);
uint8_t modemStatus();
void enableCrc();
void disableCrc();
void enableTCXO();
void disableTCXO();
byte random();
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN);
void setSPIFrequency(uint32_t frequency);
private:
void explicitHeaderMode();
void implicitHeaderMode();
void handleDio0Rise();
uint8_t readRegister(uint8_t address);
void writeRegister(uint8_t address, uint8_t value);
uint8_t singleTransfer(uint8_t address, uint8_t value);
static void onDio0Rise();
void handleLowDataRate();
void optimizeModemSensitivity();
private:
SPISettings _spiSettings;
int _ss;
int _reset;
int _dio0;
int _busy;
long _frequency;
int _packetIndex;
int _implicitHeaderMode;
bool _preinit_done;
void (*_onReceive)(int);
};
extern sx127x sx127x_modem;
#endif

@ -1,887 +0,0 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva
// Obviously still under the MIT license.
#include "sx128x.h"
#include "Boards.h"
#define MCU_1284P 0x91
#define MCU_2560 0x92
#define MCU_ESP32 0x81
#define MCU_NRF52 0x71
#if defined(__AVR_ATmega1284P__)
#define PLATFORM PLATFORM_AVR
#define MCU_VARIANT MCU_1284P
#elif defined(__AVR_ATmega2560__)
#define PLATFORM PLATFORM_AVR
#define MCU_VARIANT MCU_2560
#elif defined(ESP32)
#define PLATFORM PLATFORM_ESP32
#define MCU_VARIANT MCU_ESP32
#elif defined(NRF52840_XXAA)
#define PLATFORM PLATFORM_NRF52
#define MCU_VARIANT MCU_NRF52
#endif
#ifndef MCU_VARIANT
#error No MCU variant defined, cannot compile
#endif
#if MCU_VARIANT == MCU_ESP32
#if MCU_VARIANT == MCU_ESP32 and !defined(CONFIG_IDF_TARGET_ESP32S3)
#include "soc/rtc_wdt.h"
#endif
#define ISR_VECT IRAM_ATTR
#else
#define ISR_VECT
#endif
#define OP_RF_FREQ_8X 0x86
#define OP_SLEEP_8X 0x84
#define OP_STANDBY_8X 0x80
#define OP_TX_8X 0x83
#define OP_RX_8X 0x82
#define OP_SET_IRQ_FLAGS_8X 0x8D // also provides info such as
// preamble detection, etc for
// knowing when it's safe to switch
// antenna modes
#define OP_CLEAR_IRQ_STATUS_8X 0x97
#define OP_GET_IRQ_STATUS_8X 0x15
#define OP_RX_BUFFER_STATUS_8X 0x17
#define OP_PACKET_STATUS_8X 0x1D // get snr & rssi of last packet
#define OP_CURRENT_RSSI_8X 0x1F
#define OP_MODULATION_PARAMS_8X 0x8B // bw, sf, cr, etc.
#define OP_PACKET_PARAMS_8X 0x8C // crc, preamble, payload length, etc.
#define OP_STATUS_8X 0xC0
#define OP_TX_PARAMS_8X 0x8E // set dbm, etc
#define OP_PACKET_TYPE_8X 0x8A
#define OP_BUFFER_BASE_ADDR_8X 0x8F
#define OP_READ_REGISTER_8X 0x19
#define OP_WRITE_REGISTER_8X 0x18
#define IRQ_TX_DONE_MASK_8X 0x01
#define IRQ_RX_DONE_MASK_8X 0x02
#define IRQ_HEADER_DET_MASK_8X 0x10
#define IRQ_HEADER_ERROR_MASK_8X 0x20
#define IRQ_PAYLOAD_CRC_ERROR_MASK_8X 0x40
#define MODE_LONG_RANGE_MODE_8X 0x01
#define OP_FIFO_WRITE_8X 0x1A
#define OP_FIFO_READ_8X 0x1B
#define IRQ_PREAMBLE_DET_MASK_8X 0x80
#define REG_PACKET_SIZE 0x901
#define REG_FIRM_VER_MSB 0x154
#define REG_FIRM_VER_LSB 0x153
#define XTAL_FREQ_8X (double)52000000
#define FREQ_DIV_8X (double)pow(2.0, 18.0)
#define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X)
#if defined(NRF52840_XXAA)
extern SPIClass spiModem;
#define SPI spiModem
#endif
extern SPIClass SPI;
#define MAX_PKT_LENGTH 255
sx128x::sx128x() :
_spiSettings(8E6, MSBFIRST, SPI_MODE0),
_ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN), _rxen(LORA_DEFAULT_RXEN_PIN), _busy(LORA_DEFAULT_BUSY_PIN),
_frequency(0),
_txp(0),
_sf(0x50),
_bw(0x34),
_cr(0x01),
_packetIndex(0),
_preambleLength(18),
_implicitHeaderMode(0),
_payloadLength(255),
_crcMode(0),
_fifo_tx_addr_ptr(0),
_fifo_rx_addr_ptr(0),
_packet({0}),
_rxPacketLength(0),
_preinit_done(false),
_onReceive(NULL)
{
// overide Stream timeout value
setTimeout(0);
}
bool sx128x::preInit() {
// setup pins
pinMode(_ss, OUTPUT);
// set SS high
digitalWrite(_ss, HIGH);
SPI.begin();
// check version (retry for up to 2 seconds)
long start = millis();
uint8_t version_msb;
uint8_t version_lsb;
while (((millis() - start) < 2000) && (millis() >= start)) {
version_msb = readRegister(REG_FIRM_VER_MSB);
version_lsb = readRegister(REG_FIRM_VER_LSB);
if ((version_msb == 0xB7 && version_lsb == 0xA9) || (version_msb == 0xB5 && version_lsb == 0xA9)) {
break;
}
delay(100);
}
if ((version_msb != 0xB7 || version_lsb != 0xA9) && (version_msb != 0xB5 || version_lsb != 0xA9)) {
return false;
}
_preinit_done = true;
return true;
}
uint8_t ISR_VECT sx128x::readRegister(uint16_t address)
{
return singleTransfer(OP_READ_REGISTER_8X, address, 0x00);
}
void sx128x::writeRegister(uint16_t address, uint8_t value)
{
singleTransfer(OP_WRITE_REGISTER_8X, address, value);
}
uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_t value)
{
waitOnBusy();
uint8_t response;
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
SPI.transfer((address & 0xFF00) >> 8);
SPI.transfer(address & 0x00FF);
if (opcode == OP_READ_REGISTER_8X) {
SPI.transfer(0x00);
}
response = SPI.transfer(value);
SPI.endTransaction();
digitalWrite(_ss, HIGH);
return response;
}
void sx128x::rxAntEnable()
{
if (_txen != -1) {
digitalWrite(_txen, LOW);
}
if (_rxen != -1) {
digitalWrite(_rxen, HIGH);
}
}
void sx128x::txAntEnable()
{
if (_txen != -1) {
digitalWrite(_txen, HIGH);
}
if (_rxen != -1) {
digitalWrite(_rxen, LOW);
}
}
void sx128x::loraMode() {
// enable lora mode on the SX1262 chip
uint8_t mode = MODE_LONG_RANGE_MODE_8X;
executeOpcode(OP_PACKET_TYPE_8X, &mode, 1);
}
void sx128x::waitOnBusy() {
unsigned long time = millis();
while (digitalRead(_busy) == HIGH)
{
if (millis() >= (time + 100)) {
break;
}
// do nothing
}
}
void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size)
{
waitOnBusy();
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
for (int i = 0; i < size; i++)
{
SPI.transfer(buffer[i]);
}
SPI.endTransaction();
digitalWrite(_ss, HIGH);
}
void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size)
{
waitOnBusy();
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(opcode);
SPI.transfer(0x00);
for (int i = 0; i < size; i++)
{
buffer[i] = SPI.transfer(0x00);
}
SPI.endTransaction();
digitalWrite(_ss, HIGH);
}
void sx128x::writeBuffer(const uint8_t* buffer, size_t size)
{
waitOnBusy();
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(OP_FIFO_WRITE_8X);
SPI.transfer(_fifo_tx_addr_ptr);
for (int i = 0; i < size; i++)
{
SPI.transfer(buffer[i]);
_fifo_tx_addr_ptr++;
}
SPI.endTransaction();
digitalWrite(_ss, HIGH);
}
void sx128x::readBuffer(uint8_t* buffer, size_t size)
{
waitOnBusy();
digitalWrite(_ss, LOW);
SPI.beginTransaction(_spiSettings);
SPI.transfer(OP_FIFO_READ_8X);
SPI.transfer(_fifo_rx_addr_ptr);
SPI.transfer(0x00);
for (int i = 0; i < size; i++)
{
buffer[i] = SPI.transfer(0x00);
}
SPI.endTransaction();
digitalWrite(_ss, HIGH);
}
void sx128x::setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr) {
// because there is no access to these registers on the sx1280, we have
// to set all these parameters at once or not at all.
uint8_t buf[3];
buf[0] = sf;
buf[1] = bw;
buf[2] = cr;
executeOpcode(OP_MODULATION_PARAMS_8X, buf, 3);
if (sf <= 6) {
writeRegister(0x925, 0x1E);
} else if (sf <= 8) {
writeRegister(0x925, 0x37);
} else if (sf >= 9) {
writeRegister(0x925, 0x32);
}
writeRegister(0x093C, 0x1);
}
void sx128x::setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc) {
// because there is no access to these registers on the sx1280, we have
// to set all these parameters at once or not at all.
uint8_t buf[7];
// calculate exponent and mantissa values for modem
uint8_t e = 1;
uint8_t m = 1;
uint32_t preamblelen;
for (e <= 15; e++;) {
for (m <= 15; m++;) {
preamblelen = m * (uint32_t(1) << e);
if (preamblelen >= preamble) break;
}
if (preamblelen >= preamble) break;
}
buf[0] = (e << 4) | m;
buf[1] = headermode;
buf[2] = length;
buf[3] = crc;
// standard IQ setting (no inversion)
buf[4] = 0x40;
// unused params
buf[5] = 0x00;
buf[6] = 0x00;
executeOpcode(OP_PACKET_PARAMS_8X, buf, 7);
}
int sx128x::begin(unsigned long frequency)
{
if (_reset != -1) {
pinMode(_reset, OUTPUT);
// perform reset
digitalWrite(_reset, LOW);
delay(10);
digitalWrite(_reset, HIGH);
delay(10);
}
if (_rxen != -1) {
pinMode(_rxen, OUTPUT);
}
if (_txen != -1) {
pinMode(_txen, OUTPUT);
}
if (_busy != -1) {
pinMode(_busy, INPUT);
}
if (!_preinit_done) {
if (!preInit()) {
return false;
}
}
idle();
loraMode();
rxAntEnable();
setFrequency(frequency);
// set LNA boost
// todo: implement this
//writeRegister(REG_LNA, 0x96);
setModulationParams(_sf, _bw, _cr);
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
// set output power to 2 dBm
setTxPower(2);
// set base addresses
uint8_t basebuf[2] = {0};
executeOpcode(OP_BUFFER_BASE_ADDR_8X, basebuf, 2);
return 1;
}
void sx128x::end()
{
// put in sleep mode
sleep();
// stop SPI
SPI.end();
_preinit_done = false;
}
int sx128x::beginPacket(int implicitHeader)
{
// put in standby mode
idle();
if (implicitHeader) {
implicitHeaderMode();
} else {
explicitHeaderMode();
}
_payloadLength = 0;
_fifo_tx_addr_ptr = 0;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
return 1;
}
int sx128x::endPacket()
{
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
txAntEnable();
// put in single TX mode
uint8_t timeout[3] = {0};
executeOpcode(OP_TX_8X, timeout, 3);
uint8_t buf[2];
buf[0] = 0x00;
buf[1] = 0x00;
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
// wait for TX done
while ((buf[1] & IRQ_TX_DONE_MASK_8X) == 0) {
buf[0] = 0x00;
buf[1] = 0x00;
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
yield();
}
// clear IRQ's
uint8_t mask[2];
mask[0] = 0x00;
mask[1] = IRQ_TX_DONE_MASK_8X;
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, mask, 2);
return 1;
}
uint8_t sx128x::modemStatus() {
// imitate the register status from the sx1276 / 78
uint8_t buf[2] = {0};
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
uint8_t clearbuf[2] = {0};
uint8_t byte = 0x00;
if ((buf[0] & IRQ_PREAMBLE_DET_MASK_8X) != 0) {
byte = byte | 0x01 | 0x04;
// clear register after reading
clearbuf[0] = 0xFF;
}
if ((buf[1] & IRQ_HEADER_DET_MASK_8X) != 0) {
byte = byte | 0x02 | 0x04;
// clear register after reading
clearbuf[1] = 0xFF;
}
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, clearbuf, 2);
return byte;
}
uint8_t sx128x::currentRssiRaw() {
uint8_t byte = 0;
executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1);
return byte;
}
int ISR_VECT sx128x::currentRssi() {
uint8_t byte = 0;
executeOpcodeRead(OP_CURRENT_RSSI_8X, &byte, 1);
int rssi = -byte / 2;
return rssi;
}
uint8_t sx128x::packetRssiRaw() {
uint8_t buf[5] = {0};
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
return buf[0];
}
int ISR_VECT sx128x::packetRssi() {
// may need more calculations here
uint8_t buf[5] = {0};
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
int pkt_rssi = -buf[0] / 2;
return pkt_rssi;
}
uint8_t ISR_VECT sx128x::packetSnrRaw() {
uint8_t buf[5] = {0};
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 5);
return buf[1];
}
float ISR_VECT sx128x::packetSnr() {
uint8_t buf[5] = {0};
executeOpcodeRead(OP_PACKET_STATUS_8X, buf, 3);
return float(buf[1]) * 0.25;
}
long sx128x::packetFrequencyError()
{
int32_t freqError = 0;
// todo: implement this, page 120 of sx1280 datasheet
const float fError = 0.0;
return static_cast<long>(fError);
}
size_t sx128x::write(uint8_t byte)
{
return write(&byte, sizeof(byte));
}
size_t sx128x::write(const uint8_t *buffer, size_t size)
{
if ((_payloadLength + size) > MAX_PKT_LENGTH) {
size = MAX_PKT_LENGTH - _payloadLength;
}
// write data
writeBuffer(buffer, size);
_payloadLength = _payloadLength + size;
return size;
}
int ISR_VECT sx128x::available()
{
return _rxPacketLength - _packetIndex;
}
int ISR_VECT sx128x::read()
{
if (!available()) {
return -1;
}
uint8_t byte = _packet[_packetIndex];
_packetIndex++;
return byte;
}
int sx128x::peek()
{
if (!available()) {
return -1;
}
uint8_t b = _packet[_packetIndex];
return b;
}
void sx128x::flush()
{
}
void sx128x::onReceive(void(*callback)(int))
{
_onReceive = callback;
if (callback) {
pinMode(_dio0, INPUT);
// set preamble and header detection irqs, plus dio0 mask
uint8_t buf[8];
// set irq masks, enable all
buf[0] = 0xFF;
buf[1] = 0xFF;
// set dio0 masks
buf[2] = 0x00;
buf[3] = IRQ_RX_DONE_MASK_8X;
// set dio1 masks
buf[4] = 0x00;
buf[5] = 0x00;
// set dio2 masks
buf[6] = 0x00;
buf[7] = 0x00;
executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8);
//#ifdef SPI_HAS_NOTUSINGINTERRUPT
// SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
//#endif
attachInterrupt(digitalPinToInterrupt(_dio0), sx128x::onDio0Rise, RISING);
} else {
detachInterrupt(digitalPinToInterrupt(_dio0));
//#ifdef SPI_HAS_NOTUSINGINTERRUPT
// SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
//#endif
}
}
void sx128x::receive(int size)
{
if (size > 0) {
implicitHeaderMode();
// tell radio payload length
_rxPacketLength = size;
//_payloadLength = size;
//setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
} else {
explicitHeaderMode();
}
rxAntEnable();
uint8_t mode[3] = {0xFF, 0xFF, 0xFF}; // continuous mode
executeOpcode(OP_RX_8X, mode, 3);
}
void sx128x::idle()
{
#if HAS_TCXO
// STDBY_XOSC
uint8_t byte = 0x01;
#else
// STDBY_RC
uint8_t byte = 0x00;
#endif
executeOpcode(OP_STANDBY_8X, &byte, 1);
}
void sx128x::sleep()
{
uint8_t byte = 0x00;
executeOpcode(OP_SLEEP_8X, &byte, 1);
}
void sx128x::enableTCXO() {
// todo: need to check how to implement on sx1280
}
void sx128x::disableTCXO() {
// todo: need to check how to implement on sx1280
}
void sx128x::setTxPower(int level, int outputPin) {
if (level > 13) {
level = 13;
} else if (level < -18) {
level = -18;
}
_txp = level;
level = level + 18;
uint8_t tx_buf[2];
tx_buf[0] = level;
tx_buf[1] = 0xE0; // ramping time - 20 microseconds
executeOpcode(OP_TX_PARAMS_8X, tx_buf, 2);
}
uint8_t sx128x::getTxPower() {
return _txp;
}
void sx128x::setFrequency(unsigned long frequency) {
_frequency = frequency;
uint8_t buf[3];
uint32_t freq = (uint32_t)((double)frequency / (double)FREQ_STEP_8X);
buf[0] = ((freq >> 16) & 0xFF);
buf[1] = ((freq >> 8) & 0xFF);
buf[2] = (freq & 0xFF);
executeOpcode(OP_RF_FREQ_8X, buf, 3);
}
uint32_t sx128x::getFrequency() {
// we can't read the frequency on the sx1280
uint32_t frequency = _frequency;
return frequency;
}
void sx128x::setSpreadingFactor(int sf)
{
if (sf < 5) {
sf = 5;
} else if (sf > 12) {
sf = 12;
}
_sf = sf << 4;
setModulationParams(sf << 4, _bw, _cr);
handleLowDataRate();
}
long sx128x::getSignalBandwidth()
{
int bw = _bw;
switch (bw) {
case 0x34: return 203.125E3;
case 0x26: return 406.25E3;
case 0x18: return 812.5E3;
case 0x0A: return 1625E3;
}
return 0;
}
void sx128x::handleLowDataRate(){
// todo: do i need this??
}
void sx128x::optimizeModemSensitivity(){
// todo: check if there's anything the sx1280 can do here
}
void sx128x::setSignalBandwidth(long sbw)
{
if (sbw <= 203.125E3) {
_bw = 0x34;
} else if (sbw <= 406.25E3) {
_bw = 0x26;
} else if (sbw <= 812.5E3) {
_bw = 0x18;
} else {
_bw = 0x0A;
}
setModulationParams(_sf, _bw, _cr);
handleLowDataRate();
optimizeModemSensitivity();
}
void sx128x::setCodingRate4(int denominator)
{
if (denominator < 5) {
denominator = 5;
} else if (denominator > 8) {
denominator = 8;
}
_cr = denominator - 4;
// todo: add support for new interleaving scheme, see page 117 of sx1280
// datasheet
// update cr values for sx1280's use
setModulationParams(_sf, _bw, _cr);
}
void sx128x::setPreambleLength(long length)
{
_preambleLength = length;
setPacketParams(length, _implicitHeaderMode, _payloadLength, _crcMode);
}
void sx128x::setSyncWord(int sw)
{
// not implemented
}
void sx128x::enableCrc()
{
_crcMode = 0x20;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
}
void sx128x::disableCrc()
{
_crcMode = 0;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
}
byte sx128x::random()
{
// todo: implement
}
void sx128x::setPins(int ss, int reset, int dio0, int busy, int rxen, int txen)
{
_ss = ss;
_reset = reset;
_dio0 = dio0;
_busy = busy;
_rxen = rxen;
_txen = txen;
}
void sx128x::setSPIFrequency(uint32_t frequency)
{
_spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
}
void sx128x::dumpRegisters(Stream& out)
{
for (int i = 0; i < 128; i++) {
out.print("0x");
out.print(i, HEX);
out.print(": 0x");
out.println(readRegister(i), HEX);
}
}
void sx128x::explicitHeaderMode()
{
_implicitHeaderMode = 0;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
}
void sx128x::implicitHeaderMode()
{
_implicitHeaderMode = 0x80;
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
}
void ISR_VECT sx128x::handleDio0Rise()
{
uint8_t buf[2];
buf[0] = 0x00;
buf[1] = 0x00;
executeOpcodeRead(OP_GET_IRQ_STATUS_8X, buf, 2);
executeOpcode(OP_CLEAR_IRQ_STATUS_8X, buf, 2);
if ((buf[1] & IRQ_PAYLOAD_CRC_ERROR_MASK_8X) == 0) {
// received a packet
_packetIndex = 0;
uint8_t rxbuf[2] = {0};
executeOpcodeRead(OP_RX_BUFFER_STATUS_8X, rxbuf, 2);
_rxPacketLength = rxbuf[0];
_fifo_rx_addr_ptr = rxbuf[1];
readBuffer(_packet, _rxPacketLength);
if (_onReceive) {
_onReceive(_rxPacketLength);
}
}
}
void ISR_VECT sx128x::onDio0Rise()
{
sx128x_modem.handleDio0Rise();
}
sx128x sx128x_modem;

@ -1,144 +0,0 @@
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license.
// Modifications and additions copyright 2023 by Mark Qvist
// Obviously still under the MIT license.
#ifndef SX128X_H
#define SX128X_H
#include <Arduino.h>
#include <SPI.h>
#include "Interfaces.h"
#define LORA_DEFAULT_SS_PIN 10
#define LORA_DEFAULT_RESET_PIN 9
#define LORA_DEFAULT_DIO0_PIN 2
#define LORA_DEFAULT_RXEN_PIN -1
#define LORA_DEFAULT_TXEN_PIN -1
#define LORA_DEFAULT_BUSY_PIN 11
#define PA_OUTPUT_RFO_PIN 0
#define PA_OUTPUT_PA_BOOST_PIN 1
#define RSSI_OFFSET 157
class sx128x : public Stream {
public:
sx128x();
int begin(unsigned long frequency);
void end();
int beginPacket(int implicitHeader = false);
int endPacket();
int parsePacket(int size = 0);
int packetRssi();
int currentRssi();
uint8_t packetRssiRaw();
uint8_t currentRssiRaw();
uint8_t packetSnrRaw();
float packetSnr();
long packetFrequencyError();
// from Print
virtual size_t write(uint8_t byte);
virtual size_t write(const uint8_t *buffer, size_t size);
// from Stream
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
void onReceive(void(*callback)(int));
void receive(int size = 0);
void idle();
void sleep();
bool preInit();
uint8_t getTxPower();
void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
uint32_t getFrequency();
void setFrequency(unsigned long frequency);
void setSpreadingFactor(int sf);
long getSignalBandwidth();
void setSignalBandwidth(long sbw);
void setCodingRate4(int denominator);
void setPreambleLength(long length);
void setSyncWord(int sw);
uint8_t modemStatus();
void enableCrc();
void disableCrc();
void enableTCXO();
void disableTCXO();
void txAntEnable();
void rxAntEnable();
void loraMode();
void waitOnBusy();
void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size);
void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size);
void writeBuffer(const uint8_t* buffer, size_t size);
void readBuffer(uint8_t* buffer, size_t size);
void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc);
void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr);
// deprecated
void crc() { enableCrc(); }
void noCrc() { disableCrc(); }
byte random();
void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN, int busy = LORA_DEFAULT_BUSY_PIN, int rxen = LORA_DEFAULT_RXEN_PIN, int txen = LORA_DEFAULT_TXEN_PIN);
void setSPIFrequency(uint32_t frequency);
void dumpRegisters(Stream& out);
private:
void explicitHeaderMode();
void implicitHeaderMode();
void handleDio0Rise();
uint8_t readRegister(uint16_t address);
void writeRegister(uint16_t address, uint8_t value);
uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value);
static void onDio0Rise();
void handleLowDataRate();
void optimizeModemSensitivity();
private:
SPISettings _spiSettings;
int _ss;
int _reset;
int _dio0;
int _rxen;
int _txen;
int _busy;
int _modem;
unsigned long _frequency;
int _txp;
uint8_t _sf;
uint8_t _bw;
uint8_t _cr;
int _packetIndex;
uint32_t _preambleLength;
int _implicitHeaderMode;
int _payloadLength;
int _crcMode;
int _fifo_tx_addr_ptr;
int _fifo_rx_addr_ptr;
uint8_t _packet[256];
bool _preinit_done;
int _rxPacketLength;
void (*_onReceive)(int);
};
extern sx128x sx128x_modem;
#endif
Loading…
Cancel
Save