From da57f1cdef23df5c9ecb3d9c1ec8b0bc807dacf3 Mon Sep 17 00:00:00 2001 From: Amalinda Date: Sat, 28 Oct 2023 11:36:32 +0800 Subject: [PATCH] added functionality for LoRa Alliance TR-13 Enabling CSMA for LoRaWAN --- src/modules/SX126x/SX126x.cpp | 43 ++++++++++++++++++------ src/protocols/LoRaWAN/LoRaWAN.cpp | 55 ++++++++++++++++++++++++++++++- src/protocols/LoRaWAN/LoRaWAN.h | 11 +++++++ 3 files changed, 98 insertions(+), 11 deletions(-) diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index a312a011..560e5e4e 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -1702,27 +1702,47 @@ 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, + // default CAD parameters are shown in Semtech AN1200.48, page 41. + uint8_t detPeakValues[6] = { 22, 22, 24, 25, 26, 30}; + uint8_t symbolNumValues[6] = { 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 }; + RADIOLIB_SX126X_CAD_ON_2_SYMB, + RADIOLIB_SX126X_CAD_ON_2_SYMB }; + + // 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] = symbolNumValues[this->spreadingFactor - 7]; + 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 +1756,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 +2053,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..d87ed01f 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -637,6 +637,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 (CSMA_ENABLE) { + performCSMA(); + } + RADIOLIB_DEBUG_PRINTLN("uplinkMsg:"); RADIOLIB_DEBUG_HEXDUMP(uplinkMsg, uplinkMsgLen); @@ -1762,4 +1767,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 Reccomendation #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 = random(1, BO_MAX + 1); + + while (BO > 0) { + // DIFS: Check channel for DIFS_slots + bool channelFreeDuringDIFS = true; + for (uint8_t i = 0; i < DIFS_SLOTS; 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..1ac9f0a3 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -176,6 +176,11 @@ // the maximum number of simultaneously available channels #define RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS (8) +// CSMA definitions +#define DIFS_SLOTS (2) // Number of CADs to estimate a clear channel. +#define BO_MAX (6) // Number of maximum CADs to back-off. Set to 0 to disable. +#define CSMA_ENABLE (true) // Enables/disables CSMA functionality. + /*! \struct LoRaWANChannelSpan_t \brief Structure to save information about LoRaWAN channels. @@ -502,6 +507,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