Skip to content

CAD detection #50

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

Closed
wants to merge 13 commits into from
Closed
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include <util/atomic.h>

#include <SPI.h>
#include <LoRa.h>

/*
* This example shows how to use interrupts to catch various LoRa events:
* - RxDone
* - TxDone
* - CADDone
* - RxTimeout
* - FHSSChangeChannel
* - CADDetected
* - ValidHeader
* - CRCError
* - PLLLock
* - ModeReady
* - ClockOutput
*
* This specific example goes through the process of turning on CAD detection and
* when a successful CAD was observed, put the LoRa chip to receive mode.
*/

/*
* It's possible to handle LoRa CAD (channel activity detection, page 43) the following way via interrupts:
*
* 1. Write your interrupt service routine (ISR) which is executed when an IRQ arrives to the pin which is connected to DIO[0-5].
* 2. Configure ISR in Arduino for LoRa DIO[0-5] pin: LoRa.setInterruptMode(0, LORA_IRQ_MODE_CADDONE)
* 3. Start CAD: LoRa.cad()
* 4. Check if valid CAD detected after an IRQ arrived : LoRa.readInterrupts() & LORA_IRQ_FLAG_CAD_DETECTED
* 5. Clear IRQs to be able to restart CAD process later: LoRa.clearInterrupts(LORA_IRQ_FLAG_CAD_DETECTED | LORA_IRQ_FLAG_CAD_DONE);
*
* You may use all five DIOs concurrently connected to different Arduino pins (though those pins should intercept IRQs).
*
* Wiring of the current example: LoRa DIO0 - Arduino D3
*/

/* Copyright © 2018 Szőts Ákos <szotsaki@gmail.com> */

// Interrupt Service Routine (ISR)
// Must be as short as physically possible - assigning a variable should make it
static volatile bool interruptHappened = false;
static void isr_pinD3() {
interruptHappened = true;
}

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

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

// For other constants, like FHSS change channel, CRC error, or RX timeout, see the LoRa.h header file.
// Choose from LORA_IRQ_DIOx_ variants and use this "x" number in place of the first parameter.
// Not all DIOx and interrupt type mixes are possible.
LoRa.setInterruptMode(0 /* DIO0 */, LORA_IRQ_DIO0_CADDONE);

// Launch our ISR function when LoRa interrupt happens
attachInterrupt(digitalPinToInterrupt(3), isr_pinD3, RISING);

// Start LoRa CAD process
LoRa.cad();

Serial.println("Starting LoRa succeeded");
}

void loop() {
if (interruptHappened) {
interruptHappened = false;

const uint8_t loraInterrupts = LoRa.readInterrupts();
if (loraInterrupts & LORA_IRQ_FLAG_CAD_DETECTED) { // Here use LORA_IRQ_FLAG_* variants from LoRa.h
LoRa.parsePacket(); // Put into RXSINGLE mode
// ... Process packet if there's one
}

// Do not let the ISR function and the loop() write and read the interruptHappened variable at the same time
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
// It's possible that the ISR function had set interruptHappened to "true" while we were receiving packets.
// Check again to be sure that we clear interrupt flags only when we received no further IRQs.
if (!interruptHappened) {
LoRa.clearInterrupts(LORA_IRQ_FLAG_CAD_DETECTED | LORA_IRQ_FLAG_CAD_DONE);
}
}

// Restart CAD process
LoRa.cad();
}
}
79 changes: 57 additions & 22 deletions src/LoRa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#define REG_DETECTION_THRESHOLD 0x37
#define REG_SYNC_WORD 0x39
#define REG_DIO_MAPPING_1 0x40
#define REG_DIO_MAPPING_2 0x41
#define REG_VERSION 0x42

// modes
Expand All @@ -39,15 +40,11 @@
#define MODE_TX 0x03
#define MODE_RX_CONTINUOUS 0x05
#define MODE_RX_SINGLE 0x06
#define MODE_CAD 0x07

// PA config
#define PA_BOOST 0x80

// IRQ masks
#define IRQ_TX_DONE_MASK 0x08
#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
#define IRQ_RX_DONE_MASK 0x40

