Call xTimerStop() too often cause "assert failed: prvProcessReceivedCommands timers.c:858 (xResult)" error and restart

We have several robots have wonky limit switches which always pull high. So I put some code that when the robot move away from the end stop(limit switch from triggered state to release), I start a timer and wait for the release of the switch(interrupt). If robot detects the release(interrupt), it stop the timer. I tested the code and it works fine.

But one day, I found another robot has a limit switch toggling the high/low frequently and it restart itself. With some digging I found if xTimerStop() is being called too often too fast would “assert failed: prvProcessReceivedCommands timers.c:858 (xResult)” error.

My code look likes this

main code start the robot from the end stop

CODE: SELECT ALL

void robot_start()
{
	xTimerStart( xTimerSenChk, 0 );//Set a timer and wait for sensor interrupt from other direction 
        starMotor(CLEANING_DUTY);
}

interrupt handler send an event through the event queue

CODE: SELECT ALL

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    sensorTriggerTime=xTaskGetTickCountFromISR();
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

task for receiving event queue and clear timer.

CODE: SELECT ALL

static void gpio_task_example(void* arg)
{                  
    uint32_t io_num;
    for(;;) {
        if(xQueueReceive(gpio_evt_queue, &io_num,  1000/ portTICK_PERIOD_MS)) { //portMAX_DELAY

            ESP_LOGD(TAG,"GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));            
            leftSensorStatus = gpio_get_level(GPIO_LEFT_SENSOR);
            rightSensorStatus = gpio_get_level(GPIO_RIGHT_SENSOR);

            ESP_LOGD(TAG,"leftSensorStatus: %d rightSensorStatus: %d", leftSensorStatus, rightSensorStatus);

            if(gpio_callback && ((sensorTriggerTime-LastSensorTriggerTime)*1000/configTICK_RATE_HZ>20)){
		
		    //code determine if it is the correct sensor released
		    //if it is the correct sensor we call xTimerStop() to stop the timer
		    //else timer callback we let us know the sensor is not released
		    if( correct sensor is released)
		    {
		    	xTimerStop();
		    }
		    
                }
            }
            else
            {
                ESP_LOGI(TAG, "sensor trigger interval <20ms, debouce.");
            }
         
            LastSensorTriggerTime=sensorTriggerTime;
        }
    }
}

I thought it might not proper to call xTimerStop() if the timer is already stopped so I modified the code a bit and check the xTimer is dormant or not before by using xTimerIsTimerActive(). The code looks like this:

CODE: SELECT ALL

 if( correct sensor is released)
 {
	if(xTimerIsTimerActive() != pdFALSE)//stop timer only when timer is active
	{
		xTimerStop();
	}
		    	
}

The problem seems gone after I did the modification. I also look the official document of xTimerStop() https://www.freertos.org/FreeRTOS-timer … rStop.html It does not mention I could not call it if the timer is already stopped.

So what is the correct practice for it. Thank you in advance for any advice.

enviroment:
esp-idf 4.4.5
FreeRTOS Kernel V10.4.3

I forgot the paste the error code.

assert failed: prvProcessReceivedCommands timers.c:858 (xResult)


Backtrace: 0x40081b4e:0x3ffbb5f0 0x40088cd9:0x3ffbb610 0x4008f52d:0x3ffbb630 0x4008be9a:0x3ffbb750
0x40081b4e: panic_abort at C:/Users/Sam/esp/esp-idf/components/esp_system/panic.c:408

0x40088cd9: esp_system_abort at C:/Users/Sam/esp/esp-idf/components/esp_system/esp_system.c:137

0x4008f52d: __assert_func at C:/Users/Sam/esp/esp-idf/components/newlib/assert.c:85

0x4008be9a: prvProcessReceivedCommands at C:/Users/Sam/esp/esp-idf/components/freertos/timers.c:858
 (inlined by) prvTimerTask at C:/Users/Sam/esp/esp-idf/components/freertos/timers.c:600

@policeman0077 It’s hard to say which assertion you are hitting without knowing the FreeRTOS kernel version in use.

Giving it a bit of a guess, it could be either the assertion on line 801 or line 884 of version 10.6.2

I believe the default tick rate on the ESP32 is 100Hz which would limit your timer resolution to 10ms. The second assert would trigger if you are setting the timer to a period of less than 10ms, it would trigger this assertion.

You might try increasing your RTOS tick rate to 1000hz by modifying configTICK_RATE_HZ in idf.py menuconfig

Hi @policeman0077,

In V10.4.3, the most relevant configASSERT( xResult ); seems to be on line 822. This assertion fails when there isn’t enough space in the timer task’s command queue to accept the command. Depending on your software design, you could increase the size of the timer’s command queue by increasing configTIMER_QUEUE_LENGTH. But in most cases, the best solution is to increase the task priority of the timer task above any task that makes Timer API calls. See configTIMER_TASK_PRIORITY.

By the way, yes you are allowed to call xTimerStop() on a timer that is already stopped. But don’t call any timer functions for a timer you have deleted with xTimerDelete().

Hi Paul,
Thank you for you reply. The kernel version is Kernel V10.4.3.
Timer I set is 5 seconds. Normally it works fine. Only when the robot call xTimerStop() too often this assertion would occur.

You are right. I just checked systick is 100hz.

Hi Jeff,
Thank you for your reply. I will have a look the configTIMER_QUEUE_LENGTH. I think you are right increase queue length might not be a good design. This problem only occurs when the sensor goes wrong and toggling very fast very often. For normal condition the default queue size should be enough.

For configTIMER_TASK_PRIORITY setting, do you mean if I set timer task to the highest priority even the timer task’s queue is full, the call will be processed or the queue will never be full?
For my situation with a broken sensor, would it cause the timer task be called too often and stuck in timer task?

With configTIMER_TASK_PRIORITY set higher than all your tasks that use the timer API, the timer task will get the CPU immediately every time you call a timer API function. So the queue will never be full.

In your case it sounds like that might conveniently meter the start/stop calls because the task monitoring the broken sensor would only be able to start and stop the timer after the timer task has completed the previous request.

1 Like

Thank you Jeff for your help.