From e75fd5b84a7df7f6ea77071205651b7aa6afad86 Mon Sep 17 00:00:00 2001
From: Minoru Tomobe <w-ockham@users.noreply.github.com>
Date: Sun, 2 Feb 2020 23:14:40 +0900
Subject: [PATCH 1/4] Add Channel Activity Detection function and examples.

---
 API.md                                       | 23 +++++++-
 examples/LoRaCADCallback/LoRaCADCallback.ino | 58 ++++++++++++++++++++
 keywords.txt                                 |  2 +
 src/LoRa.cpp                                 | 36 +++++++++++-
 src/LoRa.h                                   |  3 +
 5 files changed, 119 insertions(+), 3 deletions(-)
 create mode 100644 examples/LoRaCADCallback/LoRaCADCallback.ino

diff --git a/API.md b/API.md
index 78f054d..f2f9d40 100644
--- a/API.md
+++ b/API.md
@@ -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
 
@@ -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.CAD();
+```
 ## Other radio modes
 
 ### Idle mode
diff --git a/examples/LoRaCADCallback/LoRaCADCallback.ino b/examples/LoRaCADCallback/LoRaCADCallback.ino
new file mode 100644
index 0000000..c8ee5bd
--- /dev/null
+++ b/examples/LoRaCADCallback/LoRaCADCallback.ino
@@ -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.CAD();
+}
+
+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.CAD();
+  }
+}
+
+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.CAD();
+}
diff --git a/keywords.txt b/keywords.txt
index 63e0e9a..36a176c 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -32,6 +32,8 @@ flush	KEYWORD2
 
 onReceive	KEYWORD2
 onTxDone	KEYWORD2
+onCadDone	KEYWORD2
+CAD	KEYWORD2
 receive	KEYWORD2
 idle	KEYWORD2
 sleep	KEYWORD2
diff --git a/src/LoRa.cpp b/src/LoRa.cpp
index 6980f5a..651215b 100644
--- a/src/LoRa.cpp
+++ b/src/LoRa.cpp
@@ -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
@@ -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
 
@@ -71,6 +74,7 @@ LoRaClass::LoRaClass() :
   _packetIndex(0),
   _implicitHeaderMode(0),
   _onReceive(NULL),
+  _onCadDone(NULL),
   _onTxDone(NULL)
 {
   // overide Stream timeout value
@@ -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;
   }
 
@@ -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;
@@ -400,6 +422,12 @@ void LoRaClass::receive(int size)
 
   writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
 }
+
+void LoRaClass::CAD(void)
+{
+  writeRegister(REG_DIO_MAPPING_1, 0x80);// DIO0 => CADDONE
+  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_CAD);
+}
 #endif
 
 void LoRaClass::idle()
@@ -679,7 +707,11 @@ void LoRaClass::handleDio0Rise()
       // reset FIFO address
       writeRegister(REG_FIFO_ADDR_PTR, 0);
     }
-    else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) {
+    else if ((irqFlags & IRQ_CAD_DONE_MASK) != 0) {
+      if (_onCadDone) {
+        _onCadDone((irqFlags & IRQ_CAD_DETECTED_MASK) != 0);
+      }
+    } else if ((irqFlags & IRQ_TX_DONE_MASK) != 0) {
       if (_onTxDone) {
         _onTxDone();
       }
diff --git a/src/LoRa.h b/src/LoRa.h
index c1671c1..56128d8 100644
--- a/src/LoRa.h
+++ b/src/LoRa.h
@@ -57,9 +57,11 @@ public:
 
 #ifndef ARDUINO_SAMD_MKRWAN1300
   void onReceive(void(*callback)(int));
+  void onCadDone(void(*callback)(boolean));
   void onTxDone(void(*callback)());
 
   void receive(int size = 0);
+  void CAD(void);
 #endif
   void idle();
   void sleep();
@@ -118,6 +120,7 @@ private:
   int _packetIndex;
   int _implicitHeaderMode;
   void (*_onReceive)(int);
+  void (*_onCadDone)(boolean);
   void (*_onTxDone)();
 };
 

From d8a2c0afd35698203efd7133b5ed1d14ac57285c Mon Sep 17 00:00:00 2001
From: Minoru Tomobe <w-ockham@users.noreply.github.com>
Date: Wed, 5 Feb 2020 00:17:33 +0900
Subject: [PATCH 2/4] Add Channel Activity Detection avility.

---
 API.md                                       | 23 +++++++-
 examples/LoRaCADCallback/LoRaCADCallback.ino | 58 ++++++++++++++++++++
 keywords.txt                                 |  2 +
 src/LoRa.cpp                                 | 39 +++++++++++--
 src/LoRa.h                                   |  3 +
 5 files changed, 120 insertions(+), 5 deletions(-)
 create mode 100644 examples/LoRaCADCallback/LoRaCADCallback.ino

diff --git a/API.md b/API.md
index 78f054d..f2f9d40 100644
--- a/API.md
+++ b/API.md
@@ -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
 
@@ -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.CAD();
+```
 ## Other radio modes
 
 ### Idle mode
