Deep sleep and RxDutyCycle on the CubeCell HTTC-AB01 v2

Hello all.

I’m working on a device based on the CubeCell HTCC-AB01 V2 that listens for a LoRa packets and acts upon them. Since it’s battery powered, I’m trying to use RxDutyCycle and deep sleep to maximize its battery life.

I have managed to implement a basic working proof of concept that turns on an LED after setting an RxDutyCycle and sleeping, which uses very low current, but it’s not perfect. I set up an interrupt on RADIO_DIO_1 to exit deep sleep whenever I receive a packet:

#include "Arduino.h"
#include "LoRa_APP.h"
#include "sx126x.h"
#define RF_FREQUENCY 868E6 // Hz
#define LORA_BANDWIDTH 0 
#define LORA_SPREADING_FACTOR 12 // [SF7..SF12]
#define LORA_CODINGRATE 1
#define LORA_PREAMBLE_LENGTH 64  // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 0    // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define LED_PIN GPIO4
typedef enum
{
  LOWPOWER,
  PACKET_RECEIVED,
  LED,
} States_t;

static RadioEvents_t RadioEvents;
States_t state;
void setup()
{
  Serial.begin(115200);
  // RADIO
  RadioEvents.RxDone = OnRxDone;
  Radio.Init(&RadioEvents);
  Radio.SetChannel(RF_FREQUENCY);
  Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
                    LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
                    LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON, 0, true,
                    0, 0, LORA_IQ_INVERSION_ON, true);

  // SX126xClearIrqStatus(IRQ_RADIO_ALL);
  SX126xSetDioIrqParams(IRQ_RX_DONE | IRQ_CRC_ERROR, IRQ_RX_DONE | IRQ_CRC_ERROR, IRQ_RADIO_NONE, IRQ_RADIO_NONE);
  pinMode(RADIO_DIO_1, INPUT);
  attachInterrupt(RADIO_DIO_1, handleDioIrq, RISING);

  // LED
  pinMode(LED_PIN, OUTPUT);

  state = LOWPOWER;
}

void loop()
{
  switch (state)
  {
  case PACKET_RECEIVED:
    Serial.write("RX......\r\n");
    state = LOWPOWER;
    Radio.OnDioIrq(); // Radio.IrqProcess doesn't work, since the flag IrqFired is not set
                                 // since the original interrupt is replaced
    break;
  case LOWPOWER:
    Serial.write("LOWPOWER......\r\n");
    StartRxDutyCycle();
    lowPowerHandler();
    break;
  case LED:
    Serial.write("LED starting......\r\n");
    digitalWrite(LED_PIN, HIGH);
    delay(1000);
    digitalWrite(LED_PIN, LOW);
    Serial.println("LED done......");
    state = LOWPOWER;
    break;
  }
}

void handleDioIrq(void)
{
  state = PACKET_RECEIVED;
}

void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
  // Serial.println("RX done......");
  // Serial.println((char *)payload);
  // Serial.printf("size: %d, rssi: %d, snr: %d\r\n", size, rssi, snr);
  state = LED;
}

void StartRxDutyCycle()
{
  int t_symbol = 1 << (LORA_SPREADING_FACTOR + 3);
  int t_preamble = (LORA_PREAMBLE_LENGTH + 4.25) * t_symbol;
  int t_header = 8 * t_symbol;
  int sleepPeriod = t_preamble - 8 * t_symbol;
  int rxPeriod = (t_preamble + t_header - sleepPeriod) / 2;
  int sleepTime = sleepPeriod / 15.625;
  int rxTime = rxPeriod / 15.625;
  Radio.Standby();
  Radio.SetRxDutyCycle(rxTime, sleepTime);
  Serial.printf("Duty cycle set...... %d, %d\r\n", rxTime, sleepTime);
  delay(5);
}

Had I not set that interrupt, it never exits low power mode. However, since I’m replacing the original interrupt handler (RadioOnDioIrq), the IrqFired (radio.c) flag is never set, so RadioIrqProcess exits early. I modified the library source code to export RadioOnDioIrq and call it manually, but this is less than ideal. I have tried looking up some examples, but I didn’t find any with both lowPowerHandler and RxDutyCycle.

Is there a way to accomplish what I’m trying to do without modifying the library code? I guess I could set everything up using the functions from sx126x.c, but I’d rather use the radio.c library. I’m not very experienced with either Arduino or LoRa, so any help is much appreciated.

Thank you.

2 Likes

Maybe check the following thread:


It doesn’t exactly address your question, but the discussion may help you to work out how to proceed.

2 Likes

Thanks. I think I figured it out:
My problem was that OnRxDone wasn’t being called when I was sleeping and an RxDutyCycle was set. I think I fixed it by adding the the following line before starting a cycle:

SX126xSetDioIrqParams(IRQ_RX_DONE | IRQ_CRC_ERROR, IRQ_RX_DONE | IRQ_CRC_ERROR, IRQ_RADIO_NONE, IRQ_RADIO_NONE);
Radio.SetRxDutyCycle(rxTime, sleepTime);

Now it properly calls the OnRxDone callback, even when inside the lowPowerHandler.

2 Likes