RAK11720 RUI3 – LoRaWAN works but entering low power breaks uplink (TX timeout)

Hi there,

I am working with a RAK11720 using RUI3 and LoRaWAN OTAA (Class A, EU868).
I want to test if a RAK11720 could be suitable for a low power device and currenty my goal check this. I have coded a quick proof of concept by setup and sending a periodic uplink and keep the device in low power mode between transmissions.

I followed the official examples and best-practice documents:

  • RUI3 Low Power example
  • Smart Farm LoRaWAN example
  • Several forum discussions about low power usage

Based on those, I created a proof-of-concept application using api.system.timer for periodic uplinks.

Current behavior:

  • When I enable low power mode with api.system.lpm.set(1) and keep loop() empty, everything works correctly but there is no low power mode (about 1,6mA @ idle)
  • Uplinks are sent periodically and callbacks are received.
  • However, if I call api.system.sleep.all() or destroy the scheduler inside loop(), the uplink fails with RAK_LORAMAC_STATUS_TX_TIMEOUT.

This happens even though the uplink request is accepted and the device is already joined.

My understanding so far:
It seems that forcing sleep or destroying the scheduler interferes with the LoRaWAN MAC timing (RX1/RX2), and that RUI3 already enters low power automatically when idle if LPM is enabled. (consuption lows to 30uA aprox.)

Could someone please confirm:

  1. Is it correct that api.system.sleep.all() should NOT be used when LoRaWAN and timers are active?
  2. Is leaving loop() empty the correct approach for low power operation in RUI3?
  3. Are there any additional best practices to ensure lowest power consumption in this scenario?

Thank you very much for your time and support.

Best regards.

Code:

/****************************************************************************/
/***        Include files                                                 ***/
/****************************************************************************/

/****************************************************************************/
/***        Macro Definitions                                             ***/
/****************************************************************************/

#define OTAA_PERIOD   (60000)

#define OTAA_BAND     (RAK_REGION_EU868)
#define OTAA_DEVEUI   {0xac,0x1f,0x09,0xff,0xfe,0x0d,0xc1,0x4a}
#define OTAA_APPEUI   {0xac,0x1f,0x09,0xff,0xf8,0x68,0x17,0x22}
#define OTAA_APPKEY   {0xaa,0xb8,0x33,0xef,0x94,0x80,0xb7,0x91,0x3f,0xc9,0xf2,0x8e,0x27,0x80,0x89,0x97}

/****************************************************************************/
/***        Types                                                         ***/
/****************************************************************************/

/****************************************************************************/
/***        Global Variables                                            ***/
/****************************************************************************/

//OTAA Data:

// OTAA Device EUI (MSB first)
uint8_t node_device_eui[8] = OTAA_DEVEUI;
// OTAA Application EUI (MSB first)
uint8_t node_app_eui[8] = OTAA_APPEUI;
// OTAA Application Key (MSB first)
uint8_t node_app_key[16] = OTAA_APPKEY;

uint8_t collected_data[64] = { 0 };

///

void uplink_routine(void *);


/****************************************************************************/
/***        Callbacks                                                     ***/
/****************************************************************************/

/** Packet buffer for sending */

void recvCallback(SERVICE_LORA_RECEIVE_T *data) {

    if (data->BufferSize > 0) {
        Serial.println("Downlink received!");
        Serial.print("FPort: "); Serial.println(data->Port);
        Serial.print("Data: ");
        for (int i = 0; i < data->BufferSize; i++) {
            Serial.printf("%02X ", data->Buffer[i]);
        }
        Serial.println();
    } 
}

void joinCallback(int32_t status)
{
    Serial.printf("Join status: %d\r\n", status);
}

/*************************************
 * enum type for LoRa Event
    RAK_LORAMAC_STATUS_OK = 0,
    RAK_LORAMAC_STATUS_ERROR,
    RAK_LORAMAC_STATUS_TX_TIMEOUT,
    RAK_LORAMAC_STATUS_RX1_TIMEOUT,
    RAK_LORAMAC_STATUS_RX2_TIMEOUT,
    RAK_LORAMAC_STATUS_RX1_ERROR,
    RAK_LORAMAC_STATUS_RX2_ERROR,
    RAK_LORAMAC_STATUS_JOIN_FAIL,2
    RAK_LORAMAC_STATUS_DOWNLINK_REPEATED,
    RAK_LORAMAC_STATUS_TX_DR_PAYLOAD_SIZE_ERROR,
    RAK_LORAMAC_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS,
    RAK_LORAMAC_STATUS_ADDRESS_FAIL,
    RAK_LORAMAC_STATUS_MIC_FAIL,
    RAK_LORAMAC_STATUS_MULTICAST_FAIL,
    RAK_LORAMAC_STATUS_BEACON_LOCKED,
    RAK_LORAMAC_STATUS_BEACON_LOST,
    RAK_LORAMAC_STATUS_BEACON_NOT_FOUND,
 *************************************/

