jgromes / RadioLib

Universal wireless communication library for embedded devices
https://jgromes.github.io/RadioLib/
MIT License
1.49k stars 376 forks source link

scanChannel without using DIO2 #19

Closed beegee-tokyo closed 5 years ago

beegee-tokyo commented 5 years ago

As on some modules DIO2 is not available because it is used to switch the RF antenna between RX and TX.
This means scanChannel() function cannot be used.

As simple workaround could be to change scanChannel() to use only DIO1 as IRQ input. Example code how it works (tested on eByte E22 module which uses DIO2 as RF antenna switch):

int16_t SX126x::scanChannel()
{
  // check active modem
  if (getPacketType() != SX126X_PACKET_TYPE_LORA)
  {
    return (ERR_WRONG_MODEM);
  }

  // set mode to standby
  int16_t state = standby();
  if (state != ERR_NONE)
  {
    return (state);
  }

  // set DIO pin mapping
  // state = setDioIrqParams(SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DETECTED);
  // Use DIO1 only for both CAD DONE and CAD DETECTED interrupts
  state = setDioIrqParams(SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE);
  if (state != ERR_NONE)
  {
    return (state);
  }

  // clear interrupt flags
  state = clearIrqStatus();
  if (state != ERR_NONE)
  {
    return (state);
  }

  // set mode to CAD
  state = setCad();
  if (state != ERR_NONE)
  {
    return (state);
  }

  // wait for channel activity detected or timeout
  while (!digitalRead(_mod->getInt0()))
  {
    // No need to check DIO2
    // if(digitalRead(_mod->getInt1())) {
    //   clearIrqStatus();
    //   return(LORA_DETECTED);
    // }
  }

  // Get the CAD result
  uint16_t cadResult = getIrqStatus();
  if (cadResult == 0x8000)
  {
    clearIrqStatus();
    return (CHANNEL_FREE);
  }
  else // cadResult == 0x8001
  {
    clearIrqStatus();
    return (LORA_DETECTED);
  }
  return (CHANNEL_FREE);
}
jgromes commented 5 years ago

That's not a bad idea. The reason it was done with two interrupt pins previously was that on SX127x series, DIO configurability is limited (specific interrupt sources only trigger specific DIO pins). Since that's no longer the case on SX126x, there's no reason to sacrifice additional pin.

There's going to be some changes to the examples, so I'm going to add this myself, you don't have to open PR. Thanks for the suggestion!

beegee-tokyo commented 5 years ago

:thumbsup::thumbsup::thumbsup::thumbsup::thumbsup:

But (there is always a but :wink:)

Not sure if it is my ESP32 that I use for testing, but in

uint16_t SX126x::getIrqStatus() {
  uint8_t data[2];
  SPIreadCommand(SX126X_CMD_GET_IRQ_STATUS, data, 2);
  return(((uint16_t)(data[1]) << 8) | data[0]);
}

the return value gives me Highbyte and Lowbyte reversed. The IRQ params (masks?) in SX126x.h does not match the returned values. I get 0x8000 and 0x8001 for CAD_DONE and CAD_DETECTED/_DONE instead of 0x0080 and 0x0180. If I change

return(((uint16_t)(data[1]) << 8) | data[0]);

into

return(((uint16_t)(data[0]) << 8) | data[1]);

the returned values match the masks

beegee-tokyo commented 5 years ago

Here is the final version I am testing right now, using a flag: scanChannel()

int16_t SX126x::scanChannel()
{
  // check active modem
  if (getPacketType() != SX126X_PACKET_TYPE_LORA)
  {
    return (ERR_WRONG_MODEM);
  }

  bool useDIO2 = true;
  if (_dio2RfSwitch)
  {
    // If DIO2 is used as RF switch this function does not work
    // return(ERR_CAD_UNAVAILABLE);
    // Set a flag that CAD needs to be done with DIO1 only
    useDIO2 = false;
  }

  // set mode to standby
  int16_t state = standby();
  if (state != ERR_NONE)
  {
    return (state);
  }

  // set DIO pin mapping
  if (useDIO2)
  {
    state = setDioIrqParams(SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DETECTED);
  }
  else
  {
    state = setDioIrqParams(SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DONE | SX126X_IRQ_CAD_DETECTED);
  }
  if (state != ERR_NONE)
  {
    return (state);
  }

  // clear interrupt flags
  state = clearIrqStatus();
  if (state != ERR_NONE)
  {
    return (state);
  }

  // set mode to CAD
  state = setCad();
  if (state != ERR_NONE)
  {
    return (state);
  }

  // wait for channel activity detected or timeout
  while (!digitalRead(_mod->getInt0()))
  {
    // If DIO2 was available, check status of DIO2
    if (useDIO2)
    {
      if (digitalRead(_mod->getInt1()))
      {
        clearIrqStatus();
        return (LORA_DETECTED);
      }
      else
      {
        clearIrqStatus();
        return (CHANNEL_FREE);
      }
    }
  }
  // We arrive here only if DIO2 was not available
  uint16_t cadResult = getIrqStatus();
  if (cadResult == (SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE)) // Check why high and low byte are switched
  {
    clearIrqStatus();
    return (LORA_DETECTED);
  }

  // clear interrupt flags
  clearIrqStatus();

  return (CHANNEL_FREE);
}

and getIrqStatus()

uint16_t SX126x::getIrqStatus()
{
  uint8_t data[2];
  SPIreadCommand(SX126X_CMD_GET_IRQ_STATUS, data, 2);
  return (((uint16_t)(data[0]) << 8) | data[1]);
}
jgromes commented 5 years ago

When comparing to SX126x::getPacketStatus(), it looks like the bytes are actually swapped. I'll fix that as well.

I think we can just remove the DIO2 flag completely, I see no benefit of DIO1/DIO2 CAD when compared to DIO1 only. Like I said, the second pin is just a remnant from the older LoRa series.

jgromes commented 5 years ago

Added in eddf96bbb2c31734141f93401fe814b68eddc88b, thanks for the suggestion!