Timer periodicity doesnt update after 'create'-ing again

Im reusing RAK_TIMER_0. Initially I create a oneshot timer, then make it periodic, the callback function changes but the timer still behaves as a oneshot timer.

api.system.timer.create(RAK_TIMER_0, reinterpret_cast<RAK_TIMER_HANDLER>(firstWork), RAK_TIMER_ONESHOT);
api.system.timer.start(RAK_TIMER_0, 1000);
// do some work for some time
api.system.sleep(10000);

// after a few seconds i want to reuse the timer in periodic mode
api.system.timer.stop(RAK_TIMER_0);
api.system.timer.create(RAK_TIMER_0, reinterpret_cast<RAK_TIMER_HANDLER>(secondWork), RAK_TIMER_PERIODIC);

// this way the timer triggers only once instead of periodically
api.system.timer.start(RAK_TIMER_0, 1000);

create and start return true, and it updates the callback function to secondWork but it’s still running in oneshot mode, ie secondWork only triggers once - and only if i calll api.system.timer.start(RAK_TIMER_0,1000); again

As far as I know that is not possible at the moment.
What is the return value of your second api.system.timer.create?
I am guessing it returns FALSE because the timer was already created.

it returns TRUE actually, or atleast it did for us

What is your RUI3 version? AT+VER=?

On RUI3 V4.2.0 it works:

Hi, we’re using version 4.1.1

What module are you using?
RAK3172, RAK4630 or RAK11720?

On RAK3172 it works with V4.1.1 as well:

Same for RAK4630:

Here is my test code:
RUI3-LowPower-Timer-Test.zip (8.6 KB)

I’m using the Rak11720, it doesnt seem to work for me, here’s the code im running:

{
  Serial.println("first");
}

void second(void*)
{
  Serial.println("second");
}

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

    api.system.timer.create(RAK_TIMER_1, reinterpret_cast<RAK_TIMER_HANDLER>(first), RAK_TIMER_ONESHOT);
    api.system.timer.start(RAK_TIMER_1, 1000, NULL);

    api.system.sleep.all(10000);
    // after a few seconds i want to reuse the timer in periodic mode
    api.system.timer.stop(RAK_TIMER_1);
    api.system.timer.create(RAK_TIMER_1, reinterpret_cast<RAK_TIMER_HANDLER>(second), RAK_TIMER_PERIODIC);

    // this way the timer triggers only once instead of periodically
    api.system.timer.start(RAK_TIMER_1, 1000, NULL);
}

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

Confirmed, the one module I didn’t test behaves different. Change timer from one-shot to recurring doesn’t work.

Opened a ticket with our R&D, but knowing their workload, it will take time to fix.

I guess the difference is that RAK11720 is running FreeRTOS under hood while the other two modules do not.

looks like the timer id once created is never deleted, and so create is essentially a one time only and the only things you can change really is the callback function for that timer id:

int32_t uhal_timer_create (TimerID_E timer_id, timer_handler tmr_handler, TimerMode_E mode) {
    TimerHandle_t apollo3_timer_id = get_apollo_timer_id(timer_id);

    uhal_timer_pdata[timer_id].timer_id = timer_id;
    uhal_timer_pdata[timer_id].timer_func = tmr_handler;

    if(apollo3_timer_id == NULL)
    {
        apollo3_timer_id = xTimerCreate("TMR",
                                  1,
                                  get_apollo_timer_mode(mode),
                                  NULL,
                                  uhal_timer_handler_dispatcher);
    }

    if (apollo3_timer_id != NULL) {
        set_apollo_timer_id(timer_id, apollo3_timer_id);
        return UDRV_RETURN_OK;
    } else {
        return -UDRV_INTERNAL_ERR;
    }
}

stop really just stops the timer and doesnt destroy the timer object:

int32_t uhal_timer_stop (TimerID_E timer_id) {
    TimerHandle_t apollo3_timer_id = get_apollo_timer_id(timer_id);

    if(apollo3_timer_id != NULL)
    {
        uhal_timer_pdata[timer_id].m_data = NULL;

        if( isInISR() ) 
        {
            BaseType_t xHigherPriorityTaskWoken = pdFALSE;
            if(pdPASS != xTimerStop(apollo3_timer_id, &xHigherPriorityTaskWoken))
            {
                return -UDRV_INTERNAL_ERR;
            }

            if(xHigherPriorityTaskWoken != pdFALSE)
            {
                portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
            }    
        }
        else
        {
            if (pdPASS != xTimerStop(apollo3_timer_id, OSTIMER_WAIT_FOR_QUEUE))
            {
                return -UDRV_INTERNAL_ERR;
            }
        }

        return UDRV_RETURN_OK;
    }

    return -UDRV_INTERNAL_ERR;
}

if there was a way to destroy it or if the api allowed us to use vTimerSetReloadMode id be able to change the timer mode

i guess i could try a hacky fix by calling vTimerSetReloadMode directly with the timer_id, just need to figure out how to get access to it in my application code

are you guys open to contributions?

Yes, we are open. You can start clone the repo, do the changes and then request a merge.
RAK-APOLLO3-RUI

For backward compatibility the destroy method sounds better, but I’d prefer the vTimerSetReloadMode, it seems cleaner.

Hey, looks like vTimerSetReloadMode is not part of the FreeRTOS base api, it’s in FreeRTOS+TCP. Looks like RAK uses base FreeRTOS so vTimerSetReloadMode cant be used. This also explains why it isn’t already included.

In the current state the timer would have to be destroyed and created again to be reused.

Adding a separate destroy method is going to propagate changes from uhal through udrv_timer_api all the way upto the RUI3 API so maybe that’s not the way to go

Destroying in stop is counter intuitive because you wont be able to run start on a destroyed timer

The best option I think, given current information, is to destroy the timer when creating if it already exists, reset apollo3_timer_id to NULL and proceed with creation of a new timer. What do you think?

Yes, that sounds like a good approach.

Thank you for your contribution.
Our team will check the changes and if no other problems are found it will be implemented.