diff --git a/examples/LoRaCADCallback/LoRaCADCallback.ino b/examples/LoRaCADCallback/LoRaCADCallback.ino
new file mode 100644
index 0000000..c8ee5bd
--- /dev/null
+++ b/examples/LoRaCADCallback/LoRaCADCallback.ino
@@ -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.CAD();
+}
+
+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.CAD();
+  }
+}
+
+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.CAD();
+}
diff --git a/keywords.txt b/keywords.txt
index 63e0e9a..36a176c 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -32,6 +32,8 @@ flush	KEYWORD2
 
 onReceive	KEYWORD2
 onTxDone	KEYWORD2
+onCadDone	KEYWORD2
+CAD	KEYWORD2
 receive	KEYWORD2
 idle	KEYWORD2
 sleep	KEYWORD2
diff --git a/src/LoRa.cpp b/src/LoRa.cpp
index 6980f5a..6dfeb63 100644
--- a/src/LoRa.cpp
+++ b/src/LoRa.cpp
@@ -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
@@ -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
 
@@ -71,6 +74,7 @@ LoRaClass::LoRaClass() :
   _packetIndex(0),
   _implicitHeaderMode(0),
   _onReceive(NULL),
+  _onCadDone(NULL),
   _onTxDone(NULL)
 {
   // overide Stream timeout value
@@ -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;
   }
 
@@ -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;
@@ -400,6 +422,12 @@ void LoRaClass::receive(int size)
 
   writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
 }
+
+void LoRaClass::CAD(void)
+{
+  writeRegister(REG_DIO_MAPPING_1, 0x80);// DIO0 => CADDONE
+  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_CAD);
+}
 #endif
 
 void LoRaClass::idle()
@@ -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
@@ -678,8 +710,7 @@ 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();
       }
diff --git a/src/LoRa.h b/src/LoRa.h
index c1671c1..56128d8 100644
--- a/src/LoRa.h
+++ b/src/LoRa.h
@@ -57,9 +57,11 @@ public:
 
 #ifndef ARDUINO_SAMD_MKRWAN1300
   void onReceive(void(*callback)(int));
+  void onCadDone(void(*callback)(boolean));
   void onTxDone(void(*callback)());
 
   void receive(int size = 0);
+  void CAD(void);
 #endif
   void idle();
   void sleep();
@@ -118,6 +120,7 @@ private:
   int _packetIndex;
   int _implicitHeaderMode;
   void (*_onReceive)(int);
+  void (*_onCadDone)(boolean);
   void (*_onTxDone)();
 };
 

From dcbc9a9995ccca327fbb8770f861d3551b4c0994 Mon Sep 17 00:00:00 2001
From: Minoru Tomobe <w-ockham@users.noreply.github.com>
Date: Sat, 15 Feb 2020 12:03:27 +0900
Subject: [PATCH 3/4] Change method CAD(void) to
 channelActivityDetection(void).

