Timer Doesnt Wakeup Main Loop After Sleep

Hi, I’m trying to use the main loop to manage app events. I’m using the RAK11722 dev kit. The problem is that once I run api.system.sleep.all(), main loop never continues execution after that. The software timers dont seem to wakeup the main loop.

Here’s my code:

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"


QueueHandle_t xQueue = NULL;
QueueHandle_t xQueue2 = NULL;

typedef enum
{
    APP_STATE_INIT,
    APP_STATE_WAKE,
    APP_STATE_SLEEP
} app_states_t;

app_states_t app_state = APP_STATE_INIT;

void wake(void*)
{
    app_states_t temp_app_state = APP_STATE_WAKE;
    Serial.println("Wake - Scheduling Sleep in 5s");
    api.system.timer.start(RAK_TIMER_1, 5000, NULL);
    xQueueSend(xQueue, &temp_app_state, 0);
}

void sleep(void*)
{
    app_states_t temp_app_state = APP_STATE_SLEEP;
    xQueueSend(xQueue, &temp_app_state, 0);
}

void WakeupCallback()
{
    Serial.printf("This is Wakeup Callback\r\n");
}

void setup()
{
    // todo: Disable AT mode on production
    Serial.begin(115200);


    Serial.println("Demo Firmware");
    Serial.println("---------------------------------------");
    xQueue = xQueueCreate(10, sizeof(uint32_t));
    api.system.timer.create(RAK_TIMER_0, wake , RAK_TIMER_ONESHOT);
    api.system.timer.create(RAK_TIMER_1, sleep , RAK_TIMER_ONESHOT);
    api.system.timer.start(RAK_TIMER_0, 500, NULL);
      if ( api.system.sleep.registerWakeupCallback(WakeupCallback) == false )
    {
        Serial.println("Create Wakeup Callback failed.");
    }  
}

void loop()
{
    if (xQueueReceive(xQueue, &app_state, 0) == pdTRUE)
    {
        Serial.println("Message received");

        switch(app_state)
        {
              case APP_STATE_INIT:
              {
                  Serial.println("Init");
                  break;
              }
              case APP_STATE_SLEEP:
              {
                  api.system.timer.start(RAK_TIMER_0, 5000, NULL);
                  Serial.println("Sleep - Scheduling Wake in 5s");
                  api.system.sleep.all();
                  break;
              }
              case APP_STATE_WAKE:
              {
                  Serial.println("Wake");
                  break;
              }
        }

    }
    else
    {
        Serial.println("Message not received");
        api.system.sleep.all(500);
    }
}

I expect it to go to Sleep and then Wakeup after 5 seconds, but this doesnt happen. Am I misunderstanding how the sleep system works?

RUI3 documentation registerWakeupCallback

This registers a wakeup callback function. The wakeup is triggered by an GPIO pin that is setup with api.system.sleep.setup.

For the xQueue function I am not sure if you can use it from application level.

So you’re saying it’s possible the main loop doesnt continue execution after waking up because of xQueue?

I should’ve used code with better clarity:

bool bwake = false;
bool bsleep = false;

void wake(void*)
{
    Serial.println("Wake - Scheduling Sleep in 5s");
    bwake = true;
    bsleep = false;
    api.system.timer.start(RAK_TIMER_1, 5000, NULL);
}

void sleep(void*)
{
    Serial.println("Sleep - Scheduling Wake in 5s");
    api.system.timer.start(RAK_TIMER_0, 5000, NULL);
    bwake = false;
    bsleep = true;
}

void setup()
{
    Serial.begin(115200);

    Serial.println("Demo Firmware");
    Serial.println("---------------------------------------");
    api.system.timer.create(RAK_TIMER_0, wake , RAK_TIMER_ONESHOT);
    api.system.timer.create(RAK_TIMER_1, sleep , RAK_TIMER_ONESHOT);
    api.system.timer.start(RAK_TIMER_0, 500, NULL);
}

void loop()
{ 
    if (bwake)
    {
        Serial.println("Wake");
        api.system.sleep.all(500);
    }
    else if (bsleep)
    {
        api.system.sleep.all();
    }
    else 
    {
       api.system.sleep.all(500);
    }
}

This is a more straightforward version, I was under the assumption that timer events will wakeup the device, and hence continue the execution of the main loop?

But that is not happening. Is that only possible via a gpio interrupt?

Your app hangs for me after the xQueue = xQueueCreate(10, sizeof(uint32_t)); that’s why I think you can’t use FreeRTOS functions. There might be more steps required to use it. FreeRTOS functions are not in the official RUI3 documentation, because only the RAK11700 has it. RAK3172 and RAK4630 are bare bone SDK’s.

I am never using api.system.sleep function for waiting for events. I enable low power mode with api.system.lpm.set(0) and kill my loop or let it sleep forever. Then my app is waking on interrupts from GPIO’s or from timer events.

Why do you want to wake up loop from the timer? Why don’t you do you stuff inside the timer (if it is not too long).

void wake(void *)
{
	Serial.println("Wake - Scheduling Sleep in 5s");
	digitalWrite(LED_GREEN, LOW);
	digitalWrite(LED_BLUE, HIGH);

	time_t start_wait = millis();
	while ((millis() - start_wait) < 5000)
	{
		Serial.println("Doing some stuff for 5 seconds");
		digitalWrite(LED_GREEN, !digitalRead(LED_GREEN));
		digitalWrite(LED_BLUE, !digitalRead(LED_BLUE));
		delay(1000);
	}
	digitalWrite(LED_GREEN, LOW);
	digitalWrite(LED_BLUE, LOW);
	Serial.println("Start sleeping for 10 seconds");
	Serial.flush();
	api.system.timer.start(RAK_TIMER_0, 10000, NULL);
}

void setup()
{
	pinMode(LED_GREEN, OUTPUT);
	pinMode(LED_BLUE, OUTPUT);
	digitalWrite(LED_GREEN, LOW);
	digitalWrite(LED_BLUE, LOW);

	Serial.begin(115200);

	Serial.println("Demo Firmware");
	Serial.println("---------------------------------------");
	api.system.timer.create(RAK_TIMER_0, wake, RAK_TIMER_ONESHOT);
	api.system.timer.start(RAK_TIMER_0, 500, NULL);

	api.system.lpm.set(1);
}

void loop()
{
	api.system.sleep.all();
}

Log output:

Power consumption:

Interesting, it works for me but I’ll avoid it’s usage.

I’m already using the software timers for other purposes (multiple time dependent calculations, timer based actions like turning motors on and off and periodic actions), ran out of software timers.

My device will be put into various states (via downlinks) but some states are mutually exclusive to each other, it’s easier to handle them centrally. I was using the timers in this example to simulate certain events. This works except I dont have a way to turn off the loop while the app is in the idle state, and resume on some event.

I could shift things around to try and make it so that everything is handed in ISRs, downlink and timer callbacks but i think that would affect readability.