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

Conversation

w-ockham
Copy link
Contributor

@w-ockham w-ockham commented Feb 4, 2020

This modification adds the following functions.
LoRa.onCadDone(callback) : Register the callback function for channel activity detection.
LoRa.CAD(void) : Activate channel activity detection mode.

@IoTThinks
Copy link
Collaborator

Hi,
I believe CAD is to detect if any TX activity on the channel by other LoRa nodes to avoid interference.

In the example, I see we do onCad and if there is signal we do onReceive.

What is the advantage if we dont use onCad and use onReceive right away?
Less power consumption or something else?

Thanks a lot.

@w-ockham
Copy link
Contributor Author

w-ockham commented Feb 5, 2020

That's right. The example is intended to provide a minimum description of CAD.
In the latter half of the CAD cycle, power consumption is roughly half of the full receiver mode.
You can also implement collision avoidance with onCADdone as follows,

onCADdone(boolean detected) {
   if (detected) {
       channelBusy = true;
       LoRa.receive();
   } else {
   ...
   }
} 

void loop() {
  if (txReady) {
     if (channelBusy) {
      // Backoff for a while.
     } else {
       LoRa.beginPacket();
   ....
}

@IoTThinks
Copy link
Collaborator

Yes, now I understand.
Thanks a lot for your response.

Copy link
Owner

@sandeepmistry sandeepmistry left a comment

Choose a reason for hiding this comment

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

@w-ockham thanks for taking the time to submit this pull request!

src/LoRa.h Outdated
void onTxDone(void(*callback)());

void receive(int size = 0);
void CAD(void);
Copy link
Owner

Choose a reason for hiding this comment

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

Thoughts on making this lower case or expanded to something like collisionActivityDetect()?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Doesn't it stand for Channel Activity Detection? Additionally, I thought the other day this should be named to make it clear this is CAD DONE, as there is also CadDetect

Copy link
Contributor Author

@w-ockham w-ockham Feb 10, 2020

Choose a reason for hiding this comment

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

How about this? these methods are well self-descriptive enough IMHO. channelActivityDetection switch the receiver to CAD mode, onCadDone register a callback function for CADDone interrupts.

void channelActivityDetection(void);
void onCadDone(void(*callback)(boolean));

@@ -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. :-)

@artemen
Copy link

artemen commented Feb 10, 2020

One thing, as far as I was able to understand there are two interrupts possible with CAD one is triggered on any preamble and the other on your specific one. The later one i believe should be mapped to a different dio pin, would it be possible to add that one as well. Thanks!

@morganrallen
Copy link
Collaborator

morganrallen commented Feb 10, 2020

As I pointed out in the inline comment.. #334 (comment)

One, CADDone, is triggered when the CAD is complete, on DIO0 & DIO2
The other, CADDetected is triggered only if a signal is detected, on DIO1

@artemen
Copy link

artemen commented Feb 10, 2020

ideally, would it be possible to add another interrupt on DIO1? or its sufficient to short them and use single pin on MCU as long as onReceive is not enabled at the same time. but we would still need to check the relevant register on the radio.

As I pointed out in the inline comment.. #334 (comment)

One, CADDone, is triggered when the CAD is complete, on DIO0 & DIO2
The other, CADDetected is triggered only if a signal is detected, on DIO1

@morganrallen
Copy link
Collaborator

I don't think you'd want to just short them, but yes, we should add that, I believe there's an open issue outlining it. But as you suggested, the same functionality can be achieved with just DIO0. When CadDone is raised on this pin, in software you can check the IRQ flag for CadDetected.

@ricaun
Copy link
Contributor

ricaun commented Feb 25, 2020

I create this Branch a long time, and use cad to detect receiving data with sf7, sf8, sf9, sf10, sf11 or sf12.
I used the dio0 and dio1 on the same interrupt pin with some diodes.
The ideia was to create some single gateway or something.

https://github.com/ricaun/arduino-LoRa/tree/CAD

@IoTThinks
Copy link
Collaborator

@w-ockham : I'm going to use your CAD code for my deep-sleep node.
By right, if I do CAD then make my ESP32 go to deep sleep (and wake up via DIO1)
Will my ESP32 wake up and able to receive the current coming message?

Is this possible?
I see from here https://learn.circuit.rocks/esp32-lora-gateway-battery-optimized
He needs to init LoRa correctly after wakeup otherwise the current coming message is gone.
Thanks millions for your effort.

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

 // Then ESP32 go deep sleep at DIO1
 // Setup deep sleep with wakeup by external source
 esp_sleep_enable_ext0_wakeup((gpio_num_t)PIN_LORA_DIO_1, RISING);
 // Finally set ESP32 into sleep
 esp_deep_sleep_start();

@IoTThinks
Copy link
Collaborator

@ricaun Will your CAD code wake up a deep sleep MCU and receive the current incoming message?
Thanks millions.

@IoTThinks
Copy link
Collaborator

@w-ockham : Would you mind explain to me what is B111 in isTransmitting()?
Even if it is a hex, then it should be 0xb111.
Thanks a lot for your effort.

image

@ricaun
Copy link
Contributor

ricaun commented Jun 25, 2020

@ricaun Will your CAD code wake up a deep sleep MCU and receive the current incoming message?
Thanks millions.

I believe so, but I design the code to find the spreading factor when the node receiving a message, It's not perfect but It's possible to receive a message from sf7 to sf12 (with some loss).
In the end, I don't know if how much consumption the Lora module has on the CAD state, and if it makes sense to make the Lora module to wake up the MCU with some interruption when the CAD trigger.

Is this possible?
I see from here https://learn.circuit.rocks/esp32-lora-gateway-battery-optimized
He needs to init LoRa correctly after wakeup otherwise the current coming message is gone.

Interesting, the SX126x has this SetRxDutyCycle function to make the LoRa module switch to receive and sleep.
I don't know if the SX127x has some similar functions. 😞

@IoTThinks
Copy link
Collaborator

IoTThinks commented Jun 25, 2020

The current CAD code syncs CadDone to dio0. Then check flag for cadDetected.
The check for cadDetected flag is not possible as the MCU sleeps.
If wakeup on Dio0 then it is pointless as I need CadDetected only for incoming messages.

I think I will add new code to sync CadDetected to Dio1. Then use dio1 to wakeup MCU.

Thanks a lot for your CAD code. :D

@ricaun
Copy link
Contributor

ricaun commented Jun 25, 2020

The current CAD code syncs CadDone to dio0. Then check flag for cadDetected.
The check for cadDetected flag is not possible as the MCU sleeps.
If wakeup on Dio0 then it is pointless as I need CadDetected only for incoming messages.

I think I will add new code to sync CadDetected to Dio1. Then use dio1 to wakeup MCU.

Thanks a lot for your CAD code. :D

On my code, I used the MAP_DIO0_LORA_CADDONE and MAP_DIO1_LORA_CADDETECT
and if I remembered ready when the module goes to CAD mode, the CARDONE trigger after some time and put the module on the Standby, and if the module detects something on this window the CADDETECT triggers.

I guess the SX127x Lora module is not designed to wake up the MCU with CAD. 😞

@IoTThinks
Copy link
Collaborator

IoTThinks commented Jun 25, 2020

@ricaun Most of the LoRa boards don't have DIO1 connected. e.g: TTGO, Heltec or Ready-made LoRa PCB.
After doing the CAD, it will issue a CADDone.
But not all CADDone events lead to CadDetected.
Hence, we can not base on DIO0 to know when to wakeup MCU.

We need DIO1 for CadDetected.
BTW, it may be for another PR :D
Let me try for CadDetected.

image

@IoTThinks
Copy link
Collaborator

@w-ockham : Would you mind explain to me what is B111 in isTransmitting()?
Even if it is a hex, then it should be 0xb111.
Thanks a lot for your effort.

image

@w-ockham Could you advise me on the B111?
So curious.
Thanks a lot :(y)

@IoTThinks
Copy link
Collaborator

B111 is binary 111 or 0x07.
It should be MODE_CAD instead.
#define MODE_CAD 0x07

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

@IoTThinks
Copy link
Collaborator

IoTThinks commented Jun 26, 2020

This example from @w-ockham has something "not right" too me.
https://github.com/w-ockham/arduino-LoRa/blob/master/examples/LoRaCADCallback/LoRaCADCallback.ino

void onCadDone(boolean signalDetected) {
  // detect preamble
  if (signalDetected) {
    Serial.println("Signal detected");
    // put the radio into continuous receive mode
    LoRa.receive();  <======= This line is likely to clear the current message and only receive the NEXT message.
  } else {
    // try next activity dectection
    LoRa.channelActivityDetection();
  }
}

When a CadDetected is detected, LoRa.receive() seems to clear the incoming data?
And only receive the next incoming message using Continuous RX.
Hence, the receiving node on the right only received HALF of the messages.

image

By right, after CADDetected and set to Single RX or RX Continous Mode, we should receive the current incoming message.
If I use SingleRX by parsePacket, the size will be 0.
image

@cobbm
Copy link

cobbm commented Mar 28, 2022

I have been trying to get this code to work for the last few hours, and I was running into the same problem as IOTThinks where only every other packet was being received.
I think this is caused by the LoRa module not switching back to CAD mode properly after entering the receive state (after the call to receive() in onCadDone()).

After trying several other things which didn't work, I added a call to idle() at the start of channelActivityDetection():

void LoRaClass::channelActivityDetection(void)
{
  idle(); // <-- set state to idle first 
  writeRegister(REG_DIO_MAPPING_1, 0x80);// DIO0 => CADDONE
  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_CAD);
}

