arduino-LoRa icon indicating copy to clipboard operation
arduino-LoRa copied to clipboard

Add Channel Activity Detection ability.

Open w-ockham opened this issue 5 years ago • 19 comments

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.

w-ockham avatar Feb 04 '20 15:02 w-ockham

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.

IoTThinks avatar Feb 05 '20 09:02 IoTThinks

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();
   ....
}

w-ockham avatar Feb 05 '20 12:02 w-ockham

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

IoTThinks avatar Feb 05 '20 12:02 IoTThinks

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!

artemen avatar Feb 10 '20 16:02 artemen

As I pointed out in the inline comment.. https://github.com/sandeepmistry/arduino-LoRa/pull/334#discussion_r376877207

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 avatar Feb 10 '20 18:02 morganrallen

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

artemen avatar Feb 10 '20 20:02 artemen

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.

morganrallen avatar Feb 10 '20 23:02 morganrallen

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

ricaun avatar Feb 25 '20 13:02 ricaun

@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 avatar Jun 25 '20 05:06 IoTThinks

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

IoTThinks avatar Jun 25 '20 05:06 IoTThinks

@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

IoTThinks avatar Jun 25 '20 08:06 IoTThinks

@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. 😞

ricaun avatar Jun 25 '20 14:06 ricaun

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

IoTThinks avatar Jun 25 '20 14:06 IoTThinks

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. 😞

ricaun avatar Jun 25 '20 14:06 ricaun

@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 avatar Jun 25 '20 15:06 IoTThinks

@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 avatar Jun 25 '20 15:06 IoTThinks

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 avatar Jun 26 '20 08:06 IoTThinks

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

IoTThinks avatar Jun 26 '20 11:06 IoTThinks

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! 😄

cobbm avatar Mar 28 '22 08:03 cobbm

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

morganrallen avatar Mar 15 '23 08:03 morganrallen

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.

morganrallen avatar Mar 29 '23 23:03 morganrallen