Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Channel Activity Detection ability. #334

Merged
merged 4 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ To save further pins one could connect the reset pin of the MCU with reset pin o

#### Pin dio0 interrupt callbacks

The dio0 pin can be used for transmission finish callback and/or receiving callback, check `onTxDone` and `onReceive`.
The dio0 pin can be used for channel activity detection callback, transmission finish callback and/or receiving callback, check `onCadDone` , `onTxDone`, and `onReceive`.

### Set SPI interface

Expand Down Expand Up @@ -242,6 +242,27 @@ Returns the next byte in the packet or `-1` if no bytes are available.

**Note:** Other Arduino [`Stream` API's](https://www.arduino.cc/en/Reference/Stream) can also be used to read data from the packet

## Channel Activity Detection
**WARNING**: Channel activity detection callback uses the interrupt pin on the `dio0`, check `setPins` function!

### Register callback

Register a callback function for when channel activity detection has done.
```arduino
LoRa.onCadDone(onCadDone);

void onCadDone(boolean signalDetected) {
// ...
}
```
* `onCadDone` - function to call when channel activity detection has done.
* `signalDetected` - if `true`, the radio detects the presence of other LoRa signals.

### Channel Activity detection mode
Puts the radio in channel activity detection mode.
```arduino
LoRa.channelActivityDetection();
```
## Other radio modes

### Idle mode
Expand Down
58 changes: 58 additions & 0 deletions examples/LoRaCADCallback/LoRaCADCallback.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <SPI.h>
#include "LoRa.h"

#ifdef ARDUINO_SAMD_MKRWAN1300
#error "This example is not compatible with the Arduino MKR WAN 1300 board!"
#endif

void setup() {
Serial.begin(9600);
while (!Serial);

Serial.println("LoRa Receiver Callback");

if (!LoRa.begin(915E6)) {
Serial.println("Starting LoRa failed!");
while (1);
}

// register the channel activity dectection callback
LoRa.onCadDone(onCadDone);
// register the receive callback
LoRa.onReceive(onReceive);
// put the radio into CAD mode
LoRa.channelActivityDetection();
}

void loop() {
// do nothing
}

void onCadDone(boolean signalDetected) {
// detect preamble
if (signalDetected) {
Serial.println("Signal detected");
// put the radio into continuous receive mode
LoRa.receive();
} else {
// try next activity dectection
LoRa.channelActivityDetection();
}
}

void onReceive(int packetSize) {
// received a packet
Serial.print("Received packet '");

// read packet
for (int i = 0; i < packetSize; i++) {
Serial.print((char)LoRa.read());
}

// print RSSI of packet
Serial.print("' with RSSI ");
Serial.println(LoRa.packetRssi());

// put the radio into CAD mode
LoRa.channelActivityDetection();
}
2 changes: 2 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ flush KEYWORD2

onReceive KEYWORD2
onTxDone KEYWORD2
onCadDone KEYWORD2
channelActivityDetection KEYWORD2
receive KEYWORD2
idle KEYWORD2
sleep KEYWORD2
Expand Down
40 changes: 36 additions & 4 deletions src/LoRa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#define MODE_TX 0x03
#define MODE_RX_CONTINUOUS 0x05
#define MODE_RX_SINGLE 0x06
#define MODE_CAD 0x07

// PA config
#define PA_BOOST 0x80
Expand All @@ -54,6 +55,8 @@
#define IRQ_TX_DONE_MASK 0x08
#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
#define IRQ_RX_DONE_MASK 0x40
#define IRQ_CAD_DONE_MASK 0x04
#define IRQ_CAD_DETECTED_MASK 0x01

#define MAX_PKT_LENGTH 255

Expand All @@ -71,6 +74,7 @@ LoRaClass::LoRaClass() :
_packetIndex(0),
_implicitHeaderMode(0),
_onReceive(NULL),
_onCadDone(NULL),
_onTxDone(NULL)
{
// overide Stream timeout value
Expand Down Expand Up @@ -199,7 +203,7 @@ int LoRaClass::endPacket(bool async)

bool LoRaClass::isTransmitting()
{
if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) {
if ((readRegister(REG_OP_MODE) & B111) == MODE_TX) {
return true;
}

Expand Down Expand Up @@ -367,6 +371,24 @@ void LoRaClass::onReceive(void(*callback)(int))
}
}

void LoRaClass::onCadDone(void(*callback)(boolean))
{
_onCadDone = callback;

if (callback) {
pinMode(_dio0, INPUT);
#ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.usingInterrupt(digitalPinToInterrupt(_dio0));
#endif
attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
} else {
detachInterrupt(digitalPinToInterrupt(_dio0));
#ifdef SPI_HAS_NOTUSINGINTERRUPT
SPI.notUsingInterrupt(digitalPinToInterrupt(_dio0));
#endif
}
}

void LoRaClass::onTxDone(void(*callback)())
{
_onTxDone = callback;
Expand Down Expand Up @@ -400,6 +422,12 @@ void LoRaClass::receive(int size)

writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
}

void LoRaClass::channelActivityDetection(void)
{
writeRegister(REG_DIO_MAPPING_1, 0x80);// DIO0 => CADDONE
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_CAD);
}
#endif

void LoRaClass::idle()
Expand Down Expand Up @@ -660,7 +688,11 @@ void LoRaClass::handleDio0Rise()
// clear IRQ's
writeRegister(REG_IRQ_FLAGS, irqFlags);

if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
if ((irqFlags & IRQ_CAD_DONE_MASK) != 0) {
if (_onCadDone) {
_onCadDone((irqFlags & IRQ_CAD_DETECTED_MASK) != 0);
}
} else if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {

if ((irqFlags & IRQ_RX_DONE_MASK) != 0) {
// received a packet
Expand All @@ -678,8 +710,8 @@ void LoRaClass::handleDio0Rise()

// reset FIFO address
writeRegister(REG_FIFO_ADDR_PTR, 0);
}
else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) {
} else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) {
if (_onTxDone) {
_onTxDone();
}
Expand Down
3 changes: 3 additions & 0 deletions src/LoRa.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ class LoRaClass : public Stream {

#ifndef ARDUINO_SAMD_MKRWAN1300
void onReceive(void(*callback)(int));
void onCadDone(void(*callback)(boolean));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternative name suggestions: onCollisionActivityDetectComplete?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe CAD in Semtech LoRa is Channel Activity Detection.
There is someone sending the data. and it may be not a collision, right?

In page 44, it says "Once the calculation is finished the modem generates the CadDone interrupt. If the correlation was successful, CadDetected is generated simultaneously."

So the name should be onCadDone or onCadDetected.
My 2 cents.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But those interrupts are generated on different pins, if you check Table 18, CadDone is raised on DIO0 & DIO3 and CadDetected is raised on DIO1. They also have different functions. CAD is just the process to see if there is any activity on the channel, CadDone raised when it's done. CadDetect is only fired if activity is detected.

Copy link
Collaborator

@IoTThinks IoTThinks Feb 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then onCadDone is fine or onChannelActivityDetectionDone (quite long).
As channel activity doesnt imply channel collission.
When there is channel avtivity and we are sending other packets, then there is collission.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer onCadDone since it's quite simpler than on24Done. :-)

void onTxDone(void(*callback)());

void receive(int size = 0);
void channelActivityDetection(void);
#endif
void idle();
void sleep();
Expand Down Expand Up @@ -118,6 +120,7 @@ class LoRaClass : public Stream {
int _packetIndex;
int _implicitHeaderMode;
void (*_onReceive)(int);
void (*_onCadDone)(boolean);
void (*_onTxDone)();
};

Expand Down