ESP32 ESP-IDF FreeRTOS Timer and Delay using ESP-IDF

In this guide, we will show you how to use FreeRTOS timers and delays using ESP32 and ESP-IDF. This guide also includes a comparison of vTaskDelay() function provided by FreeRTOS with different delay values in milliseconds. The High Resolution Timer (ESP Timer) provided by FreeRTOS uses a 64 bit hardware timer. The FreeRTOS also provides software timers which are more convenient to use, however, they do come with some shortcomings. A low priority task is assigned for timer callbacks and the maximum resolution is set to the RTOS tick period. With the esp_timer, periodic timers are generated which have a resolution of microsecond time and have a range of 64 bit. Moreover, timer callbacks are issued from a high priority esp_timer task.

Before we move ahead, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.

ESP32 FreeRTOS Timer

As ESP-IDF is based on the FreeRTOS operating system, hence we will start by including the general FreeRTOS library. To use the timer functionality, we will include the “freertos/timers.h” header file.

#include "freertos/timers.h"

To obtain the time since boot, in microseconds, we use the function, esp_timer_get_time(). This function returns the number of microseconds since esp_timer was initialized which gets started before the app_main() function.

int64_t esp_timer_get_time(void)

Code to get Timer Time (time in microseconds since boot)

This is the complete code we are using to print the timer time in the serial monitor. The esp_timer_get_time() will give us the time in microseconds since the application started.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
 
void app_main()
{   
  printf("Timer: %lld μs\n", esp_timer_get_time()); 
}

The output shows that it took 169219 microseconds from the start of the application.

FreeRTOS get time

Get Time between Interrupts (portTick_PERIOD_MS / portTICK_RATE_MS)

The FreeRTOS operating system has different tick schedules. This is the time between the interrupts. Some of them are predefined like the ticks between interrupts. In FreeRTOS, it is defined as portTick_PERIOD_MS. Its older version was defined as portTICK_RATE_MS.

This is the complete code that we will use to print the portTick_PERIOD_MS and portTICK_RATE_MS in the serial terminal.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
 
void app_main()
{   
  printf("portTICK_PERIOD_MS :  %d\n", portTICK_PERIOD_MS); 
  printf("portTICK_RATE_MS : %d\n", portTICK_RATE_MS); 
}

The output shows that the portTick_PERIOD_MS and portTICK_RATE_MS is predefined as 10 milliseconds. It is suitable for 100 MHz rate set in the processor.

FreeRTOS port_TICK_PERIOD_MS

FreeRTOS Delay in Ticks

Next we will show you how to add delays using FreeRTOS. To generate a delay, we will use “vTaskDelay.” It takes in a parameter of type integer (uint32) which is the constant TickType depicting the ticks to delay. This number corresponds to the tick which is the frequency of the chip. In the function vTaskDelay(), the delay that we obtain is that of the particular task in the number of ticks.

vTaskDelay(1);

In the following program we will measure the timer before and after a delay of 1 tick. We will therefore be able to find the minimum difference for task delay.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
 
void app_main()
{   
  long long int Timer1 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer1);  
  vTaskDelay(1);
  long long int Timer2 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer2);  
  long long int diff = Timer2 - Timer1;
  printf("Difference: %lld μs\n", diff); 
}

In the output we can see the two timer values (time in microseconds since boot) that we obtained after a delay of 1 tick. The difference between them is 5383 microseconds. As we mentioned before, the tick time period is predefined as 10 milliseconds. However, it took less than 10 milliseconds.

FreeRTOS vTaskDelay in Ticks

FreeRTOS Delay in Milliseconds

In the previous example we looked upon vTaskDelay() function and measured the delay in ticks. However, we mostly use this function to generate delays in milliseconds.

To convert tick into milliseconds, we will divide number of milliseconds by portTICK_PERIOD_MS or portTICK_RATE_MS. The portTICK_PERIOD_MS is part of the #defines. It gets configured when we compile our sketch and by default is set to how fast the chip is running. So basically 1 divided by amount of ticks that the chip is running will give us the milliseconds.

vTaskDelay(1 /portTICK_PERIOD_MS);
vTaskDelay(1 /portTICK_RATE_MS);

Alternatively, we can also specify the argument of vTaskDelay() as pdMS_TO_TICKS() and specify the number of milliseconds in the bracket.

