The basics

ESP-IDF uses FreeRTOS and this is a “tasks” based Operating System.

The FreeRTOS default tick rate is 100Hz (you can configure this, but 100 is a good tradeoff between ISR overhead and responsiveness).

This means portTICK_PERIOD_MS is 10 (10ms per tick).

If you use a vTaskDelay < 10mS, it is effectively a 0 tick delay, ie no delay at all. This is interpreted as “yield to higher priority tasks, otherwise keep running”. So any task which does this will starve lower priority tasks (like the FreeRTOS default IDLE task) from running.

So, a default ESP-IDF program needs to have any looping task in it idle for a minimum of 10mS. like this:

vTaskDelay(10 / portTICK_PERIOD_MS);

ESP-IDF creates a task for each CPU core (called “IDLE0”, and “IDLE 1” if there is a second core) whose sole purpose is to do nothing, ie idle. They are given the lowest possible priority that the framework allows, so that any other task created will always have a higher priority.

These idle tasks actually do a bit more than nothing, it is important they are allowed to run at least occasionally, as they do the following:

  • Reset the watchdog timeout that has been activated that IDLE# process.
  • Cleanup of deleted tasks

Note that the FreeRTOS timer task also runs at low priority.

Task breaks

If any task having a higher priority never takes a break (such as by calling vTaskDelay with a value >= portTICK_PERIOD_MS), then no task of lower priority (including the CPU core IDLE tasks), can “run”.

Changing the tick rate

You could set the tick rate to 1000Hz. FreeRTOS will spend more time in context switch interrupts, so you have a few less CPU cycles available for your program, but you’d be able to delay a task for >= 1ms.


Tasks in more depth

A RTOS application can be structured as a set of independent tasks

Only one task within the application can be executing at any point in time and the real time RTOS scheduler is responsible for deciding which task this should be.

The RTOS scheduler may repeatedly start and stop each task as the application executes. The scheduler ensures that the processor context (register values, stack contents, etc) when a task is swapped in is exactly that as when the same task was swapped out. To achieve this each task is has its own stack. When the task is swapped out the execution context is saved to its stack.

We benefit hugely from resources on the web so we decided we should try and give back some of our knowledge and resources to the community by opening up many of our company’s internal notes and libraries through mini sites like this. We hope you find the site helpful.
Please feel free to comment if you can add help to this page or point out issues and solutions you have found, but please note that we do not provide support on this site. If you need help with a problem please use one of the many online forums.


Your email address will not be published. Required fields are marked *