#define MAX_PKT_LENGTH 255

LoRaClass::LoRaClass() :
Expand Down Expand Up @@ -146,20 +143,20 @@ int LoRaClass::endPacket()
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);

// wait for TX done
while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {
while((readRegister(REG_IRQ_FLAGS) & LORA_IRQ_FLAG_TX_DONE) == 0) {
yield();
}

// clear IRQ's
writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);
clearInterrupts(LORA_IRQ_FLAG_TX_DONE);

return 1;
}

int LoRaClass::parsePacket(int size)
{
int packetLength = 0;
int irqFlags = readRegister(REG_IRQ_FLAGS);
const uint8_t irqFlags = readInterrupts();

if (size > 0) {
implicitHeaderMode();
Expand All @@ -169,10 +166,11 @@ int LoRaClass::parsePacket(int size)
explicitHeaderMode();
}

// clear IRQ's
writeRegister(REG_IRQ_FLAGS, irqFlags);
if (irqFlags) {
clearInterrupts(irqFlags);
}

if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
if ((irqFlags & LORA_IRQ_FLAG_RX_DONE) && (irqFlags & LORA_IRQ_FLAG_PAYLOAD_CRC_ERROR) == 0) {
// received a packet
_packetIndex = 0;

Expand Down Expand Up @@ -280,10 +278,9 @@ void LoRaClass::onReceive(void(*callback)(int))

if (callback) {
pinMode(_dio0, INPUT);
setInterruptMode(0, LORA_IRQ_DIO0_RXDONE);

writeRegister(REG_DIO_MAPPING_1, 0x00);

attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0RiseRx, RISING);
} else {
detachInterrupt(digitalPinToInterrupt(_dio0));
}
Expand Down Expand Up @@ -312,6 +309,12 @@ void LoRaClass::sleep()
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
}

void LoRaClass::cad()
{
idle();
writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_CAD);
}

void LoRaClass::setTxPower(int level, int outputPin)
{
if (PA_OUTPUT_RFO_PIN == outputPin) {
Expand Down Expand Up @@ -413,11 +416,17 @@ void LoRaClass::setPreambleLength(long length)
writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0));
}

void LoRaClass::setSyncWord(int sw)
void LoRaClass::setSyncWord(uint8_t sw)
{
writeRegister(REG_SYNC_WORD, sw);
}

void LoRaClass::enableLowDataRateOptimize(bool enabled)
{
uint8_t regValue = readRegister(REG_MODEM_CONFIG_3);
writeRegister(REG_MODEM_CONFIG_3, bitWrite(regValue, 3, enabled));
}

void LoRaClass::enableCrc()
{
writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04);
Expand All @@ -428,6 +437,31 @@ void LoRaClass::disableCrc()
writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb);
}

void LoRaClass::setInterruptMode(byte pin, byte mode)
{
if (pin <= 3) {
uint8_t mapping = readRegister(REG_DIO_MAPPING_1);
bitWrite(mapping, 6 - (pin * 2), bitRead(mode, 0));
bitWrite(mapping, 6 - (pin * 2) + 1, bitRead(mode, 1));
writeRegister(REG_DIO_MAPPING_1, mapping);
} else if (pin <= 5){
uint8_t mapping = readRegister(REG_DIO_MAPPING_2);
bitWrite(mapping, 14 - (pin * 2), bitRead(mode, 0));
bitWrite(mapping, 14 - (pin * 2) + 1, bitRead(mode, 1));
writeRegister(REG_DIO_MAPPING_2, mapping);
}
}

uint8_t LoRaClass::readInterrupts()
{
return readRegister(REG_IRQ_FLAGS);
}

void LoRaClass::clearInterrupts(uint8_t irqFlags)
{
writeRegister(REG_IRQ_FLAGS, irqFlags);
}

byte LoRaClass::random()
{
return readRegister(REG_RSSI_WIDEBAND);
Expand Down Expand Up @@ -469,14 +503,15 @@ void LoRaClass::implicitHeaderMode()
writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01);
}

