[LR11x0] Added CAD support (#679)

pull/1075/head
jgromes 2024-04-20 21:29:32 +02:00
rodzic b283c1b117
commit 01208574d9
4 zmienionych plików z 335 dodań i 2 usunięć

Wyświetl plik

@ -0,0 +1,75 @@
/*
RadioLib LR11x0 Blocking Channel Activity Detection Example
This example uses LR1110 to scan the current LoRa
channel and detect ongoing LoRa transmissions.
Unlike SX127x CAD, LR11x0 can detect any part
of LoRa transmission, not just the preamble.
Other modules from LR11x0 family can also be used.
Using blocking CAD is not recommended, as it will lead
to significant amount of timeouts, inefficient use of processor
time and can some miss packets!
Instead, interrupt CAD is recommended.
For default module settings, see the wiki page
https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lora-modem
For full API reference, see the GitHub Pages
https://jgromes.github.io/RadioLib/
*/
// include the library
#include <RadioLib.h>
// LR1110 has the following connections:
// NSS pin: 10
// DIO1 pin: 2
// NRST pin: 3
// BUSY pin: 9
LR1110 radio = new Module(10, 2, 3, 9);
// or using RadioShield
// https://github.com/jgromes/RadioShield
//LR1110 radio = RadioShield.ModuleA;
void setup() {
Serial.begin(9600);
// initialize LR1110 with default settings
Serial.print(F("[LR1110] 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);
}
}
void loop() {
Serial.print(F("[LR1110] Scanning channel for LoRa transmission ... "));
// start scanning current channel
int state = radio.scanChannel();
if (state == RADIOLIB_LORA_DETECTED) {
// LoRa preamble was detected
Serial.println(F("detected!"));
} else if (state == RADIOLIB_CHANNEL_FREE) {
// no preamble was detected, channel is free
Serial.println(F("channel is free!"));
} else {
// some other error occurred
Serial.print(F("failed, code "));
Serial.println(state);
}
// wait 100 ms before new scan
delay(100);
}

Wyświetl plik

@ -0,0 +1,110 @@
/*
RadioLib LR11x0 Channel Activity Detection Example
This example uses LR1110 to scan the current LoRa
channel and detect ongoing LoRa transmissions.
Unlike SX127x CAD, LR11x0 can detect any part
of LoRa transmission, not just the preamble.
Other modules from LR11x0 family can also be used.
For default module settings, see the wiki page
https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lora-modem
For full API reference, see the GitHub Pages
https://jgromes.github.io/RadioLib/
*/
// include the library
#include <RadioLib.h>
// LR1110 has the following connections:
// NSS pin: 10
// DIO1 pin: 2
// NRST pin: 3
// BUSY pin: 9
LR1110 radio = new Module(10, 2, 3, 9);
// or using RadioShield
// https://github.com/jgromes/RadioShield
//LR1110 radio = RadioShield.ModuleA;
void setup() {
Serial.begin(9600);
// initialize LR1110 with default settings
Serial.print(F("[LR1110] 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("[LR1110] 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("[LR1110] Packet detected!"));
} else if (state == RADIOLIB_CHANNEL_FREE) {
// channel is free
Serial.println(F("[LR1110] Channel is free!"));
} else {
// some other error occurred
Serial.print(F("[LR1110] Failed, code "));
Serial.println(state);
}
// start scanning the channel again
Serial.print(F("[LR1110] 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);
}
}
}

Wyświetl plik

@ -315,6 +315,24 @@ int16_t LR11x0::receiveDirect() {
return(RADIOLIB_ERR_UNKNOWN);
}
int16_t LR11x0::scanChannel() {
return(this->scanChannel(RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT));
}
int16_t LR11x0::scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
// set mode to CAD
int state = startChannelScan(symbolNum, detPeak, detMin);
RADIOLIB_ASSERT(state);
// wait for channel activity detected or timeout
while(!this->mod->hal->digitalRead(this->mod->getIrq())) {
this->mod->hal->yield();
}
// check CAD result
return(getChannelScanResult());
}
int16_t LR11x0::standby() {
return(LR11x0::standby(RADIOLIB_LR11X0_STANDBY_RC));
}
@ -539,6 +557,62 @@ int16_t LR11x0::readData(uint8_t* data, size_t len) {
return(state);
}
int16_t LR11x0::startChannelScan() {
return(this->startChannelScan(RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT));
}
int16_t LR11x0::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
// check active modem
int16_t state = RADIOLIB_ERR_NONE;
uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
state = getPacketType(&modem);
RADIOLIB_ASSERT(state);
if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// set mode to standby
state = standby();
RADIOLIB_ASSERT(state);
// set RF switch (if present)
this->mod->setRfSwitchState(Module::MODE_RX);
// set DIO pin mapping
state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_CAD_DETECTED | RADIOLIB_LR11X0_IRQ_CAD_DONE, RADIOLIB_LR11X0_IRQ_NONE);
RADIOLIB_ASSERT(state);
// clear interrupt flags
state = clearIrq(RADIOLIB_LR11X0_IRQ_ALL);
RADIOLIB_ASSERT(state);
// set mode to CAD
return(startCad(symbolNum, detPeak, detMin));
}
int16_t LR11x0::getChannelScanResult() {
// check active modem
int16_t state = RADIOLIB_ERR_NONE;
uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
state = getPacketType(&modem);
RADIOLIB_ASSERT(state);
if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// check CAD result
uint32_t cadResult = getIrqStatus();
if(cadResult & RADIOLIB_LR11X0_IRQ_CAD_DETECTED) {
// detected some LoRa activity
return(RADIOLIB_LORA_DETECTED);
} else if(cadResult & RADIOLIB_LR11X0_IRQ_CAD_DONE) {
// channel is free
return(RADIOLIB_CHANNEL_FREE);
}
return(RADIOLIB_ERR_UNKNOWN);
}
int16_t LR11x0::setBandwidth(float bw) {
// check active modem
uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
@ -1282,8 +1356,6 @@ int16_t LR11x0::config(uint8_t modem) {
state = this->setRxTxFallbackMode(RADIOLIB_LR11X0_FALLBACK_MODE_STBY_RC);
RADIOLIB_ASSERT(state);
// TODO set some CAD parameters - will be overwritten when calling CAD anyway
// clear IRQ
state = this->clearIrq(RADIOLIB_LR11X0_IRQ_ALL);
state |= this->setDioIrqParams(RADIOLIB_LR11X0_IRQ_NONE, RADIOLIB_LR11X0_IRQ_NONE);
@ -1332,6 +1404,42 @@ int16_t LR11x0::setPacketMode(uint8_t mode, uint8_t len) {
return(state);
}
int16_t LR11x0::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) {
// check active modem
uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE;
int16_t state = getPacketType(&type);
RADIOLIB_ASSERT(state);
if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) {
return(RADIOLIB_ERR_WRONG_MODEM);
}
// select CAD parameters
// TODO the magic numbers are based on Semtech examples, this is probably suboptimal
uint8_t num = symbolNum;
if(num == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) {
num = 2;
}
uint8_t detPeakValues[8] = { 48, 48, 50, 55, 55, 59, 61, 65 };
uint8_t peak = detPeak;
if(peak == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) {
peak = detPeakValues[this->spreadingFactor - 5];
}
uint8_t min = detMin;
if(min == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) {
min = 10;
}
// set CAD parameters
// TODO add configurable exit mode and timeout
state = setCadParams(num, peak, min, RADIOLIB_LR11X0_CAD_EXIT_MODE_STBY_RC, 0);
RADIOLIB_ASSERT(state);
// start CAD
return(setCad());
}
Module* LR11x0::getMod() {
return(this->mod);
}