void sendCallback(int32_t status)
{

    switch (status) {
        case RAK_LORAMAC_STATUS_OK:
            Serial.println("Uplink success (ACK or downlink received)");
            break;

        case RAK_LORAMAC_STATUS_RX1_TIMEOUT:
        case RAK_LORAMAC_STATUS_RX2_TIMEOUT:
            Serial.printf("Uplink sent, no downlink (normal for unconfirmed) [%d]\n",status);
            break;

        default:
            Serial.printf("LoRaWAN error: %d\n", status);
            break;
    }

}

/****************************************************************************/
/***        Functions                                                     ***/
/****************************************************************************/


/****************************************************************************/
/***        Routines                                                      ***/
/****************************************************************************/

void uplink_routine(void *) {


    /** Payload of Uplink */
    uint8_t data_len = 4;
    memcpy(collected_data, "test", 4);
    
    bool confirmed = false;

    /** Send the data package */
    bool ok = api.lorawan.send(
        data_len,
        collected_data,
        confirmed,      // FPort
        false,      // confirmed
        1       // retry
    );
  

    Serial.printf("Send requested, confirmed=%d, result=%d\n", confirmed, ok);  

}

/****************************************************************************/
/***        Main functions                                               ***/
/****************************************************************************/

void setup() {

    Serial.begin(115200, RAK_CUSTOM_MODE);
    
    //pinMode(LED_GREEN, OUTPUT);
	//pinMode(LED_BLUE, OUTPUT);

	//digitalWrite(LED_GREEN, HIGH);

    delay(2000);
  
    Serial.println("RAKwireless LoRaWan OTAA Example");
    Serial.println("--------------------------------");
  
    if (!api.system.lpm.set(1)) {
        Serial.printf("LoRaWan Smart Farm - set low power mode is incorrect! \r\n");
        return;
    }
  
    if (!api.lorawan.nwm.set()) {
        Serial.printf("LoRaWan Smart Farm - set network working mode is incorrect! \r\n");
        return;
    }

    if (!api.lorawan.appeui.set(node_app_eui, 8)) {
        Serial.printf("LoRaWan OTAA - set application EUI is incorrect! \r\n");
        return;
    }

    if (!api.lorawan.appkey.set(node_app_key, 16)) {
        Serial.printf("LoRaWan OTAA - set applicati2on key is incorrect! \r\n");
        return;
    }

    if (!api.lorawan.deui.set(node_device_eui, 8)) {
        Serial.printf("LoRaWan OTAA - set device EUI is incorrect! \r\n");
        return;
    }

    if (!api.lorawan.band.set(OTAA_BAND)) {
        Serial.printf("LoRaWan OTAA - set band is incorrect! \r\n");
        return;
    }

    if (!api.lorawan.deviceClass.set(RAK_LORA_CLASS_A)) {
        Serial.printf("LoRaWan OTAA - set device class is incorrect! \r\n");
        return;
    }

    if (!api.lorawan.njm.set(RAK_LORA_OTAA)) {
        Serial.printf("LoRaWan OTAA - set network join mode is incorrect! \r\n");
        return;
    }

    api.lorawan.registerJoinCallback(joinCallback);
    Serial.printf("Joining ... \r\n");

    /** Wait for Join success */

    while (api.lorawan.njs.get() == 0) {

        Serial.print("Wait for LoRaWAN join...");
        
        // Join to Gateway
        if (!api.lorawan.join()) {
            Serial.printf("LoRaWan OTAA - join fail! - Retring\r\n");
        }

        delay(10000);

    }

    if (!api.lorawan.adr.set(true)) {
        Serial.printf("LoRaWan OTAA - set adaptive data rate is incorrect! \r\n");
        return;
    }

    if (!api.lorawan.rety.set(1)) {
        Serial.printf("LoRaWan OTAA - set retry times is incorrect! \r\n");
        return;
    }
 
    /** Check LoRaWan Status*/
    Serial.printf("Duty cycle is %s\r\n", api.lorawan.dcs.get()? "ON" : "OFF");	// Check Duty Cycle status
    Serial.printf("Packet is %s\r\n", api.lorawan.cfm.get()? "CONFIRMED" : "UNCONFIRMED");	// Check Confirm status
    uint8_t assigned_dev_addr[4] = { 0 };
    api.lorawan.daddr.get(assigned_dev_addr, 4);
    Serial.printf("Device Address is %02X%02X%02X%02X\r\n", assigned_dev_addr[0], assigned_dev_addr[1], assigned_dev_addr[2], assigned_dev_addr[3]);	// Check Device Address
    Serial.printf("Uplink period is %ums\r\n", OTAA_PERIOD);
    Serial.println("");

    api.lorawan.registerRecvCallback(recvCallback);
    api.lorawan.registerSendCallback(sendCallback);

    Serial.printf("Uplink DR: %d\n", api.lorawan.dr.get());
    Serial.printf("RX2 freq: %u Hz\n", api.lorawan.rx2fq.get());
    Serial.printf("RX2 DR: %d\n", api.lorawan.rx2dr.get());

    // Create a timer.
	api.system.timer.create(RAK_TIMER_0, uplink_routine, RAK_TIMER_PERIODIC);

    // Start a timer.
	api.system.timer.start(RAK_TIMER_0, OTAA_PERIOD, NULL);

}

void loop() {

    //api.system.sleep.all(OTAA_PERIOD);
    //api.system.scheduler.task.destroy();

}

Welcome to the forum @Javier

What firmware version are you using?
You can check with AT+VER=?

We recently released the latest RUI3 V4.2.3 which afaik has some bug fixes for the RAK11720, even if not listed in the change log.

Hi @beegee

Thank you for the welcome and your reply.

AT+VER=RUI_4.2.3_RAK11720

OK

There is no known bug in RUi3 regarding low power mode TX problems.

For your questions:

  • Is it correct that api.system.sleep.all() should NOT be used when LoRaWAN and timers are active?
  • Is leaving loop() empty the correct approach for low power operation in RUI3?
  • Are there any additional best practices to ensure lowest power consumption in this scenario?

It depends on your firmware design concept. I am using api.system.sleep.all() only in the loop(), enable low power mode in setup() and control the device with interrupts, callbacks and timers. This gives me the best low power results.

I cannot say whether this fits with your specific application.
My PoC’s that I share in RUI3-Best-Practices are all based on the above concept.

Using the RUI3-LowPower-Example I do not see any problem with uplinks and low power mode enabled.

Hello @beegee

I flashed your code with simila results. I am not sure if i am doing something wrong. I also have launched a fresh chirpstack instance and configured the device profile as follows:

This the output

RAKwireless RUI3 Node
------------------------------------------------------
Setup the device with WisToolBox or AT commands before using it
Version RUI3-Low-Power-V1.0.0
------------------------------------------------------
[AT_CMD] Got flag: AA
[AT_CMD] Got send interval: 0000EA60
[AT_CMD] Send interval found 60000
[SETUP] Confirmed disabled
[SETUP] Retry = 1
[SETUP] DR = 4
Current Work Mode: LoRaWAN.
WAKE_UP
OK
+EVT:JOINED
[JOIN-CB] LoRaWan OTAA - joined! 

SLEEP
[UPLINK] Start
[UPLINK] Sending packet # 1
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE
[UPLINK] Start
[UPLINK] Sending packet # 2
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE
[UPLINK] Start
[UPLINK] Sending packet # 3
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE
[UPLINK] Start
[UPLINK] Sending packet # 4
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE
[UPLINK] Start

As you may notice, there is a TX-CB notification with status 2 (TX_TIMEOUT) but a event +EVT:TX_DONE is also received.

I’m not sure if this relates to the confirmation and retry parameters. Any feedback would be appreciated.

Thanks.

TX status 2 means RAK_LORAMAC_STATUS_TX_TIMEOUT which is an error that comes directly from the LoRa transceiver.

I have not seen that error happening before.
Do you see the uplinks in the LNS device log?

I’ve done some tests in different scenarios with, apparently, the same result.

ChirpStack v4 Events:

TTN Events:

End device output:

RAKwireless RUI3 Node
------------------------------------------------------
Setup the device with WisToolBox or AT commands before using it
Version RUI3-Low-Power-V1.0.0
------------------------------------------------------
[AT_CMD] Got flag: AA
[AT_CMD] Got send interval: 0000EA60
[AT_CMD] Send interval found 60000
[SETUP] Confirmed disabled
[SETUP] Retry = 1
[SETUP] DR = 4
Current Work Mode: LoRaWAN.
WAKE_UP
OK
+EVT:JOINED
[JOIN-CB] LoRaWan OTAA - joined! 

SLEEP
[UPLINK] Start
[UPLINK] Sending packet # 1
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE
[UPLINK] Start
[UPLINK] Sending packet # 2
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE
[UPLINK] Start
[UPLINK] Sending packet # 3
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2


RAKwireless RUI3 Node
------------------------------------------------------
Setup the device with WisToolBox or AT commands before using it
Version RUI3-Low-Power-V1.0.0
------------------------------------------------------
[AT_CMD] Got flag: AA
[AT_CMD] Got send interval: 0000EA60
[AT_CMD] Send interval found 60000
[SETUP] Confirmed disabled
[SETUP] Retry = 1
[SETUP] DR = 4
Current Work Mode: LoRaWAN.
WAKE_UP
OK
+EVT:JOINED
[JOIN-CB] LoRaWan OTAA - joined! 

SLEEP
[UPLINK] Start
[UPLINK] Sending packet # 1
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE
[UPLINK] Start
[UPLINK] Sending packet # 2
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE
[UPLINK] Start
[UPLINK] Sending packet # 3
[UPLINK] Packet enqueued, size 4
[TX-CB] TX status 2
+EVT:TX_DONE

Thanks.