From b65fb885260fe1c345f61433645e09b5752497fc Mon Sep 17 00:00:00 2001 From: jgromes Date: Sun, 24 Sep 2023 18:17:32 +0200 Subject: [PATCH 01/20] [RFM9x] Use RFM9x only as alias for SX127x (#833) --- src/modules/RFM9x/RFM95.cpp | 93 ------------------------------------ src/modules/RFM9x/RFM95.h | 80 ------------------------------- src/modules/RFM9x/RFM96.cpp | 94 ------------------------------------- src/modules/RFM9x/RFM96.h | 88 ---------------------------------- src/modules/RFM9x/RFM97.cpp | 42 ----------------- src/modules/RFM9x/RFM97.h | 45 ------------------ src/modules/SX127x/SX1276.h | 6 +++ src/modules/SX127x/SX1277.h | 6 +++ src/modules/SX127x/SX1278.h | 6 +++ 9 files changed, 18 insertions(+), 442 deletions(-) delete mode 100644 src/modules/RFM9x/RFM95.cpp delete mode 100644 src/modules/RFM9x/RFM95.h delete mode 100644 src/modules/RFM9x/RFM96.cpp delete mode 100644 src/modules/RFM9x/RFM96.h delete mode 100644 src/modules/RFM9x/RFM97.cpp delete mode 100644 src/modules/RFM9x/RFM97.h diff --git a/src/modules/RFM9x/RFM95.cpp b/src/modules/RFM9x/RFM95.cpp deleted file mode 100644 index 90f1bc46..00000000 --- a/src/modules/RFM9x/RFM95.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "RFM95.h" -#if !defined(RADIOLIB_EXCLUDE_RFM9X) - -RFM95::RFM95(Module* mod) : SX1278(mod) { - -} - -int16_t RFM95::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) { - // execute common part - int16_t state = SX127x::begin(RADIOLIB_RFM9X_CHIP_VERSION_OFFICIAL, syncWord, preambleLength); - if(state == RADIOLIB_ERR_CHIP_NOT_FOUND) { - // SX127X_REG_VERSION might be set 0x12 - state = SX127x::begin(RADIOLIB_RFM9X_CHIP_VERSION_UNOFFICIAL, syncWord, preambleLength); - RADIOLIB_ASSERT(state); - } else if(state != RADIOLIB_ERR_NONE) { - // some other error - return(state); - } - RADIOLIB_DEBUG_PRINTLN("M\tSX1278"); - RADIOLIB_DEBUG_PRINTLN("M\tRFM95"); - - // configure publicly accessible settings - state = setBandwidth(bw); - RADIOLIB_ASSERT(state); - - state = setFrequency(freq); - RADIOLIB_ASSERT(state); - - state = setSpreadingFactor(sf); - RADIOLIB_ASSERT(state); - - state = setCodingRate(cr); - RADIOLIB_ASSERT(state); - - state = setOutputPower(power); - RADIOLIB_ASSERT(state); - - state = setGain(gain); - - return(state); -} - -int16_t RFM95::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) { - // execute common part - int16_t state = SX127x::beginFSK(RADIOLIB_RFM9X_CHIP_VERSION_OFFICIAL, freqDev, rxBw, preambleLength, enableOOK); - if(state == RADIOLIB_ERR_CHIP_NOT_FOUND) { - // SX127X_REG_VERSION might be set 0x12 - state = SX127x::beginFSK(RADIOLIB_RFM9X_CHIP_VERSION_UNOFFICIAL, freqDev, rxBw, preambleLength, enableOOK); - RADIOLIB_ASSERT(state); - } else if(state != RADIOLIB_ERR_NONE) { - // some other error - return(state); - } - RADIOLIB_DEBUG_PRINTLN("M\tSX1278"); - RADIOLIB_DEBUG_PRINTLN("M\tRFM95"); - - // configure settings not accessible by API - state = configFSK(); - RADIOLIB_ASSERT(state); - - // configure publicly accessible settings - state = setFrequency(freq); - RADIOLIB_ASSERT(state); - - state = setBitRate(br); - RADIOLIB_ASSERT(state); - - state = setOutputPower(power); - RADIOLIB_ASSERT(state); - - if(enableOOK) { - state = setDataShapingOOK(RADIOLIB_SHAPING_NONE); - RADIOLIB_ASSERT(state); - } else { - state = setDataShaping(RADIOLIB_SHAPING_NONE); - RADIOLIB_ASSERT(state); - } - - return(state); -} - -int16_t RFM95::setFrequency(float freq) { - RADIOLIB_CHECK_RANGE(freq, 862.0, 1020.0, RADIOLIB_ERR_INVALID_FREQUENCY); - - // set frequency and if successful, save the new setting - int16_t state = SX127x::setFrequencyRaw(freq); - if(state == RADIOLIB_ERR_NONE) { - SX127x::frequency = freq; - } - return(state); -} - -#endif diff --git a/src/modules/RFM9x/RFM95.h b/src/modules/RFM9x/RFM95.h deleted file mode 100644 index c0128003..00000000 --- a/src/modules/RFM9x/RFM95.h +++ /dev/null @@ -1,80 +0,0 @@ -#if !defined(_RADIOLIB_RFM95_H) -#define _RADIOLIB_RFM95_H - -#include "../../TypeDef.h" - -#if !defined(RADIOLIB_EXCLUDE_RFM9X) - -#include "../../Module.h" -#include "../SX127x/SX127x.h" -#include "../SX127x/SX1278.h" - -// SX127X_REG_VERSION -#define RADIOLIB_RFM9X_CHIP_VERSION_OFFICIAL 0x11 -#define RADIOLIB_RFM9X_CHIP_VERSION_UNOFFICIAL 0x12 // according to datasheet, only 0x11 should be possible, but some modules seem to have 0x12 - -/*! - \class RFM95 - \brief Derived class for %RFM95 modules. Overrides some methods from SX1278 due to different parameter ranges. -*/ -class RFM95: public SX1278 { - public: - - // constructor - - /*! - \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. - \param mod Instance of Module that will be used to communicate with the %LoRa chip. - */ - RFM95(Module* mod); - - // basic methods - - /*! - \brief %LoRa modem initialization method. Must be called at least once from Arduino sketch to initialize the module. - \param freq Carrier frequency in MHz. Allowed values range from 868.0 MHz to 915.0 MHz. - \param bw %LoRa link bandwidth in kHz. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz. - \param sf %LoRa link spreading factor. Allowed values range from 6 to 12. - \param cr %LoRa link coding rate denominator. Allowed values range from 5 to 8. - \param syncWord %LoRa sync word. Can be used to distinguish different networks. Note that value 0x34 is reserved for LoRaWAN networks. - \param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm. - \param preambleLength Length of %LoRa transmission preamble in symbols. The actual preamble length is 4.25 symbols longer than the set number. - Allowed values range from 6 to 65535. - \param gain Gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain. - Set to 0 to enable automatic gain control (recommended). - \returns \ref status_codes - */ - int16_t begin(float freq = 915.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = RADIOLIB_SX127X_SYNC_WORD, int8_t power = 10, uint16_t preambleLength = 8, uint8_t gain = 0); - - /*! - \brief FSK modem initialization method. Must be called at least once from Arduino sketch to initialize the module. - \param freq Carrier frequency in MHz. Allowed values range from 137.0 MHz to 525.0 MHz. - \param br Bit rate of the FSK transmission in kbps (kilobits per second). Allowed values range from 1.2 to 300.0 kbps. - \param freqDev Frequency deviation of the FSK transmission in kHz. Allowed values range from 0.6 to 200.0 kHz. - Note that the allowed range changes based on bit rate setting, so that the condition FreqDev + BitRate/2 <= 250 kHz is always met. - \param rxBw Receiver bandwidth in kHz. Allowed values are 2.6, 3.1, 3.9, 5.2, 6.3, 7.8, 10.4, 12.5, 15.6, 20.8, 25, 31.3, 41.7, 50, 62.5, 83.3, 100, 125, 166.7, 200 and 250 kHz. - \param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm. - \param preambleLength Length of FSK preamble in bits. - \param enableOOK Use OOK modulation instead of FSK. - \returns \ref status_codes - */ - int16_t beginFSK(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 125.0, int8_t power = 10, uint16_t preambleLength = 16, bool enableOOK = false); - - // configuration methods - - /*! - \brief Sets carrier frequency. Allowed values range from 868.0 MHz to 915.0 MHz. - \param freq Carrier frequency to be set in MHz. - \returns \ref status_codes - */ - int16_t setFrequency(float freq); - -#if !defined(RADIOLIB_GODMODE) - private: -#endif - -}; - -#endif - -#endif diff --git a/src/modules/RFM9x/RFM96.cpp b/src/modules/RFM9x/RFM96.cpp deleted file mode 100644 index 0c744184..00000000 --- a/src/modules/RFM9x/RFM96.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "RFM96.h" -#if !defined(RADIOLIB_EXCLUDE_RFM9X) - -RFM96::RFM96(Module* mod) : SX1278(mod) { - -} - -int16_t RFM96::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) { - // execute common part - int16_t state = SX127x::begin(RADIOLIB_RFM9X_CHIP_VERSION_OFFICIAL, syncWord, preambleLength); - if(state == RADIOLIB_ERR_CHIP_NOT_FOUND) { - // SX127X_REG_VERSION might be set 0x12 - state = SX127x::begin(RADIOLIB_RFM9X_CHIP_VERSION_UNOFFICIAL, syncWord, preambleLength); - RADIOLIB_ASSERT(state); - } else if(state != RADIOLIB_ERR_NONE) { - // some other error - return(state); - } - RADIOLIB_DEBUG_PRINTLN("M\tSX1278"); - RADIOLIB_DEBUG_PRINTLN("M\tRFM96"); - - // configure publicly accessible settings - state = setBandwidth(bw); - RADIOLIB_ASSERT(state); - - state = setFrequency(freq); - RADIOLIB_ASSERT(state); - - state = setSpreadingFactor(sf); - RADIOLIB_ASSERT(state); - - state = setCodingRate(cr); - RADIOLIB_ASSERT(state); - - state = setOutputPower(power); - RADIOLIB_ASSERT(state); - - state = setGain(gain); - RADIOLIB_ASSERT(state); - - return(state); -} - -int16_t RFM96::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) { - // execute common part - int16_t state = SX127x::beginFSK(RADIOLIB_RFM9X_CHIP_VERSION_OFFICIAL, freqDev, rxBw, preambleLength, enableOOK); - if(state == RADIOLIB_ERR_CHIP_NOT_FOUND) { - // SX127X_REG_VERSION might be set 0x12 - state = SX127x::beginFSK(RADIOLIB_RFM9X_CHIP_VERSION_UNOFFICIAL, freqDev, rxBw, preambleLength, enableOOK); - RADIOLIB_ASSERT(state); - } else if(state != RADIOLIB_ERR_NONE) { - // some other error - return(state); - } - RADIOLIB_DEBUG_PRINTLN("M\tSX1278"); - RADIOLIB_DEBUG_PRINTLN("M\tRFM96"); - - // configure settings not accessible by API - state = configFSK(); - RADIOLIB_ASSERT(state); - - // configure publicly accessible settings - state = setFrequency(freq); - RADIOLIB_ASSERT(state); - - state = setBitRate(br); - RADIOLIB_ASSERT(state); - - state = setOutputPower(power); - RADIOLIB_ASSERT(state); - - if(enableOOK) { - state = setDataShapingOOK(RADIOLIB_SHAPING_NONE); - RADIOLIB_ASSERT(state); - } else { - state = setDataShaping(RADIOLIB_SHAPING_NONE); - RADIOLIB_ASSERT(state); - } - - return(state); -} - -int16_t RFM96::setFrequency(float freq) { - RADIOLIB_CHECK_RANGE(freq, 410.0, 525.0, RADIOLIB_ERR_INVALID_FREQUENCY); - - // set frequency and if successful, save the new setting - int16_t state = SX127x::setFrequencyRaw(freq); - if(state == RADIOLIB_ERR_NONE) { - SX127x::frequency = freq; - } - return(state); -} - -#endif diff --git a/src/modules/RFM9x/RFM96.h b/src/modules/RFM9x/RFM96.h deleted file mode 100644 index e5990197..00000000 --- a/src/modules/RFM9x/RFM96.h +++ /dev/null @@ -1,88 +0,0 @@ -#if !defined(_RADIOLIB_RFM96_H) -#define _RADIOLIB_RFM96_H - -#include "../../TypeDef.h" - -#if !defined(RADIOLIB_EXCLUDE_RFM9X) - -#include "../../Module.h" -#include "../SX127x/SX127x.h" -#include "../SX127x/SX1278.h" - -// SX127X_REG_VERSION -#define RADIOLIB_RFM9X_CHIP_VERSION_OFFICIAL 0x11 -#define RADIOLIB_RFM9X_CHIP_VERSION_UNOFFICIAL 0x12 // according to datasheet, only 0x11 should be possible, but some modules seem to have 0x12 - -/*! - \class RFM96 - \brief Derived class for %RFM96 modules. Overrides some methods from SX1278 due to different parameter ranges. -*/ -class RFM96: public SX1278 { - public: - - // constructor - - /*! - \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. - \param mod Instance of Module that will be used to communicate with the %LoRa chip. - */ - RFM96(Module* mod); - - // basic methods - - /*! - \brief %LoRa modem initialization method. Must be called at least once from Arduino sketch to initialize the module. - \param freq Carrier frequency in MHz. Allowed values range from 433.0 MHz to 470.0 MHz. - \param bw %LoRa link bandwidth in kHz. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz. - \param sf %LoRa link spreading factor. Allowed values range from 6 to 12. - \param cr %LoRa link coding rate denominator. Allowed values range from 5 to 8. - \param syncWord %LoRa sync word. Can be used to distinguish different networks. - Note that value 0x34 is reserved for LoRaWAN networks. - \param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm. - \param preambleLength Length of %LoRa transmission preamble in symbols. The actual preamble length is 4.25 symbols - longer than the set number. Allowed values range from 6 to 65535. - \param gain Gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 - where 1 is the highest gain. Set to 0 to enable automatic gain control (recommended). - \returns \ref status_codes - */ - int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = RADIOLIB_SX127X_SYNC_WORD, int8_t power = 10, uint16_t preambleLength = 8, uint8_t gain = 0); - - /*! - \brief FSK modem initialization method. Must be called at least once from Arduino sketch to initialize the module. - \param freq Carrier frequency in MHz. Allowed values range from 137.0 MHz to 525.0 MHz. - \param br Bit rate of the FSK transmission in kbps (kilobits per second). Allowed values range from 1.2 to 300.0 kbps. - \param freqDev Frequency deviation of the FSK transmission in kHz. Allowed values range from 0.6 to 200.0 kHz. - Note that the allowed range changes based on bit rate setting, so that the condition FreqDev + BitRate/2 <= 250 kHz is always met. - \param rxBw Receiver bandwidth in kHz. Allowed values are 2.6, 3.1, 3.9, 5.2, 6.3, 7.8, 10.4, 12.5, 15.6, 20.8, 25, - 31.3, 41.7, 50, 62.5, 83.3, 100, 125, 166.7, 200 and 250 kHz. - \param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm. - \param preambleLength Length of FSK preamble in bits. - \param enableOOK Use OOK modulation instead of FSK. - \returns \ref status_codes - */ - int16_t beginFSK(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 125.0, int8_t power = 10, uint16_t preambleLength = 16, bool enableOOK = false); - - // configuration methods - - /*! - \brief Sets carrier frequency. Allowed values range from 433.0 MHz to 470.0 MHz. - \param freq Carrier frequency to be set in MHz. - \returns \ref status_codes - */ - int16_t setFrequency(float freq); - -#if !defined(RADIOLIB_GODMODE) - private: -#endif - -}; - -/*! - \class RFM98 - \brief Only exists as alias for RFM96, since there seems to be no difference between %RFM96 and %RFM98 modules. -*/ -RADIOLIB_TYPE_ALIAS(RFM96, RFM98); - -#endif - -#endif diff --git a/src/modules/RFM9x/RFM97.cpp b/src/modules/RFM9x/RFM97.cpp deleted file mode 100644 index 0d02c707..00000000 --- a/src/modules/RFM9x/RFM97.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "RFM97.h" -#if !defined(RADIOLIB_EXCLUDE_RFM9X) - -RFM97::RFM97(Module* mod) : RFM95(mod) { - -}; - -int16_t RFM97::setSpreadingFactor(uint8_t sf) { - // check active modem - if(getActiveModem() != RADIOLIB_SX127X_LORA) { - return(RADIOLIB_ERR_WRONG_MODEM); - } - - uint8_t newSpreadingFactor; - - // check allowed spreading factor values - switch(sf) { - case 6: - newSpreadingFactor = RADIOLIB_SX127X_SF_6; - break; - case 7: - newSpreadingFactor = RADIOLIB_SX127X_SF_7; - break; - case 8: - newSpreadingFactor = RADIOLIB_SX127X_SF_8; - break; - case 9: - newSpreadingFactor = RADIOLIB_SX127X_SF_9; - break; - default: - return(RADIOLIB_ERR_INVALID_SPREADING_FACTOR); - } - - // set spreading factor and if successful, save the new setting - int16_t state = SX1278::setSpreadingFactorRaw(newSpreadingFactor); - if(state == RADIOLIB_ERR_NONE) { - SX127x::spreadingFactor = sf; - } - return(state); -} - -#endif diff --git a/src/modules/RFM9x/RFM97.h b/src/modules/RFM9x/RFM97.h deleted file mode 100644 index b4623495..00000000 --- a/src/modules/RFM9x/RFM97.h +++ /dev/null @@ -1,45 +0,0 @@ -#if !defined(_RADIOLIB_RFM97_H) -#define _RADIOLIB_RFM97_H - -#include "../../TypeDef.h" - -#if !defined(RADIOLIB_EXCLUDE_RFM9X) - -#include "../../Module.h" -#include "../SX127x/SX127x.h" -#include "../SX127x/SX1278.h" -#include "RFM95.h" - -/*! - \class RFM97 - \brief Derived class for %RFM97 modules. Overrides some methods from RFM95 due to different parameter ranges. -*/ -class RFM97: public RFM95 { - public: - - // constructor - - /*! - \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. - \param mod Instance of Module that will be used to communicate with the %LoRa chip. - */ - RFM97(Module* mod); - - // configuration methods - - /*! - \brief Sets %LoRa link spreading factor. Allowed values range from 6 to 9. Only available in %LoRa mode. - \param sf %LoRa link spreading factor to be set. - \returns \ref status_codes - */ - int16_t setSpreadingFactor(uint8_t sf); - -#if !defined(RADIOLIB_GODMODE) - private: -#endif - -}; - -#endif - -#endif diff --git a/src/modules/SX127x/SX1276.h b/src/modules/SX127x/SX1276.h index 0d83fb36..659e22d7 100644 --- a/src/modules/SX127x/SX1276.h +++ b/src/modules/SX127x/SX1276.h @@ -69,6 +69,12 @@ class SX1276: public SX1278 { }; +/*! + \class RFM96 + \brief Only exists as alias for SX1276, since there seems to be no difference between %RFM96 and %SX1276 modules. +*/ +RADIOLIB_TYPE_ALIAS(SX1276, RFM96); + #endif #endif diff --git a/src/modules/SX127x/SX1277.h b/src/modules/SX127x/SX1277.h index 88fb58fe..15804d61 100644 --- a/src/modules/SX127x/SX1277.h +++ b/src/modules/SX127x/SX1277.h @@ -83,6 +83,12 @@ class SX1277: public SX1278 { }; +/*! + \class RFM97 + \brief Only exists as alias for SX1277, since there seems to be no difference between %RFM97 and %SX1277 modules. +*/ +RADIOLIB_TYPE_ALIAS(SX1277, RFM97); + #endif #endif diff --git a/src/modules/SX127x/SX1278.h b/src/modules/SX127x/SX1278.h index 92c996ed..a6db4297 100644 --- a/src/modules/SX127x/SX1278.h +++ b/src/modules/SX127x/SX1278.h @@ -299,6 +299,12 @@ class SX1278: public SX127x { }; +/*! + \class RFM98 + \brief Only exists as alias for SX1278, since there seems to be no difference between %RFM98 and %SX1278 modules. +*/ +RADIOLIB_TYPE_ALIAS(SX1278, RFM98); + #endif #endif From 10d225fadb6bcecea440788689330d66cee2fca2 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sun, 24 Sep 2023 18:19:19 +0200 Subject: [PATCH 02/20] [SX127x] Allow alternate chip versions --- src/modules/SX127x/SX1272.cpp | 6 ++++-- src/modules/SX127x/SX1273.cpp | 3 ++- src/modules/SX127x/SX1276.cpp | 6 ++++-- src/modules/SX127x/SX1277.cpp | 6 ++++-- src/modules/SX127x/SX1278.cpp | 6 ++++-- src/modules/SX127x/SX1278.h | 4 +++- src/modules/SX127x/SX1279.cpp | 6 ++++-- src/modules/SX127x/SX127x.cpp | 24 +++++++++++++++--------- src/modules/SX127x/SX127x.h | 12 +++++++----- 9 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/modules/SX127x/SX1272.cpp b/src/modules/SX127x/SX1272.cpp index ac572101..45bf8802 100644 --- a/src/modules/SX127x/SX1272.cpp +++ b/src/modules/SX127x/SX1272.cpp @@ -8,7 +8,8 @@ SX1272::SX1272(Module* mod) : SX127x(mod) { int16_t SX1272::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) { // execute common part - int16_t state = SX127x::begin(RADIOLIB_SX1272_CHIP_VERSION, syncWord, preambleLength); + uint8_t version = RADIOLIB_SX1272_CHIP_VERSION; + int16_t state = SX127x::begin(&version, 1, syncWord, preambleLength); RADIOLIB_ASSERT(state); // configure publicly accessible settings @@ -39,7 +40,8 @@ int16_t SX1272::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync int16_t SX1272::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) { // execute common part - int16_t state = SX127x::beginFSK(RADIOLIB_SX1272_CHIP_VERSION, freqDev, rxBw, preambleLength, enableOOK); + uint8_t version = RADIOLIB_SX1272_CHIP_VERSION; + int16_t state = SX127x::beginFSK(&version, 1, freqDev, rxBw, preambleLength, enableOOK); RADIOLIB_ASSERT(state); // configure settings not accessible by API diff --git a/src/modules/SX127x/SX1273.cpp b/src/modules/SX127x/SX1273.cpp index 4b180694..ea021c66 100644 --- a/src/modules/SX127x/SX1273.cpp +++ b/src/modules/SX127x/SX1273.cpp @@ -7,7 +7,8 @@ SX1273::SX1273(Module* mod) : SX1272(mod) { int16_t SX1273::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) { // execute common part - int16_t state = SX127x::begin(RADIOLIB_SX1272_CHIP_VERSION, syncWord, preambleLength); + uint8_t version = RADIOLIB_SX1272_CHIP_VERSION; + int16_t state = SX127x::begin(&version, 1, syncWord, preambleLength); RADIOLIB_ASSERT(state); // configure publicly accessible settings diff --git a/src/modules/SX127x/SX1276.cpp b/src/modules/SX127x/SX1276.cpp index 21eadb57..b20fd7d1 100644 --- a/src/modules/SX127x/SX1276.cpp +++ b/src/modules/SX127x/SX1276.cpp @@ -7,7 +7,8 @@ SX1276::SX1276(Module* mod) : SX1278(mod) { int16_t SX1276::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) { // execute common part - int16_t state = SX127x::begin(RADIOLIB_SX1278_CHIP_VERSION, syncWord, preambleLength); + uint8_t versions[] = { RADIOLIB_SX1278_CHIP_VERSION, RADIOLIB_SX1278_CHIP_VERSION_ALT, RADIOLIB_SX1278_CHIP_VERSION_RFM9X }; + int16_t state = SX127x::begin(versions, 3, syncWord, preambleLength); RADIOLIB_ASSERT(state); // configure publicly accessible settings @@ -38,7 +39,8 @@ int16_t SX1276::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync int16_t SX1276::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) { // execute common part - int16_t state = SX127x::beginFSK(RADIOLIB_SX1278_CHIP_VERSION, freqDev, rxBw, preambleLength, enableOOK); + uint8_t versions[] = { RADIOLIB_SX1278_CHIP_VERSION, RADIOLIB_SX1278_CHIP_VERSION_ALT, RADIOLIB_SX1278_CHIP_VERSION_RFM9X }; + int16_t state = SX127x::beginFSK(versions, 3, freqDev, rxBw, preambleLength, enableOOK); RADIOLIB_ASSERT(state); // configure settings not accessible by API diff --git a/src/modules/SX127x/SX1277.cpp b/src/modules/SX127x/SX1277.cpp index d466a385..5875bd9f 100644 --- a/src/modules/SX127x/SX1277.cpp +++ b/src/modules/SX127x/SX1277.cpp @@ -7,7 +7,8 @@ SX1277::SX1277(Module* mod) : SX1278(mod) { int16_t SX1277::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) { // execute common part - int16_t state = SX127x::begin(RADIOLIB_SX1278_CHIP_VERSION, syncWord, preambleLength); + uint8_t versions[] = { RADIOLIB_SX1278_CHIP_VERSION, RADIOLIB_SX1278_CHIP_VERSION_ALT, RADIOLIB_SX1278_CHIP_VERSION_RFM9X }; + int16_t state = SX127x::begin(versions, 3, syncWord, preambleLength); RADIOLIB_ASSERT(state); // configure publicly accessible settings @@ -38,7 +39,8 @@ int16_t SX1277::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync int16_t SX1277::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) { // execute common part - int16_t state = SX127x::beginFSK(RADIOLIB_SX1278_CHIP_VERSION, freqDev, rxBw, preambleLength, enableOOK); + uint8_t versions[] = { RADIOLIB_SX1278_CHIP_VERSION, RADIOLIB_SX1278_CHIP_VERSION_ALT, RADIOLIB_SX1278_CHIP_VERSION_RFM9X }; + int16_t state = SX127x::beginFSK(versions, 3, freqDev, rxBw, preambleLength, enableOOK); RADIOLIB_ASSERT(state); // configure settings not accessible by API diff --git a/src/modules/SX127x/SX1278.cpp b/src/modules/SX127x/SX1278.cpp index 9fb0c5df..90325ff0 100644 --- a/src/modules/SX127x/SX1278.cpp +++ b/src/modules/SX127x/SX1278.cpp @@ -8,7 +8,8 @@ SX1278::SX1278(Module* mod) : SX127x(mod) { int16_t SX1278::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) { // execute common part - int16_t state = SX127x::begin(RADIOLIB_SX1278_CHIP_VERSION, syncWord, preambleLength); + uint8_t versions[] = { RADIOLIB_SX1278_CHIP_VERSION, RADIOLIB_SX1278_CHIP_VERSION_ALT, RADIOLIB_SX1278_CHIP_VERSION_RFM9X }; + int16_t state = SX127x::begin(versions, 3, syncWord, preambleLength); RADIOLIB_ASSERT(state); // configure publicly accessible settings @@ -39,7 +40,8 @@ int16_t SX1278::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync int16_t SX1278::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) { // execute common part - int16_t state = SX127x::beginFSK(RADIOLIB_SX1278_CHIP_VERSION, freqDev, rxBw, preambleLength, enableOOK); + uint8_t versions[] = { RADIOLIB_SX1278_CHIP_VERSION, RADIOLIB_SX1278_CHIP_VERSION_ALT, RADIOLIB_SX1278_CHIP_VERSION_RFM9X }; + int16_t state = SX127x::beginFSK(versions, 3, freqDev, rxBw, preambleLength, enableOOK); RADIOLIB_ASSERT(state); // configure settings not accessible by API diff --git a/src/modules/SX127x/SX1278.h b/src/modules/SX127x/SX1278.h index a6db4297..c9f966a2 100644 --- a/src/modules/SX127x/SX1278.h +++ b/src/modules/SX127x/SX1278.h @@ -67,7 +67,9 @@ #define RADIOLIB_SX1278_AGC_AUTO_ON 0b00000100 // 2 2 LNA gain set by internal AGC loop // SX127X_REG_VERSION -#define RADIOLIB_SX1278_CHIP_VERSION 0x12 +#define RADIOLIB_SX1278_CHIP_VERSION 0x12 // this is the "official" version listed in datasheet +#define RADIOLIB_SX1278_CHIP_VERSION_ALT 0x13 // appears sometimes +#define RADIOLIB_SX1278_CHIP_VERSION_RFM9X 0x11 // this value is used for the RFM9x // SX1278 FSK modem settings // SX127X_REG_PA_RAMP diff --git a/src/modules/SX127x/SX1279.cpp b/src/modules/SX127x/SX1279.cpp index ae79f0f8..4fdd4daa 100644 --- a/src/modules/SX127x/SX1279.cpp +++ b/src/modules/SX127x/SX1279.cpp @@ -7,7 +7,8 @@ SX1279::SX1279(Module* mod) : SX1278(mod) { int16_t SX1279::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, uint8_t gain) { // execute common part - int16_t state = SX127x::begin(RADIOLIB_SX1278_CHIP_VERSION, syncWord, preambleLength); + uint8_t versions[] = { RADIOLIB_SX1278_CHIP_VERSION, RADIOLIB_SX1278_CHIP_VERSION_ALT, RADIOLIB_SX1278_CHIP_VERSION_RFM9X }; + int16_t state = SX127x::begin(versions, 3, syncWord, preambleLength); RADIOLIB_ASSERT(state); // configure publicly accessible settings @@ -38,7 +39,8 @@ int16_t SX1279::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync int16_t SX1279::beginFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, bool enableOOK) { // execute common part - int16_t state = SX127x::beginFSK(RADIOLIB_SX1278_CHIP_VERSION, freqDev, rxBw, preambleLength, enableOOK); + uint8_t versions[] = { RADIOLIB_SX1278_CHIP_VERSION, RADIOLIB_SX1278_CHIP_VERSION_ALT, RADIOLIB_SX1278_CHIP_VERSION_RFM9X }; + int16_t state = SX127x::beginFSK(versions, 3, freqDev, rxBw, preambleLength, enableOOK); RADIOLIB_ASSERT(state); // configure settings not accessible by API diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 1a8f2eaf..7198e432 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -10,14 +10,14 @@ Module* SX127x::getMod() { return(this->mod); } -int16_t SX127x::begin(uint8_t chipVersion, uint8_t syncWord, uint16_t preambleLength) { +int16_t SX127x::begin(uint8_t* chipVersions, uint8_t numVersions, uint8_t syncWord, uint16_t preambleLength) { // set module properties this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); // try to find the SX127x chip - if(!SX127x::findChip(chipVersion)) { + if(!SX127x::findChip(chipVersions, numVersions)) { RADIOLIB_DEBUG_PRINTLN("No SX127x found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); @@ -61,14 +61,14 @@ int16_t SX127x::begin(uint8_t chipVersion, uint8_t syncWord, uint16_t preambleLe return(state); } -int16_t SX127x::beginFSK(uint8_t chipVersion, float freqDev, float rxBw, uint16_t preambleLength, bool enableOOK) { +int16_t SX127x::beginFSK(uint8_t* chipVersions, uint8_t numVersions, float freqDev, float rxBw, uint16_t preambleLength, bool enableOOK) { // set module properties this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); // try to find the SX127x chip - if(!SX127x::findChip(chipVersion)) { + if(!SX127x::findChip(chipVersions, numVersions)) { RADIOLIB_DEBUG_PRINTLN("No SX127x found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); @@ -1473,7 +1473,7 @@ int16_t SX127x::setPacketMode(uint8_t mode, uint8_t len) { return(state); } -bool SX127x::findChip(uint8_t ver) { +bool SX127x::findChip(uint8_t* vers, uint8_t num) { uint8_t i = 0; bool flagFound = false; while((i < 10) && !flagFound) { @@ -1482,13 +1482,19 @@ bool SX127x::findChip(uint8_t ver) { // check version register int16_t version = getChipVersion(); - if(version == ver) { - flagFound = true; - } else { - RADIOLIB_DEBUG_PRINTLN("SX127x not found! (%d of 10 tries) RADIOLIB_SX127X_REG_VERSION == 0x%04X, expected 0x00%X", i + 1, version, ver); + for(uint8_t i = 0; i < num; i++) { + if(version == vers[i]) { + flagFound = true; + break; + } + } + + if(!flagFound) { + RADIOLIB_DEBUG_PRINTLN("SX127x not found! (%d of 10 tries) RADIOLIB_SX127X_REG_VERSION == 0x%04X", i + 1, version); this->mod->hal->delay(10); i++; } + } return(flagFound); diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index 9ef2890f..9a622538 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -601,12 +601,13 @@ class SX127x: public PhysicalLayer { /*! \brief Initialization method. Will be called with appropriate parameters when calling initialization method from derived class. - \param chipVersion Value in SPI version register. Used to verify the connection and hardware version. + \param chipVersion Array of possible values in SPI version register. Used to verify the connection and hardware version. + \param numVersions Number of possible chip versions. \param syncWord %LoRa sync word. \param preambleLength Length of %LoRa transmission preamble in symbols. \returns \ref status_codes */ - int16_t begin(uint8_t chipVersion, uint8_t syncWord, uint16_t preambleLength); + int16_t begin(uint8_t* chipVersions, uint8_t numVersions, uint8_t syncWord, uint16_t preambleLength); /*! \brief Reset method. Will reset the chip to the default state using RST pin. Declared pure virtual since SX1272 and SX1278 implementations differ. @@ -615,14 +616,15 @@ class SX127x: public PhysicalLayer { /*! \brief Initialization method for FSK modem. Will be called with appropriate parameters when calling FSK initialization method from derived class. - \param chipVersion Value in SPI version register. Used to verify the connection and hardware version. + \param chipVersion Array of possible values in SPI version register. Used to verify the connection and hardware version. + \param numVersions Number of possible chip versions. \param freqDev Frequency deviation of the FSK transmission in kHz. \param rxBw Receiver bandwidth in kHz. \param preambleLength Length of FSK preamble in bits. \param enableOOK Flag to specify OOK mode. This modulation is similar to FSK. \returns \ref status_codes */ - int16_t beginFSK(uint8_t chipVersion, float freqDev, float rxBw, uint16_t preambleLength, bool enableOOK); + int16_t beginFSK(uint8_t* chipVersions, uint8_t numVersions, float freqDev, float rxBw, uint16_t preambleLength, bool enableOOK); /*! \brief Binary transmit method. Will transmit arbitrary binary data up to 255 bytes long using %LoRa or up to 63 bytes using FSK modem. @@ -1211,7 +1213,7 @@ class SX127x: public PhysicalLayer { bool packetLengthQueried = false; // FSK packet length is the first byte in FIFO, length can only be queried once uint8_t packetLengthConfig = RADIOLIB_SX127X_PACKET_VARIABLE; - bool findChip(uint8_t ver); + bool findChip(uint8_t* vers, uint8_t num); int16_t setMode(uint8_t mode); int16_t setActiveModem(uint8_t modem); void clearIRQFlags(); From b817819c60b1b2af6dcd69f678ce4dcaa41db1b0 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sun, 24 Sep 2023 18:19:48 +0200 Subject: [PATCH 03/20] [RM9x] Drop RFM9x as separate class --- src/BuildOpt.h | 1 - src/RadioLib.h | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/BuildOpt.h b/src/BuildOpt.h index 5fe2e0ba..275539c6 100644 --- a/src/BuildOpt.h +++ b/src/BuildOpt.h @@ -68,7 +68,6 @@ //#define RADIOLIB_EXCLUDE_SI443X //#define RADIOLIB_EXCLUDE_RFM2X // dependent on RADIOLIB_EXCLUDE_SI443X //#define RADIOLIB_EXCLUDE_SX127X - //#define RADIOLIB_EXCLUDE_RFM9X // dependent on RADIOLIB_EXCLUDE_SX127X //#define RADIOLIB_EXCLUDE_SX126X //#define RADIOLIB_EXCLUDE_STM32WLX // dependent on RADIOLIB_EXCLUDE_SX126X //#define RADIOLIB_EXCLUDE_SX128X diff --git a/src/RadioLib.h b/src/RadioLib.h index 8eaa072a..ee7026d2 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -76,9 +76,6 @@ #include "modules/RF69/RF69.h" #include "modules/RFM2x/RFM22.h" #include "modules/RFM2x/RFM23.h" -#include "modules/RFM9x/RFM95.h" -#include "modules/RFM9x/RFM96.h" -#include "modules/RFM9x/RFM97.h" #include "modules/Si443x/Si4430.h" #include "modules/Si443x/Si4431.h" #include "modules/Si443x/Si4432.h" From 44bdf0dba422e24276b473de13bc138728f49c99 Mon Sep 17 00:00:00 2001 From: jgromes Date: Mon, 25 Sep 2023 06:42:15 +0200 Subject: [PATCH 04/20] [SX126x] Changed default whitening initial value for SX127x/LoRaWAN compatibility (#832) --- src/modules/SX126x/SX126x.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 5b18b969..21db66eb 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -870,11 +870,11 @@ class SX126x: public PhysicalLayer { /*! \brief Sets FSK whitening parameters. \param enabled True = Whitening enabled - \param initial Initial value used for the whitening LFSR in FSK mode. Defaults to 0x0100, - use 0x01FF for SX127x compatibility. + \param initial Initial value used for the whitening LFSR in FSK mode. + By default set to 0x01FF for compatibility with SX127x and LoRaWAN. \returns \ref status_codes */ - int16_t setWhitening(bool enabled, uint16_t initial = 0x0100); + int16_t setWhitening(bool enabled, uint16_t initial = 0x01FF); /*! \brief Sets TCXO (Temperature Compensated Crystal Oscillator) configuration. @@ -1020,7 +1020,7 @@ class SX126x: public PhysicalLayer { #if !defined(RADIOLIB_EXCLUDE_DIRECT_RECEIVE) /*! - \brief Set interrupt service routine function to call when data bit is receveid in direct mode. + \brief Set interrupt service routine function to call when data bit is received in direct mode. \param func Pointer to interrupt service routine. */ void setDirectAction(void (*func)(void)); From d329c609063ec59040233e5c6dab63e4115bced2 Mon Sep 17 00:00:00 2001 From: BayCom GmbH Date: Fri, 29 Sep 2023 14:27:31 +0200 Subject: [PATCH 05/20] [SX127x] disable syncword generation & detection, add method to set preamble polarity (#834) * allow syncword to be disabled if length is 0 * add method to change preamble polarity in FSK mode * add new method 'setPreamblePolarity' * move RADIOLIB_SX127X_PREAMBLE_POLARITY_55 from ::config to ::begin & ::beginFSK * [SX127x] Remove FSK preamble config from LoRa init method * [SX127x] Rename preamble inversion method --------- Co-authored-by: BayCom GmbH Co-authored-by: jgromes --- keywords.txt | 1 + src/modules/SX127x/SX127x.cpp | 34 ++++++++++++++++++++++++++++++---- src/modules/SX127x/SX127x.h | 7 +++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/keywords.txt b/keywords.txt index 36bfb988..a7d5d12e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -124,6 +124,7 @@ setSyncWord KEYWORD2 setOutputPower KEYWORD2 setCurrentLimit KEYWORD2 setPreambleLength KEYWORD2 +invertPreamble KEYWORD2 setGain KEYWORD2 getFrequencyError KEYWORD2 getRSSI KEYWORD2 diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 7198e432..38fb6979 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -118,6 +118,10 @@ int16_t SX127x::beginFSK(uint8_t* chipVersions, uint8_t numVersions, float freqD state = SX127x::setPreambleLength(preambleLength); RADIOLIB_ASSERT(state); + // set preamble polarity + state = invertPreamble(false); + RADIOLIB_ASSERT(state); + // set default sync word uint8_t syncWord[] = {0x12, 0xAD}; state = setSyncWord(syncWord, 2); @@ -782,6 +786,25 @@ int16_t SX127x::setPreambleLength(size_t preambleLength) { return(RADIOLIB_ERR_UNKNOWN); } +int16_t SX127x::invertPreamble(bool enable) { + // set mode to standby + int16_t state = setMode(RADIOLIB_SX127X_STANDBY); + RADIOLIB_ASSERT(state); + + // check active modem + uint8_t modem = getActiveModem(); + if(modem == RADIOLIB_SX127X_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } else if(modem == RADIOLIB_SX127X_FSK_OOK) { + // set preamble polarity + uint8_t polarity = enable ? RADIOLIB_SX127X_PREAMBLE_POLARITY_AA : RADIOLIB_SX127X_PREAMBLE_POLARITY_55; + state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_SYNC_CONFIG, polarity, 5, 5); + return(state); + } + + return(RADIOLIB_ERR_UNKNOWN); +} + float SX127x::getFrequencyError(bool autoCorrect) { int16_t modem = getActiveModem(); if(modem == RADIOLIB_SX127X_LORA) { @@ -999,6 +1022,13 @@ int16_t SX127x::setSyncWord(uint8_t* syncWord, size_t len) { // check active modem uint8_t modem = getActiveModem(); if(modem == RADIOLIB_SX127X_FSK_OOK) { + + // disable sync word in case len is 0 + if(len == 0) { + int16_t state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_SYNC_CONFIG, RADIOLIB_SX127X_SYNC_OFF, 4, 4); + return(state); + } + RADIOLIB_CHECK_RANGE(len, 1, 8, RADIOLIB_ERR_INVALID_SYNC_WORD); // sync word must not contain value 0x00 @@ -1428,10 +1458,6 @@ int16_t SX127x::configFSK() { state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_PACKET_CONFIG_2, RADIOLIB_SX127X_DATA_MODE_PACKET | RADIOLIB_SX127X_IO_HOME_OFF, 6, 5); RADIOLIB_ASSERT(state); - // set preamble polarity - state =this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_SYNC_CONFIG, RADIOLIB_SX127X_PREAMBLE_POLARITY_55, 5, 5); - RADIOLIB_ASSERT(state); - // set FIFO threshold state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_THRESH, RADIOLIB_SX127X_TX_START_FIFO_NOT_EMPTY, 7, 7); state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_THRESH, RADIOLIB_SX127X_FIFO_THRESH, 5, 0); diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index 9a622538..59fc29a5 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -878,6 +878,13 @@ class SX127x: public PhysicalLayer { */ int16_t setPreambleLength(size_t preambleLength) override; + /*! + \brief Invert FSK preamble polarity. The default (non-inverted) is 0x55, the inverted is 0xAA. + \param enable Preamble polarity in FSK mode - 0xAA when true, 0x55 when false. + \returns \ref status_codes + */ + int16_t invertPreamble(bool enable); + /*! \brief Gets frequency error of the latest received packet. \param autoCorrect When set to true, frequency will be automatically corrected. From 49a0a1cf44e19ddf7a84a02d309a212fb0e522b4 Mon Sep 17 00:00:00 2001 From: jgromes Date: Mon, 9 Oct 2023 17:40:22 +0200 Subject: [PATCH 06/20] [CC1101] Added list of supported bandwidths (#842) --- src/modules/CC1101/CC1101.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/CC1101/CC1101.h b/src/modules/CC1101/CC1101.h index a13327e9..2e83d926 100644 --- a/src/modules/CC1101/CC1101.h +++ b/src/modules/CC1101/CC1101.h @@ -740,7 +740,8 @@ class CC1101: public PhysicalLayer { int16_t setBitRate(float br); /*! - \brief Sets receiver bandwidth. Allowed values range from 58.0 to 812.0 kHz. + \brief Sets receiver bandwidth. Allowed values are 58, 68, 81, 102, 116, 135, 162, + 203, 232, 270, 325, 406, 464, 541, 650 and 812 kHz. \param rxBw Receiver bandwidth to be set in kHz. \returns \ref status_codes */ From ddcce424c8d3fc8d54c00ec4cfabd5fd53e933a0 Mon Sep 17 00:00:00 2001 From: chemary Date: Wed, 11 Oct 2023 07:20:11 +0200 Subject: [PATCH 07/20] Incorrectly checking sx1280 command status (#843) --- src/modules/SX128x/SX128x.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp index bdd2e0c2..3e26382e 100644 --- a/src/modules/SX128x/SX128x.cpp +++ b/src/modules/SX128x/SX128x.cpp @@ -1499,11 +1499,11 @@ int16_t SX128x::config(uint8_t modem) { } int16_t SX128x::SPIparseStatus(uint8_t in) { - if((in & 0b00001110) == RADIOLIB_SX128X_STATUS_CMD_TIMEOUT) { + if((in & 0b00011100) == RADIOLIB_SX128X_STATUS_CMD_TIMEOUT) { return(RADIOLIB_ERR_SPI_CMD_TIMEOUT); - } else if((in & 0b00001110) == RADIOLIB_SX128X_STATUS_CMD_ERROR) { + } else if((in & 0b00011100) == RADIOLIB_SX128X_STATUS_CMD_ERROR) { return(RADIOLIB_ERR_SPI_CMD_INVALID); - } else if((in & 0b00001110) == RADIOLIB_SX128X_STATUS_CMD_FAILED) { + } else if((in & 0b00011100) == RADIOLIB_SX128X_STATUS_CMD_FAILED) { return(RADIOLIB_ERR_SPI_CMD_FAILED); } else if((in == 0x00) || (in == 0xFF)) { return(RADIOLIB_ERR_CHIP_NOT_FOUND); From 6e2685268921c47d1910d38369cf9d2445a8aa31 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 14 Oct 2023 10:27:31 +0200 Subject: [PATCH 08/20] Fixed debug float print --- src/BuildOpt.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/BuildOpt.h b/src/BuildOpt.h index 275539c6..c764f4d0 100644 --- a/src/BuildOpt.h +++ b/src/BuildOpt.h @@ -455,6 +455,9 @@ #if defined(RADIOLIB_BUILD_ARDUINO) #define RADIOLIB_DEBUG_PRINT(...) Module::serialPrintf(__VA_ARGS__) #define RADIOLIB_DEBUG_PRINTLN(M, ...) Module::serialPrintf(M "\n", ##__VA_ARGS__) + + // some platforms do not support printf("%f"), so it has to be done this way + #define RADIOLIB_DEBUG_PRINT_FLOAT(VAL, DECIMALS) RADIOLIB_DEBUG_PORT.print(VAL, DECIMALS) #else #if !defined(RADIOLIB_DEBUG_PRINT) #define RADIOLIB_DEBUG_PRINT(...) fprintf(RADIOLIB_DEBUG_PORT, __VA_ARGS__) @@ -462,11 +465,13 @@ #if !defined(RADIOLIB_DEBUG_PRINTLN) #define RADIOLIB_DEBUG_PRINTLN(M, ...) fprintf(RADIOLIB_DEBUG_PORT, M "\n", ##__VA_ARGS__) #endif + #define RADIOLIB_DEBUG_PRINT_FLOAT(VAL, DECIMALS) RADIOLIB_DEBUG_PRINT("%.3f", VAL) #endif #define RADIOLIB_DEBUG_HEXDUMP(...) Module::hexdump(__VA_ARGS__) #else #define RADIOLIB_DEBUG_PRINT(...) {} #define RADIOLIB_DEBUG_PRINTLN(...) {} + #define RADIOLIB_DEBUG_PRINT_FLOAT(VAL, DECIMALS) {} #define RADIOLIB_DEBUG_HEXDUMP(...) {} #endif From 96f90c8ee00fb51b9fbb26be5e0066695c572f5d Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 14 Oct 2023 10:27:53 +0200 Subject: [PATCH 09/20] [Si443x] Fixed debug float print --- src/modules/Si443x/Si443x.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/Si443x/Si443x.cpp b/src/modules/Si443x/Si443x.cpp index 62695fc4..2b2bf835 100644 --- a/src/modules/Si443x/Si443x.cpp +++ b/src/modules/Si443x/Si443x.cpp @@ -770,7 +770,8 @@ int16_t Si443x::updateClockRecovery() { // print that whole mess RADIOLIB_DEBUG_PRINTLN("%X\n%X\n%X", bypass, decRate, manch); - RADIOLIB_DEBUG_PRINTLN("%f\t%d\t%X\n%lu\t%lX\n%d\t%X", rxOsr, rxOsr_fixed, rxOsr_fixed, ncoOff, ncoOff, crGain, crGain); + RADIOLIB_DEBUG_PRINT_FLOAT(rxOsr, 2); + RADIOLIB_DEBUG_PRINTLN("\t%d\t%X\n%lu\t%lX\n%d\t%X", rxOsr_fixed, rxOsr_fixed, ncoOff, ncoOff, crGain, crGain); // update oversampling ratio int16_t state = this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_OFFSET_2, (uint8_t)((rxOsr_fixed & 0x0700) >> 3), 7, 5); From f4f00537c619e299b8b00d7607b49e4f855d5c03 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 14 Oct 2023 10:28:27 +0200 Subject: [PATCH 10/20] [LoRaWAN] Fixed debug float print (#844) --- src/protocols/LoRaWAN/LoRaWAN.cpp | 32 +++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index d2a1860f..37abf745 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -222,7 +222,9 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe uint32_t freq = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_POS + 3*i], 3); availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = (float)freq/10000.0; availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i] = availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]; - RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", i, availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]); + RADIOLIB_DEBUG_PRINT("Channel UL/DL %d frequency = ", i); + RADIOLIB_DEBUG_PRINT_FLOAT(availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i], 3); + RADIOLIB_DEBUG_PRINTLN(" MHz"); } } else { @@ -249,7 +251,9 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe uint8_t dir = this->band->defaultChannels[chSpan].direction; float freq = this->band->defaultChannels[chSpan].freqStart + chNum*this->band->defaultChannels[chSpan].freqStep; availableChannelsFreq[dir][channelId] = freq; - RADIOLIB_DEBUG_PRINTLN("Channel %cL %d frequency = %f MHz", dir ? 'U': 'D', channelId, availableChannelsFreq[dir][channelId]); + RADIOLIB_DEBUG_PRINT("Channel %cL %d frequency = ", dir ? 'U': 'D', channelId); + RADIOLIB_DEBUG_PRINT_FLOAT(availableChannelsFreq[dir][channelId], 3); + RADIOLIB_DEBUG_PRINTLN(" MHz"); channelId++; } @@ -1018,7 +1022,9 @@ int16_t LoRaWANNode::findChannelFreq(uint8_t dir, uint8_t ch, float* freq) { int16_t LoRaWANNode::configureChannel(uint8_t dir) { // set the frequency - RADIOLIB_DEBUG_PRINTLN("Channel frequency %cL = %f MHz", dir ? 'D' : 'U', this->channelFreq[dir]); + RADIOLIB_DEBUG_PRINT("Channel frequency %cL = ", dir ? 'D' : 'U'); + RADIOLIB_DEBUG_PRINT_FLOAT(this->channelFreq[dir], 3); + RADIOLIB_DEBUG_PRINTLN(" MHz"); int state = this->phyLayer->setFrequency(this->channelFreq[dir]); RADIOLIB_ASSERT(state); @@ -1254,14 +1260,14 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { uint8_t rx1DrOffset = (cmd->payload[0] & 0x70) >> 4; uint8_t rx2DataRate = cmd->payload[0] & 0x0F; uint32_t freqRaw = LoRaWANNode::ntoh(&cmd->payload[1], 3); - float freq = (float)freqRaw/10000.0; - RADIOLIB_DEBUG_PRINTLN("RX Param: rx1DrOffset = %d, rx2DataRate = %d, freq = %f", rx1DrOffset, rx2DataRate, freq); - + RADIOLIB_DEBUG_PRINTLN("RX Param: rx1DrOffset = %d, rx2DataRate = %d, freq = %d", rx1DrOffset, rx2DataRate, freqRaw); + // apply the configuration - this->backupFreq = freq; + float freq = (float)freqRaw/10000.0; float prevFreq = this->channelFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]; uint8_t chanAck = 0; if(this->phyLayer->setFrequency(freq) == RADIOLIB_ERR_NONE) { + this->backupFreq = freq; chanAck = 1; this->phyLayer->setFrequency(prevFreq); } @@ -1301,7 +1307,9 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { float freq = (float)freqRaw/10000.0; uint8_t maxDr = (cmd->payload[4] & 0xF0) >> 4; uint8_t minDr = cmd->payload[4] & 0x0F; - RADIOLIB_DEBUG_PRINTLN("New channel: index = %d, freq = %f MHz, maxDr = %d, minDr = %d", chIndex, freq, maxDr, minDr); + RADIOLIB_DEBUG_PRINT("New channel: index = %d, freq = ", chIndex); + RADIOLIB_DEBUG_PRINT_FLOAT(freq, 3); + RADIOLIB_DEBUG_PRINTLN(" MHz, maxDr = %d, minDr = %d", maxDr, minDr); // TODO implement this (void)chIndex; @@ -1354,7 +1362,9 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { uint8_t chIndex = cmd->payload[0]; uint32_t freqRaw = LoRaWANNode::ntoh(&cmd->payload[1], 3); float freq = (float)freqRaw/10000.0; - RADIOLIB_DEBUG_PRINTLN("DL channel: index = %d, freq = %f MHz", chIndex, freq); + RADIOLIB_DEBUG_PRINT("DL channel: index = %d, freq = ", chIndex); + RADIOLIB_DEBUG_PRINT_FLOAT(freq, 3); + RADIOLIB_DEBUG_PRINTLN(" MHz"); // TODO implement this (void)chIndex; @@ -1387,7 +1397,9 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { // TODO implement this - sent by gateway as reply to node request uint32_t gpsEpoch = LoRaWANNode::ntoh(&cmd->payload[0]); uint8_t fraction = cmd->payload[4]; - RADIOLIB_DEBUG_PRINTLN("Network time: gpsEpoch = %d s, delayExp = %f", gpsEpoch, (float)fraction/256.0f); + RADIOLIB_DEBUG_PRINT("Network time: gpsEpoch = %d s, delayExp = ", gpsEpoch, (float)fraction/256.0f); + RADIOLIB_DEBUG_PRINT_FLOAT((float)fraction/256.0f, 2); + RADIOLIB_DEBUG_PRINTLN(); (void)gpsEpoch; (void)fraction; return(5); From 0d438910707ad0dd19de44bfb5473bceceaafb59 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 14 Oct 2023 14:05:55 +0200 Subject: [PATCH 11/20] [STM32WLx] Added missing interrupt actions (#844) --- src/modules/SX126x/STM32WLx.cpp | 24 ++++++++++++++++++++++++ src/modules/SX126x/STM32WLx.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/modules/SX126x/STM32WLx.cpp b/src/modules/SX126x/STM32WLx.cpp index 095e13ec..f64b4933 100644 --- a/src/modules/SX126x/STM32WLx.cpp +++ b/src/modules/SX126x/STM32WLx.cpp @@ -126,4 +126,28 @@ void STM32WLx::clearDio1Action() { SubGhz.detachInterrupt(); } +void STM32WLx::setPacketReceivedAction(void (*func)(void)) { + this->setDio1Action(func); +} + +void STM32WLx::clearPacketReceivedAction() { + this->clearDio1Action(); +} + +void STM32WLx::setPacketSentAction(void (*func)(void)) { + this->setDio1Action(func); +} + +void STM32WLx::clearPacketSentAction() { + this->clearDio1Action(); +} + +void STM32WLx::setChannelScanAction(void (*func)(void)) { + this->setDio1Action(func); +} + +void STM32WLx::clearChannelScanAction() { + this->clearDio1Action(); +} + #endif // !defined(RADIOLIB_EXCLUDE_STM32WLX) diff --git a/src/modules/SX126x/STM32WLx.h b/src/modules/SX126x/STM32WLx.h index aa964917..cab2fc54 100644 --- a/src/modules/SX126x/STM32WLx.h +++ b/src/modules/SX126x/STM32WLx.h @@ -120,6 +120,39 @@ class STM32WLx : public SX1262 { */ void clearDio1Action(); + /*! + \brief Sets interrupt service routine to call when a packet is received. + \param func ISR to call. + */ + void setPacketReceivedAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when a packet is received. + */ + void clearPacketReceivedAction(); + + /*! + \brief Sets interrupt service routine to call when a packet is sent. + \param func ISR to call. + */ + void setPacketSentAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when a packet is sent. + */ + void clearPacketSentAction(); + + /*! + \brief Sets interrupt service routine to call when a channel scan is finished. + \param func ISR to call. + */ + void setChannelScanAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when a channel scan is finished. + */ + void clearChannelScanAction(); + #if !defined(RADIOLIB_GODMODE) protected: #endif From 4e0ed033db37aba5fc91dc9b936a607202a124ad Mon Sep 17 00:00:00 2001 From: jgromes Date: Sun, 15 Oct 2023 08:48:37 +0200 Subject: [PATCH 12/20] [HAL] Fixed persistent storage on Sparkfun Apollo (#848) --- src/ArduinoHal.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ArduinoHal.cpp b/src/ArduinoHal.cpp index b1d4a92f..d585590b 100644 --- a/src/ArduinoHal.cpp +++ b/src/ArduinoHal.cpp @@ -106,6 +106,8 @@ void ArduinoHal::readPersistentStorage(uint32_t addr, uint8_t* buff, size_t len) #if !defined(RADIOLIB_EEPROM_UNSUPPORTED) #if defined(RADIOLIB_ESP32) EEPROM.begin(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE); + #elif defined(ARDUINO_ARCH_APOLLO3) + EEPROM.init(); #endif for(size_t i = 0; i < len; i++) { buff[i] = EEPROM.read(addr + i); @@ -120,6 +122,8 @@ void ArduinoHal::writePersistentStorage(uint32_t addr, uint8_t* buff, size_t len #if !defined(RADIOLIB_EEPROM_UNSUPPORTED) #if defined(RADIOLIB_ESP32) EEPROM.begin(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE); + #elif defined(ARDUINO_ARCH_APOLLO3) + EEPROM.init(); #endif for(size_t i = 0; i < len; i++) { EEPROM.write(addr + i, buff[i]); From 46bf0445fa9ef7aa7457c2ebe81d09042f8f3749 Mon Sep 17 00:00:00 2001 From: jgromes Date: Tue, 17 Oct 2023 20:14:26 +0200 Subject: [PATCH 13/20] [SX126x] Decrease startup wait to 10 ms (#850) --- src/modules/SX126x/SX126x.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 7f739cf8..a312a011 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -220,7 +220,7 @@ int16_t SX126x::reset(bool verify) { } // wait a bit to not spam the module - this->mod->hal->delay(100); + this->mod->hal->delay(10); } } From eabc7527032b0889917edac0ded83dad8a9921e3 Mon Sep 17 00:00:00 2001 From: jgromes Date: Wed, 18 Oct 2023 17:47:09 +0200 Subject: [PATCH 14/20] [APRS] Fix array length calculation in static only mode --- src/protocols/APRS/APRS.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/protocols/APRS/APRS.cpp b/src/protocols/APRS/APRS.cpp index 780dddf4..3458272e 100644 --- a/src/protocols/APRS/APRS.cpp +++ b/src/protocols/APRS/APRS.cpp @@ -35,14 +35,14 @@ int16_t APRSClient::begin(char sym, char* callsign, uint8_t ssid, bool alt) { } int16_t APRSClient::sendPosition(char* destCallsign, uint8_t destSSID, char* lat, char* lon, char* msg, char* time) { + size_t len = 1 + strlen(lat) + 1 + strlen(lon); + if(msg != NULL) { + len += 1 + strlen(msg); + } + if(time != NULL) { + len += strlen(time); + } #if !defined(RADIOLIB_STATIC_ONLY) - size_t len = 1 + strlen(lat) + 1 + strlen(lon); - if(msg != NULL) { - len += 1 + strlen(msg); - } - if(time != NULL) { - len += strlen(time); - } char* info = new char[len + 1]; #else char info[RADIOLIB_STATIC_ARRAY_SIZE]; From f1f3336e5995b4c76e059c9f84b1ff94c13c2998 Mon Sep 17 00:00:00 2001 From: jgromes Date: Fri, 20 Oct 2023 19:37:44 +0200 Subject: [PATCH 15/20] [SX126x] Make setPaConfig public (#852) --- keywords.txt | 1 + src/modules/SX126x/SX126x.h | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/keywords.txt b/keywords.txt index a7d5d12e..73568a89 100644 --- a/keywords.txt +++ b/keywords.txt @@ -222,6 +222,7 @@ spectralScanStart KEYWORD2 spectralScanAbort KEYWORD2 spectralScanGetStatus KEYWORD2 spectralScanGetResult KEYWORD2 +setPaConfig KEYWORD2 # nRF24 setIrqAction KEYWORD2 diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 21db66eb..13efeadd 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -1070,6 +1070,19 @@ class SX126x: public PhysicalLayer { */ int16_t spectralScanGetResult(uint16_t* results); + /*! + \brief Set the PA configuration. Allows user to optimize PA for a specific output power + and matching network. Any calls to this method must be done after calling begin/beginFSK and/or setOutputPower. + WARNING: Use at your own risk! Setting invalid values can and will lead to permanent damage! + \param paDutyCycle PA duty cycle raw value. + \param deviceSel Device select, usually RADIOLIB_SX126X_PA_CONFIG_SX1261, + RADIOLIB_SX126X_PA_CONFIG_SX1262 or RADIOLIB_SX126X_PA_CONFIG_SX1268. + \param hpMax hpMax raw value. + \param paLut paLut PA lookup table raw value. + \returns \ref status_codes + */ + int16_t setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMax = RADIOLIB_SX126X_PA_CONFIG_HP_MAX, uint8_t paLut = RADIOLIB_SX126X_PA_CONFIG_PA_LUT); + #if !defined(RADIOLIB_GODMODE) protected: #endif From 29c891e01784c147bd9110baec81becf8c534604 Mon Sep 17 00:00:00 2001 From: jgromes Date: Fri, 20 Oct 2023 19:45:50 +0200 Subject: [PATCH 16/20] [SX126x] Fixed duplicate setPaConfig --- src/modules/SX126x/SX126x.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index 13efeadd..30900802 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -1091,7 +1091,6 @@ class SX126x: public PhysicalLayer { int16_t setTx(uint32_t timeout = 0); int16_t setRx(uint32_t timeout); int16_t setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); - int16_t setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMax = RADIOLIB_SX126X_PA_CONFIG_HP_MAX, uint8_t paLut = RADIOLIB_SX126X_PA_CONFIG_PA_LUT); int16_t writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); int16_t readRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); int16_t writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset = 0x00); From 556f37f608a3208430814e52fd4249463e90d61e Mon Sep 17 00:00:00 2001 From: StevenCellist <47155822+StevenCellist@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:50:16 +0200 Subject: [PATCH 17/20] [LoRaWAN] Implement full session persistence & more v1.1 specification (#835) * Implement session persistence & more 1.1 protocol * [LoRaW] Improve session persistence, check frame counters & Nonces, multiple MAC commands * [LoRaWAN] fix popping MAC command from queue I just realized that the method popMacCommand did not correctly remove items from the queue - this should solve the problem * [LoRaWAN] implement improvements from #835 * [LoRaWAN] String --> uint8_t[] * [LoRaWAN] Fix typo --- keywords.txt | 8 +- src/BuildOpt.h | 2 +- src/Hal.h | 48 +++- src/TypeDef.h | 20 ++ src/protocols/LoRaWAN/LoRaWAN.cpp | 386 +++++++++++++++++++++++++----- src/protocols/LoRaWAN/LoRaWAN.h | 22 +- 6 files changed, 404 insertions(+), 82 deletions(-) diff --git a/keywords.txt b/keywords.txt index 73568a89..4cd39fb4 100644 --- a/keywords.txt +++ b/keywords.txt @@ -291,8 +291,9 @@ setModem KEYWORD2 # LoRaWAN wipe KEYWORD2 +restoreOTAA KEYWORD2 beginOTAA KEYWORD2 -beginAPB KEYWORD2 +beginABP KEYWORD2 uplink KEYWORD2 downlink KEYWORD2 configureChannel KEYWORD2 @@ -401,5 +402,10 @@ RADIOLIB_ERR_INVALID_PORT LITERAL1 RADIOLIB_ERR_NO_RX_WINDOW LITERAL1 RADIOLIB_ERR_INVALID_CHANNEL LITERAL1 RADIOLIB_ERR_INVALID_CID LITERAL1 +RADIOLIB_ERR_UPLINK_UNAVAILABLE LITERAL1 RADIOLIB_ERR_COMMAND_QUEUE_FULL LITERAL1 RADIOLIB_ERR_COMMAND_QUEUE_EMPTY LITERAL1 +RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND LITERAL1 +RADIOLIB_ERR_JOIN_NONCE_INVALID LITERAL1 +RADIOLIB_ERR_N_FCNT_DOWN_INVALID LITERAL1 +RADIOLIB_ERR_A_FCNT_DOWN_INVALID LITERAL1 \ No newline at end of file diff --git a/src/BuildOpt.h b/src/BuildOpt.h index c764f4d0..324a7fbe 100644 --- a/src/BuildOpt.h +++ b/src/BuildOpt.h @@ -442,7 +442,7 @@ // the amount of space allocated to the persistent storage #if !defined(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE) - #define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0x60) + #define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0xD0) #endif // This only compiles on STM32 boards with SUBGHZ module, but also diff --git a/src/Hal.h b/src/Hal.h index 6fa800b1..9d89811f 100644 --- a/src/Hal.h +++ b/src/Hal.h @@ -7,25 +7,47 @@ #include "BuildOpt.h" // list of persistent parameters -#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID (0) -#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID (1) -#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID (2) -#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (3) -#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID (4) -#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID (5) -#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID (6) -#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID (7) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID (0) // this is NOT the LoRaWAN version, but version of this table +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (1) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID (2) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID (3) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID (4) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID (5) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID (6) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID (7) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID (8) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID (9) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID (10) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID (11) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID (12) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID (13) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID (14) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID (15) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_ID (16) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_ID (17) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID (18) static const uint32_t RadioLibPersistentParamTable[] = { - 0x00, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID - 0x04, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID - 0x08, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID - 0x0C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID + 0x00, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION + 0x08, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID + 0x0C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID 0x10, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID 0x20, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID 0x30, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID 0x40, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID - 0x50, // end + 0x50, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID + 0x54, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID + 0x58, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID + 0x5C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID + 0x60, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST + 0x70, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID + 0x74, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID + 0x78, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_AFCNT_DOWN_ID + 0x7C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NFCNT_DOWN_ID + 0x80, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_ID + 0x84, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_ID + 0x88, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID + 0xD0, // end }; /*! diff --git a/src/TypeDef.h b/src/TypeDef.h index 11b3f7f6..0ebcbbb1 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -533,6 +533,26 @@ */ #define RADIOLIB_ERR_COMMAND_QUEUE_EMPTY (-1110) +/*! + \brief Unable to delete MAC command because it was not found in the queue. +*/ +#define RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND (-1111) + +/*! + \brief Unable to join network because JoinNonce is not higher than saved value. +*/ +#define RADIOLIB_ERR_JOIN_NONCE_INVALID (-1112) + +/*! + \brief Received downlink Network frame counter is invalid (lower than last heard value). +*/ +#define RADIOLIB_ERR_N_FCNT_DOWN_INVALID (-1113) + +/*! + \brief Received downlink Application frame counter is invalid (lower than last heard value). +*/ +#define RADIOLIB_ERR_A_FCNT_DOWN_INVALID (-1114) + /*! \} */ diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 37abf745..b903343c 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -44,7 +44,7 @@ void LoRaWANNode::wipe() { mod->hal->wipePersistentStorage(); } -int16_t LoRaWANNode::begin() { +int16_t LoRaWANNode::restoreOTAA() { int16_t state = this->setPhyProperties(); RADIOLIB_ASSERT(state); @@ -55,12 +55,99 @@ int16_t LoRaWANNode::begin() { return(RADIOLIB_ERR_NETWORK_NOT_JOINED); } + // in case of future revisions of NVM, use a version parameter to allow transitioning from one version to another while keeping session alive + uint16_t nvm_table_version = mod->hal->getPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID); + // if (RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION > nvm_table_version) { + // // set default values for variables that are new or something + // } + (void)nvm_table_version; + // pull all needed information from persistent storage this->devAddr = mod->hal->getPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID); mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE); + RADIOLIB_DEBUG_PRINTLN("appSKey:"); + RADIOLIB_DEBUG_HEXDUMP(this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); + + uint32_t dlSettings = mod->hal->getPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID); + this->rev = (dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) >> 7; + uint8_t rx1DrOffset = (dlSettings & 0x70) >> 4; + uint8_t rx2DataRate = dlSettings & 0x0F; + RADIOLIB_DEBUG_PRINTLN("LoRaWAN revision: %d", this->rev); + + // TODO process the RX2 data rate + (void)rx2DataRate; + + // TODO process the data rate offset + (void)rx1DrOffset; + + // parse Rx1 delay (and subsequently Rx2) + this->rxDelays[0] = mod->hal->getPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID); + if(this->rxDelays[0] == 0) { + this->rxDelays[0] = RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS; + } + this->rxDelays[1] = this->rxDelays[0] + 1000; + + // process CFlist if any bit is non-zero + uint8_t cfList[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 }; + uint8_t allZero[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 }; + mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID), cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN); + RADIOLIB_DEBUG_PRINTLN("cfList:"); + RADIOLIB_DEBUG_HEXDUMP(cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN); + if(memcmp(cfList, allZero, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN)) { + if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) { + // list of frequencies + for(uint8_t i = 0; i < 5; i++) { + uint32_t freq = LoRaWANNode::ntoh(&cfList[3*i], 3); + availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = (float)freq/10000.0; + availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i] = availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]; + RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", i, availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]); + } + + } else { + // frequency mask, we need to find out which frequencies are actually being used + uint8_t channelId = 0; + uint8_t chSpan = 0; + uint8_t chNum = 0; + for(uint8_t i = 0; i < 5; i++) { + uint16_t mask = LoRaWANNode::ntoh(&cfList[2*i]); + RADIOLIB_DEBUG_PRINTLN("mask[%d] = 0x%04x", i, mask); + for(uint8_t j = 0; j < 16; j++) { + if(chNum >= this->band->defaultChannels[chSpan].numChannels) { + chNum -= this->band->defaultChannels[chSpan].numChannels; + chSpan++; + + if(chSpan >= this->band->numChannelSpans) { + RADIOLIB_DEBUG_PRINTLN("channel bitmask overrun!"); + return(RADIOLIB_ERR_UNKNOWN); + } + } + + if(mask & (1UL << j)) { + RADIOLIB_DEBUG_PRINTLN("chNum = %d, chSpan = %d", chNum, chSpan); + uint8_t dir = this->band->defaultChannels[chSpan].direction; + float freq = this->band->defaultChannels[chSpan].freqStart + chNum*this->band->defaultChannels[chSpan].freqStep; + availableChannelsFreq[dir][channelId] = freq; + RADIOLIB_DEBUG_PRINTLN("Channel %cL %d frequency = %f MHz", dir ? 'U': 'D', channelId, availableChannelsFreq[dir][channelId]); + channelId++; + } + + chNum++; + } + } + + } + } + + uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)] = { 0 }; + mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t)); + memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t)); + + state = this->setupChannels(); + RADIOLIB_ASSERT(state); + return(RADIOLIB_ERR_NONE); } @@ -69,7 +156,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe Module* mod = this->phyLayer->getMod(); if(!force && (mod->hal->getPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) == RADIOLIB_LORAWAN_MAGIC)) { // the device has joined already, we can just pull the data from persistent storage - return(this->begin()); + return(this->restoreOTAA()); } // set the physical layer configuration @@ -175,9 +262,23 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe RADIOLIB_DEBUG_PRINTLN("joinAcceptMsg:"); RADIOLIB_DEBUG_HEXDUMP(joinAcceptMsg, lenRx); + // get current JoinNonce from downlink and previous JoinNonce from NVM + uint32_t joinNonce = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3); + uint32_t joinNoncePrev = mod->hal->getPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID); + RADIOLIB_DEBUG_PRINTLN("JoinNoncePrev: %d, JoinNonce: %d", joinNoncePrev, joinNonce); + + // JoinNonce received must be greater than the last JoinNonce heard, else error + if(joinNonce <= joinNoncePrev) { + return(RADIOLIB_ERR_JOIN_NONCE_INVALID); + } + // check LoRaWAN revision (the MIC verification depends on this) uint8_t dlSettings = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DL_SETTINGS_POS]; - if(dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) { + this->rev = (dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) >> 7; + RADIOLIB_DEBUG_PRINTLN("LoRaWAN revision: 1.%d", this->rev); + + // verify MIC + if(this->rev == 1) { // 1.1 version, first we need to derive the join accept integrity key uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_JS_INT_KEY; @@ -203,11 +304,20 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe } } + uint8_t rx1DrOffset = (dlSettings & 0x70) >> 4; + uint8_t rx2DataRate = dlSettings & 0x0F; + + // TODO process the RX2 data rate + (void)rx2DataRate; - // parse the contents - uint32_t joinNonce = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3); + // TODO process the data rate offset + (void)rx1DrOffset; + + // parse other contents uint32_t homeNetId = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], 3); this->devAddr = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS]); + + // parse Rx1 delay (and subsequently Rx2) this->rxDelays[0] = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_RX_DELAY_POS]*1000; if(this->rxDelays[0] == 0) { this->rxDelays[0] = RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS; @@ -215,7 +325,9 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe this->rxDelays[1] = this->rxDelays[0] + 1000; // process CFlist if present + uint8_t cfList[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 }; if(lenRx == RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN) { + memcpy(&cfList[0], &joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_POS], RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN); if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) { // list of frequencies for(uint8_t i = 0; i < 5; i++) { @@ -262,15 +374,14 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe } } - - } + } // prepare buffer for key derivation uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], joinNonce, 3); // check protocol version (1.0 vs 1.1) - if(dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) { + if(this->rev == 1) { // 1.1 version, derive the keys LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_EUI_POS], joinEUI); LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_NONCE_POS], devNonce); @@ -292,7 +403,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->nwkSEncKey); // enqueue the RekeyInd MAC command to be sent in the next uplink - this->rev = 1; LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LORAWAN_MAC_CMD_REKEY, .len = sizeof(uint8_t), @@ -304,7 +414,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe } else { // 1.0 version, just derive the keys - this->rev = 0; LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], homeNetId, 3); LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS], devNonce); keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY; @@ -329,13 +438,27 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE); + // save uplink parameters + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID, joinNonce); + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID, homeNetId); + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID, this->rxDelays[0]); + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID, (uint32_t)dlSettings); + + // save cfList (all 0 if none is present) + mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID), cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN); + // all complete, reset device counters and set the magic number mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, 0); + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID, 0); + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID, 0); mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC); + + // everything written to NVM, write current version to NVM + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID, RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION); return(RADIOLIB_ERR_NONE); } -int16_t LoRaWANNode::beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey) { +int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey) { this->devAddr = addr; memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE); memcpy(this->nwkSEncKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE); @@ -355,7 +478,13 @@ int16_t LoRaWANNode::beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, // setup uplink/downlink frequencies and datarates state = this->setupChannels(); - return(state); + RADIOLIB_ASSERT(state); + + // everything written to NVM, write current version to NVM + Module* mod = this->phyLayer->getMod(); + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID, RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION); + + return(RADIOLIB_ERR_NONE); } #if defined(RADIOLIB_BUILD_ARDUINO) @@ -373,12 +502,24 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) { if(port > 0xDF) { return(RADIOLIB_ERR_INVALID_PORT); } + // port 0 is only allowed for MAC-only payloads + if(port == RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) { + if (!isMACPayload) { + return(RADIOLIB_ERR_INVALID_PORT); + } + // if this is MAC only payload, continue and reset for next uplink + isMACPayload = false; + } + + Module* mod = this->phyLayer->getMod(); - // check if there are some MAC commands to piggyback - size_t foptsLen = 0; - if(this->commandsUp.numCommands > 0) { + // check if there are some MAC commands to piggyback (only when piggybacking onto a application-frame) + uint8_t foptsLen = 0; + size_t foptsBufSize = 0; + if(this->commandsUp.numCommands > 0 && port != RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) { // there are, assume the maximum possible FOpts len for buffer allocation - foptsLen = 15; + foptsLen = this->commandsUp.len; + foptsBufSize = 15; } // check maximum payload len as defined in phy @@ -392,7 +533,6 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) { RADIOLIB_ASSERT(state); // check if sufficient time has elapsed since the last uplink - Module* mod = this->phyLayer->getMod(); if(mod->hal->millis() - this->rxDelayStart < rxDelays[1]) { // not enough time elapsed since the last uplink, we may still be in an RX window return(RADIOLIB_ERR_UPLINK_UNAVAILABLE); @@ -400,7 +540,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) { // build the uplink message // the first 16 bytes are reserved for MIC calculation blocks - size_t uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen); + size_t uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsBufSize); #if defined(RADIOLIB_STATIC_ONLY) uint8_t uplinkMsg[RADIOLIB_STATIC_ARRAY_SIZE]; #else @@ -420,22 +560,37 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) { mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, fcnt); LoRaWANNode::hton(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)fcnt); - // check if we have some MAC command to append - // TODO implement appending multiple MAC commands - LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, }; - if(popMacCommand(&cmd, &this->commandsUp) == RADIOLIB_ERR_NONE) { - // we do, add it to fopts - uint8_t foptsBuff[RADIOLIB_AES128_BLOCK_SIZE]; - foptsBuff[0] = cmd.cid; - for(size_t i = 1; i < cmd.len; i++) { - foptsBuff[i] = cmd.payload[i]; + // check if we have some MAC commands to append + if(foptsLen > 0) { + uint8_t foptsNum = this->commandsUp.numCommands; + uint8_t foptsBuff[foptsBufSize]; + size_t idx = 0; + for (size_t i = 0; i < foptsNum; i++) { + LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, }; + popMacCommand(&cmd, &this->commandsUp, i); + if (cmd.cid == 0) { + break; + } + foptsBuff[idx] = cmd.cid; + for(size_t i = 1; i < cmd.len; i++) { + foptsBuff[idx + i] = cmd.payload[i]; + } + idx += cmd.len + 1; } - foptsLen = 1 + cmd.len; + + RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", foptsNum); + RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize); + uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen); uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= foptsLen; // encrypt it processAES(foptsBuff, foptsLen, this->nwkSEncKey, &uplinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x01, true); + + // write the current MAC command queue to nvm for next uplink + uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)]; + memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t)); + mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t)); } // set the port @@ -448,6 +603,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) { } // encrypt the frame payload + // TODO check ctrId --> erratum says it should be 0x01? processAES(data, len, encKey, &uplinkMsg[RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(foptsLen)], fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x00, true); // create blocks for MIC calculation @@ -685,15 +841,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) { if(state == RADIOLIB_ERR_LORA_HEADER_DAMAGED) { state = RADIOLIB_ERR_NONE; } - - // get the frame counter and set it to the MIC calculation block - // TODO this will not handle overflow into 32-bits! - // TODO cache the ADR bit? - uint16_t fcnt = LoRaWANNode::ntoh(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS]); - LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt); - - RADIOLIB_DEBUG_PRINTLN("downlinkMsg:"); - RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen); if(state != RADIOLIB_ERR_NONE) { #if !defined(RADIOLIB_STATIC_ONLY) @@ -702,6 +849,55 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) { return(state); } + // get the frame counter and set it to the MIC calculation block + // TODO cache the ADR bit? + uint16_t fcnt16 = LoRaWANNode::ntoh(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS]); + LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt16); + uint32_t fcnt32 = fcnt16; // calculate possible rollover once decided if this is network downlink or application downlink + + RADIOLIB_DEBUG_PRINTLN("downlinkMsg:"); + RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen); + + // calculate length of FOpts and payload + uint8_t foptsLen = downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK; + int payLen = downlinkMsgLen - 8 - foptsLen - sizeof(uint32_t); + + bool isAppDownlink = true; + if (payLen <= 0 && this->rev == 1) { // no payload => MAC commands only => Network frame (LoRaWAN v1.1 only) + isAppDownlink = false; + } + + // check the FcntDown value (Network or Application) + uint32_t fcntDownPrev = 0; + if (isAppDownlink) { + fcntDownPrev = mod->hal->getPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID); + } else { + fcntDownPrev = mod->hal->getPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID); + } + + // assume a 16-bit to 32-bit rollover when difference in LSB is smaller than MAX_FCNT_GAP + // if that isn't the case and the received fcnt is smaller or equal to the last heard fcnt, then error + if ((fcnt16 <= fcntDownPrev) && ((0xFFFF - (uint16_t)fcntDownPrev + fcnt16) > RADIOLIB_LORAWAN_MAX_FCNT_GAP)) { + #if !defined(RADIOLIB_STATIC_ONLY) + delete[] downlinkMsg; + #endif + if (isAppDownlink) { + return(RADIOLIB_ERR_A_FCNT_DOWN_INVALID); + } else { + return(RADIOLIB_ERR_N_FCNT_DOWN_INVALID); + } + } else if (fcnt16 <= fcntDownPrev) { + uint16_t msb = (fcntDownPrev >> 16) + 1; // assume a rollover + fcnt32 |= (msb << 16); // add back the MSB part + } + + // save current fcnt to NVM + if (isAppDownlink) { + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID, fcnt32); + } else { + mod->hal->setPersistentParameter(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID, fcnt32); + } + // check the MIC if(!verifyMIC(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen, this->sNwkSIntKey)) { #if !defined(RADIOLIB_STATIC_ONLY) @@ -720,15 +916,14 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) { return(RADIOLIB_ERR_DOWNLINK_MALFORMED); } - // check fopts len - uint8_t foptsLen = downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK; + // process FOpts (if there are any) if(foptsLen > 0) { // there are some Fopts, decrypt them uint8_t fopts[RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK]; - // according to the specification, the last two arguments should be 0x00 and false, - // but that will fail even for LoRaWAN 1.1.0 server - processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x01, true); + // TODO it COULD be the case that the assumed rollover is incorrect, if possible figure out a way to catch this and retry with just fcnt16 + uint8_t ctrId = 0x01 + isAppDownlink; // see LoRaWAN v1.1 errata + processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, ctrId, true); RADIOLIB_DEBUG_PRINTLN("fopts:"); RADIOLIB_DEBUG_HEXDUMP(fopts, foptsLen); @@ -753,23 +948,59 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) { remLen -= processedLen; foptsPtr += processedLen; } + + // if FOptsLen for the next uplink is larger than can be piggybacked onto an uplink, send separate uplink + if(this->commandsUp.len > 15) { + uint8_t foptsNum = this->commandsUp.numCommands; + size_t foptsBufSize = this->commandsUp.len; + uint8_t foptsBuff[foptsBufSize]; + size_t idx = 0; + for(size_t i = 0; i < foptsNum; i++) { + LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, }; + popMacCommand(&cmd, &this->commandsUp, i); + if(cmd.cid == 0) { + break; + } + foptsBuff[idx] = cmd.cid; + for(size_t i = 1; i < cmd.len; i++) { + foptsBuff[idx + i] = cmd.payload[i]; + } + idx += cmd.len + 1; + } + RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", foptsNum); + RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize); + + isMACPayload = true; + this->uplink(foptsBuff, foptsBufSize, RADIOLIB_LORAWAN_FPORT_MAC_COMMAND); + uint8_t strDown[this->band->payloadLenMax[this->dataRate[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]]]; + size_t lenDown = 0; + state = this->downlink(strDown, &lenDown); + RADIOLIB_ASSERT(state); + } + + // write the MAC command queue to nvm for next uplink + uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)]; + memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t)); + mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t)); } - // fopts are processed or not present, check if there is payload - int payLen = downlinkMsgLen - 8 - foptsLen - sizeof(uint32_t); + // process payload (if there is any) if(payLen <= 0) { // no payload *len = 0; #if !defined(RADIOLIB_STATIC_ONLY) delete[] downlinkMsg; #endif + return(RADIOLIB_ERR_NONE); } // there is payload, and so there should be a port too // TODO pass the port? *len = payLen - 1; - processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], downlinkMsgLen, this->appSKey, data, fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true); + // TODO it COULD be the case that the assumed rollover is incorrect, then figure out a way to catch this and retry with just fcnt16 + // TODO does the erratum hold here as well? + processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], downlinkMsgLen, this->appSKey, data, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true); #if !defined(RADIOLIB_STATIC_ONLY) delete[] downlinkMsg; @@ -1124,38 +1355,69 @@ int16_t LoRaWANNode::pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQ queue->commands[queue->numCommands - 1].payload[4], queue->commands[queue->numCommands - 1].repeat);*/ queue->numCommands++; + queue->len += 1 + cmd->len; // 1 byte for command ID, len bytes for payload return(RADIOLIB_ERR_NONE); } -int16_t LoRaWANNode::popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, bool force) { +int16_t LoRaWANNode::popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, size_t index) { if(queue->numCommands == 0) { return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY); } if(cmd) { - /*RADIOLIB_DEBUG_PRINTLN("pop MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ", - queue->commands[queue->numCommands - 1].cid, - queue->commands[queue->numCommands - 1].len, - queue->commands[queue->numCommands - 1].payload[0], - queue->commands[queue->numCommands - 1].payload[1], - queue->commands[queue->numCommands - 1].payload[2], - queue->commands[queue->numCommands - 1].payload[3], - queue->commands[queue->numCommands - 1].payload[4], - queue->commands[queue->numCommands - 1].repeat);*/ - memcpy(cmd, &queue->commands[queue->numCommands - 1], sizeof(LoRaWANMacCommand_t)); + // RADIOLIB_DEBUG_PRINTLN("pop MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ", + // queue->commands[index].cid, + // queue->commands[index].len, + // queue->commands[index].payload[0], + // queue->commands[index].payload[1], + // queue->commands[index].payload[2], + // queue->commands[index].payload[3], + // queue->commands[index].payload[4], + // queue->commands[index].repeat); + memcpy(cmd, &queue->commands[index], sizeof(LoRaWANMacCommand_t)); } - if((!force) && (queue->commands[queue->numCommands - 1].repeat > 0)) { - queue->commands[queue->numCommands - 1].repeat--; + if(queue->commands[index].repeat > 0) { + queue->commands[index].repeat--; } else { - queue->commands[queue->numCommands - 1].repeat = 0; - queue->numCommands--; + deleteMacCommand(queue->commands[index].cid, queue); } return(RADIOLIB_ERR_NONE); } +int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue) { + if(queue->numCommands == 0) { + return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY); + } + + for(size_t index = 0; index < queue->numCommands; index++) { + if(queue->commands[index].cid == cid) { + // RADIOLIB_DEBUG_PRINTLN("delete MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ", + // queue->commands[index].cid, + // queue->commands[index].len, + // queue->commands[index].payload[0], + // queue->commands[index].payload[1], + // queue->commands[index].payload[2], + // queue->commands[index].payload[3], + // queue->commands[index].payload[4], + // queue->commands[index].repeat); + queue->len -= (1 + queue->commands[index].len); // 1 byte for command ID, len for payload + // move all subsequent commands one forward in the queue + if(index < RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1) { + memmove(&queue->commands[index], &queue->commands[index + 1], (RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - index) * sizeof(LoRaWANMacCommand_t)); + } + // set the latest element to all 0 + memset(&queue->commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1], 0x00, sizeof(LoRaWANMacCommand_t)); + queue->numCommands--; + return(RADIOLIB_ERR_NONE); + } + } + + return(RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND); +} + size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { RADIOLIB_DEBUG_PRINTLN("exe MAC CID = %02x, len = %d", cmd->cid, cmd->len); @@ -1171,7 +1433,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion); if(srvVersion == this->rev) { // valid server version, stop sending the ResetInd MAC command - popMacCommand(NULL, &this->commandsUp, true); + deleteMacCommand(RADIOLIB_LORAWAN_MAC_CMD_RESET, &this->commandsUp); } return(1); } break; @@ -1378,7 +1640,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion); if((srvVersion > 0) && (srvVersion <= this->rev)) { // valid server version, stop sending the ReKey MAC command - popMacCommand(NULL, &this->commandsUp, true); + deleteMacCommand(RADIOLIB_LORAWAN_MAC_CMD_REKEY, &this->commandsUp); } return(1); } break; diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index e0ec0af4..65efe7eb 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -5,6 +5,9 @@ #include "../PhysicalLayer/PhysicalLayer.h" #include "../../utils/Cryptography.h" +// version of NVM table layout (NOT the LoRaWAN version) +#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION (0x01) + // preamble format #define RADIOLIB_LORAWAN_LORA_SYNC_WORD (0x34) #define RADIOLIB_LORAWAN_LORA_PREAMBLE_LEN (8) @@ -257,7 +260,7 @@ struct LoRaWANMacCommand_t { uint8_t cid; /*! \brief Length of the payload */ - size_t len; + uint8_t len; /*! \brief Payload buffer (5 bytes is the longest possible) */ uint8_t payload[5]; @@ -269,6 +272,7 @@ struct LoRaWANMacCommand_t { struct LoRaWANMacCommandQueue_t { LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE]; size_t numCommands; + size_t len; }; /*! @@ -306,10 +310,10 @@ class LoRaWANNode { void wipe(); /*! - \brief Join network by loading information from persistent storage. + \brief Restore OTAA session by loading information from persistent storage. \returns \ref status_codes */ - int16_t begin(); + int16_t restoreOTAA(); /*! \brief Join network by performing over-the-air activation. By this procedure, @@ -333,7 +337,7 @@ class LoRaWANNode { \param sNwkSIntKey Pointer to the network session S key (LoRaWAN 1.1), unused for LoRaWAN 1.0. \returns \ref status_codes */ - int16_t beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL); + int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL); #if defined(RADIOLIB_BUILD_ARDUINO) /*! @@ -395,10 +399,12 @@ class LoRaWANNode { LoRaWANMacCommandQueue_t commandsUp = { .commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } }, .numCommands = 0, + .len = 0, }; LoRaWANMacCommandQueue_t commandsDown = { .commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } }, .numCommands = 0, + .len = 0, }; // the following is either provided by the network server (OTAA) @@ -438,6 +444,9 @@ class LoRaWANNode { // device status - battery level uint8_t battLevel = 0xFF; + // indicates whether an uplink has MAC commands as payload + bool isMACPayload = false; + // method to generate message integrity code uint32_t generateMIC(uint8_t* msg, size_t len, uint8_t* key); @@ -475,7 +484,10 @@ class LoRaWANNode { int16_t pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue); // pop MAC command from queue, done by copy unless CMD is NULL - int16_t popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, bool force = false); + int16_t popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, size_t index); + + // delete a specific MAC command from queue, indicated by the command ID + int16_t deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue); // execute mac command, return the number of processed bytes for sequential processing size_t execMacCommand(LoRaWANMacCommand_t* cmd); From 039fa0fc59af9e367ea8c4b8429466f297f7d7ce Mon Sep 17 00:00:00 2001 From: Lewis He Date: Sun, 29 Oct 2023 01:41:51 +0800 Subject: [PATCH 18/20] Update api adapt esp core 3.0.0-alpha2 (#860) --- src/ArduinoHal.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ArduinoHal.cpp b/src/ArduinoHal.cpp index d585590b..f2ed4f8c 100644 --- a/src/ArduinoHal.cpp +++ b/src/ArduinoHal.cpp @@ -145,10 +145,16 @@ void inline ArduinoHal::tone(uint32_t pin, unsigned int frequency, unsigned long // ESP32 tone() emulation (void)duration; if(prev == -1) { +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0) ledcAttachPin(pin, RADIOLIB_TONE_ESP32_CHANNEL); +#endif } if(prev != frequency) { +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0) ledcWriteTone(RADIOLIB_TONE_ESP32_CHANNEL, frequency); +#else + ledcWriteTone(pin, frequency); +#endif } prev = frequency; #elif defined(RADIOLIB_MBED_TONE_OVERRIDE) @@ -178,8 +184,13 @@ void inline ArduinoHal::noTone(uint32_t pin) { return; } // ESP32 tone() emulation +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0) ledcDetachPin(pin); ledcWrite(RADIOLIB_TONE_ESP32_CHANNEL, 0); +#else + ledcDetach(pin); + ledcWrite(pin, 0); +#endif prev = -1; #elif defined(RADIOLIB_MBED_TONE_OVERRIDE) if(pin == RADIOLIB_NC) { From 912333c408b107623c46a75c0b2e56ceeb543f29 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sat, 28 Oct 2023 21:54:31 +0200 Subject: [PATCH 19/20] [HAL] Added check for defined ESP version macro (#860) --- src/ArduinoHal.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ArduinoHal.cpp b/src/ArduinoHal.cpp index f2ed4f8c..322a6906 100644 --- a/src/ArduinoHal.cpp +++ b/src/ArduinoHal.cpp @@ -145,16 +145,16 @@ void inline ArduinoHal::tone(uint32_t pin, unsigned int frequency, unsigned long // ESP32 tone() emulation (void)duration; if(prev == -1) { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0) + #if !defined(ESP_IDF_VERSION) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0)) ledcAttachPin(pin, RADIOLIB_TONE_ESP32_CHANNEL); -#endif + #endif } if(prev != frequency) { -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0) + #if !defined(ESP_IDF_VERSION) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0)) ledcWriteTone(RADIOLIB_TONE_ESP32_CHANNEL, frequency); -#else + #else ledcWriteTone(pin, frequency); -#endif + #endif } prev = frequency; #elif defined(RADIOLIB_MBED_TONE_OVERRIDE) @@ -184,13 +184,13 @@ void inline ArduinoHal::noTone(uint32_t pin) { return; } // ESP32 tone() emulation -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0) + #if !defined(ESP_IDF_VERSION) || (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0)) ledcDetachPin(pin); ledcWrite(RADIOLIB_TONE_ESP32_CHANNEL, 0); -#else + #else ledcDetach(pin); ledcWrite(pin, 0); -#endif + #endif prev = -1; #elif defined(RADIOLIB_MBED_TONE_OVERRIDE) if(pin == RADIOLIB_NC) { From aca1d78a9742e17947eaf5cddb63a10f10e3fafe Mon Sep 17 00:00:00 2001 From: Amalinda Gamage <5582466+jgamage91@users.noreply.github.com> Date: Sun, 29 Oct 2023 21:19:00 +0800 Subject: [PATCH 20/20] added functionality for LoRa Alliance TR-13 Enabling CSMA for LoRaWAN (#859) * added functionality for LoRa Alliance TR-13 Enabling CSMA for LoRaWAN * Addressed feedback on CSMA implementation * symbolNumValues[6] array no longer needed as we will utilize only two symbol CAD operations for all SFs. --- src/modules/SX126x/SX126x.cpp | 43 ++++++++++++++------ src/protocols/LoRaWAN/LoRaWAN.cpp | 66 ++++++++++++++++++++++++++++++- src/protocols/LoRaWAN/LoRaWAN.h | 25 ++++++++++++ 3 files changed, 120 insertions(+), 14 deletions(-) diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index a312a011..7c6f8baa 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -1702,27 +1702,41 @@ int16_t SX126x::setRx(uint32_t timeout) { return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX, data, 3, true, false)); } + int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { - // default CAD parameters for assigned SF as per Semtech AN1200.48, Rev 2.1, Page 50 - uint8_t detPeakValues[8] = { 22, 22, 22, 22, 23, 24, 25, 28}; - uint8_t symbolNumValues[8] = { RADIOLIB_SX126X_CAD_ON_2_SYMB, - RADIOLIB_SX126X_CAD_ON_2_SYMB, - RADIOLIB_SX126X_CAD_ON_2_SYMB, - RADIOLIB_SX126X_CAD_ON_2_SYMB, - RADIOLIB_SX126X_CAD_ON_4_SYMB, - RADIOLIB_SX126X_CAD_ON_4_SYMB, - RADIOLIB_SX126X_CAD_ON_4_SYMB, - RADIOLIB_SX126X_CAD_ON_4_SYMB }; + // default CAD parameters are shown in Semtech AN1200.48, page 41. + uint8_t detPeakValues[6] = { 22, 22, 24, 25, 26, 30}; + + // CAD parameters aren't available for SF-6. Just to be safe. + if(this->spreadingFactor < 7) { + this->spreadingFactor = 7; + } else if(this->spreadingFactor > 12) { + this->spreadingFactor = 12; + } + // build the packet uint8_t data[7]; - data[0] = symbolNumValues[this->spreadingFactor - 5]; - data[1] = detPeakValues[this->spreadingFactor - 5]; + data[0] = RADIOLIB_SX126X_CAD_ON_2_SYMB; + data[1] = detPeakValues[this->spreadingFactor - 7]; data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN; data[3] = RADIOLIB_SX126X_CAD_GOTO_STDBY; data[4] = 0x00; data[5] = 0x00; data[6] = 0x00; + + /* + CAD Configuration Note: + The default CAD configuration applied by `scanChannel` overrides the optimal SF-specific configurations, leading to suboptimal detection. + I.e., anything that is not RADIOLIB_SX126X_CAD_PARAM_DEFAULT is overridden. But CAD settings are SF specific. + To address this, the user override has been commented out, ensuring consistent application of the optimal CAD settings as + per Semtech's Application Note AN1200.48 (page 41) for the 125KHz setting. This approach significantly reduces false CAD occurrences. + Testing has shown that there is no reason for a user to change CAD settings for anything other than most optimal ones described in AN1200.48 . + However, this change deos not respect CAD configs from the LoRaWAN layer. Future considerations or use cases might require revisiting this decision. + Hence this note. +*/ + +/* // set user-provided values if(symbolNum != RADIOLIB_SX126X_CAD_PARAM_DEFAULT) { data[0] = symbolNum; @@ -1736,6 +1750,9 @@ int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { data[2] = detMin; } +*/ + + // configure parameters int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_CAD_PARAMS, data, 7); RADIOLIB_ASSERT(state); @@ -2030,7 +2047,7 @@ int16_t SX126x::config(uint8_t modem) { state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE, data, 1); RADIOLIB_ASSERT(state); - // set some CAD parameters - will be overwritten whel calling CAD anyway + // set some CAD parameters - will be overwritten when calling CAD anyway data[0] = RADIOLIB_SX126X_CAD_ON_8_SYMB; data[1] = this->spreadingFactor + 13; data[2] = RADIOLIB_SX126X_CAD_PARAM_DET_MIN; diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index b903343c..30ffe5ee 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -37,6 +37,10 @@ LoRaWANNode::LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band) { this->startChannel = -1; this->numChannels = -1; this->backupFreq = this->band->backupChannel.freqStart; + this->difsSlots = 2; + this->backoffMax = 6; + this->enableCSMA = false; + } void LoRaWANNode::wipe() { @@ -44,6 +48,13 @@ void LoRaWANNode::wipe() { mod->hal->wipePersistentStorage(); } +void LoRaWANNode::setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA) { + this->backoffMax = backoffMax; + this->difsSlots = difsSlots; + this->enableCSMA = enableCSMA; +} + + int16_t LoRaWANNode::restoreOTAA() { int16_t state = this->setPhyProperties(); RADIOLIB_ASSERT(state); @@ -637,6 +648,11 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) { LoRaWANNode::hton(&uplinkMsg[uplinkMsgLen - sizeof(uint32_t)], micF); } + // perform CSMA if enabled. + if (enableCSMA) { + performCSMA(); + } + RADIOLIB_DEBUG_PRINTLN("uplinkMsg:"); RADIOLIB_DEBUG_HEXDUMP(uplinkMsg, uplinkMsgLen); @@ -1762,4 +1778,52 @@ void LoRaWANNode::hton(uint8_t* buff, T val, size_t size) { } } -#endif +// The following function enables LMAC, a CSMA scheme for LoRa as specified +// in the LoRa Alliance Technical Recommendation #13. +// A user may enable CSMA to provide frames an additional layer of protection from interference. +// https://resources.lora-alliance.org/technical-recommendations/tr013-1-0-0-csma +void LoRaWANNode::performCSMA() { + + // Compute initial random back-off. + // When BO is reduced to zero, the function returns and the frame is transmitted. + uint32_t BO = this->phyLayer->random(1, this->backoffMax + 1); + + while (BO > 0) { + // DIFS: Check channel for DIFS_slots + bool channelFreeDuringDIFS = true; + for (uint8_t i = 0; i < this->difsSlots; i++) { + if (performCAD()) { + RADIOLIB_DEBUG_PRINTLN("OCCUPIED CHANNEL DURING DIFS"); + channelFreeDuringDIFS = false; + // Channel is occupied during DIFS, hop to another. + this->setupChannels(); + break; + } + } + + // Start reducing BO counter if DIFS slot was free. + if (channelFreeDuringDIFS) { + // Continue decrementing BO with per each CAD reporting free channel. + while (BO > 0) { + if (performCAD()) { + RADIOLIB_DEBUG_PRINTLN("OCCUPIED CHANNEL DURING BO"); + // Channel is busy during CAD, hop to another and return to DIFS state again. + this->setupChannels(); + break; // Exit loop. Go back to DIFS state. + } + BO--; // Decrement BO by one if channel is free + } + } + } +} + +bool LoRaWANNode::performCAD() { + int16_t state = this->phyLayer->scanChannel(); + + if ((state == RADIOLIB_PREAMBLE_DETECTED) || (state == RADIOLIB_LORA_DETECTED)) { + return true; // Channel is busy + } + return false; // Channel is free +} + +#endif \ No newline at end of file diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 65efe7eb..82b08127 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -296,6 +296,17 @@ class LoRaWANNode { (e.g. 8 for US915 FSB2 used by TTN). By default -1 (no channel offset). */ int8_t numChannels; + /*! \brief Num of Back Off(BO) slots to be decremented after DIFS phase. 0 to disable BO. + A random BO avoids collisions in the case where two or more nodes start the CSMA + process at the same time. */ + uint8_t backoffMax; + + /*! \brief Num of CADs to estimate a clear CH. */ + uint8_t difsSlots; + + /*! \brief enable/disable CSMA for LoRaWAN. */ + bool enableCSMA; + /*! \brief Default constructor. \param phy Pointer to the PhysicalLayer radio module. @@ -309,6 +320,14 @@ class LoRaWANNode { */ void wipe(); + /*! + \brief Configures CSMA for LoRaWAN as per TR-13, LoRa Alliance. + \param backoffMax Num of BO slots to be decremented after DIFS phase. 0 to disable BO. + \param difsSlots Num of CADs to estimate a clear CH. + \param enableCSMA enable/disable CSMA for LoRaWAN. + */ + void setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA = false); + /*! \brief Restore OTAA session by loading information from persistent storage. \returns \ref status_codes @@ -502,6 +521,12 @@ class LoRaWANNode { // host-to-network conversion method - takes data from host variable and and converts it to network packet endians template static void hton(uint8_t* buff, T val, size_t size = 0); + + // perform a single CAD operation for the under SF/CH combination. Returns either busy or otherwise. + bool performCAD(); + + // Performs CSMA as per LoRa Alliance Technical Reccomendation 13 (TR-013). + void performCSMA(); }; #endif