Wyświetl plik

@ -312,6 +312,7 @@
#define RADIOLIB_LR11X0_CAD_EXIT_MODE_STBY_RC (0x00UL << 0) // 7 0 mode to set after CAD: standby with RC
#define RADIOLIB_LR11X0_CAD_EXIT_MODE_RX (0x01UL << 0) // 7 0 receive if activity detected
#define RADIOLIB_LR11X0_CAD_EXIT_MODE_LBT (0x10UL << 0) // 7 0 transmit if no activity detected
#define RADIOLIB_LR11X0_CAD_PARAM_DEFAULT (0xFFUL << 0) // 7 0 used by the CAD methods to specify default parameter value
// RADIOLIB_LR11X0_CMD_SET_PACKET_TYPE
#define RADIOLIB_LR11X0_PACKET_TYPE_NONE (0x00UL << 0) // 2 0 packet type: none
@ -620,6 +621,21 @@ class LR11x0: public PhysicalLayer {
*/
int16_t receiveDirect() override;
/*!
\brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload.
\returns \ref status_codes
*/
int16_t scanChannel() override;
/*!
\brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload.
\param symbolNum Number of symbols for CAD detection.
\param detPeak Peak value for CAD detection.
\param detMin Minimum value for CAD detection.
\returns \ref status_codes
*/
int16_t scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin);
/*!
\brief Sets the module to standby mode (overload for PhysicalLayer compatibility, uses 13 MHz RC oscillator).
\returns \ref status_codes
@ -731,6 +747,29 @@ class LR11x0: 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 Interrupt-driven channel activity detection method. DIO1 will be activated
when LoRa preamble is detected, or upon timeout.
\param symbolNum Number of symbols for CAD detection.
\param detPeak Peak value for CAD detection.
\param detMin Minimum value for CAD detection.
\returns \ref status_codes
*/
int16_t startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin);
/*!
\brief Read the channel scan result
\returns \ref status_codes
*/
int16_t getChannelScanResult() override;
// configuration methods
@ -1133,6 +1172,7 @@ class LR11x0: public PhysicalLayer {
bool findChip(uint8_t ver);
int16_t config(uint8_t modem);
int16_t setPacketMode(uint8_t mode, uint8_t len);
int16_t startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin);
// common methods to avoid some copy-paste
int16_t bleBeaconCommon(uint16_t cmd, uint8_t chan, uint8_t* payload, size_t len);