void LoRaClass::handleDio0Rise()
void LoRaClass::handleDio0RiseRx()
{
int irqFlags = readRegister(REG_IRQ_FLAGS);
const uint8_t irqFlags = readInterrupts();

// clear IRQ's
writeRegister(REG_IRQ_FLAGS, irqFlags);
if (irqFlags) {
clearInterrupts(irqFlags);
}

if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
if ((irqFlags & LORA_IRQ_FLAG_PAYLOAD_CRC_ERROR) == 0) {
// received a packet
_packetIndex = 0;

Expand Down Expand Up @@ -521,9 +556,9 @@ uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value)
return response;
}

void LoRaClass::onDio0Rise()
void LoRaClass::onDio0RiseRx()
{
LoRa.handleDio0Rise();
LoRa.handleDio0RiseRx();
}

LoRaClass LoRa;
42 changes: 39 additions & 3 deletions src/LoRa.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,36 @@
#define PA_OUTPUT_RFO_PIN 0
#define PA_OUTPUT_PA_BOOST_PIN 1

// Interrupt related
#define LORA_IRQ_DIO0_RXDONE 0x0
#define LORA_IRQ_DIO0_TXDONE 0x1
#define LORA_IRQ_DIO0_CADDONE 0x2

#define LORA_IRQ_DIO1_RXTIMEOUT 0x0
#define LORA_IRQ_DIO1_FHSSCHCH 0x1
#define LORA_IRQ_DIO1_CADDETECTED 0x2

#define LORA_IRQ_DIO2_FHSSCHCH 0x0

#define LORA_IRQ_DIO3_CADDONE 0x0
#define LORA_IRQ_DIO3_VALIDHEADER 0x1
#define LORA_IRQ_DIO3_CRCERROR 0x2

#define LORA_IRQ_DIO4_CADDETECTED 0x0
#define LORA_IRQ_DIO4_PLLLOCK 0x1

#define LORA_IRQ_DIO5_MODEREADY 0x0
#define LORA_IRQ_DIO5_CLKOUT 0x1

#define LORA_IRQ_FLAG_RX_TIMEOUT B10000000
#define LORA_IRQ_FLAG_RX_DONE B01000000
#define LORA_IRQ_FLAG_PAYLOAD_CRC_ERROR B00100000
#define LORA_IRQ_FLAG_VALID_HEADER B00010000
#define LORA_IRQ_FLAG_TX_DONE B00001000
#define LORA_IRQ_FLAG_CAD_DONE B00000100
#define LORA_IRQ_FLAG_FHSS_CHANGE_CH B00000010
#define LORA_IRQ_FLAG_CAD_DETECTED B00000001

class LoRaClass : public Stream {
public:
LoRaClass();
Expand Down Expand Up @@ -43,17 +73,23 @@ class LoRaClass : public Stream {
void receive(int size = 0);
void idle();
void sleep();
void cad(); // Channel activity detection

void setTxPower(int level, int outputPin = PA_OUTPUT_PA_BOOST_PIN);
void setFrequency(long frequency);
void setSpreadingFactor(int sf);
void setSignalBandwidth(long sbw);
void setCodingRate4(int denominator);
void setPreambleLength(long length);
void setSyncWord(int sw);
void setSyncWord(uint8_t sw);
void enableLowDataRateOptimize(bool enabled);
void enableCrc();
void disableCrc();

void setInterruptMode(byte pin, byte mode); // pin: [DIO]0..5; mode: see LORA_IRQ_DIO*
uint8_t readInterrupts(); // See LORA_IRQ_FLAG_* for testing against a specific one
void clearInterrupts(uint8_t irqFlags);

// deprecated
void crc() { enableCrc(); }
void noCrc() { disableCrc(); }
Expand All @@ -69,13 +105,13 @@ class LoRaClass : public Stream {
void explicitHeaderMode();
void implicitHeaderMode();

void handleDio0Rise();
void handleDio0RiseRx();

uint8_t readRegister(uint8_t address);
void writeRegister(uint8_t address, uint8_t value);
uint8_t singleTransfer(uint8_t address, uint8_t value);

static void onDio0Rise();
static void onDio0RiseRx();

private:
SPISettings _spiSettings;
Expand Down