Inital RadioLib support

This commit is contained in:
jacob.eva 2024-10-03 19:26:51 +01:00
parent 2cdf2951e6
commit f93c400eef
No known key found for this signature in database
GPG Key ID: 0B92E083BBCCAA1E
9 changed files with 886 additions and 3686 deletions

View File

@ -699,7 +699,7 @@
#define HAS_PMU true
#define HAS_NP false
#define HAS_SD false
#define CONFIG_UART_BUFFER_SIZE 40000
#define CONFIG_UART_BUFFER_SIZE 6144
#define CONFIG_QUEUE_0_SIZE 6144
#define CONFIG_QUEUE_MAX_LENGTH 200
#define EEPROM_SIZE 296
@ -711,7 +711,7 @@
#define INTERFACE_COUNT 1
// first interface in list is the primary
const uint8_t interfaces[INTERFACE_COUNT] = {SX126X};
const uint8_t interfaces[INTERFACE_COUNT] = {SX1262};
const bool interface_cfg[INTERFACE_COUNT][3] = {
// SX1262
{
@ -738,10 +738,10 @@
#elif BOARD_VARIANT == MODEL_13 || BOARD_VARIANT == MODEL_14 || BOARD_VARIANT == MODEL_21
#define INTERFACE_COUNT 2
#define CONFIG_QUEUE_1_SIZE 40000
#define CONFIG_QUEUE_1_SIZE 37500
// first interface in list is the primary
const uint8_t interfaces[INTERFACE_COUNT] = {SX126X, SX128X};
const uint8_t interfaces[INTERFACE_COUNT] = {INT_SX1262, INT_SX1280};
const bool interface_cfg[INTERFACE_COUNT][3] = {
// SX1262
{

View File

@ -15,6 +15,7 @@
#include "ROM.h"
#include "Boards.h"
#include <RadioLib.h>
#ifndef CONFIG_H
#define CONFIG_H
@ -95,10 +96,6 @@
bool serial_in_frame = false;
FIFOBuffer packet_rdy_interfaces;
uint8_t packet_rdy_interfaces_buf[MAX_INTERFACES];
// Incoming packet buffer
uint8_t pbuf[MTU];
@ -141,7 +138,57 @@
// Subinterfaces
// select interface 0 by default
uint8_t interface = 0;
RadioInterface* selected_radio;
RadioInterface* interface_obj[INTERFACE_COUNT];
RadioInterface* interface_obj_sorted[INTERFACE_COUNT];
PhysicalLayer* selected_radio;
PhysicalLayer* interface_obj[INTERFACE_COUNT];
PhysicalLayer* interface_obj_sorted[INTERFACE_COUNT];
// \todo move to another file
struct radio_vars {
bool radio_locked = false;
bool radio_online = false;
float st_airtime_limit = 0.0;
float lt_airtime_limit = 0.0;
bool airtime_lock = false;
uint16_t airtime_bins[AIRTIME_BINS] = {0};
uint16_t longterm_bins[AIRTIME_BINS] = {0};
float airtime = 0.0;
float longterm_airtime = 0.0;
float local_channel_util = 0.0;
float total_channel_util = 0.0;
float longterm_channel_util = 0.0;
uint32_t last_status_update = 0;
bool stat_signal_detected = false;
bool stat_signal_synced = false;
bool stat_rx_ongoing = false;
uint32_t last_dcd = 0;
uint16_t dcd_count = 0;
bool dcd = false;
bool dcd_led = false;
bool dcd_waiting = false;
long dcd_wait_until = 0;
bool util_samples[DCD_SAMPLES] = {false};
int dcd_sample = 0;
uint32_t post_tx_yield_timeout = 0;
uint8_t csma_p = 0;
int csma_slot_ms = 50;
float csma_p_min = 0.1;
float csma_p_max = 0.8;
long preamble_length = 0;
float lora_symbol_time_ms = 0.0;
float lora_symbol_rate = 0.0;
float lora_us_per_byte = 0.0;
uint32_t bitrate = 0;
int8_t txp = 0;
uint8_t sf = 0;
uint8_t cr = 0;
float bw = 0.0;
float freq = 0.0;
};
struct radio_vars radio_details[INTERFACE_COUNT];
SX1280* sx1280_interfaces[INTERFACE_COUNT];
SX1262* sx1262_interfaces[INTERFACE_COUNT];
volatile bool tx_flag = false;
#endif

View File

@ -378,11 +378,11 @@ void draw_bt_icon(int px, int py) {
}
}
void draw_lora_icon(RadioInterface* radio, int px, int py) {
void draw_lora_icon(uint8_t index, int px, int py) {
// todo: make display show other interfaces
if (radio_online) {
#if DISPLAY == OLED
if (online_interface_list[interface_page] != radio->getIndex()) {
if (online_interface_list[interface_page] != index) {
stat_area.drawBitmap(px - 1, py-1, bm_dot_sqr, 18, 19, SSD1306_WHITE, SSD1306_BLACK);
// redraw stat area on next refresh
@ -394,13 +394,13 @@ void draw_lora_icon(RadioInterface* radio, int px, int py) {
stat_area.drawBitmap(px, py, bm_rf+0*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
}
#elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
if (online_interface_list[interface_page] != radio->getIndex()) {
if (online_interface_list[interface_page] != index) {
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()) {
if (radio_details[index].radio_online) {
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);
@ -417,7 +417,7 @@ void draw_lora_icon(RadioInterface* radio, int px, int py) {
void draw_mw_icon(int px, int py) {
if (INTERFACE_COUNT >= 2) {
if (interface_obj[1]->getRadioOnline()) {
if (radio_details[1].radio_online) {
#if DISPLAY == OLED
stat_area.drawBitmap(px, py, bm_rf+3*32, 16, 16, SSD1306_WHITE, SSD1306_BLACK);
#elif DISP_H == 122 && (DISPLAY == EINK_BW || DISPLAY == EINK_3C)
@ -527,7 +527,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)interface_obj[interface_page]->getSpreadingFactor()*Q_SNR_STEP;
float snr_min = Q_SNR_MIN_BASE-(int)radio_details[interface_page].sf*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;
@ -641,7 +641,8 @@ void draw_signal_bars(int px, int py) {
#define WF_PIXEL_WIDTH 22
#endif
void draw_waterfall(int px, int py) {
int rssi_val = interface_obj[interface_page]->currentRssi();
// \todo, this may not work, check
int rssi_val = interface_obj[interface_page]->getRSSI();
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;
@ -681,9 +682,9 @@ void draw_stat_area() {
if (millis()-last_interface_page_flip >= page_interval) {
int online_interfaces_check = 0;
// todo, is there a more efficient way of doing this?
// \todo, is there a more efficient way of doing this?
for (int i = 0; i < INTERFACE_COUNT; i++) {
if (interface_obj[i]->getRadioOnline()) {
if (radio_details[i].radio_online) {
online_interfaces_check++;
}
}
@ -701,7 +702,7 @@ void draw_stat_area() {
uint8_t index = 0;
for (int i = 0; i < INTERFACE_COUNT; i++) {
if (interface_obj[i]->getRadioOnline()) {
if (radio_details[i].radio_online) {
online_interface_list[index] = i;
index++;
}
@ -716,27 +717,27 @@ void draw_stat_area() {
#if DISPLAY == OLED
draw_cable_icon(3, 8);
draw_bt_icon(3, 30);
draw_lora_icon(interface_obj[0], 45, 8);
draw_lora_icon(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_lora_icon(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(interface_obj[0], 86, 18);
draw_lora_icon(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_lora_icon(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()) {
if (radio_details[i].radio_online) {
radio_online = true;
break;
}
@ -876,7 +877,6 @@ void draw_disp_area() {
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[online_interface_list[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
@ -886,25 +886,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)(selected_radio->getBitrate())/1000.0);
disp_area.printf("%.1fKbps", (float)(radio_details[online_interface_list[interface_page]].bitrate)/1000.0);
disp_area.setCursor(2, 36);
disp_area.print("Airtime:");
disp_area.setCursor(7+12, 53);
if (selected_radio->getTotalChannelUtil() < 0.099) {
disp_area.printf("%.1f%%", selected_radio->getAirtime()*100.0);
if (radio_details[online_interface_list[interface_page]].total_channel_util < 0.099) {
disp_area.printf("%.1f%%", radio_details[online_interface_list[interface_page]].airtime*100.0);
} else {
disp_area.printf("%.0f%%", selected_radio->getAirtime()*100.0);
disp_area.printf("%.0f%%", radio_details[online_interface_list[interface_page]].airtime*100.0);
}
disp_area.drawBitmap(2, 41, bm_hg_low, 10, 18, GxEPD_WHITE, GxEPD_BLACK);
disp_area.setCursor(64+17, 53);
if (selected_radio->getLongtermChannelUtil() < 0.099) {
disp_area.printf("%.1f%%", selected_radio->getLongtermAirtime()*100.0);
if (radio_details[online_interface_list[interface_page]].longterm_channel_util < 0.099) {
disp_area.printf("%.1f%%", radio_details[online_interface_list[interface_page]].longterm_airtime*100.0);
} else {
disp_area.printf("%.0f%%", selected_radio->getLongtermAirtime()*100.0);
disp_area.printf("%.0f%%", radio_details[online_interface_list[interface_page]].longterm_airtime*100.0);
}
disp_area.drawBitmap(64, 41, bm_hg_high, 10, 18, GxEPD_WHITE, GxEPD_BLACK);
@ -916,18 +916,18 @@ void draw_disp_area() {
disp_area.print("Load:");
disp_area.setCursor(7+12, 110);
if (selected_radio->getTotalChannelUtil() < 0.099) {
disp_area.printf("%.1f%%", selected_radio->getTotalChannelUtil()*100.0);
if (radio_details[online_interface_list[interface_page]].total_channel_util < 0.099) {
disp_area.printf("%.1f%%", radio_details[online_interface_list[interface_page]].total_channel_util*100.0);
} else {
disp_area.printf("%.0f%%", selected_radio->getTotalChannelUtil()*100.0);
disp_area.printf("%.0f%%", radio_details[online_interface_list[interface_page]].total_channel_util*100.0);
}
disp_area.drawBitmap(2, 98, bm_hg_low, 10, 18, GxEPD_BLACK, GxEPD_WHITE);
disp_area.setCursor(64+17, 110);
if (selected_radio->getLongtermChannelUtil() < 0.099) {
disp_area.printf("%.1f%%", selected_radio->getLongtermChannelUtil()*100.0);
if (radio_details[online_interface_list[interface_page]].longterm_channel_util < 0.099) {
disp_area.printf("%.1f%%", radio_details[online_interface_list[interface_page]].longterm_channel_util*100.0);
} else {
disp_area.printf("%.0f%%", selected_radio->getLongtermChannelUtil()*100.0);
disp_area.printf("%.0f%%", radio_details[online_interface_list[interface_page]].longterm_channel_util*100.0);
}
disp_area.drawBitmap(64, 98, bm_hg_high, 10, 18, GxEPD_BLACK, GxEPD_WHITE);
#endif

View File

@ -15,7 +15,7 @@
#ifndef FRAMING_H
#define FRAMING_H
#define CMD_DEBUG 0xFD
#define FEND 0xC0
#define FESC 0xDB
#define TFEND 0xDC
@ -80,7 +80,7 @@
#define CMD_INT1_DATA 0x10
#define CMD_INT2_DATA 0x20
#define CMD_INT3_DATA 0x70
#define CMD_INT4_DATA 0x80
#define CMD_INT4_DATA 0x75
#define CMD_INT5_DATA 0x90
#define CMD_INT6_DATA 0xA0
#define CMD_INT7_DATA 0xB0
@ -92,8 +92,8 @@
#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_INT3 0x74
#define CMD_SEL_INT4 0x7F
#define CMD_SEL_INT5 0x9F
#define CMD_SEL_INT6 0xAF
#define CMD_SEL_INT7 0xBF

View File

@ -1,7 +1,7 @@
#define SX127X 0x00
#define SX1276 0x01
#define SX1278 0x02
#define SX126X 0x10
#define SX1262 0x11
#define SX128X 0x20
#define SX1280 0x21
#define INT_SX127X 0x00
#define INT_SX1276 0x01
#define INT_SX1278 0x02
#define INT_SX126X 0x10
#define INT_SX1262 0x11
#define INT_SX128X 0x20
#define INT_SX1280 0x21

View File

@ -16,6 +16,16 @@
#include <Arduino.h>
#include <SPI.h>
#include "Utilities.h"
#include <RadioLib.h>
#if PLATFORM == PLATFORM_ESP32
#if defined(ESP32) and !defined(CONFIG_IDF_TARGET_ESP32S3)
#include "soc/rtc_wdt.h"
#endif
#define ISR_VECT IRAM_ATTR
#else
#define ISR_VECT
#endif
#if MCU_VARIANT == MCU_NRF52
#define INTERFACE_SPI
@ -127,12 +137,12 @@ void setup() {
pinMode(pin_led_tx, OUTPUT);
#endif
for (int i = 0; i < INTERFACE_COUNT; i++) {
if (interface_pins[i][9] != -1) {
pinMode(interface_pins[i][9], OUTPUT);
digitalWrite(interface_pins[i][9], HIGH);
}
}
// for (int i = 0; i < INTERFACE_COUNT; i++) {
// if (interface_pins[i][9] != -1) {
// pinMode(interface_pins[i][9], OUTPUT);
// digitalWrite(interface_pins[i][9], HIGH);
// }
// }
// Initialise buffers
memset(pbuf, 0, sizeof(pbuf));
@ -147,75 +157,206 @@ void setup() {
packet_queue[i] = (uint8_t*)malloc(getQueueSize(i)+1);
}
memset(packet_rdy_interfaces_buf, 0, sizeof(packet_rdy_interfaces_buf));
fifo_init(&packet_rdy_interfaces, packet_rdy_interfaces_buf, MAX_INTERFACES);
// Create and configure interface objects
int16_t status;
for (uint8_t i = 0; i < INTERFACE_COUNT; i++) {
switch (interfaces[i]) {
case SX126X:
case SX1262:
//case SX126X:
case INT_SX1262:
{
sx126x* obj;
// if default spi enabled
SX1262* radio;
// If default SPI
if (interface_cfg[i][0]) {
obj = new sx126x(i, &SPI, interface_cfg[i][1],
interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1],
interface_pins[i][2], interface_pins[i][3], interface_pins[i][6],
interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]);
radio = new SX1262(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], interface_pins[i][4]));
} else {
interface_spi[0].begin();
radio = new SX1262(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], interface_pins[i][4], interface_spi[0]));
}
else {
obj = new sx126x(i, &interface_spi[i], interface_cfg[i][1],
interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1],
interface_pins[i][2], interface_pins[i][3], interface_pins[i][6],
interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]);
if (interface_pins[i][8] != -1) {
// Enable antenna power
pinMode(interface_pins[i][8], OUTPUT);
digitalWrite(interface_pins[i][8], HIGH);
}
interface_obj[i] = (PhysicalLayer*)radio;
interface_obj_sorted[i] = (PhysicalLayer*)radio;
struct radio_vars* config = &radio_details[i];
// Init default modulation parameters
config->freq = 434.0;
config->sf = 5;
config->cr = 5;
config->bw = 125.0;
status = radio->begin(config->freq, config->bw, config->sf, config->cr, 0x14, config->txp);
radio->setDio1Action(packet_received);
if (status == RADIOLIB_ERR_NONE) {
status = radio->explicitHeader();
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->setCRC(2);
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->sleep();
}
if (status == RADIOLIB_ERR_NONE) {
modems_installed = true;
}
interface_obj[i] = obj;
interface_obj_sorted[i] = obj;
break;
}
case SX127X:
case SX1276:
case SX1278:
// \todo CURRENTLY NOT SUPPORTED DUE TO REQUIREMENT FOR DIO1 pin in RadioLib, should be fixed soon...
/*case INT_SX1272:
{
sx127x* obj;
// if default spi enabled
SX1272* radio;
if (interface_cfg[i][0]) {
obj = new sx127x(i, &SPI, interface_pins[i][0],
interface_pins[i][1], interface_pins[i][2], interface_pins[i][3],
interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]);
}
else {
obj = new sx127x(i, &interface_spi[i], interface_pins[i][0],
interface_pins[i][1], interface_pins[i][2], interface_pins[i][3],
interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]);
}
interface_obj[i] = obj;
interface_obj_sorted[i] = obj;
break;
radio = new SX1272(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], RADIOLIB_NC));
} else {
interface_spi[0].begin();
radio = new SX1272(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], RADIOLIB_NC, interface_spi[0]));
}
case SX128X:
case SX1280:
interface_obj[i] = (PhysicalLayer*)radio;
interface_obj_sorted[i] = (PhysicalLayer*)radio;
struct radio_vars* config = &radio_details[i];
// Init default modulation parameters
config->freq = 915.0;
config->sf = 7;
config->cr = 7;
config->bw = 125.0;
status = radio->begin(config->freq, config->bw, config->sf, config->cr, 0x12, config->txp);
radio->setDio1Action(packet_received);
if (status == RADIOLIB_ERR_NONE) {
status = radio->explicitHeader();
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->setCRC(2);
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->sleep();
}
if (status == RADIOLIB_ERR_NONE) {
modems_installed = true;
}
break;
}
case INT_SX1276:
{
sx128x* obj;
// if default spi enabled
SX1276* radio;
if (interface_cfg[i][0]) {
obj = new sx128x(i, &SPI, interface_cfg[i][1],
interface_pins[i][0], interface_pins[i][1], interface_pins[i][2],
interface_pins[i][3], interface_pins[i][6], interface_pins[i][5],
interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]);
radio = new SX1276(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], RADIOLIB_NC));
} else {
interface_spi[0].begin();
}radio = new SX1276(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], RADIOLIB_NC, interface_spi[0]));
}
else {
obj = new sx128x(i, &interface_spi[i], interface_cfg[i][1],
interface_pins[i][0], interface_pins[i][1], interface_pins[i][2],
interface_pins[i][3], interface_pins[i][6], interface_pins[i][5],
interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]);
interface_obj[i] = (PhysicalLayer*)radio;
interface_obj_sorted[i] = (PhysicalLayer*)radio;
struct radio_vars* config = &radio_details[i];
// Init default modulation parameters
config->freq = 434.0;
config->sf = 7;
config->cr = 7;
config->bw = 125.0;
status = radio->begin(config->freq, config->bw, config->sf, config->cr, 0x12, config->txp);
radio->setDio1Action(packet_received);
if (status == RADIOLIB_ERR_NONE) {
status = radio->explicitHeader();
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->setCRC(2);
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->sleep();
}
if (status == RADIOLIB_ERR_NONE) {
modems_installed = true;
}
break;
}
case INT_SX1278:
{
SX1278* radio;
if (interface_cfg[i][0]) {
radio = new SX1278(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], RADIOLIB_NC));
} else {
interface_spi[0].begin();
radio = new SX1278(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], RADIOLIB_NC, interface_spi[0]));
}
interface_obj[i] = (PhysicalLayer*)radio;
interface_obj_sorted[i] = (PhysicalLayer*)radio;
struct radio_vars* config = &radio_details[i];
// Init default modulation parameters
config->freq = 434.0;
config->sf = 7;
config->cr = 7;
config->bw = 125.0;
status = radio->begin(config->freq, config->bw, config->sf, config->cr, 0x12, config->txp);
radio->setDio1Action(packet_received);
if (status == RADIOLIB_ERR_NONE) {
status = radio->explicitHeader();
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->setCRC(2);
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->sleep();
}
if (status == RADIOLIB_ERR_NONE) {
modems_installed = true;
}
break;
}
*/
case INT_SX1280:
{
SX1280* radio;
// If default SPI
if (interface_cfg[i][0]) {
radio = new SX1280(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], interface_pins[i][4]));
} else {
interface_spi[0].begin();
radio = new SX1280(new Module(interface_pins[i][0], interface_pins[i][5], interface_pins[i][6], interface_pins[i][4], interface_spi[0]));
}
// If TXEN and RXEN pins set
if (interface_pins[i][8] != -1 && interface_pins[i][7] != -1) {
radio->setRfSwitchPins(interface_pins[i][8], interface_pins[i][7]);
}
interface_obj[i] = (PhysicalLayer*)radio;
interface_obj_sorted[i] = (PhysicalLayer*)radio;
struct radio_vars* config = &radio_details[i];
// Init default modulation parameters
config->freq = 2401.0;
config->sf = 5;
config->cr = 5;
config->bw = 203.125;
status = radio->begin(config->freq, config->bw, config->sf, config->cr, 0x14, config->txp);
radio->setDio1Action(packet_received);
if (status == RADIOLIB_ERR_NONE) {
status = radio->explicitHeader();
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->setCRC(2);
}
if (status == RADIOLIB_ERR_NONE) {
status = radio->sleep();
}
if (status == RADIOLIB_ERR_NONE) {
modems_installed = true;
}
interface_obj[i] = obj;
interface_obj_sorted[i] = obj;
break;
}
@ -226,45 +367,51 @@ void setup() {
// Check installed transceiver chip(s) and probe boot parameters. If any of
// the configured modems cannot be initialised, do not boot
for (int i = 0; i < INTERFACE_COUNT; i++) {
/*for (int i = 0; i < INTERFACE_COUNT; i++) {
int16_t status = 0;
switch (interfaces[i]) {
case SX126X:
case SX1262:
// \todo
break;
case SX127X:
case SX1276:
case SX1278:
// \todo
break;
case SX128X:
case SX1280:
selected_radio = interface_obj[i];
status = interface_obj[i]->begin();
break;
default:
modems_installed = false;
break;
}
if (selected_radio->preInit()) {
modems_installed = true;
uint32_t lfr = selected_radio->getFrequency();
if (lfr == 0) {
// Normal boot
} else if (lfr == M_FRQ_R) {
// Quick reboot
#if HAS_CONSOLE
if (rtc_get_reset_reason(0) == POWERON_RESET) {
console_active = true;
}
#endif
} else {
// Unknown boot
}
selected_radio->setFrequency(M_FRQ_S);
} else {
modems_installed = false;
}
if (!modems_installed) {
break;
}
}
if (status == RADIOLIB_ERR_NONE) {
modems_installed = true;*/
// \todo, fixme
//uint32_t lfr = selected_radio->getFrequency();
//if (lfr == 0) {
// // Normal boot
//} else if (lfr == M_FRQ_R) {
// // Quick reboot
// #if HAS_CONSOLE
// if (rtc_get_reset_reason(0) == POWERON_RESET) {
// console_active = true;
// }
// #endif
//} else {
// // Unknown boot
//}
// } else {
// modems_installed = false;
// }
// if (!modems_installed) {
// break;
// }
//}
//modems_installed = true;
#if HAS_DISPLAY
#if HAS_EEPROM
@ -307,11 +454,16 @@ void setup() {
validate_status();
}
void lora_receive(RadioInterface* radio) {
if (!implicit) {
radio->receive();
void ISR_VECT packet_received() {
if (!tx_flag) {
for (int i = 0; i < INTERFACE_COUNT; i++) {
if (digitalRead(interface_pins[i][5])) {
receive_callback(interface_obj[i], i, interface_obj[i]->getPacketLength());
break;
}
}
} else {
radio->receive(implicit_l);
tx_flag = false;
}
}
@ -335,15 +487,16 @@ inline void kiss_write_packet(int index) {
packet_ready = false;
}
inline void getPacketData(RadioInterface* radio, uint16_t len) {
while (len-- && read_len < MTU) {
pbuf[read_len++] = radio->read();
}
inline void getPacketData(uint8_t* data, uint16_t len) {
memcpy(pbuf+read_len, data, len);
read_len += len;
}
void receive_callback(uint8_t index, int packet_size) {
void receive_callback(PhysicalLayer* radio, uint8_t index, int packet_size) {
selected_radio = interface_obj[index];
bool ready = false;
uint8_t tempbuf[MAX_PKT_LENGTH];
if (!promisc) {
// The standard operating mode allows large
// packets with a payload up to 500 bytes,
@ -351,7 +504,11 @@ void receive_callback(uint8_t index, int packet_size) {
// We read the 1-byte header and extract
// packet sequence number and split flags
uint8_t header = selected_radio->read(); packet_size--;
radio->readData(tempbuf, packet_size);
uint8_t header = tempbuf[0]; packet_size--;
uint8_t* tempbufp = tempbuf;
tempbufp++;
uint8_t sequence = packetSequence(header);
if (isSplitPacket(header) && seq == SEQ_UNSET) {
@ -361,14 +518,14 @@ void receive_callback(uint8_t index, int packet_size) {
read_len = 0;
seq = sequence;
getPacketData(selected_radio, packet_size);
getPacketData(tempbufp, packet_size);
} else if (isSplitPacket(header) && seq == sequence) {
// This is the second part of a split
// packet, so we add it to the buffer
// and set the ready flag.
getPacketData(selected_radio, packet_size);
getPacketData(tempbufp, packet_size);
seq = SEQ_UNSET;
packet_interface = index;
@ -382,7 +539,7 @@ void receive_callback(uint8_t index, int packet_size) {
read_len = 0;
seq = sequence;
getPacketData(selected_radio, packet_size);
getPacketData(tempbufp, packet_size);
} else if (!isSplitPacket(header)) {
// This is not a split packet, so we
@ -396,7 +553,7 @@ void receive_callback(uint8_t index, int packet_size) {
seq = SEQ_UNSET;
}
getPacketData(selected_radio, packet_size);
getPacketData(tempbufp, packet_size);
packet_interface = index;
packet_ready = true;
@ -405,22 +562,57 @@ void receive_callback(uint8_t index, int packet_size) {
// In promiscuous mode, raw packets are
// output directly to the host
read_len = 0;
radio->readData(tempbuf, packet_size);
getPacketData(selected_radio, packet_size);
getPacketData(tempbuf, packet_size);
packet_interface = index;
packet_ready = true;
}
radio->startReceive();
last_rx = millis();
}
bool startRadio(RadioInterface* radio) {
update_radio_lock(radio);
bool startRadio(PhysicalLayer* radio, uint8_t index) {
struct radio_vars* config = &radio_details[index];
//update_radio_lock(radio);
if (modems_installed && !console_active) {
if (!radio->getRadioLock() && hw_ready) {
if (!radio->begin()) {
//if (modems_installed && !console_active) {
//if (!radio->getRadioLock() && hw_ready) {
int16_t status = 0;
switch (interfaces[index]) {
case INT_SX1262:
// wake up module
digitalWrite(interface_pins[index][0], LOW);
delay(10);
digitalWrite(interface_pins[index][0], HIGH);
status = radio->standby();
update_radio_params(radio, config);
radio->setFrequency(config->freq);
break;
case INT_SX1276:
case INT_SX1278:
// \todo
break;
case INT_SX1280:
// wake up module
digitalWrite(interface_pins[index][0], LOW);
delay(10);
digitalWrite(interface_pins[index][0], HIGH);
status = radio->standby();
update_radio_params(radio, config);
radio->setFrequency(config->freq);
break;
default:
modems_installed = false;
break;
}
if (status != RADIOLIB_ERR_NONE) {
// The radio could not be started.
// Indicate this failure over both the
// serial port and with the onboard LEDs
@ -428,64 +620,72 @@ bool startRadio(RadioInterface* radio) {
led_indicate_error(0);
return false;
} else {
radio->enableCrc();
radio->onReceive(receive_callback);
radio->updateBitrate();
sort_interfaces();
kiss_indicate_phy_stats(radio);
lora_receive(radio);
status = radio->startReceive();
if (status == RADIOLIB_ERR_NONE) {
config->radio_online = true;
update_bitrate(radio, index);
kiss_indicate_phy_stats(index);
}
else {
// RX failed
kiss_indicate_error(ERROR_INITRADIO);
led_indicate_error(0);
return false;
}
// \todo enable again
//sort_interfaces();
// Flash an info pattern to indicate
// that the radio is now on
kiss_indicate_radiostate(radio);
kiss_indicate_radiostate(index);
led_indicate_info(3);
return true;
}
} else {
// Flash a warning pattern to indicate
// that the radio was locked, and thus
// not started
kiss_indicate_radiostate(radio);
led_indicate_warning(3);
return false;
}
} else {
// If radio is already on, we silently
// ignore the request.
kiss_indicate_radiostate(radio);
return true;
}
//} else {
// // Flash a warning pattern to indicate
// // that the radio was locked, and thus
// // not started
// kiss_indicate_radiostate(radio);
// led_indicate_warning(3);
// return false;
//}
//} else {
// // If radio is already on, we silently
// // ignore the request.
// kiss_indicate_radiostate(radio);
// return true;
//}
}
void stopRadio(RadioInterface* radio) {
radio->end();
sort_interfaces();
kiss_indicate_radiostate(radio);
void stopRadio(PhysicalLayer* radio, uint8_t index) {
struct radio_vars* config = &radio_details[index];
radio->sleep();
config->radio_online = false;
// \todo finish
//sort_interfaces();
//kiss_indicate_radiostate(radio);
}
void update_radio_lock(RadioInterface* radio) {
if (radio->getFrequency() != 0 && radio->getSignalBandwidth() != 0 && radio->getTxPower() != 0xFF && radio->getSpreadingFactor() != 0) {
radio->setRadioLock(false);
} else {
radio->setRadioLock(true);
}
void update_radio_lock(PhysicalLayer* radio, uint8_t index) {
// \todo finish
//if (radio->getFrequency() != 0 && radio->getSignalBandwidth() != 0 && radio->getTxPower() != 0xFF && radio->getSpreadingFactorVal() != 0) {
radio_details[index].radio_locked = false;
//} else {
// radio->setRadioLock(true);
//}
}
// Check if the queue is full for the selected radio.
// Returns true if full, false if not
bool queueFull(RadioInterface* radio) {
return (queue_height[radio->getIndex()] >= (CONFIG_QUEUE_MAX_LENGTH) || queued_bytes[radio->getIndex()] >= (getQueueSize(radio->getIndex())));
bool queueFull(uint8_t index) {
return (queue_height[index] >= (CONFIG_QUEUE_MAX_LENGTH) || queued_bytes[index] >= (getQueueSize(index)));
}
volatile bool queue_flushing = false;
// Flushes all packets for the interface
void flushQueue(RadioInterface* radio) {
uint8_t index = radio->getIndex();
void flushQueue(PhysicalLayer* radio, uint8_t index) {
if (!queue_flushing) {
queue_flushing = true;
@ -502,51 +702,64 @@ void flushQueue(RadioInterface* radio) {
uint16_t pos = (start+i)%(getQueueSize(index));
tbuf[i] = packet_queue[index][pos];
}
transmit(radio, length);
transmit(radio, index, length);
processed++;
}
}
lora_receive(radio);
radio->startReceive();
led_tx_off();
radio->setPostTxYieldTimeout(millis()+(lora_post_tx_yield_slots*selected_radio->getCSMASlotMS()));
radio_details[index].post_tx_yield_timeout = millis()+(lora_post_tx_yield_slots*(radio_details[index].csma_slot_ms));
}
queue_height[index] = 0;
queued_bytes[index] = 0;
selected_radio->updateAirtime();
update_airtime(index);
queue_flushing = false;
}
void transmit(RadioInterface* radio, uint16_t size) {
if (radio->getRadioOnline()) {
void transmit(PhysicalLayer* radio, uint8_t index, uint16_t size) {
if (radio_details[index].radio_online) {
int16_t status;
if (!promisc) {
uint16_t written = 0;
uint8_t header = random(256) & 0xF0;
uint8_t txbuf[SINGLE_MTU] = {0};
if (size > SINGLE_MTU - HEADER_L) {
header = header | FLAG_SPLIT;
}
radio->beginPacket();
radio->write(header); written++;
txbuf[0] = header; written++;
for (uint16_t i=0; i < size; i++) {
radio->write(tbuf[i]);
txbuf[written] = tbuf[i];
written++;
if (written == 255) {
radio->endPacket(); radio->addAirtime(written);
radio->beginPacket();
radio->write(header);
tx_flag = true;
status = radio->transmit(txbuf, written); add_airtime(index, written);
if (status != RADIOLIB_ERR_NONE) {
serial_write(status);
kiss_indicate_error(ERROR_TXFAILED);
led_indicate_error(5);
}
txbuf[0] = header;
written = 1;
}
}
radio->endPacket(); radio->addAirtime(written);
tx_flag = true;
status = radio->transmit(txbuf, written); add_airtime(index, written);
if (status != RADIOLIB_ERR_NONE) {
serial_write(status >> 8);
serial_write(status & 0x00FF);
kiss_indicate_error(ERROR_TXFAILED);
led_indicate_error(5);
}
} else {
// In promiscuous mode, we only send out
// plain raw LoRa packets with a maximum
@ -559,20 +772,16 @@ void transmit(RadioInterface* radio, uint16_t size) {
size = SINGLE_MTU;
}
// \todo check this with radiolib
// If implicit header mode has been set,
// set packet length to payload data length
if (!implicit) {
radio->beginPacket();
} else {
radio->beginPacket(size);
}
//if (!implicit) {
// radio->beginPacket();
//} else {
// radio->beginPacket(size);
//}
for (uint16_t i=0; i < size; i++) {
radio->write(tbuf[i]);
written++;
}
radio->endPacket(); radio->addAirtime(written);
radio->transmit(tbuf, size); add_airtime(index, written);
}
last_tx = millis();
} else {
@ -696,10 +905,17 @@ void serialCallback(uint8_t sbyte) {
selected_radio = interface_obj[interface];
if (freq == 0) {
kiss_indicate_frequency(selected_radio);
kiss_indicate_frequency(interface);
} else {
if (op_mode == MODE_HOST) selected_radio->setFrequency(freq);
kiss_indicate_frequency(selected_radio);
int16_t status = RADIOLIB_ERR_NONE;
float freq_f = freq / 1000000.0;
if (radio_details[interface].radio_online) {
if (op_mode == MODE_HOST) status = selected_radio->setFrequency(freq_f);
}
if (status == RADIOLIB_ERR_NONE) {
radio_details[interface].freq = freq_f;
}
kiss_indicate_frequency(interface);
}
interface = 0;
}
@ -721,13 +937,18 @@ void serialCallback(uint8_t sbyte) {
selected_radio = interface_obj[interface];
if (bw == 0) {
kiss_indicate_bandwidth(selected_radio);
kiss_indicate_bandwidth(interface);
} else {
if (op_mode == MODE_HOST) selected_radio->setSignalBandwidth(bw);
selected_radio->updateBitrate();
float bw_f = bw / 1000.0;
if (radio_details[interface].radio_online) {
if (op_mode == MODE_HOST) set_bandwidth(selected_radio, interface, bw_f);
update_bitrate(selected_radio, interface);
kiss_indicate_phy_stats(interface);
} else {
radio_details[interface].bw = bw_f;
}
sort_interfaces();
kiss_indicate_bandwidth(selected_radio);
kiss_indicate_phy_stats(selected_radio);
kiss_indicate_bandwidth(interface);
}
interface = 0;
}
@ -735,45 +956,57 @@ void serialCallback(uint8_t sbyte) {
selected_radio = interface_obj[interface];
if (sbyte == 0xFF) {
kiss_indicate_txpower(selected_radio);
kiss_indicate_txpower(interface);
} else {
int8_t txp = (int8_t)sbyte;
if (op_mode == MODE_HOST) setTXPower(selected_radio, txp);
kiss_indicate_txpower(selected_radio);
if (radio_details[interface].radio_online) {
if (op_mode == MODE_HOST) setTXPower(selected_radio, interface, txp);
} else {
radio_details[interface].txp = txp;
}
kiss_indicate_txpower(interface);
}
interface = 0;
} else if (command == CMD_SF) {
selected_radio = interface_obj[interface];
if (sbyte == 0xFF) {
kiss_indicate_spreadingfactor(selected_radio);
kiss_indicate_spreadingfactor(interface);
} else {
int sf = sbyte;
if (sf < 5) sf = 5;
if (sf > 12) sf = 12;
if (op_mode == MODE_HOST) selected_radio->setSpreadingFactor(sf);
selected_radio->updateBitrate();
if (radio_details[interface].radio_online) {
if (op_mode == MODE_HOST) set_spreading_factor(selected_radio, interface, sf);
update_bitrate(selected_radio, interface);
kiss_indicate_phy_stats(interface);
} else {
radio_details[interface].sf = sf;
}
sort_interfaces();
kiss_indicate_spreadingfactor(selected_radio);
kiss_indicate_phy_stats(selected_radio);
kiss_indicate_spreadingfactor(interface);
}
interface = 0;
} else if (command == CMD_CR) {
selected_radio = interface_obj[interface];
if (sbyte == 0xFF) {
kiss_indicate_codingrate(selected_radio);
kiss_indicate_codingrate(interface);
} else {
int cr = sbyte;
if (cr < 5) cr = 5;
if (cr > 8) cr = 8;
if (op_mode == MODE_HOST) selected_radio->setCodingRate4(cr);
selected_radio->updateBitrate();
if (radio_details[interface].radio_online) {
if (op_mode == MODE_HOST) set_coding_rate(selected_radio, interface, cr);
update_bitrate(selected_radio, interface);
kiss_indicate_phy_stats(interface);
} else {
radio_details[interface].cr = cr;
}
sort_interfaces();
kiss_indicate_codingrate(selected_radio);
kiss_indicate_phy_stats(selected_radio);
kiss_indicate_codingrate(interface);
}
interface = 0;
} else if (command == CMD_IMPLICIT) {
@ -791,11 +1024,11 @@ void serialCallback(uint8_t sbyte) {
selected_radio = interface_obj[interface];
if (bt_state != BT_STATE_CONNECTED) cable_state = CABLE_STATE_CONNECTED;
if (sbyte == 0xFF) {
kiss_indicate_radiostate(selected_radio);
kiss_indicate_radiostate(interface);
} else if (sbyte == 0x00) {
stopRadio(selected_radio);
stopRadio(selected_radio, interface);
} else if (sbyte == 0x01) {
startRadio(selected_radio);
startRadio(selected_radio, interface);
}
interface = 0;
} else if (command == CMD_ST_ALOCK) {
@ -815,13 +1048,13 @@ void serialCallback(uint8_t sbyte) {
uint16_t at = (uint16_t)cmdbuf[0] << 8 | (uint16_t)cmdbuf[1];
if (at == 0) {
selected_radio->setSTALock(0.0);
radio_details[interface].st_airtime_limit = 0.0;
} else {
int st_airtime_limit = (float)at/(100.0*100.0);
if (st_airtime_limit >= 1.0) { st_airtime_limit = 0.0; }
selected_radio->setSTALock(st_airtime_limit);
radio_details[interface].st_airtime_limit = st_airtime_limit;
}
kiss_indicate_st_alock(selected_radio);
kiss_indicate_st_alock(interface);
}
interface = 0;
} else if (command == CMD_LT_ALOCK) {
@ -841,13 +1074,13 @@ void serialCallback(uint8_t sbyte) {
uint16_t at = (uint16_t)cmdbuf[0] << 8 | (uint16_t)cmdbuf[1];
if (at == 0) {
selected_radio->setLTALock(0.0);
radio_details[interface].lt_airtime_limit = 0.0;
} else {
int lt_airtime_limit = (float)at/(100.0*100.0);
if (lt_airtime_limit >= 1.0) { lt_airtime_limit = 0.0; }
selected_radio->setLTALock(lt_airtime_limit);
radio_details[interface].lt_airtime_limit = lt_airtime_limit;
}
kiss_indicate_lt_alock(selected_radio);
kiss_indicate_lt_alock(interface);
}
interface = 0;
} else if (command == CMD_STAT_RX) {
@ -858,8 +1091,8 @@ void serialCallback(uint8_t sbyte) {
kiss_indicate_stat_rssi();
} else if (command == CMD_RADIO_LOCK) {
selected_radio = interface_obj[interface];
update_radio_lock(selected_radio);
kiss_indicate_radio_lock(selected_radio);
update_radio_lock(selected_radio, interface);
kiss_indicate_radio_lock(interface);
interface = 0;
} else if (command == CMD_BLINK) {
led_indicate_info(sbyte);
@ -883,7 +1116,7 @@ void serialCallback(uint8_t sbyte) {
kiss_indicate_promisc();
} else if (command == CMD_READY) {
selected_radio = interface_obj[interface];
if (!queueFull(selected_radio)) {
if (!queueFull(interface)) {
kiss_indicate_ready();
} else {
kiss_indicate_not_ready();
@ -1175,8 +1408,8 @@ void loop() {
#elif MCU_VARIANT == MCU_NRF52
portENTER_CRITICAL();
#endif
last_rssi = selected_radio->packetRssi();
last_snr_raw = selected_radio->packetSnrRaw();
last_rssi = selected_radio->getRSSI();
last_snr_raw = selected_radio->getSNR(); // \todo, this is not the raw value, a conversion will be required to get this value correct!!
#if MCU_VARIANT == MCU_ESP32
portEXIT_CRITICAL(&update_lock);
#elif MCU_VARIANT == MCU_NRF52
@ -1189,9 +1422,9 @@ void loop() {
bool ready = false;
for (int i = 0; i < INTERFACE_COUNT; i++) {
selected_radio = interface_obj[i];
if (selected_radio->getRadioOnline()) {
selected_radio->checkModemStatus();
struct radio_vars* config = &radio_details[i];
if (config->radio_online) {
check_modem_status(interface_obj[i], i);
ready = true;
}
}
@ -1200,44 +1433,32 @@ void loop() {
// If at least one radio is online then we can continue
if (ready) {
for (int i = 0; i < INTERFACE_COUNT; i++) {
selected_radio = interface_obj_sorted[i];
struct radio_vars* config = &radio_details[i];
selected_radio = interface_obj[i];//_sorted[i];
if (selected_radio->calculateALock() || !selected_radio->getRadioOnline()) {
if (calculate_alock(config) || !config->radio_online) {
// skip this interface
continue;
}
// If a higher data rate interface has received a packet after its
// loop, it still needs to be the first to transmit, so check if this
// is the case.
for (int j = 0; j < INTERFACE_COUNT; j++) {
if (!interface_obj_sorted[j]->calculateALock() && interface_obj_sorted[j]->getRadioOnline()) {
if (interface_obj_sorted[j]->getBitrate() > selected_radio->getBitrate()) {
if (queue_height[interface_obj_sorted[j]->getIndex()] > 0) {
selected_radio = interface_obj_sorted[j];
}
}
}
}
if (queue_height[selected_radio->getIndex()] > 0) {
if (queue_height[i] > 0) {
uint32_t check_time = millis();
if (check_time > selected_radio->getPostTxYieldTimeout()) {
if (selected_radio->getDCDWaiting() && (check_time >= selected_radio->getDCDWaitUntil())) { selected_radio->setDCDWaiting(false); }
if (!selected_radio->getDCDWaiting()) {
if (check_time > config->post_tx_yield_timeout) {
if (config->dcd_waiting && (check_time >= config->dcd_wait_until)) { config->dcd_waiting = false; }
if (!config->dcd_waiting) {
// todo, will the delay here slow down transmission with
// multiple interfaces? needs investigation
for (uint8_t dcd_i = 0; dcd_i < DCD_THRESHOLD*2; dcd_i++) {
delay(STATUS_INTERVAL_MS); selected_radio->updateModemStatus();
delay(STATUS_INTERVAL_MS); update_modem_status(selected_radio, i);
}
if (!selected_radio->getDCD()) {
if (!config->dcd) {
uint8_t csma_r = (uint8_t)random(256);
if (selected_radio->getCSMAp() >= csma_r) {
flushQueue(selected_radio);
if (config->csma_p >= csma_r) {
flushQueue(selected_radio, i);
} else {
selected_radio->setDCDWaiting(true);
selected_radio->setDCDWaitUntil(millis()+selected_radio->getCSMASlotMS());
config->dcd_waiting = true;
config->dcd_wait_until = millis()+config->csma_slot_ms;
}
}
}
@ -1258,7 +1479,7 @@ void loop() {
led_indicate_not_ready();
// shut down all radio interfaces
for (int i = 0; i < INTERFACE_COUNT; i++) {
stopRadio(interface_obj[i]);
stopRadio(interface_obj[i], i);
}
}
}

2663
Radio.cpp

File diff suppressed because it is too large Load Diff

607
Radio.hpp
View File

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

View File

@ -20,6 +20,10 @@
#include <algorithm>
#include <iterator>
// For CSMA
#define _e 2.71828183
#define _S 10.0
#if HAS_EEPROM
#include <EEPROM.h>
#elif PLATFORM == PLATFORM_NRF52
@ -565,16 +569,9 @@ 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);
//std::sort(std::begin(interface_obj_sorted), std::end(interface_obj_sorted), interface_bitrate_cmp);
}
void serial_write(uint8_t byte) {
@ -621,10 +618,10 @@ void kiss_indicate_error(uint8_t error_code) {
serial_write(FEND);
}
void kiss_indicate_radiostate(RadioInterface* radio) {
void kiss_indicate_radiostate(uint8_t index) {
serial_write(FEND);
serial_write(CMD_RADIO_STATE);
serial_write(radio->getRadioOnline());
serial_write(radio_details[index].radio_online);
serial_write(FEND);
}
@ -665,24 +662,25 @@ void kiss_indicate_stat_snr() {
serial_write(FEND);
}
void kiss_indicate_radio_lock(RadioInterface* radio) {
void kiss_indicate_radio_lock(uint8_t index) {
serial_write(FEND);
serial_write(CMD_RADIO_LOCK);
serial_write(radio->getRadioLock());
serial_write(radio_details[index].radio_locked);
serial_write(FEND);
}
void kiss_indicate_spreadingfactor(RadioInterface* radio) {
void kiss_indicate_spreadingfactor(uint8_t index) {
// \todo, add ability to choose FSK when multiple modem mode support added
serial_write(FEND);
serial_write(CMD_SF);
serial_write(radio->getSpreadingFactor());
serial_write(radio_details[index].sf);
serial_write(FEND);
}
void kiss_indicate_codingrate(RadioInterface* radio) {
void kiss_indicate_codingrate(uint8_t index) {
serial_write(FEND);
serial_write(CMD_CR);
serial_write(radio->getCodingRate4());
serial_write(radio_details[index].cr);
serial_write(FEND);
}
@ -693,16 +691,16 @@ void kiss_indicate_implicit_length() {
serial_write(FEND);
}
void kiss_indicate_txpower(RadioInterface* radio) {
int8_t txp = radio->getTxPower();
void kiss_indicate_txpower(uint8_t index) {
int8_t txp = radio_details[index].txp;
serial_write(FEND);
serial_write(CMD_TXPOWER);
serial_write(txp);
serial_write(FEND);
}
void kiss_indicate_bandwidth(RadioInterface* radio) {
uint32_t bw = radio->getSignalBandwidth();
void kiss_indicate_bandwidth(uint8_t index) {
uint32_t bw = radio_details[index].bw * 1000;
serial_write(FEND);
serial_write(CMD_BANDWIDTH);
escaped_serial_write(bw>>24);
@ -712,8 +710,8 @@ void kiss_indicate_bandwidth(RadioInterface* radio) {
serial_write(FEND);
}
void kiss_indicate_frequency(RadioInterface* radio) {
uint32_t freq = radio->getFrequency();
void kiss_indicate_frequency(uint8_t index) {
uint32_t freq = uint32_t(radio_details[index].freq * 1000000);
serial_write(FEND);
serial_write(CMD_FREQUENCY);
escaped_serial_write(freq>>24);
@ -732,8 +730,8 @@ void kiss_indicate_interface(int index) {
serial_write(FEND);
}
void kiss_indicate_st_alock(RadioInterface* radio) {
uint16_t at = (uint16_t)(radio->getSTALock()*100*100);
void kiss_indicate_st_alock(uint8_t index) {
uint16_t at = (uint16_t)(radio_details[index].st_airtime_limit*100*100);
serial_write(FEND);
serial_write(CMD_ST_ALOCK);
escaped_serial_write(at>>8);
@ -741,8 +739,8 @@ void kiss_indicate_st_alock(RadioInterface* radio) {
serial_write(FEND);
}
void kiss_indicate_lt_alock(RadioInterface* radio) {
uint16_t at = (uint16_t)(radio->getLTALock()*100*100);
void kiss_indicate_lt_alock(uint8_t index) {
uint16_t at = (uint16_t)(radio_details[index].lt_airtime_limit*100*100);
serial_write(FEND);
serial_write(CMD_LT_ALOCK);
escaped_serial_write(at>>8);
@ -750,11 +748,11 @@ void kiss_indicate_lt_alock(RadioInterface* radio) {
serial_write(FEND);
}
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);
void kiss_indicate_channel_stats(uint8_t index) {
uint16_t ats = (uint16_t)(radio_details[index].airtime*100*100);
uint16_t atl = (uint16_t)(radio_details[index].longterm_airtime*100*100);
uint16_t cls = (uint16_t)(radio_details[index].total_channel_util*100*100);
uint16_t cll = (uint16_t)(radio_details[index].longterm_channel_util*100*100);
serial_write(FEND);
serial_write(CMD_STAT_CHTM);
escaped_serial_write(ats>>8);
@ -768,12 +766,12 @@ void kiss_indicate_channel_stats(RadioInterface* radio) {
serial_write(FEND);
}
void kiss_indicate_phy_stats(RadioInterface* radio) {
uint16_t lst = (uint16_t)(radio->getSymbolTime()*1000);
uint16_t lsr = (uint16_t)(radio->getSymbolRate());
uint16_t prs = (uint16_t)(radio->getPreambleLength()+4);
uint16_t prt = (uint16_t)((radio->getPreambleLength()+4)*radio->getSymbolTime());
uint16_t cst = (uint16_t)(radio->getCSMASlotMS());
void kiss_indicate_phy_stats(uint8_t index) {
uint16_t lst = (uint16_t)(radio_details[index].lora_symbol_time_ms*1000);
uint16_t lsr = (uint16_t)(radio_details[index].lora_symbol_rate);
uint16_t prs = (uint16_t)(radio_details[index].preamble_length+4);
uint16_t prt = (uint16_t)((radio_details[index].preamble_length+4)*radio_details[index].lora_symbol_time_ms);
uint16_t cst = (uint16_t)(radio_details[index].csma_slot_ms);
serial_write(FEND);
serial_write(CMD_STAT_PHYPRM);
escaped_serial_write(lst>>8);
@ -964,6 +962,203 @@ void kiss_indicate_mcu() {
serial_write(FEND);
}
void update_radio_params(PhysicalLayer* radio, struct radio_vars* config) {
DataRate_t params;
params.lora.spreadingFactor = config->sf;
params.lora.codingRate = config->cr;
params.lora.bandwidth = config->bw;
int16_t status = radio->setDataRate(params);
if (status != RADIOLIB_ERR_NONE) {
kiss_indicate_error(ERROR_INITRADIO);
led_indicate_error(0);
}
}
int16_t set_spreading_factor(PhysicalLayer* radio, uint8_t index, uint8_t sf) {
struct radio_vars* config = &radio_details[index];
config->sf = sf;
update_radio_params(radio, config);
}
int16_t set_coding_rate(PhysicalLayer* radio, uint8_t index, uint8_t cr) {
struct radio_vars* config = &radio_details[index];
config->cr = cr;
update_radio_params(radio, config);
}
int16_t set_bandwidth(PhysicalLayer* radio, uint8_t index, float bw) {
struct radio_vars* config = &radio_details[index];
config->bw = bw;
update_radio_params(radio, config);
}
void update_modem_status(PhysicalLayer* radio, uint8_t index) {
struct radio_vars* config = &radio_details[index];
#if PLATFORM == PLATFORM_ESP32
portENTER_CRITICAL(&update_lock);
#elif PLATFORM == PLATFORM_NRF52
portENTER_CRITICAL();
#endif
// \todo implement again
uint8_t status = 0;//modemStatus();
config->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) { config->stat_signal_detected = true; } else { config->stat_signal_detected = false; }
if ((status & SIG_SYNCED) == SIG_SYNCED) { config->stat_signal_synced = true; } else { config->stat_signal_synced = false; }
if ((status & RX_ONGOING) == RX_ONGOING) { config->stat_rx_ongoing = true; } else { config->stat_rx_ongoing = false; }
if (config->stat_signal_detected || config->stat_signal_synced) {
if (config->stat_rx_ongoing) {
if (config->dcd_count < DCD_THRESHOLD) {
config->dcd_count++;
} else {
config->last_dcd = config->last_status_update;
config->dcd_led = true;
config->dcd = true;
}
}
} else {
if (config->dcd_count == 0) {
config->dcd_led = false;
} else if (config->dcd_count > DCD_LED_STEP_D) {
config->dcd_count -= DCD_LED_STEP_D;
} else {
config->dcd_count = 0;
}
if (config->last_status_update > config->last_dcd+config->csma_slot_ms) {
config->dcd = false;
config->dcd_led = false;
config->dcd_count = 0;
}
}
if (config->dcd_led) {
led_rx_on();
} else {
if (config->airtime_lock) {
led_indicate_airtime_lock();
} else {
led_rx_off();
}
}
};
float csma_slope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); };
void update_csma_p(struct radio_vars* config) {
config->csma_p = (uint8_t)((1.0-(config->csma_p_min+(config->csma_p_max-config->csma_p_min)*csma_slope(config->airtime)))*255.0);
};
bool calculate_alock(struct radio_vars* config) {
bool airtime_lock = false;
if (config->st_airtime_limit != 0.0 && config->airtime >= config->st_airtime_limit) {
airtime_lock = true;
config->airtime_lock = true;
}
if (config->lt_airtime_limit != 0.0 && config->longterm_airtime >= config->lt_airtime_limit) {
airtime_lock = true;
config->airtime_lock = true;
}
return airtime_lock;
};
void add_airtime(uint8_t index, uint16_t written) {
// \todo is referencing it this was actually necessary? Could I not just do it without the pointer?
struct radio_vars* config = &radio_details[index];
float packet_cost_ms = 0.0;
float payload_cost_ms = ((float)written * config->lora_us_per_byte)/1000.0;
packet_cost_ms += payload_cost_ms;
packet_cost_ms += (config->preamble_length+4.25)*config->lora_symbol_time_ms;
packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * config->lora_symbol_time_ms;
uint16_t cb = current_airtime_bin();
uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
config->airtime_bins[cb] += packet_cost_ms;
config->airtime_bins[nb] = 0;
};
void update_airtime(uint8_t index) {
struct radio_vars* config = &radio_details[index];
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; }
config->airtime_bins[nb] = 0;
config->airtime = (float)(config->airtime_bins[cb]+config->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 += config->airtime_bins[bin];
}
config->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 += config->longterm_bins[bin];
}
config->longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS;
update_csma_p(config);
//kiss_indicate_channel_stats(); // todo: enable me!
};
void check_modem_status(PhysicalLayer* radio, uint8_t index) {
struct radio_vars* config = &radio_details[index];
if (millis()-(config->last_status_update) >= STATUS_INTERVAL_MS) {
update_modem_status(radio, index);
config->util_samples[config->dcd_sample] = config->dcd;
config->dcd_sample = (config->dcd_sample+1)%DCD_SAMPLES;
if (config->dcd_sample % UTIL_UPDATE_INTERVAL == 0) {
int util_count = 0;
for (int ui = 0; ui < DCD_SAMPLES; ui++) {
if (config->util_samples[ui]) util_count++;
}
config->local_channel_util = (float)util_count / (float)DCD_SAMPLES;
config->total_channel_util = config->local_channel_util + config->airtime;
if (config->total_channel_util > 1.0) config->total_channel_util = 1.0;
int16_t cb = current_airtime_bin();
uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; }
if (config->total_channel_util > config->longterm_bins[cb]) config->longterm_bins[cb] = config->total_channel_util;
config->longterm_bins[nb] = 0.0;
update_airtime(index);
}
}
};
void update_bitrate(PhysicalLayer* radio, uint8_t index) {
struct radio_vars* config = &radio_details[index];
if (config->radio_online) {
config->lora_symbol_rate = (config->bw*1000)/(float)(pow(2, config->sf));
config->lora_symbol_time_ms = (1.0/config->lora_symbol_rate)*1000.0;
config->bitrate = (uint32_t)(config->sf * ( (4.0/(float)(config->cr+4)) / ((float)(pow(2, config->sf))/config->bw) ) * 1000.0);
config->lora_us_per_byte = 1000000.0/((float)config->bitrate/8.0);
//_csma_slot_ms = _lora_symbol_time_ms*10;
float target_preamble_symbols = (LORA_PREAMBLE_TARGET_MS/config->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);
}
// \todo actually update this on the radio
config->preamble_length = target_preamble_symbols;
radio->setPreambleLength(target_preamble_symbols);
} else {
config->bitrate = 0;
}
}
inline bool isSplitPacket(uint8_t header) {
return (header & FLAG_SPLIT);
}
@ -981,66 +1176,69 @@ void set_implicit_length(uint8_t len) {
}
}
void setTXPower(RadioInterface* radio, int txp) {
void setTXPower(PhysicalLayer* radio, uint8_t index, int txp) {
// Todo, revamp this function. The current parameters for setTxPower are
// suboptimal, as some chips have power amplifiers which means that the max
// dBm is not always the same.
if (model == MODEL_11) {
if (interfaces[radio->getIndex()] == SX128X) {
radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
} else {
radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
}
}
if (model == MODEL_12) {
if (interfaces[radio->getIndex()] == SX128X) {
radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
} else {
radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
}
}
int8_t set_pwr;
radio->checkOutputPower(txp, &set_pwr);
radio_details[index].txp = set_pwr;
//if (model == MODEL_11) {
// if (interfaces[radio->getIndex()] == SX128X) {
// radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
// } else {
// radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
// }
//}
//if (model == MODEL_12) {
// if (interfaces[radio->getIndex()] == SX128X) {
// radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
// } else {
// radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
// }
//}
if (model == MODEL_21) {
if (interfaces[radio->getIndex()] == SX128X) {
radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
} else {
radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
}
}
//if (model == MODEL_21) {
// if (interfaces[radio->getIndex()] == SX128X) {
// radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
// } else {
// radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
// }
//}
if (model == MODEL_A1) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_A2) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_A3) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
if (model == MODEL_A4) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
if (model == MODEL_A5) 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_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_A5) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
//if (model == MODEL_A6) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_A7) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_A8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_A9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_B3) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_B4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_B8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_B9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_B3) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_B4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_B8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_B9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_C4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_C9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_C4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_C9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_E4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_E9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_E3) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_E8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_E4) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_E9) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_E3) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_E8) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_FE) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
if (model == MODEL_FF) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
//if (model == MODEL_FE) radio->setTxPower(txp, PA_OUTPUT_PA_BOOST_PIN);
//if (model == MODEL_FF) radio->setTxPower(txp, PA_OUTPUT_RFO_PIN);
}
uint8_t getRandom(RadioInterface* radio) {
if (radio->getRadioOnline()) {
return radio->random();
} else {
uint8_t getRandom(PhysicalLayer* radio) {
//if (radio->getRadioOnline()) {
// return radio->random();
//} else {
return 0x00;
}
//}
}
uint8_t getInterfaceIndex(uint8_t byte) {
@ -1475,57 +1673,59 @@ bool eeprom_have_conf() {
}
}
void eeprom_conf_load(RadioInterface* radio) {
if (eeprom_have_conf()) {
if (!(radio->getRadioOnline())) {
#if HAS_EEPROM
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
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_load(PhysicalLayer* radio) {
// \todo remove, or will attermann need it?
//if (eeprom_have_conf()) {
// if (!(radio->getRadioOnline())) {
// #if HAS_EEPROM
// 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
// 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->setCodingRate(cr);
// setTXPower(radio, txp);
// radio->setFrequency(freq);
// radio->setBandwidth(bw);
// radio->updateBitrate();
// }
//}
}
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());
void eeprom_conf_save(PhysicalLayer* radio) {
// \todo fixme
//if (hw_ready && radio->getRadioOnline()) {
// eeprom_update(eeprom_addr(ADDR_CONF_SF), radio->getSpreadingFactorVal());
// eeprom_update(eeprom_addr(ADDR_CONF_CR), radio->getCodingRateVal());
// eeprom_update(eeprom_addr(ADDR_CONF_TXP), radio->getTXPowerVal());
uint32_t bw = radio->getSignalBandwidth();
// uint32_t bw = radio->getBandwidthVal() * 1000;
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_BW)+0x00, bw>>24);
// eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x01, bw>>16);
// eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x02, bw>>8);
// eeprom_update(eeprom_addr(ADDR_CONF_BW)+0x03, bw);
uint32_t freq = radio->getFrequency();
// uint32_t freq = radio->getFrequencyVal() * 1000;
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_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);
} else {
led_indicate_warning(10);
}
// eeprom_update(eeprom_addr(ADDR_CONF_OK), CONF_OK_BYTE);
// led_indicate_info(10);
//} else {
// led_indicate_warning(10);
//}
}
void eeprom_conf_delete() {