This seems to have fixed the problem and CAD is working as expected and I'm now receiving all packets! 😄

@morganrallen
Copy link
Collaborator

@Cangjie103 there are still fixes that need to be addressed on this, please look at the inline comments and fix those before merging.

@morganrallen morganrallen mentioned this pull request Mar 29, 2023
@morganrallen morganrallen merged commit dcbc9a9 into sandeepmistry:master Mar 29, 2023
@morganrallen
Copy link
Collaborator

This PR has been modified, tested and merged and currently is available in the 'master' branch. Please test and check for side effects.

Additionally while reading some of the details on CAD Flow in the SX1276 datasheet, Pages 43 and specifically 44 I realized there is a point that probably needs clarification. CAD seems to only work to detect a packet preamble.
From the datasheet page 44...

During a CAD the following operations take place:

  • The PLL locks
  • The radio receiver captures LoRa preamble symbol of data from the channel. ...
  • phase corresponds to the specified Rx mode current
  • The radio receiver and the PLL turn off, and the modem digital processing starts.
  • The modem searches for a correlation between the radio captured samples and the ideal preamble waveform. This
  • correlation process takes a little bit less than a symbol period to perform. The radio current consumption during that
  • phase is greatly reduced.
  • Once the calculation is finished the modem generates the CadDone interrupt. If the correlation was successful,
  • CadDetected is generated simultaneously.
  • The chip goes back to Standby mode.
  • If a preamble was detected, clear the interrupt, then initiate the reception by putting the radio in RX single mode or RX continuous mode.

This last line in particular seems to indicate the purpose is to detect if you are able to receive, not if you're able to transmit. Maybe further testing will indicate if both are the case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants