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 keeploop()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 insideloop(), the uplink fails withRAK_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:
- 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?
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();
}