---
 API.md                                       | 2 +-
 examples/LoRaCADCallback/LoRaCADCallback.ino | 6 +++---
 keywords.txt                                 | 2 +-
 src/LoRa.cpp                                 | 2 +-
 src/LoRa.h                                   | 2 +-
 5 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/API.md b/API.md
index f2f9d40..1eebd9a 100644
--- a/API.md
+++ b/API.md
@@ -261,7 +261,7 @@ void onCadDone(boolean signalDetected) {
 ### Channel Activity detection mode
 Puts the radio in channel activity detection mode.
 ```arduino
-LoRa.CAD();
+LoRa.channelActivityDetection();
 ```
 ## Other radio modes
 
diff --git a/examples/LoRaCADCallback/LoRaCADCallback.ino b/examples/LoRaCADCallback/LoRaCADCallback.ino
index c8ee5bd..b3bead2 100644
--- a/examples/LoRaCADCallback/LoRaCADCallback.ino
+++ b/examples/LoRaCADCallback/LoRaCADCallback.ino
@@ -21,7 +21,7 @@ void setup() {
   // register the receive callback
   LoRa.onReceive(onReceive);
   // put the radio into CAD mode
-  LoRa.CAD();
+  LoRa.channelActivityDetection();
 }
 
 void loop() {
@@ -36,7 +36,7 @@ void onCadDone(boolean signalDetected) {
     LoRa.receive();
   } else {
     // try next activity dectection
-    LoRa.CAD();
+    LoRa.channelActivityDetection();
   }
 }
 
@@ -54,5 +54,5 @@ void onReceive(int packetSize) {
   Serial.println(LoRa.packetRssi());
 
   // put the radio into CAD mode
-  LoRa.CAD();
+  LoRa.channelActivityDetection();
 }
diff --git a/keywords.txt b/keywords.txt
index 36a176c..5367d52 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -33,7 +33,7 @@ flush	KEYWORD2
 onReceive	KEYWORD2
 onTxDone	KEYWORD2
 onCadDone	KEYWORD2
-CAD	KEYWORD2
+channelActivityDetection	KEYWORD2
 receive	KEYWORD2
 idle	KEYWORD2
 sleep	KEYWORD2
diff --git a/src/LoRa.cpp b/src/LoRa.cpp
index 4ac33a8..5dc9f4f 100644
--- a/src/LoRa.cpp
+++ b/src/LoRa.cpp
@@ -423,7 +423,7 @@ void LoRaClass::receive(int size)
   writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
 }
 
-void LoRaClass::CAD(void)
+void LoRaClass::channelActivityDetection(void)
 {
   writeRegister(REG_DIO_MAPPING_1, 0x80);// DIO0 => CADDONE
   writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_CAD);
diff --git a/src/LoRa.h b/src/LoRa.h
index 56128d8..5e05c79 100644
--- a/src/LoRa.h
+++ b/src/LoRa.h
@@ -61,7 +61,7 @@ public:
   void onTxDone(void(*callback)());
 
   void receive(int size = 0);
-  void CAD(void);
+  void channelActivityDetection(void);
 #endif
   void idle();
   void sleep();

From 1f2e4f81d9716f3ab6fe5bbae22709982147215b Mon Sep 17 00:00:00 2001
From: "Morgan 'ARR\\!' Allen" <morganrallen@gmail.com>
Date: Wed, 29 Mar 2023 15:57:32 -0700
Subject: [PATCH 4/4] revert to more consistent and clear irq checking

---
 src/LoRa.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/LoRa.cpp b/src/LoRa.cpp
index 5dc9f4f..be4f24e 100644
--- a/src/LoRa.cpp
+++ b/src/LoRa.cpp
@@ -203,7 +203,7 @@ int LoRaClass::endPacket(bool async)
 
 bool LoRaClass::isTransmitting()
 {
-  if ((readRegister(REG_OP_MODE) & B111) == MODE_TX) {
+  if ((readRegister(REG_OP_MODE) & MODE_TX) == MODE_TX) {
     return true;
   }