From bcd8a05cd49eecb8c54b20f121eab81742ef8b34 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sun, 5 May 2024 12:42:03 +0100 Subject: [PATCH] [SX128x] Added interface for interrupt-driven CAD (#1085) --- ...x_Channel_Activity_Detection_Interrupt.ino | 108 ++++++++++++++++++ src/modules/SX128x/SX128x.cpp | 81 +++++++------ src/modules/SX128x/SX128x.h | 13 +++ 3 files changed, 169 insertions(+), 33 deletions(-) create mode 100644 examples/SX128x/SX128x_Channel_Activity_Detection_Interrupt/SX128x_Channel_Activity_Detection_Interrupt.ino diff --git a/examples/SX128x/SX128x_Channel_Activity_Detection_Interrupt/SX128x_Channel_Activity_Detection_Interrupt.ino b/examples/SX128x/SX128x_Channel_Activity_Detection_Interrupt/SX128x_Channel_Activity_Detection_Interrupt.ino new file mode 100644 index 000000000..8daf63579 --- /dev/null +++ b/examples/SX128x/SX128x_Channel_Activity_Detection_Interrupt/SX128x_Channel_Activity_Detection_Interrupt.ino @@ -0,0 +1,108 @@ +/* + RadioLib SX128x Channel Activity Detection Example + + This example uses SX1280 to scan the current LoRa + channel and detect ongoing LoRa transmissions. + + Other modules from SX128x family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx128x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_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 packet or timeout is detected + radio.setDio1Action(setFlag); + + // start scanning the channel + Serial.print(F("[SX1280] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } +} + +// flag to indicate that a packet was detected or CAD timed out +volatile bool scanFlag = false; + +// this function is called when a complete packet +// is received by the module +// IMPORTANT: this function MUST be 'void' type +// and MUST NOT have any arguments! +#if defined(ESP8266) || defined(ESP32) + ICACHE_RAM_ATTR +#endif +void setFlag(void) { + // something happened, set the flag + scanFlag = true; +} + +void loop() { + // check if the flag is set + if(scanFlag) { + // reset flag + scanFlag = false; + + // check CAD result + int state = radio.getChannelScanResult(); + + if (state == RADIOLIB_LORA_DETECTED) { + // LoRa packet was detected + Serial.println(F("[SX1280] Packet detected!")); + + } else if (state == RADIOLIB_CHANNEL_FREE) { + // channel is free + Serial.println(F("[SX1280] Channel is free!")); + + } else { + // some other error occurred + Serial.print(F("[SX1280] Failed, code ")); + Serial.println(state); + + } + + // start scanning the channel again + Serial.print(F("[SX1280] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } + } +} diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp index 00f5b5e28..0a5333f24 100644 --- a/src/modules/SX128x/SX128x.cpp +++ b/src/modules/SX128x/SX128x.cpp @@ -411,28 +411,8 @@ int16_t SX128x::receiveDirect() { } int16_t SX128x::scanChannel() { - // check active modem - if(getPacketType() != RADIOLIB_SX128X_PACKET_TYPE_LORA) { - return(RADIOLIB_ERR_WRONG_MODEM); - } - - // set mode to standby - int16_t state = standby(); - RADIOLIB_ASSERT(state); - - // set DIO pin mapping - state = setDioIrqParams(RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE, RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE); - RADIOLIB_ASSERT(state); - - // clear interrupt flags - state = clearIrqStatus(); - RADIOLIB_ASSERT(state); - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_RX); - // set mode to CAD - state = setCad(); + int16_t state = startChannelScan(); RADIOLIB_ASSERT(state); // wait for channel activity detected or timeout @@ -441,18 +421,7 @@ int16_t SX128x::scanChannel() { } // check CAD result - uint16_t cadResult = getIrqStatus(); - if(cadResult & RADIOLIB_SX128X_IRQ_CAD_DETECTED) { - // detected some LoRa activity - clearIrqStatus(); - return(RADIOLIB_LORA_DETECTED); - } else if(cadResult & RADIOLIB_SX128X_IRQ_CAD_DONE) { - // channel is free - clearIrqStatus(); - return(RADIOLIB_CHANNEL_FREE); - } - - return(RADIOLIB_ERR_UNKNOWN); + return(getChannelScanResult()); } int16_t SX128x::sleep(bool retainConfig) { @@ -664,6 +633,52 @@ int16_t SX128x::readData(uint8_t* data, size_t len) { return(state); } +int16_t SX128x::startChannelScan() { + // check active modem + if(getPacketType() != RADIOLIB_SX128X_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set DIO pin mapping + state = setDioIrqParams(RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE, RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_RX); + + // set mode to CAD + return(setCad()); +} + +int16_t SX128x::getChannelScanResult() { + // check active modem + if(getPacketType() != RADIOLIB_SX128X_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // check CAD result + uint16_t cadResult = getIrqStatus(); + int16_t state = RADIOLIB_ERR_UNKNOWN; + if(cadResult & RADIOLIB_SX128X_IRQ_CAD_DETECTED) { + // detected some LoRa activity + state = RADIOLIB_LORA_DETECTED; + } else if(cadResult & RADIOLIB_SX128X_IRQ_CAD_DONE) { + // channel is free + state = RADIOLIB_CHANNEL_FREE; + } + + clearIrqStatus(); + return(state); +} + int16_t SX128x::setFrequency(float freq) { RADIOLIB_CHECK_RANGE(freq, 2400.0, 2500.0, RADIOLIB_ERR_INVALID_FREQUENCY); diff --git a/src/modules/SX128x/SX128x.h b/src/modules/SX128x/SX128x.h index 80618d71f..a68f6c5b6 100644 --- a/src/modules/SX128x/SX128x.h +++ b/src/modules/SX128x/SX128x.h @@ -568,6 +568,19 @@ class SX128x: public PhysicalLayer { \returns \ref status_codes */ int16_t readData(uint8_t* data, size_t len) override; + + /*! + \brief Interrupt-driven channel activity detection method. DIO1 will be activated + when LoRa preamble is detected, or upon timeout. Defaults to CAD parameter values recommended by AN1200.48. + \returns \ref status_codes + */ + int16_t startChannelScan() override; + + /*! + \brief Read the channel scan result + \returns \ref status_codes + */ + int16_t getChannelScanResult() override; // configuration methods