From: jacob.eva Date: Tue, 6 Aug 2024 16:37:47 +0000 (+0100) Subject: Fix SPI semaphore lockup issue on ESP32 X-Git-Url: https://git.tnoah.ca/?a=commitdiff_plain;h=721cee3603a9c59bd62d2907cd3b57c1b59fb086;p=lora-radio.git Fix SPI semaphore lockup issue on ESP32 --- diff --git a/Boards.h b/Boards.h index c8c4d94..75f5284 100644 --- a/Boards.h +++ b/Boards.h @@ -726,17 +726,6 @@ }; #endif - #define INTERFACE_SPI - // Required because on RAK4631, non-default SPI pins must be initialised when class is declared. - const SPIClass interface_spi[1] = { - // SX1262 - SPIClass( - NRF_SPIM2, - interface_pins[0][3], - interface_pins[0][1], - interface_pins[0][2] - ) - }; const int pin_disp_cs = SS; const int pin_disp_dc = WB_IO1; @@ -752,10 +741,4 @@ #endif #endif - #ifndef INTERFACE_SPI - // INTERFACE_SPI is only required on NRF52 platforms, as the SPI pins are set in the class constructor and not by a setter method. - // Even if custom SPI interfaces are not needed, the array must exist to prevent compilation errors. - #define INTERFACE_SPI - const SPIClass interface_spi[1]; - #endif #endif diff --git a/RNode_Firmware_CE.ino b/RNode_Firmware_CE.ino index c91ac31..dabe7c9 100644 --- a/RNode_Firmware_CE.ino +++ b/RNode_Firmware_CE.ino @@ -17,10 +17,25 @@ #include #include "Utilities.h" -#if BOARD_MODEL == BOARD_HELTEC32_V3 -// Default stack size for loop function on Heltec32 V3 is not large enough, -// must be increased to 11kb to prevent crashes. -SET_LOOP_TASK_STACK_SIZE(11 * 1024); // 11KB +#if MCU_VARIANT == MCU_NRF52 + #define INTERFACE_SPI + // Required because on RAK4631, non-default SPI pins must be initialised when class is declared. + SPIClass interface_spi[1] = { + // SX1262 + SPIClass( + NRF_SPIM2, + interface_pins[0][3], + interface_pins[0][1], + interface_pins[0][2] + ) + }; +#endif + +#ifndef INTERFACE_SPI +// INTERFACE_SPI is only required on NRF52 platforms, as the SPI pins are set in the class constructor and not by a setter method. +// Even if custom SPI interfaces are not needed, the array must exist to prevent compilation errors. +#define INTERFACE_SPI +SPIClass interface_spi[1]; #endif FIFOBuffer serialFIFO; @@ -134,13 +149,13 @@ void setup() { sx126x* obj; // if default spi enabled if (interface_cfg[i][0]) { - obj = new sx126x(i, SPI, interface_cfg[i][1], + obj = new sx126x(i, &SPI, interface_cfg[i][1], interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1], interface_pins[i][2], interface_pins[i][3], interface_pins[i][6], interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]); } else { - obj = new sx126x(i, interface_spi[i], interface_cfg[i][1], + obj = new sx126x(i, &interface_spi[i], interface_cfg[i][1], interface_cfg[i][2], interface_pins[i][0], interface_pins[i][1], interface_pins[i][2], interface_pins[i][3], interface_pins[i][6], interface_pins[i][5], interface_pins[i][4], interface_pins[i][8]); @@ -157,12 +172,12 @@ void setup() { sx127x* obj; // if default spi enabled if (interface_cfg[i][0]) { - obj = new sx127x(i, SPI, interface_pins[i][0], + obj = new sx127x(i, &SPI, interface_pins[i][0], interface_pins[i][1], interface_pins[i][2], interface_pins[i][3], interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]); } else { - obj = new sx127x(i, interface_spi[i], interface_pins[i][0], + obj = new sx127x(i, &interface_spi[i], interface_pins[i][0], interface_pins[i][1], interface_pins[i][2], interface_pins[i][3], interface_pins[i][6], interface_pins[i][5], interface_pins[i][4]); } @@ -177,13 +192,13 @@ void setup() { sx128x* obj; // if default spi enabled if (interface_cfg[i][0]) { - obj = new sx128x(i, SPI, interface_cfg[i][1], + obj = new sx128x(i, &SPI, interface_cfg[i][1], interface_pins[i][0], interface_pins[i][1], interface_pins[i][2], interface_pins[i][3], interface_pins[i][6], interface_pins[i][5], interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]); } else { - obj = new sx128x(i, interface_spi[i], interface_cfg[i][1], + obj = new sx128x(i, &interface_spi[i], interface_cfg[i][1], interface_pins[i][0], interface_pins[i][1], interface_pins[i][2], interface_pins[i][3], interface_pins[i][6], interface_pins[i][5], interface_pins[i][4], interface_pins[i][8], interface_pins[i][7]); diff --git a/Radio.cpp b/Radio.cpp index 585cceb..cb949c2 100644 --- a/Radio.cpp +++ b/Radio.cpp @@ -4,7 +4,7 @@ // Modifications and additions copyright 2024 by Mark Qvist & Jacob Eva // Obviously still under the MIT license. -#include "Radio.h" +#include "Radio.hpp" #if PLATFORM == PLATFORM_ESP32 #if defined(ESP32) and !defined(CONFIG_IDF_TARGET_ESP32S3) @@ -106,7 +106,7 @@ void ISR_VECT onDio0Rise() { } } -sx126x::sx126x(uint8_t index, SPIClass spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen) : +sx126x::sx126x(uint8_t index, SPIClass* spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen) : RadioInterface(index), _spiSettings(8E6, MSBFIRST, SPI_MODE0), _spiModem(spi), _ss(ss), _sclk(sclk), _mosi(mosi), _miso(miso), _reset(reset), _dio0(dio0), @@ -130,12 +130,12 @@ bool sx126x::preInit() { // todo: check if this change causes issues on any platforms #if MCU_VARIANT == MCU_ESP32 if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) { - _spiModem.begin(_sclk, _miso, _mosi, _ss); + _spiModem->begin(_sclk, _miso, _mosi, _ss); } else { - _spiModem.begin(); + _spiModem->begin(); } #else - _spiModem.begin(); + _spiModem->begin(); #endif // check version (retry for up to 2 seconds) @@ -177,15 +177,15 @@ uint8_t ISR_VECT sx126x::singleTransfer(uint8_t opcode, uint16_t address, uint8_ digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(opcode); - _spiModem.transfer((address & 0xFF00) >> 8); - _spiModem.transfer(address & 0x00FF); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(opcode); + _spiModem->transfer((address & 0xFF00) >> 8); + _spiModem->transfer(address & 0x00FF); if (opcode == OP_READ_REGISTER_6X) { - _spiModem.transfer(0x00); + _spiModem->transfer(0x00); } - response = _spiModem.transfer(value); - _spiModem.endTransaction(); + response = _spiModem->transfer(value); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); @@ -222,15 +222,15 @@ void sx126x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(opcode); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(opcode); for (int i = 0; i < size; i++) { - _spiModem.transfer(buffer[i]); + _spiModem->transfer(buffer[i]); } - _spiModem.endTransaction(); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); } @@ -241,16 +241,16 @@ void sx126x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(opcode); - _spiModem.transfer(0x00); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(opcode); + _spiModem->transfer(0x00); for (int i = 0; i < size; i++) { - buffer[i] = _spiModem.transfer(0x00); + buffer[i] = _spiModem->transfer(0x00); } - _spiModem.endTransaction(); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); } @@ -261,17 +261,17 @@ void sx126x::writeBuffer(const uint8_t* buffer, size_t size) digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(OP_FIFO_WRITE_6X); - _spiModem.transfer(_fifo_tx_addr_ptr); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(OP_FIFO_WRITE_6X); + _spiModem->transfer(_fifo_tx_addr_ptr); for (int i = 0; i < size; i++) { - _spiModem.transfer(buffer[i]); + _spiModem->transfer(buffer[i]); _fifo_tx_addr_ptr++; } - _spiModem.endTransaction(); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); } @@ -282,17 +282,17 @@ void sx126x::readBuffer(uint8_t* buffer, size_t size) digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(OP_FIFO_READ_6X); - _spiModem.transfer(_fifo_rx_addr_ptr); - _spiModem.transfer(0x00); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(OP_FIFO_READ_6X); + _spiModem->transfer(_fifo_rx_addr_ptr); + _spiModem->transfer(0x00); for (int i = 0; i < size; i++) { - buffer[i] = _spiModem.transfer(0x00); + buffer[i] = _spiModem->transfer(0x00); } - _spiModem.endTransaction(); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); } @@ -451,7 +451,7 @@ void sx126x::end() sleep(); // stop SPI - _spiModem.end(); + _spiModem->end(); _bitrate = 0; @@ -676,7 +676,7 @@ void sx126x::onReceive(void(*callback)(uint8_t, int)) executeOpcode(OP_SET_IRQ_FLAGS_6X, buf, 8); #ifdef SPI_HAS_NOTUSINGINTERRUPT - _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0)); + _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0)); #endif // make function available extern void onDio0Rise(); @@ -685,7 +685,7 @@ void sx126x::onReceive(void(*callback)(uint8_t, int)) } else { detachInterrupt(digitalPinToInterrupt(_dio0)); #ifdef SPI_HAS_NOTUSINGINTERRUPT - _spiModem.notUsingInterrupt(digitalPinToInterrupt(_dio0)); + _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0)); #endif } } @@ -1081,7 +1081,7 @@ void sx126x::clearIRQStatus() { #define SYNC_WORD_7X 0x12 -sx127x::sx127x(uint8_t index, SPIClass spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy) : +sx127x::sx127x(uint8_t index, SPIClass* spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy) : RadioInterface(index), _spiSettings(8E6, MSBFIRST, SPI_MODE0), _spiModem(spi), @@ -1116,12 +1116,12 @@ bool sx127x::preInit() { // todo: check if this change causes issues on any platforms #if MCU_VARIANT == MCU_ESP32 if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) { - _spiModem.begin(_sclk, _miso, _mosi, _ss); + _spiModem->begin(_sclk, _miso, _mosi, _ss); } else { - _spiModem.begin(); + _spiModem->begin(); } #else - _spiModem.begin(); + _spiModem->begin(); #endif // Check modem version @@ -1143,10 +1143,10 @@ uint8_t ISR_VECT sx127x::singleTransfer(uint8_t address, uint8_t value) { uint8_t response; digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(address); - response = _spiModem.transfer(value); - _spiModem.endTransaction(); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(address); + response = _spiModem->transfer(value); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); return response; @@ -1193,7 +1193,7 @@ int sx127x::begin() { void sx127x::end() { sleep(); - _spiModem.end(); + _spiModem->end(); _bitrate = 0; _radio_online = false; _preinit_done = false; @@ -1331,7 +1331,7 @@ void sx127x::onReceive(void(*callback)(uint8_t, int)) { writeRegister(REG_DIO_MAPPING_1_7X, 0x00); #ifdef SPI_HAS_NOTUSINGINTERRUPT - _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0)); + _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0)); #endif // make function available @@ -1343,7 +1343,7 @@ void sx127x::onReceive(void(*callback)(uint8_t, int)) { detachInterrupt(digitalPinToInterrupt(_dio0)); #ifdef SPI_HAS_NOTUSINGINTERRUPT - _spiModem.notUsingInterrupt(digitalPinToInterrupt(_dio0)); + _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0)); #endif } } @@ -1599,7 +1599,7 @@ void sx127x::clearIRQStatus() { #define FREQ_DIV_8X (double)pow(2.0, 18.0) #define FREQ_STEP_8X (double)(XTAL_FREQ_8X / FREQ_DIV_8X) -sx128x::sx128x(uint8_t index, SPIClass spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen) : +sx128x::sx128x(uint8_t index, SPIClass* spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen) : RadioInterface(index), _spiSettings(8E6, MSBFIRST, SPI_MODE0), _spiModem(spi), @@ -1626,12 +1626,12 @@ bool sx128x::preInit() { // todo: check if this change causes issues on any platforms #if MCU_VARIANT == MCU_ESP32 if (_sclk != -1 && _miso != -1 && _mosi != -1 && _ss != -1) { - _spiModem.begin(_sclk, _miso, _mosi, _ss); + _spiModem->begin(_sclk, _miso, _mosi, _ss); } else { - _spiModem.begin(); + _spiModem->begin(); } #else - _spiModem.begin(); + _spiModem->begin(); #endif // check version (retry for up to 2 seconds) @@ -1676,15 +1676,15 @@ uint8_t ISR_VECT sx128x::singleTransfer(uint8_t opcode, uint16_t address, uint8_ digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(opcode); - _spiModem.transfer((address & 0xFF00) >> 8); - _spiModem.transfer(address & 0x00FF); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(opcode); + _spiModem->transfer((address & 0xFF00) >> 8); + _spiModem->transfer(address & 0x00FF); if (opcode == OP_READ_REGISTER_8X) { - _spiModem.transfer(0x00); + _spiModem->transfer(0x00); } - response = _spiModem.transfer(value); - _spiModem.endTransaction(); + response = _spiModem->transfer(value); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); @@ -1734,15 +1734,15 @@ void sx128x::executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size) digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(opcode); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(opcode); for (int i = 0; i < size; i++) { - _spiModem.transfer(buffer[i]); + _spiModem->transfer(buffer[i]); } - _spiModem.endTransaction(); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); } @@ -1753,16 +1753,16 @@ void sx128x::executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size) digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(opcode); - _spiModem.transfer(0x00); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(opcode); + _spiModem->transfer(0x00); for (int i = 0; i < size; i++) { - buffer[i] = _spiModem.transfer(0x00); + buffer[i] = _spiModem->transfer(0x00); } - _spiModem.endTransaction(); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); } @@ -1773,17 +1773,17 @@ void sx128x::writeBuffer(const uint8_t* buffer, size_t size) digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(OP_FIFO_WRITE_8X); - _spiModem.transfer(_fifo_tx_addr_ptr); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(OP_FIFO_WRITE_8X); + _spiModem->transfer(_fifo_tx_addr_ptr); for (int i = 0; i < size; i++) { - _spiModem.transfer(buffer[i]); + _spiModem->transfer(buffer[i]); _fifo_tx_addr_ptr++; } - _spiModem.endTransaction(); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); } @@ -1794,17 +1794,17 @@ void sx128x::readBuffer(uint8_t* buffer, size_t size) digitalWrite(_ss, LOW); - _spiModem.beginTransaction(_spiSettings); - _spiModem.transfer(OP_FIFO_READ_8X); - _spiModem.transfer(_fifo_rx_addr_ptr); - _spiModem.transfer(0x00); + _spiModem->beginTransaction(_spiSettings); + _spiModem->transfer(OP_FIFO_READ_8X); + _spiModem->transfer(_fifo_rx_addr_ptr); + _spiModem->transfer(0x00); for (int i = 0; i < size; i++) { - buffer[i] = _spiModem.transfer(0x00); + buffer[i] = _spiModem->transfer(0x00); } - _spiModem.endTransaction(); + _spiModem->endTransaction(); digitalWrite(_ss, HIGH); } @@ -1921,7 +1921,7 @@ void sx128x::end() sleep(); // stop SPI - _spiModem.end(); + _spiModem->end(); _bitrate = 0; @@ -2130,7 +2130,7 @@ void sx128x::onReceive(void(*callback)(uint8_t, int)) executeOpcode(OP_SET_IRQ_FLAGS_8X, buf, 8); //#ifdef SPI_HAS_NOTUSINGINTERRUPT -// _spiModem.usingInterrupt(digitalPinToInterrupt(_dio0)); +// _spiModem->usingInterrupt(digitalPinToInterrupt(_dio0)); //#endif // make function available @@ -2140,7 +2140,7 @@ void sx128x::onReceive(void(*callback)(uint8_t, int)) } else { detachInterrupt(digitalPinToInterrupt(_dio0)); //#ifdef SPI_HAS_NOTUSINGINTERRUPT -// _spiModem.notUsingInterrupt(digitalPinToInterrupt(_dio0)); +// _spiModem->notUsingInterrupt(digitalPinToInterrupt(_dio0)); //#endif } } diff --git a/Radio.h b/Radio.h deleted file mode 100644 index 13bb0da..0000000 --- a/Radio.h +++ /dev/null @@ -1,666 +0,0 @@ -// Copyright (c) Sandeep Mistry. All rights reserved. -// Licensed under the MIT license. - -// Modifications and additions copyright 2023 by Mark Qvist & Jacob Eva -// Obviously still under the MIT license. - -#ifndef RADIO_H -#define RADIO_H - -#include -#include -#include "Interfaces.h" -#include "Boards.h" - -#define MAX_PKT_LENGTH 255 - -// TX -#define PA_OUTPUT_RFO_PIN 0 -#define PA_OUTPUT_PA_BOOST_PIN 1 - -// DCD -#define STATUS_INTERVAL_MS 3 -#define DCD_SAMPLES 2500 -#define UTIL_UPDATE_INTERVAL_MS 1000 -#define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS) -#define AIRTIME_LONGTERM 3600 -#define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000) -#define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES) -#define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS) -#define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS -#define DCD_THRESHOLD 2 -#define DCD_LED_STEP_D 3 -#define LORA_PREAMBLE_SYMBOLS_HW 4 -#define LORA_PREAMBLE_SYMBOLS_MIN 18 -#define LORA_PREAMBLE_TARGET_MS 15 -#define LORA_PREAMBLE_FAST_TARGET_MS 1 -#define LORA_FAST_BITRATE_THRESHOLD 40000 - -#define RSSI_OFFSET 157 - -#define PHY_HEADER_LORA_SYMBOLS 8 - -#define _e 2.71828183 -#define _S 10.0 - -// Status flags -const uint8_t SIG_DETECT = 0x01; -const uint8_t SIG_SYNCED = 0x02; -const uint8_t RX_ONGOING = 0x04; - -// forward declare Utilities.h LED functions -void led_rx_on(); - -void led_rx_off(); -void led_indicate_airtime_lock(); - -#if PLATFORM == PLATFORM_ESP32 -// get update_lock for ESP32 -extern portMUX_TYPE update_lock; -#endif - -class RadioInterface : public Stream { -public: - // todo: in the future define _spiModem and _spiSettings from here for inheritence by child classes - RadioInterface(uint8_t index) : _index(index), _radio_locked(false), - _radio_online(false), _st_airtime_limit(0.0), _lt_airtime_limit(0.0), - _airtime_lock(false), _airtime(0.0), _longterm_airtime(0.0), - _local_channel_util(0.0), _total_channel_util(0.0), - _longterm_channel_util(0.0), _last_status_update(0), - _stat_signal_detected(false), _stat_signal_synced(false),_stat_rx_ongoing(false), _last_dcd(0), - _dcd_count(0), _dcd(false), _dcd_led(false), - _dcd_waiting(false), _dcd_wait_until(0), _dcd_sample(0), - _post_tx_yield_timeout(0), _csma_slot_ms(50), _csma_p_min(0.1), - _csma_p_max(0.8), _preambleLength(6), _lora_symbol_time_ms(0.0), - _lora_symbol_rate(0.0), _lora_us_per_byte(0.0), _bitrate(0), - _packet{0}, _onReceive(NULL) {}; - virtual int begin() = 0; - virtual void end() = 0; - - virtual int beginPacket(int implicitHeader = false) = 0; - virtual int endPacket() = 0; - - virtual int packetRssi() = 0; - virtual int currentRssi() = 0; - virtual uint8_t packetRssiRaw() = 0; - virtual uint8_t currentRssiRaw() = 0; - virtual uint8_t packetSnrRaw() = 0; - virtual float packetSnr() = 0; - virtual long packetFrequencyError() = 0; - - // from Print - virtual size_t write(uint8_t byte) = 0; - virtual size_t write(const uint8_t *buffer, size_t size) = 0; - - // from Stream - virtual int available() = 0; - virtual int read() = 0; - virtual int peek() = 0; - virtual void flush() = 0; - - virtual void onReceive(void(*callback)(uint8_t, int)) = 0; - - virtual void receive(int size = 0) = 0; - virtual void standby() = 0; - virtual void sleep() = 0; - - virtual bool preInit() = 0; - virtual uint8_t getTxPower() = 0; - virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN) = 0; - virtual uint32_t getFrequency() = 0; - virtual void setFrequency(uint32_t frequency) = 0; - virtual void setSpreadingFactor(int sf) = 0; - virtual uint8_t getSpreadingFactor() = 0; - virtual uint32_t getSignalBandwidth() = 0; - virtual void setSignalBandwidth(uint32_t sbw) = 0; - virtual void setCodingRate4(int denominator) = 0; - virtual uint8_t getCodingRate4() = 0; - virtual void setPreambleLength(long length) = 0; - virtual uint8_t modemStatus() = 0; - virtual void enableCrc() = 0; - virtual void disableCrc() = 0; - virtual void enableTCXO() = 0; - virtual void disableTCXO() = 0; - - virtual byte random() = 0; - - virtual void setSPIFrequency(uint32_t frequency) = 0; - - virtual void updateBitrate() = 0; - virtual void handleDio0Rise() = 0; - virtual void clearIRQStatus() = 0; - uint32_t getBitrate() { return _bitrate; }; - uint8_t getIndex() { return _index; }; - void setRadioLock(bool lock) { _radio_locked = lock; }; - bool getRadioLock() { return _radio_locked; }; - void setRadioOnline(bool online) { _radio_online = online; }; - bool getRadioOnline() { return _radio_online; }; - void setSTALock(float at) { _st_airtime_limit = at; }; - float getSTALock() { return _st_airtime_limit; }; - void setLTALock(float at) { _lt_airtime_limit = at; }; - float getLTALock() { return _lt_airtime_limit; }; - bool calculateALock() { - _airtime_lock = false; - if (_st_airtime_limit != 0.0 && _airtime >= _st_airtime_limit) { - _airtime_lock = true; - } - if (_lt_airtime_limit != 0.0 && _longterm_airtime >= _lt_airtime_limit) { - _airtime_lock = true; - } - return _airtime_lock; - }; - void updateAirtime() { - uint16_t cb = current_airtime_bin(); - uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; } - uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } - _airtime_bins[nb] = 0; - _airtime = (float)(_airtime_bins[cb]+_airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS); - - uint32_t longterm_airtime_sum = 0; - for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { - longterm_airtime_sum += _airtime_bins[bin]; - } - _longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS; - - float longterm_channel_util_sum = 0.0; - for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { - longterm_channel_util_sum += _longterm_bins[bin]; - } - _longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS; - - updateCSMAp(); - - //kiss_indicate_channel_stats(); // todo: enable me! - }; - void addAirtime(uint16_t written) { - float packet_cost_ms = 0.0; - float payload_cost_ms = ((float)written * _lora_us_per_byte)/1000.0; - packet_cost_ms += payload_cost_ms; - packet_cost_ms += (_preambleLength+4.25)*_lora_symbol_time_ms; - packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * _lora_symbol_time_ms; - uint16_t cb = current_airtime_bin(); - uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } - _airtime_bins[cb] += packet_cost_ms; - _airtime_bins[nb] = 0; - }; - void checkModemStatus() { - if (millis()-_last_status_update >= STATUS_INTERVAL_MS) { - updateModemStatus(); - - _util_samples[_dcd_sample] = _dcd; - _dcd_sample = (_dcd_sample+1)%DCD_SAMPLES; - if (_dcd_sample % UTIL_UPDATE_INTERVAL == 0) { - int util_count = 0; - for (int ui = 0; ui < DCD_SAMPLES; ui++) { - if (_util_samples[ui]) util_count++; - } - _local_channel_util = (float)util_count / (float)DCD_SAMPLES; - _total_channel_util = _local_channel_util + _airtime; - if (_total_channel_util > 1.0) _total_channel_util = 1.0; - - int16_t cb = current_airtime_bin(); - uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } - if (_total_channel_util > _longterm_bins[cb]) _longterm_bins[cb] = _total_channel_util; - _longterm_bins[nb] = 0.0; - - updateAirtime(); - } - } - }; - void updateModemStatus() { - #if PLATFORM == PLATFORM_ESP32 - portENTER_CRITICAL(&update_lock); - #elif PLATFORM == PLATFORM_NRF52 - portENTER_CRITICAL(); - #endif - - uint8_t status = modemStatus(); - - _last_status_update = millis(); - - #if PLATFORM == PLATFORM_ESP32 - portEXIT_CRITICAL(&update_lock); - #elif PLATFORM == PLATFORM_NRF52 - portEXIT_CRITICAL(); - #endif - - if ((status & SIG_DETECT) == SIG_DETECT) { _stat_signal_detected = true; } else { _stat_signal_detected = false; } - if ((status & SIG_SYNCED) == SIG_SYNCED) { _stat_signal_synced = true; } else { _stat_signal_synced = false; } - if ((status & RX_ONGOING) == RX_ONGOING) { _stat_rx_ongoing = true; } else { _stat_rx_ongoing = false; } - - // if (stat_signal_detected || stat_signal_synced || stat_rx_ongoing) { - if (_stat_signal_detected || _stat_signal_synced) { - if (_stat_rx_ongoing) { - if (_dcd_count < DCD_THRESHOLD) { - _dcd_count++; - } else { - _last_dcd = _last_status_update; - _dcd_led = true; - _dcd = true; - } - } - } else { - if (_dcd_count == 0) { - _dcd_led = false; - } else if (_dcd_count > DCD_LED_STEP_D) { - _dcd_count -= DCD_LED_STEP_D; - } else { - _dcd_count = 0; - } - - if (_last_status_update > _last_dcd+_csma_slot_ms) { - _dcd = false; - _dcd_led = false; - _dcd_count = 0; - } - } - - - if (_dcd_led) { - led_rx_on(); - } else { - if (_airtime_lock) { - led_indicate_airtime_lock(); - } else { - led_rx_off(); - } - } - }; - void setPostTxYieldTimeout(uint32_t timeout) { _post_tx_yield_timeout = timeout; }; - uint32_t getPostTxYieldTimeout() { return _post_tx_yield_timeout; }; - void setDCD(bool dcd) { _dcd = dcd; }; - bool getDCD() { return _dcd; }; - void setDCDWaiting(bool dcd_waiting) { _dcd_waiting = dcd_waiting; }; - bool getDCDWaiting() { return _dcd_waiting; }; - void setDCDWaitUntil(uint32_t dcd_wait_until) { _dcd_wait_until = dcd_wait_until; }; - bool getDCDWaitUntil() { return _dcd_wait_until; }; - float getAirtime() { return _airtime; }; - float getLongtermAirtime() { return _longterm_airtime; }; - float getTotalChannelUtil() { return _total_channel_util; }; - float getLongtermChannelUtil() { return _longterm_channel_util; }; - float CSMASlope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); }; - void updateCSMAp() { - _csma_p = (uint8_t)((1.0-(_csma_p_min+(_csma_p_max-_csma_p_min)*CSMASlope(_airtime)))*255.0); - }; - uint8_t getCSMAp() { return _csma_p; }; - void setCSMASlotMS(int slot_size) { _csma_slot_ms = slot_size; }; - int getCSMASlotMS() { return _csma_slot_ms; }; - float getSymbolTime() { return _lora_symbol_time_ms; }; - float getSymbolRate() { return _lora_symbol_rate; }; - long getPreambleLength() { return _preambleLength; }; -protected: - virtual void explicitHeaderMode() = 0; - virtual void implicitHeaderMode() = 0; - - uint8_t _index; - bool _radio_locked; - bool _radio_online; - float _st_airtime_limit; - float _lt_airtime_limit; - bool _airtime_lock; - uint16_t _airtime_bins[AIRTIME_BINS] = {0}; - uint16_t _longterm_bins[AIRTIME_BINS] = {0}; - float _airtime; - float _longterm_airtime; - float _local_channel_util; - float _total_channel_util; - float _longterm_channel_util; - uint32_t _last_status_update; - bool _stat_signal_detected; - bool _stat_signal_synced; - bool _stat_rx_ongoing; - uint32_t _last_dcd; - uint16_t _dcd_count; - bool _dcd; - bool _dcd_led; - bool _dcd_waiting; - long _dcd_wait_until; - bool _util_samples[DCD_SAMPLES] = {false}; - int _dcd_sample; - uint32_t _post_tx_yield_timeout; - uint8_t _csma_p; - int _csma_slot_ms; - float _csma_p_min; - float _csma_p_max; - long _preambleLength; - float _lora_symbol_time_ms; - float _lora_symbol_rate; - float _lora_us_per_byte; - uint32_t _bitrate; - uint8_t _packet[255]; - void (*_onReceive)(uint8_t, int); -}; - -class sx126x : public RadioInterface { -public: - sx126x(uint8_t index, SPIClass spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int - dio0, int busy, int rxen); - - int begin(); - void end(); - - int beginPacket(int implicitHeader = false); - int endPacket(); - - int packetRssi(); - int currentRssi(); - uint8_t packetRssiRaw(); - uint8_t currentRssiRaw(); - uint8_t packetSnrRaw(); - float packetSnr(); - long packetFrequencyError(); - - // from Print - size_t write(uint8_t byte); - size_t write(const uint8_t *buffer, size_t size); - - // from Stream - int available(); - int read(); - int peek(); - void flush(); - - void onReceive(void(*callback)(uint8_t, int)); - - void receive(int size = 0); - void standby(); - void sleep(); - - bool preInit(); - uint8_t getTxPower(); - void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); - uint32_t getFrequency(); - void setFrequency(uint32_t frequency); - void setSpreadingFactor(int sf); - uint8_t getSpreadingFactor(); - uint32_t getSignalBandwidth(); - void setSignalBandwidth(uint32_t sbw); - void setCodingRate4(int denominator); - uint8_t getCodingRate4(); - void setPreambleLength(long length); - uint8_t modemStatus(); - void enableCrc(); - void disableCrc(); - void enableTCXO(); - void disableTCXO(); - - - byte random(); - - void setSPIFrequency(uint32_t frequency); - - void dumpRegisters(Stream& out); - - void updateBitrate(); - - void handleDio0Rise(); -private: - void writeBuffer(const uint8_t* buffer, size_t size); - void readBuffer(uint8_t* buffer, size_t size); - void loraMode(); - void rxAntEnable(); - void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); - void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); - void setSyncWord(uint16_t sw); - void waitOnBusy(); - void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); - void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); - void explicitHeaderMode(); - void implicitHeaderMode(); - - - uint8_t readRegister(uint16_t address); - void writeRegister(uint16_t address, uint8_t value); - uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); - - static void onDio0Rise(); - - void handleLowDataRate(); - void optimizeModemSensitivity(); - - void reset(void); - void calibrate(void); - void calibrate_image(uint32_t frequency); - void clearIRQStatus(); - -private: - SPISettings _spiSettings; - SPIClass _spiModem; - int _ss; - int _sclk; - int _mosi; - int _miso; - int _reset; - int _dio0; - int _rxen; - int _busy; - uint32_t _frequency; - int _txp; - uint8_t _sf; - uint8_t _bw; - uint8_t _cr; - uint8_t _ldro; - int _packetIndex; - int _implicitHeaderMode; - int _payloadLength; - int _crcMode; - int _fifo_tx_addr_ptr; - int _fifo_rx_addr_ptr; - bool _preinit_done; - uint8_t _index; - bool _tcxo; - bool _dio2_as_rf_switch; -}; - -class sx127x : public RadioInterface { -public: - sx127x(uint8_t index, SPIClass spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy); - - int begin(); - void end(); - - int beginPacket(int implicitHeader = false); - int endPacket(); - - int packetRssi(); - int currentRssi(); - uint8_t packetRssiRaw(); - uint8_t currentRssiRaw(); - uint8_t packetSnrRaw(); - float packetSnr(); - long packetFrequencyError(); - - // from Print - size_t write(uint8_t byte); - size_t write(const uint8_t *buffer, size_t size); - - // from Stream - int available(); - int read(); - int peek(); - void flush(); - - void onReceive(void(*callback)(uint8_t, int)); - - void receive(int size = 0); - void standby(); - void sleep(); - - bool preInit(); - uint8_t getTxPower(); - void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); - uint32_t getFrequency(); - void setFrequency(uint32_t frequency); - void setSpreadingFactor(int sf); - uint8_t getSpreadingFactor(); - uint32_t getSignalBandwidth(); - void setSignalBandwidth(uint32_t sbw); - void setCodingRate4(int denominator); - uint8_t getCodingRate4(); - void setPreambleLength(long length); - uint8_t modemStatus(); - void enableCrc(); - void disableCrc(); - void enableTCXO(); - void disableTCXO(); - - byte random(); - - void setSPIFrequency(uint32_t frequency); - - void updateBitrate(); - - void handleDio0Rise(); - void clearIRQStatus(); -private: - void setSyncWord(uint8_t sw); - void explicitHeaderMode(); - void implicitHeaderMode(); - - - uint8_t readRegister(uint8_t address); - void writeRegister(uint8_t address, uint8_t value); - uint8_t singleTransfer(uint8_t address, uint8_t value); - - static void onDio0Rise(); - - void handleLowDataRate(); - void optimizeModemSensitivity(); - -private: - SPISettings _spiSettings; - SPIClass _spiModem; - int _ss; - int _sclk; - int _mosi; - int _miso; - int _reset; - int _dio0; - int _busy; - uint32_t _frequency; - int _packetIndex; - int _implicitHeaderMode; - bool _preinit_done; - uint8_t _index; - uint8_t _sf; - uint8_t _cr; -}; - -class sx128x : public RadioInterface { -public: - sx128x(uint8_t index, SPIClass spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen); - - int begin(); - void end(); - - int beginPacket(int implicitHeader = false); - int endPacket(); - - int packetRssi(); - int currentRssi(); - uint8_t packetRssiRaw(); - uint8_t currentRssiRaw(); - uint8_t packetSnrRaw(); - float packetSnr(); - long packetFrequencyError(); - - // from Print - size_t write(uint8_t byte); - size_t write(const uint8_t *buffer, size_t size); - - // from Stream - int available(); - int read(); - int peek(); - void flush(); - - void onReceive(void(*callback)(uint8_t, int)); - - void receive(int size = 0); - void standby(); - void sleep(); - - bool preInit(); - uint8_t getTxPower(); - void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); - uint32_t getFrequency(); - void setFrequency(uint32_t frequency); - void setSpreadingFactor(int sf); - uint8_t getSpreadingFactor(); - uint32_t getSignalBandwidth(); - void setSignalBandwidth(uint32_t sbw); - void setCodingRate4(int denominator); - uint8_t getCodingRate4(); - void setPreambleLength(long length); - uint8_t modemStatus(); - void enableCrc(); - void disableCrc(); - void enableTCXO(); - void disableTCXO(); - - byte random(); - - void setSPIFrequency(uint32_t frequency); - - void dumpRegisters(Stream& out); - - void updateBitrate(); - - void handleDio0Rise(); - - void clearIRQStatus(); -private: - void writeBuffer(const uint8_t* buffer, size_t size); - void readBuffer(uint8_t* buffer, size_t size); - void txAntEnable(); - void rxAntEnable(); - void loraMode(); - void waitOnBusy(); - void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); - void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); - void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); - void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); - void setSyncWord(int sw); - void explicitHeaderMode(); - void implicitHeaderMode(); - - - uint8_t readRegister(uint16_t address); - void writeRegister(uint16_t address, uint8_t value); - uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); - - static void onDio0Rise(); - - void handleLowDataRate(); - void optimizeModemSensitivity(); - -private: - SPISettings _spiSettings; - SPIClass _spiModem; - int _ss; - int _sclk; - int _mosi; - int _miso; - int _reset; - int _dio0; - int _rxen; - int _txen; - int _busy; - int _modem; - uint32_t _frequency; - int _txp; - uint8_t _sf; - uint8_t _bw; - uint8_t _cr; - int _packetIndex; - int _implicitHeaderMode; - int _payloadLength; - int _crcMode; - int _fifo_tx_addr_ptr; - int _fifo_rx_addr_ptr; - bool _preinit_done; - int _rxPacketLength; - uint8_t _index; - bool _tcxo; -}; -#endif diff --git a/Radio.hpp b/Radio.hpp new file mode 100644 index 0000000..fa42abd --- /dev/null +++ b/Radio.hpp @@ -0,0 +1,666 @@ +// Copyright (c) Sandeep Mistry. All rights reserved. +// Licensed under the MIT license. + +// Modifications and additions copyright 2023 by Mark Qvist & Jacob Eva +// Obviously still under the MIT license. + +#ifndef RADIO_H +#define RADIO_H + +#include +#include +#include "Interfaces.h" +#include "Boards.h" + +#define MAX_PKT_LENGTH 255 + +// TX +#define PA_OUTPUT_RFO_PIN 0 +#define PA_OUTPUT_PA_BOOST_PIN 1 + +// DCD +#define STATUS_INTERVAL_MS 3 +#define DCD_SAMPLES 2500 +#define UTIL_UPDATE_INTERVAL_MS 1000 +#define UTIL_UPDATE_INTERVAL (UTIL_UPDATE_INTERVAL_MS/STATUS_INTERVAL_MS) +#define AIRTIME_LONGTERM 3600 +#define AIRTIME_LONGTERM_MS (AIRTIME_LONGTERM*1000) +#define AIRTIME_BINLEN_MS (STATUS_INTERVAL_MS*DCD_SAMPLES) +#define AIRTIME_BINS ((AIRTIME_LONGTERM*1000)/AIRTIME_BINLEN_MS) +#define current_airtime_bin(void) (millis()%AIRTIME_LONGTERM_MS)/AIRTIME_BINLEN_MS +#define DCD_THRESHOLD 2 +#define DCD_LED_STEP_D 3 +#define LORA_PREAMBLE_SYMBOLS_HW 4 +#define LORA_PREAMBLE_SYMBOLS_MIN 18 +#define LORA_PREAMBLE_TARGET_MS 15 +#define LORA_PREAMBLE_FAST_TARGET_MS 1 +#define LORA_FAST_BITRATE_THRESHOLD 40000 + +#define RSSI_OFFSET 157 + +#define PHY_HEADER_LORA_SYMBOLS 8 + +#define _e 2.71828183 +#define _S 10.0 + +// Status flags +const uint8_t SIG_DETECT = 0x01; +const uint8_t SIG_SYNCED = 0x02; +const uint8_t RX_ONGOING = 0x04; + +// forward declare Utilities.h LED functions +void led_rx_on(); + +void led_rx_off(); +void led_indicate_airtime_lock(); + +#if PLATFORM == PLATFORM_ESP32 +// get update_lock for ESP32 +extern portMUX_TYPE update_lock; +#endif + +class RadioInterface : public Stream { +public: + // todo: in the future define _spiModem and _spiSettings from here for inheritence by child classes + RadioInterface(uint8_t index) : _index(index), _radio_locked(false), + _radio_online(false), _st_airtime_limit(0.0), _lt_airtime_limit(0.0), + _airtime_lock(false), _airtime(0.0), _longterm_airtime(0.0), + _local_channel_util(0.0), _total_channel_util(0.0), + _longterm_channel_util(0.0), _last_status_update(0), + _stat_signal_detected(false), _stat_signal_synced(false),_stat_rx_ongoing(false), _last_dcd(0), + _dcd_count(0), _dcd(false), _dcd_led(false), + _dcd_waiting(false), _dcd_wait_until(0), _dcd_sample(0), + _post_tx_yield_timeout(0), _csma_slot_ms(50), _csma_p_min(0.1), + _csma_p_max(0.8), _preambleLength(6), _lora_symbol_time_ms(0.0), + _lora_symbol_rate(0.0), _lora_us_per_byte(0.0), _bitrate(0), + _packet{0}, _onReceive(NULL) {}; + virtual int begin() = 0; + virtual void end() = 0; + + virtual int beginPacket(int implicitHeader = false) = 0; + virtual int endPacket() = 0; + + virtual int packetRssi() = 0; + virtual int currentRssi() = 0; + virtual uint8_t packetRssiRaw() = 0; + virtual uint8_t currentRssiRaw() = 0; + virtual uint8_t packetSnrRaw() = 0; + virtual float packetSnr() = 0; + virtual long packetFrequencyError() = 0; + + // from Print + virtual size_t write(uint8_t byte) = 0; + virtual size_t write(const uint8_t *buffer, size_t size) = 0; + + // from Stream + virtual int available() = 0; + virtual int read() = 0; + virtual int peek() = 0; + virtual void flush() = 0; + + virtual void onReceive(void(*callback)(uint8_t, int)) = 0; + + virtual void receive(int size = 0) = 0; + virtual void standby() = 0; + virtual void sleep() = 0; + + virtual bool preInit() = 0; + virtual uint8_t getTxPower() = 0; + virtual void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN) = 0; + virtual uint32_t getFrequency() = 0; + virtual void setFrequency(uint32_t frequency) = 0; + virtual void setSpreadingFactor(int sf) = 0; + virtual uint8_t getSpreadingFactor() = 0; + virtual uint32_t getSignalBandwidth() = 0; + virtual void setSignalBandwidth(uint32_t sbw) = 0; + virtual void setCodingRate4(int denominator) = 0; + virtual uint8_t getCodingRate4() = 0; + virtual void setPreambleLength(long length) = 0; + virtual uint8_t modemStatus() = 0; + virtual void enableCrc() = 0; + virtual void disableCrc() = 0; + virtual void enableTCXO() = 0; + virtual void disableTCXO() = 0; + + virtual byte random() = 0; + + virtual void setSPIFrequency(uint32_t frequency) = 0; + + virtual void updateBitrate() = 0; + virtual void handleDio0Rise() = 0; + virtual void clearIRQStatus() = 0; + uint32_t getBitrate() { return _bitrate; }; + uint8_t getIndex() { return _index; }; + void setRadioLock(bool lock) { _radio_locked = lock; }; + bool getRadioLock() { return _radio_locked; }; + void setRadioOnline(bool online) { _radio_online = online; }; + bool getRadioOnline() { return _radio_online; }; + void setSTALock(float at) { _st_airtime_limit = at; }; + float getSTALock() { return _st_airtime_limit; }; + void setLTALock(float at) { _lt_airtime_limit = at; }; + float getLTALock() { return _lt_airtime_limit; }; + bool calculateALock() { + _airtime_lock = false; + if (_st_airtime_limit != 0.0 && _airtime >= _st_airtime_limit) { + _airtime_lock = true; + } + if (_lt_airtime_limit != 0.0 && _longterm_airtime >= _lt_airtime_limit) { + _airtime_lock = true; + } + return _airtime_lock; + }; + void updateAirtime() { + uint16_t cb = current_airtime_bin(); + uint16_t pb = cb-1; if (cb-1 < 0) { pb = AIRTIME_BINS-1; } + uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } + _airtime_bins[nb] = 0; + _airtime = (float)(_airtime_bins[cb]+_airtime_bins[pb])/(2.0*AIRTIME_BINLEN_MS); + + uint32_t longterm_airtime_sum = 0; + for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { + longterm_airtime_sum += _airtime_bins[bin]; + } + _longterm_airtime = (float)longterm_airtime_sum/(float)AIRTIME_LONGTERM_MS; + + float longterm_channel_util_sum = 0.0; + for (uint16_t bin = 0; bin < AIRTIME_BINS; bin++) { + longterm_channel_util_sum += _longterm_bins[bin]; + } + _longterm_channel_util = (float)longterm_channel_util_sum/(float)AIRTIME_BINS; + + updateCSMAp(); + + //kiss_indicate_channel_stats(); // todo: enable me! + }; + void addAirtime(uint16_t written) { + float packet_cost_ms = 0.0; + float payload_cost_ms = ((float)written * _lora_us_per_byte)/1000.0; + packet_cost_ms += payload_cost_ms; + packet_cost_ms += (_preambleLength+4.25)*_lora_symbol_time_ms; + packet_cost_ms += PHY_HEADER_LORA_SYMBOLS * _lora_symbol_time_ms; + uint16_t cb = current_airtime_bin(); + uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } + _airtime_bins[cb] += packet_cost_ms; + _airtime_bins[nb] = 0; + }; + void checkModemStatus() { + if (millis()-_last_status_update >= STATUS_INTERVAL_MS) { + updateModemStatus(); + + _util_samples[_dcd_sample] = _dcd; + _dcd_sample = (_dcd_sample+1)%DCD_SAMPLES; + if (_dcd_sample % UTIL_UPDATE_INTERVAL == 0) { + int util_count = 0; + for (int ui = 0; ui < DCD_SAMPLES; ui++) { + if (_util_samples[ui]) util_count++; + } + _local_channel_util = (float)util_count / (float)DCD_SAMPLES; + _total_channel_util = _local_channel_util + _airtime; + if (_total_channel_util > 1.0) _total_channel_util = 1.0; + + int16_t cb = current_airtime_bin(); + uint16_t nb = cb+1; if (nb == AIRTIME_BINS) { nb = 0; } + if (_total_channel_util > _longterm_bins[cb]) _longterm_bins[cb] = _total_channel_util; + _longterm_bins[nb] = 0.0; + + updateAirtime(); + } + } + }; + void updateModemStatus() { + #if PLATFORM == PLATFORM_ESP32 + portENTER_CRITICAL(&update_lock); + #elif PLATFORM == PLATFORM_NRF52 + portENTER_CRITICAL(); + #endif + + uint8_t status = modemStatus(); + + _last_status_update = millis(); + + #if PLATFORM == PLATFORM_ESP32 + portEXIT_CRITICAL(&update_lock); + #elif PLATFORM == PLATFORM_NRF52 + portEXIT_CRITICAL(); + #endif + + if ((status & SIG_DETECT) == SIG_DETECT) { _stat_signal_detected = true; } else { _stat_signal_detected = false; } + if ((status & SIG_SYNCED) == SIG_SYNCED) { _stat_signal_synced = true; } else { _stat_signal_synced = false; } + if ((status & RX_ONGOING) == RX_ONGOING) { _stat_rx_ongoing = true; } else { _stat_rx_ongoing = false; } + + // if (stat_signal_detected || stat_signal_synced || stat_rx_ongoing) { + if (_stat_signal_detected || _stat_signal_synced) { + if (_stat_rx_ongoing) { + if (_dcd_count < DCD_THRESHOLD) { + _dcd_count++; + } else { + _last_dcd = _last_status_update; + _dcd_led = true; + _dcd = true; + } + } + } else { + if (_dcd_count == 0) { + _dcd_led = false; + } else if (_dcd_count > DCD_LED_STEP_D) { + _dcd_count -= DCD_LED_STEP_D; + } else { + _dcd_count = 0; + } + + if (_last_status_update > _last_dcd+_csma_slot_ms) { + _dcd = false; + _dcd_led = false; + _dcd_count = 0; + } + } + + + if (_dcd_led) { + led_rx_on(); + } else { + if (_airtime_lock) { + led_indicate_airtime_lock(); + } else { + led_rx_off(); + } + } + }; + void setPostTxYieldTimeout(uint32_t timeout) { _post_tx_yield_timeout = timeout; }; + uint32_t getPostTxYieldTimeout() { return _post_tx_yield_timeout; }; + void setDCD(bool dcd) { _dcd = dcd; }; + bool getDCD() { return _dcd; }; + void setDCDWaiting(bool dcd_waiting) { _dcd_waiting = dcd_waiting; }; + bool getDCDWaiting() { return _dcd_waiting; }; + void setDCDWaitUntil(uint32_t dcd_wait_until) { _dcd_wait_until = dcd_wait_until; }; + bool getDCDWaitUntil() { return _dcd_wait_until; }; + float getAirtime() { return _airtime; }; + float getLongtermAirtime() { return _longterm_airtime; }; + float getTotalChannelUtil() { return _total_channel_util; }; + float getLongtermChannelUtil() { return _longterm_channel_util; }; + float CSMASlope(float u) { return (pow(_e,_S*u-_S/2.0))/(pow(_e,_S*u-_S/2.0)+1.0); }; + void updateCSMAp() { + _csma_p = (uint8_t)((1.0-(_csma_p_min+(_csma_p_max-_csma_p_min)*CSMASlope(_airtime)))*255.0); + }; + uint8_t getCSMAp() { return _csma_p; }; + void setCSMASlotMS(int slot_size) { _csma_slot_ms = slot_size; }; + int getCSMASlotMS() { return _csma_slot_ms; }; + float getSymbolTime() { return _lora_symbol_time_ms; }; + float getSymbolRate() { return _lora_symbol_rate; }; + long getPreambleLength() { return _preambleLength; }; +protected: + virtual void explicitHeaderMode() = 0; + virtual void implicitHeaderMode() = 0; + + uint8_t _index; + bool _radio_locked; + bool _radio_online; + float _st_airtime_limit; + float _lt_airtime_limit; + bool _airtime_lock; + uint16_t _airtime_bins[AIRTIME_BINS] = {0}; + uint16_t _longterm_bins[AIRTIME_BINS] = {0}; + float _airtime; + float _longterm_airtime; + float _local_channel_util; + float _total_channel_util; + float _longterm_channel_util; + uint32_t _last_status_update; + bool _stat_signal_detected; + bool _stat_signal_synced; + bool _stat_rx_ongoing; + uint32_t _last_dcd; + uint16_t _dcd_count; + bool _dcd; + bool _dcd_led; + bool _dcd_waiting; + long _dcd_wait_until; + bool _util_samples[DCD_SAMPLES] = {false}; + int _dcd_sample; + uint32_t _post_tx_yield_timeout; + uint8_t _csma_p; + int _csma_slot_ms; + float _csma_p_min; + float _csma_p_max; + long _preambleLength; + float _lora_symbol_time_ms; + float _lora_symbol_rate; + float _lora_us_per_byte; + uint32_t _bitrate; + uint8_t _packet[255]; + void (*_onReceive)(uint8_t, int); +}; + +class sx126x : public RadioInterface { +public: + sx126x(uint8_t index, SPIClass* spi, bool tcxo, bool dio2_as_rf_switch, int ss, int sclk, int mosi, int miso, int reset, int + dio0, int busy, int rxen); + + int begin(); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(); + + int packetRssi(); + int currentRssi(); + uint8_t packetRssiRaw(); + uint8_t currentRssiRaw(); + uint8_t packetSnrRaw(); + float packetSnr(); + long packetFrequencyError(); + + // from Print + size_t write(uint8_t byte); + size_t write(const uint8_t *buffer, size_t size); + + // from Stream + int available(); + int read(); + int peek(); + void flush(); + + void onReceive(void(*callback)(uint8_t, int)); + + void receive(int size = 0); + void standby(); + void sleep(); + + bool preInit(); + uint8_t getTxPower(); + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + uint32_t getFrequency(); + void setFrequency(uint32_t frequency); + void setSpreadingFactor(int sf); + uint8_t getSpreadingFactor(); + uint32_t getSignalBandwidth(); + void setSignalBandwidth(uint32_t sbw); + void setCodingRate4(int denominator); + uint8_t getCodingRate4(); + void setPreambleLength(long length); + uint8_t modemStatus(); + void enableCrc(); + void disableCrc(); + void enableTCXO(); + void disableTCXO(); + + + byte random(); + + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + + void updateBitrate(); + + void handleDio0Rise(); +private: + void writeBuffer(const uint8_t* buffer, size_t size); + void readBuffer(uint8_t* buffer, size_t size); + void loraMode(); + void rxAntEnable(); + void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); + void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, int ldro); + void setSyncWord(uint16_t sw); + void waitOnBusy(); + void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); + void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); + void explicitHeaderMode(); + void implicitHeaderMode(); + + + uint8_t readRegister(uint16_t address); + void writeRegister(uint16_t address, uint8_t value); + uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); + + static void onDio0Rise(); + + void handleLowDataRate(); + void optimizeModemSensitivity(); + + void reset(void); + void calibrate(void); + void calibrate_image(uint32_t frequency); + void clearIRQStatus(); + +private: + SPISettings _spiSettings; + SPIClass* _spiModem; + int _ss; + int _sclk; + int _mosi; + int _miso; + int _reset; + int _dio0; + int _rxen; + int _busy; + uint32_t _frequency; + int _txp; + uint8_t _sf; + uint8_t _bw; + uint8_t _cr; + uint8_t _ldro; + int _packetIndex; + int _implicitHeaderMode; + int _payloadLength; + int _crcMode; + int _fifo_tx_addr_ptr; + int _fifo_rx_addr_ptr; + bool _preinit_done; + uint8_t _index; + bool _tcxo; + bool _dio2_as_rf_switch; +}; + +class sx127x : public RadioInterface { +public: + sx127x(uint8_t index, SPIClass* spi, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy); + + int begin(); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(); + + int packetRssi(); + int currentRssi(); + uint8_t packetRssiRaw(); + uint8_t currentRssiRaw(); + uint8_t packetSnrRaw(); + float packetSnr(); + long packetFrequencyError(); + + // from Print + size_t write(uint8_t byte); + size_t write(const uint8_t *buffer, size_t size); + + // from Stream + int available(); + int read(); + int peek(); + void flush(); + + void onReceive(void(*callback)(uint8_t, int)); + + void receive(int size = 0); + void standby(); + void sleep(); + + bool preInit(); + uint8_t getTxPower(); + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + uint32_t getFrequency(); + void setFrequency(uint32_t frequency); + void setSpreadingFactor(int sf); + uint8_t getSpreadingFactor(); + uint32_t getSignalBandwidth(); + void setSignalBandwidth(uint32_t sbw); + void setCodingRate4(int denominator); + uint8_t getCodingRate4(); + void setPreambleLength(long length); + uint8_t modemStatus(); + void enableCrc(); + void disableCrc(); + void enableTCXO(); + void disableTCXO(); + + byte random(); + + void setSPIFrequency(uint32_t frequency); + + void updateBitrate(); + + void handleDio0Rise(); + void clearIRQStatus(); +private: + void setSyncWord(uint8_t sw); + void explicitHeaderMode(); + void implicitHeaderMode(); + + + uint8_t readRegister(uint8_t address); + void writeRegister(uint8_t address, uint8_t value); + uint8_t singleTransfer(uint8_t address, uint8_t value); + + static void onDio0Rise(); + + void handleLowDataRate(); + void optimizeModemSensitivity(); + +private: + SPISettings _spiSettings; + SPIClass* _spiModem; + int _ss; + int _sclk; + int _mosi; + int _miso; + int _reset; + int _dio0; + int _busy; + uint32_t _frequency; + int _packetIndex; + int _implicitHeaderMode; + bool _preinit_done; + uint8_t _index; + uint8_t _sf; + uint8_t _cr; +}; + +class sx128x : public RadioInterface { +public: + sx128x(uint8_t index, SPIClass* spi, bool tcxo, int ss, int sclk, int mosi, int miso, int reset, int dio0, int busy, int rxen, int txen); + + int begin(); + void end(); + + int beginPacket(int implicitHeader = false); + int endPacket(); + + int packetRssi(); + int currentRssi(); + uint8_t packetRssiRaw(); + uint8_t currentRssiRaw(); + uint8_t packetSnrRaw(); + float packetSnr(); + long packetFrequencyError(); + + // from Print + size_t write(uint8_t byte); + size_t write(const uint8_t *buffer, size_t size); + + // from Stream + int available(); + int read(); + int peek(); + void flush(); + + void onReceive(void(*callback)(uint8_t, int)); + + void receive(int size = 0); + void standby(); + void sleep(); + + bool preInit(); + uint8_t getTxPower(); + void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN); + uint32_t getFrequency(); + void setFrequency(uint32_t frequency); + void setSpreadingFactor(int sf); + uint8_t getSpreadingFactor(); + uint32_t getSignalBandwidth(); + void setSignalBandwidth(uint32_t sbw); + void setCodingRate4(int denominator); + uint8_t getCodingRate4(); + void setPreambleLength(long length); + uint8_t modemStatus(); + void enableCrc(); + void disableCrc(); + void enableTCXO(); + void disableTCXO(); + + byte random(); + + void setSPIFrequency(uint32_t frequency); + + void dumpRegisters(Stream& out); + + void updateBitrate(); + + void handleDio0Rise(); + + void clearIRQStatus(); +private: + void writeBuffer(const uint8_t* buffer, size_t size); + void readBuffer(uint8_t* buffer, size_t size); + void txAntEnable(); + void rxAntEnable(); + void loraMode(); + void waitOnBusy(); + void executeOpcode(uint8_t opcode, uint8_t *buffer, uint8_t size); + void executeOpcodeRead(uint8_t opcode, uint8_t *buffer, uint8_t size); + void setPacketParams(uint32_t preamble, uint8_t headermode, uint8_t length, uint8_t crc); + void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr); + void setSyncWord(int sw); + void explicitHeaderMode(); + void implicitHeaderMode(); + + + uint8_t readRegister(uint16_t address); + void writeRegister(uint16_t address, uint8_t value); + uint8_t singleTransfer(uint8_t opcode, uint16_t address, uint8_t value); + + static void onDio0Rise(); + + void handleLowDataRate(); + void optimizeModemSensitivity(); + +private: + SPISettings _spiSettings; + SPIClass* _spiModem; + int _ss; + int _sclk; + int _mosi; + int _miso; + int _reset; + int _dio0; + int _rxen; + int _txen; + int _busy; + int _modem; + uint32_t _frequency; + int _txp; + uint8_t _sf; + uint8_t _bw; + uint8_t _cr; + int _packetIndex; + int _implicitHeaderMode; + int _payloadLength; + int _crcMode; + int _fifo_tx_addr_ptr; + int _fifo_rx_addr_ptr; + bool _preinit_done; + int _rxPacketLength; + uint8_t _index; + bool _tcxo; +}; +#endif diff --git a/Utilities.h b/Utilities.h index 3f0a8b8..2d82b15 100644 --- a/Utilities.h +++ b/Utilities.h @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "Radio.h" +#include "Radio.hpp" #include "Config.h" // Included for sorting