vTaskDelay(pdMS_TO_TICKS(1));

All these three commands should give us a delay of 1 milliseconds. However, the vTaskDelay() function is not very accurate for very small values of delay. Let us test this.

In the following code, we are obtaining the timer values after a delay of 1 milliseconds.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
 
void app_main()
{   
  long long int Timer1 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer1);  
  vTaskDelay(1/ portTICK_PERIOD_MS);
  long long int Timer2 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer2);  
  float diff = Timer2 - Timer1;
  printf("Difference: %f ms\n", diff/1000); 
}

As you can see in the output, the difference between the two Timer values is greater than 1 millisecond as the delay was too small to work perfectly. We were able to generate a delay approximately 1.64 milliseconds.

FreeRTOS vTaskDelay for 1 ms

Let us try increasing the delay to 1000 milliseconds. We will generate a delay of 1000 milliseconds using the following line of code:

  vTaskDelay(1000/ portTICK_PERIOD_MS);

This is the complete code that we are using:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
 
void app_main()
{   
  long long int Timer1 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer1);  
  vTaskDelay(1000/ portTICK_PERIOD_MS);
  long long int Timer2 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer2);  
  float diff = Timer2 - Timer1;
  printf("Difference: %f ms\n", diff/1000); 
}

Now as you can see in the output, the delay between the two Timer values is now very close to 1000 milliseconds. Hence the vTaskDelay() function works more accurately with slightly larger delays.

FreeRTOS vTaskDelay for 1000 ms

Comparison of vTaskDelay() in milliseconds

Now let us compare delay for 1, 10, 100 and 1000 milliseconds using the vTaskDelay() function. In the code below, we have used a similar program like the previous example. However, we have now increased the number of times we are printing the timer values with an increased delay. Hence, we will print the timer values after every 1 milliseconds, 10 milliseconds, 100 milliseconds and 1000 milliseconds. These will be used to compare how accurate the delay generation was.

This is the complete code that we will be using:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
 
void app_main()
{   
  printf("Timer for 1 millisecond\n");
  long long int Timer1 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer1);  
  vTaskDelay(1/ portTICK_PERIOD_MS);
  long long int Timer2 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer2);  
  float diff1 = Timer2 - Timer1;
  printf("Difference: %f ms\n", diff1/1000); 

  printf("\n");
  printf("Timer for 10 milliseconds\n");
  long long int Timer3 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer3);  
  vTaskDelay(10/ portTICK_PERIOD_MS);
  long long int Timer4 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer4);  
  float diff2 = Timer4 - Timer3;
  printf("Difference: %f ms\n", diff2/1000); 

  printf("\n");
  printf("Timer for 100 milliseconds\n");
  long long int Timer5 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer5);  
  vTaskDelay(100/ portTICK_PERIOD_MS);
  long long int Timer6 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer6);  
  float diff3 = Timer6 - Timer5;
  printf("Difference: %f ms\n", diff3/1000); 

  printf("\n");
  printf("Timer for 1000 milliseconds\n");
  long long int Timer7 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer7);  
  vTaskDelay(1000/ portTICK_PERIOD_MS);
  long long int Timer8 = esp_timer_get_time();
  printf("Timer: %lld μs\n", Timer8);  
  float diff4 = Timer8 - Timer7;
  printf("Difference: %f ms\n", diff4/1000); 
}

As you can see in the output, increasing the delay gives us a more accurate result as compared to smaller delay values. This was also proven in the previous example.

FreeRTOS vTaskDelay comparison in milliseconds

You may also like to read:

1 thought on “ESP32 ESP-IDF FreeRTOS Timer and Delay using ESP-IDF”

  1. Shouldn’t you get the two times back to back with the delay between, then print them both? Doesn’t the prints add an unknown delay between getting the first time and second time passed?

    {
    long long int Timer1 = esp_timer_get_time();
    vTaskDelay(1);
    long long int Timer2 = esp_timer_get_time();
    printf(“Timer: %lld μs\n”, Timer1);
    printf(“Timer: %lld μs\n”, Timer2);
    long long int diff = Timer2 – Timer1;
    printf(“Difference: %lld μs\n”, diff);
    }

    Reply

Leave a Comment