From ebc4852fc26d62ff8315fee692f0e63a236b2ee6 Mon Sep 17 00:00:00 2001 From: jgromes Date: Thu, 28 Oct 2021 15:44:25 +0200 Subject: [PATCH] [SX127x] Added interrupt-driven CAD (#396) --- ...x_Channel_Activity_Detection_Interrupt.ino | 136 ++++++++++++++++++ keywords.txt | 2 + src/modules/SX127x/SX127x.cpp | 48 ++++--- src/modules/SX127x/SX127x.h | 9 +- 4 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 examples/SX127x/SX127x_Channel_Activity_Detection_Interrupt/SX127x_Channel_Activity_Detection_Interrupt.ino diff --git a/examples/SX127x/SX127x_Channel_Activity_Detection_Interrupt/SX127x_Channel_Activity_Detection_Interrupt.ino b/examples/SX127x/SX127x_Channel_Activity_Detection_Interrupt/SX127x_Channel_Activity_Detection_Interrupt.ino new file mode 100644 index 00000000..a2c0f758 --- /dev/null +++ b/examples/SX127x/SX127x_Channel_Activity_Detection_Interrupt/SX127x_Channel_Activity_Detection_Interrupt.ino @@ -0,0 +1,136 @@ +/* + RadioLib SX127x Channel Activity Detection with Interrupts Example + + This example scans the current LoRa channel and detects + valid LoRa preambles. Preamble is the first part of + LoRa transmission, so this can be used to check + if the LoRa channel is free, or if you should start + receiving a message. + + Other modules from SX127x/RFM9x family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx127xrfm9x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1278 has the following connections: +// NSS pin: 10 +// DIO0 pin: 2 +// RESET pin: 9 +// DIO1 pin: 3 +SX1278 radio = new Module(10, 2, 9, 3); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1278 radio = RadioShield.ModuleA; + +// save state between loops +int scanState = ERR_NONE; + +void setup() { + Serial.begin(9600); + + // initialize SX1278 with default settings + Serial.print(F("[SX1278] Initializing ... ")); + int state = radio.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when LoRa preamble is not detected within CAD timeout period + radio.setDio0Action(setFlagTimeout); + + // set the function that will be called + // when LoRa preamble is detected + radio.setDio1Action(setFlagDetected); + + // start scanning the channel + Serial.print(F("[SX1278] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } +} + +// flag to indicate that a preamble was not detected +volatile bool timeoutFlag = false; + +// flag to indicate that a preamble was detected +volatile bool detectedFlag = false; + +// disable interrupt when it's not needed +volatile bool enableInterrupt = true; + +// this function is called when no preamble +// is detected within timeout period +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +void setFlagTimeout(void) { + // check if the interrupt is enabled + if(!enableInterrupt) { + return; + } + + // we timed out, set the flag + timeoutFlag = true; +} + +// this function is called when LoRa preamble +// is detected within timeout period +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +void setFlagDetected(void) { + // check if the interrupt is enabled + if(!enableInterrupt) { + return; + } + + // we got a preamble, set the flag + detectedFlag = true; +} + +void loop() { + // check if we got a preamble + if(detectedFlag) { + // disable the interrupt service routine while + // processing the data + enableInterrupt = false; + + // reset flag + detectedFlag = false; + + // LoRa preamble was detected + Serial.print(F("[SX1278] Preamble detected!")); + + } + + // check if we need to restart channel activity detection + if(detectedFlag || timeoutFlag) { + // start scanning the channel + Serial.print(F("[SX1278] Starting scan for LoRa preamble ... ")); + + // start scanning current channel + int state = radio.startChannelScan(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } + + } +} diff --git a/keywords.txt b/keywords.txt index b6ed434d..8548153e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -96,6 +96,8 @@ clearDio1Action KEYWORD2 startTransmit KEYWORD2 startReceive KEYWORD2 readData KEYWORD2 +startChannelScan KEYWORD2 +getChannelScanResult KEYWORD2 setBandwidth KEYWORD2 setSpreadingFactor KEYWORD2 setCodingRate KEYWORD2 diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index b0f1f8ab..81c24eef 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -247,27 +247,8 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { } int16_t SX127x::scanChannel() { - // check active modem - if(getActiveModem() != SX127X_LORA) { - return(ERR_WRONG_MODEM); - } - - // set mode to standby - int16_t state = setMode(SX127X_STANDBY); - RADIOLIB_ASSERT(state); - - // set DIO pin mapping - state = _mod->SPIsetRegValue(SX127X_REG_DIO_MAPPING_1, SX127X_DIO0_CAD_DONE | SX127X_DIO1_CAD_DETECTED, 7, 4); - RADIOLIB_ASSERT(state); - - // clear interrupt flags - clearIRQFlags(); - - // set RF switch (if present) - _mod->setRfSwitchState(HIGH, LOW); - - // set mode to CAD - state = setMode(SX127X_CAD); + // start CAD + int16_t state = startChannelScan(); RADIOLIB_ASSERT(state); // wait for channel activity detected or timeout @@ -566,6 +547,31 @@ int16_t SX127x::readData(uint8_t* data, size_t len) { return(ERR_NONE); } +int16_t SX127x::startChannelScan() { + // check active modem + if(getActiveModem() != SX127X_LORA) { + return(ERR_WRONG_MODEM); + } + + // set mode to standby + int16_t state = setMode(SX127X_STANDBY); + RADIOLIB_ASSERT(state); + + // set DIO pin mapping + state = _mod->SPIsetRegValue(SX127X_REG_DIO_MAPPING_1, SX127X_DIO0_CAD_DONE | SX127X_DIO1_CAD_DETECTED, 7, 4); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + clearIRQFlags(); + + // set RF switch (if present) + _mod->setRfSwitchState(HIGH, LOW); + + // set mode to CAD + state = setMode(SX127X_CAD); + return(state); +} + int16_t SX127x::setSyncWord(uint8_t syncWord) { // check active modem if(getActiveModem() != SX127X_LORA) { diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index dc33ec08..bde973ac 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -734,6 +734,13 @@ class SX127x: public PhysicalLayer { */ int16_t readData(uint8_t* data, size_t len) override; + /*! + \brief Interrupt-driven channel activity detection method. DIO0 will be activated when LoRa preamble is detected. + DIO1 will be activated if there's no preamble detected before timeout. + + \returns \ref status_codes + */ + int16_t startChannelScan(); // configuration methods @@ -1085,7 +1092,7 @@ class SX127x: public PhysicalLayer { * \returns bandwidth in manitsa and exponent format */ static uint8_t calculateBWManExp(float bandwidth); - + virtual void errataFix(bool rx) = 0; };