Espressif IDF – Tasks

Last Updated on 15. October 2026 by SuffocationLink status: OK

A task is a (recurring) job for the processor. Multiple tasks compete for the CPU's attention. This article briefly outlines how they are created and what data structures are available for data transmission and synchronisation.

Tasks können zu einer sehr komplexen Angelegenheit werden, weshalb ich hier nur die Spitze des Eisberges ankratzen kann. Dennoch handelt es sich hierbei um den bisher längsten Beitrag der Serie. Für die weitere Recherche sei auch auf die Espressif Website – Kapitel FreeRtosLink status: OK referred.

Basics

Tasks können als gekapselte Aufgaben betrachtet werden. In der Espressif IDF können je nach Anzahl der Prozessor-Kerne ein oder mehrere Tasks parallel/gleichzeitig ausgeführt werden. Grundsätzlich können aber mehrere Tasks gestartet werden, die um den/die Prozessorkern(e) konkurrieren. Immer wenn ein Task den Prozessorkern nicht mehr braucht, gibt er ihn frei und der nächste Task kommt an die Reihe.

Welcher Task als Nächstes bevorzugt wird, hängt grundsätzlich mit seiner Priorität zusammen und wie lange er schon wartet. Wie genau die Abarbeitungsreihenfolge ist, hängt von der Implementierung des Schedulers ab. Die Tasks in der IDF stammen aus dem FreeRtos, hier wacht zusätzlich ein Wachhund (Watchdog) darüber, dass die Tasks nicht zu lange brauchen. Weiterhin wird davon ausgegangen, dass Tasks ewig laufen, sollte ein Task beendet werden, muss dieser vorher gelöscht werden, sonst kommt es zu einem Fehler.

Viele der im Folgenden beschriebenen Funktionen gibt es mehrfach. Zum einen kann bei der Erstellung bestimmt werden, ob der Speicher dynamisch oder statisch zur Kompilierzeit reserviert werden soll. Die statischen Funktionen enden immer mit static.

Zum anderen gibt es Funktionen, die aus einem Interrupt gerufen werden können, diese enden immer mit ‚FromISR‘.

Es wird im Folgenden meist nur auf die Funktionen mit dynamischer Speicherreservierung eingegangen, für weitere Infos sei auch hier auf die IDF Referenz verwiesen.

Task erstellen

Libraries

#include "freertos/FreeRTOS.h"
#include "freertos/task.h""

Tasks are created with the command:

xTaskCreate(
   <pointer zur="" task="" funktion,="" , 
   , 
   , 
   ,
   );;

Gute Programmierpraxis ist es, einen Task immer only so lange laufen zu lassen, wie wirklich nötig ist. Muss ein Task auf einen Wert oder eine Aktion warten, sollte er sich schlafen legen und den Prozessor an einen anderen Task weitergeben. Das Schlafen kann mit der folgenden Funktion eingeleitet werden.

void vTaskDelay(const TickType_t xTicksToDelay);

Mit diesem Befehl wartet der Task eine bestimmte Zeit, bevor er wieder bereit zur Ausführung ist. Generell schlafen legen kann man den Task mit dem folgenden Befehl:

// Suspend a specific task
void vTaskSuspend();
// Suspend a task from an interrupt service routine
BaseType_t xTaskResumeFromISR()
// Suspend your own task
vTaskSuspend(NULL);;

In diesem Fall wartet der Task, bis er wieder von außen aktiviert wird. Dies geschieht mit dem folgenden Befehl:

// normal resumption
void xTaskResume();
// from interrupt service routine
void xTaskResumeFromISR();;

FreeRtos-Tasks werden normal nicht beendet, wenn sie wie im Beispiel dennoch enden, wird ein Fehler geworfen. Um dies zu verhindern, muss der Task vor Ende gelöscht werden. Dies geschieht mit der Anweisung:

vTaskDelete( xHandles[task_index] );

Zu beachten ist: Wenn wie im Beispiel das xTaskDelete in seiner eigenen Task-Funktion aufgerufen wird, werden alle Anweisungen, die auf diesen Befehl folgen nicht mehr ausgeführt, weil der Task dann schon gelöscht ist.

Das folgende Beispiel erstellt mehrere Tasks, die unterschiedlich lange laufen und, wenn sie aktiv sind, eine Ausgabe erzeugen:

Source code main.c (GitHubLink status: OK)

/** idf example
 *
 * This example shows how to create tasks
 *
 * Some parts are copied from the official idf examples found on 
 * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/
 * 
 * Modified by SteFam @ http://www.fambach.net
 *
 * GPLv3 
 * Blah blah blah, free to use. 
 * I invested some time and money in those examples if you want to delete this header 
 * please do not forget to mention at least my website, thanks!
*/


#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_system.h"
#include "bootloader_random.h"


static const char* TAG = "L6_Task";

// event groupe things
static TaskHandle_t xHandles[5];
static EventGroupHandle_t xEventGroup;
static EventBits_t uxBits;

#define BIT_0    ( 1 << 0 )
#define BIT_1    ( 1 << 1 )
#define BIT_2    ( 1 << 2 )
#define BIT_3    ( 1 << 3 )
#define BIT_4    ( 1 << 4 )


/** tasks 
 *  Simply produce some output
*/
static void task(void* arg)
{
	// get back task index
    uint32_t task_index = (uint32_t*) arg;
	ESP_LOGI(TAG,"Sub Task %i started!",task_index+1);	
		
	// run at minimum 2 times 
	int count = 2 +  esp_random()%10;
			
	for(int i = 0; i < count ; i++){
				
		ESP_LOGD(TAG,"Task %i (run %i / %i )", task_index+1, i, count);
		
		// random delay 250 - 500 ms
		int delay = 250 +  esp_random()%250;
		
		vTaskDelay(delay);
	}
		
	// Do not forgett to stop the task.
	// If delete is missing an exception will be thrown
	ESP_LOGD(TAG,"Stopping %s ", pcTaskGetName(xHandles[task_index]));
	vTaskDelete( xHandles[task_index] );
	
	// you will never see this message, because task was deleted before!
	ESP_LOGI(TAG,"Sub Task %i finished!",task_index+1);
	
}


void app_main(void)
{
	ESP_LOGI(TAG,"Main Task started!");	
	//Allow other core to finish initialization
    vTaskDelay(pdMS_TO_TICKS(100));
	
	// activate random number genneration without wifi
	// deactivate before the use of wifi or I2C
	ESP_LOGD(TAG,"Init Random without RF");
	bootloader_random_enable();
				
	//* init switches and set interupt routines
	for(uint32_t i = 0; i <5;i++){
			
		char buf [20];
		sprintf(buf, "Task_%i",i+1);
			
		ESP_LOGD(TAG,"Creating %s", buf);
		
		//start gpio task to handle the buttons
		xTaskCreate(task, buf, 2048, i, 10, &xHandles[i]);
			
	}
	ESP_LOGI(TAG,"Main Task finihed!");	
}

Synchronisation

FreeRtos bietet Datenstrukturen zur Synchronisation und Übergabe der Daten von einem Task zum anderen. Folgende Hilfsmittel konnte ich finden:

  • Queues – Dies ist eine Liste. Hier wartet der lesende Task, bis ein Eintrag in der Liste vorhanden ist.
  • Traffic lights – Werden genutzt, um Tasks miteinander zu synchronisieren oder gemeinsam genutzte Ressourcen vor Datenfehlern zu schützen.
  • Event Groups – Werden zur Synchronisation von Tasks verwendet, sie warten auf das Setzen definierter Bits durch Tasks. Warten kann die Gruppe auf ein oder alle Bits. Sind die Bits gesetzt, wird das Warten der Gruppe beendet.
  • Stream Buffer – Datenübertragung per Stream. Der lesende Task wartet so lange, bis im Stream Daten vorhanden sind.
  • Message Buffer – Basiert auf dem Stream Buffer. Ein Task kann Nachrichten in den Buffer legen und ein anderer kann auf sie warten.
  • Hooks – Werden verwendet, um Tasks auszuführen, wenn der Prozessor gerade nichts zu tun hat.
  • Ring Buffer – Ein Ringbuffer ist eine Datenstruktur mit einem fixen Speicher. Wird das Ende des Speichers erreicht, wird versucht, im vorderen Bereich wieder Speicher zu finden. Da der Ringbuffer etwas komplexer ist, habe ich diesen erst mal außen vor gelassen.
  • Timer – Timer starten Tasks einmalig oder periodisch, für Timer habe ich einen eigenen PostLink status: OK created.

If these data structures are used from interrupt routines, the ISR functions with the same name must be used for the call.

Es wird bei den Datenstrukturen, außer bei den Semaphoren, immer davon ausgegangen, dass es nur einen Sende-Task und einen Empfänger-Task gibt. Wollen beispielsweise mehrere Ressourcen senden, muss die Sendefunktion als kritischer Bereich (z.B. mit Semaphor) geschützt werden.

Queues

Queues werden verwendet, um Daten auszutauschen. Der sendende Task übergibt Daten an die Queue und kann danach weiterlaufen. Der konsumierende Task holt sich die Daten, wenn er bereit ist.

Library

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

Create Queue:

QueueHandle_t xQueue; 
xQueue = xQueueCreate( , );
if (xQueue == 0)
{
// Error detection
  ESP_LOGE(TAG, "Error during queue creation");
}

Send data

if(!xQueueSendToBack(, (void*)&, )) {
   // queue full
   ESP_LOGW(TAG, "q - Queue Full");
}

Daten empfangen:

if( ! xQueueReceive( , , ) ){
  ESP_LOGW(TAG, "q - Queue empty!");
}

Das Beispiel unten arbeitet mit zwei Tasks. Einer füllt die Queue, der andere liest die Werte aus. Dadurch, dass der Lese-Task am Anfang langsamer als der Schreibe-Task ist, wird die Queue gefüllt, bis sie voll ist und keinen Wert mehr aufnehmen kann. Durch die folgende Beschleunigung des Lese-Tasks wird die Queue kürzer, bis sie leer ist und der Lese-Task auf den Schreibe-Task warten muss.

Source code main.c (GitHubLink status: OK)

/** idf example
 *
 * Tasks and queues
 *
 * Some parts are copied from the official idf examples found on 
 * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/
 * 
 * Modified by SteFam @ http://www.fambach.net
 *
 * GPLv3 
 * Blah blah blah, free to use. 
 * I invested some time and money in those examples if you want to delete this header 
 * please do not forget to mention at least my website, thanks!
*/


#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_system.h"
#include "bootloader_random.h"


static const char* TAG = "L6_Task_Queues";

// event groupe things
static TaskHandle_t writeTaskHandle;
static TaskHandle_t readTaskHandle;
static QueueHandle_t xQueue;
static const int queueSize = 5;

/**
 * Read from queue
*/
static void write_task (void* arg)
{
	const TickType_t xTicksToWait = ( TickType_t )0xff;
	
	// run forever like a highlander
	while(1)
	{
		// generate random value
		uint32_t value = esp_random()%100;
		
		// try to send something
		if(!xQueueSendToBack(xQueue, (void*)&value, xTicksToWait)){
			ESP_LOGW(TAG,"q - Queue Full");
			vTaskDelay(500); // wait longer if queue is full
		}else {
			ESP_LOGI(TAG,"w <- %i", value);
		}
		
		vTaskDelay(500);
	}
	
}



/** 
 *  Write to queue
*/
static void read_task(void* arg)
{
	const TickType_t xTicksToWait = ( TickType_t )0xff;
	uint32_t value;
	int i = 1200;
	// run forever like a highlander
	while(1)
	{
		// increase read speed 
		if(i > 100){ 
			i-=50;
		}
		
		// try to receive some data
		if( xQueueReceive( xQueue, &value, xTicksToWait ) ){
			ESP_LOGI(TAG,"r -> %i", value);
		} else {
			ESP_LOGW(TAG,"q - Queue empty!");
		}
		
		// wait a while 
		vTaskDelay(i);
	}
}

void app_main(void)
{
	
	// Create a queue of queueSize x uint32_t
	xQueue = xQueueCreate( queueSize, sizeof( uint32_t ) );
	if( xQueue == 0 )
	{
		ESP_LOGE(TAG,"Error during queue creation");
	}
	
	//Allow other core to finish initialization
    vTaskDelay(pdMS_TO_TICKS(100));
	
	ESP_LOGI(TAG,"Create write task");
	xTaskCreate(write_task, "Write Task", 2048, NULL , 10, &writeTaskHandle);
	configASSERT( writeTaskHandle );
	
	ESP_LOGI(TAG,"Create read task");
	xTaskCreate(read_task, "Read Task", 2048, NULL, 10, &readTaskHandle);
	configASSERT( readTaskHandle );
	
}

Traffic lights

Semaphoren und Muteces dienen dazu, Bereiche zu sichern und Threads zu synchronisieren. Ich beschreibe hier die Handhabung mit Hilfe des binären Semaphors. Muteces und rekursive Semaphore werden analog erstellt, haben aber noch zusätzliche Funktionalitäten.

Library

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h""

Create semaphore:

SemaphoreHandle_t xSemaphore = NULL;
xSemaphore = xSemaphoreCreateBinary();
if( xSemaphore == NULL ) {
   // Error
}

Vor der ersten Nutzung muss der Semaphor einmal freigegeben werden.

Fetch semaphore

if( xSemaphoreTake( , timeout) == pdTRUE )
 {
    // it worked ...
    xSemaphoreGive(  );
} else {
    // it didn't work
}

Freigeben des Semaphors:

xSemaphoreGive();;

Das Beispiel unten erstellt 5 Tasks. Alle möchten eine Variable hochzählen. Damit es zu keinen Datenfehlern kommt, ist der kritische Bereich (Hochzählen) mit einem Semaphor gesichert. Innerhalb dieses Bereiches wird den anderen Tasks kurz der Prozessor übergeben, sodass sie versuchen können, den Semaphor zu belegen. Es wird hier explizit der Wert der Variablen erst kopiert, dann den anderen Prozessen die Gelegenheit zur Änderung gegeben und dann erst der geänderte Wert zurückgeschrieben. Dies ist keine gute Praxis, hilft aber dem Beispiel ;). Ihr könnt den Semaphor herausnehmen, indem ihr das u.s. define ausdokumentiert. Die Tasks verhalten sich nun anders ;).

// (de)activate this to disable/enable the semaphores
#define enable_semaphore

Source code main.c (GitHubLink status: OK)

/** idf example
 *
 * This is an example for the use of semaphores
 * We have some tasks that tries to change the same resource
 * the source is shielded by the semaphore.
 *
 * Some parts are copied from the official idf examples found on 
 * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/
 * 
 * Modified by SteFam @ http://www.fambach.net
 *
 * GPLv3 
 * Blah blah blah, free to use. 
 * I invested some time and money in those examples if you want to delete this header 
 * please do not forget to mention at least my website, thanks!
*/


#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_system.h"
#include "bootloader_random.h"


static const char* TAG = "L6_Task";

// event groupe things
static TaskHandle_t xHandles[5];
SemaphoreHandle_t xSemaphore = NULL; 

static uint32_t specialResource = 0;

// (de)activat this to dis-/en-able the semaphores
#define enable_semaphore

/** tasks 
 *  Simply produce some output
*/
static void task(void* arg)
{
	// get back task index
    uint32_t task_index = (uint32_t*) arg;
		
	// run at minimum 2 times 
	int count = 2 +  esp_random()%10;
			
	for(int i = 0; i < count ; i++){
				
		ESP_LOGD(TAG,"Task %i, try to get the semapore",task_index+1);
		
#ifdef enable_semaphore
		while(! xSemaphoreTake( xSemaphore, 90)){
			ESP_LOGW(TAG,"Task %i, didn't get the semaphore",task_index+1);
		}
#endif
		
		ESP_LOGI(TAG,"Task %i, got the semophore",task_index+1);
		
		//local copy of resource 
		uint32_t localCopy = specialResource;
				
		// a short delay to give the other task the chance 
		// to get the semaphor and change the value ;)
		vTaskDelay(200);

		// simply increase the value
		specialResource = localCopy + 1; 

		ESP_LOGI(TAG,"Task %i, Value = %i -> Give semaphor back",task_index+1,specialResource);
		
#ifdef enable_semaphore		
		// give back the semaphore
		xSemaphoreGive( xSemaphore );
#endif		
		
		// random delay 250 - 500 ms
		int delay = 250 +  esp_random()%250;
		vTaskDelay(delay);
	}
	
	// Do not forgett to stop the task.
	// If delete is missing an exception will be thrown
	ESP_LOGI(TAG,"Stopping %s ", pcTaskGetName(xHandles[task_index]));
	vTaskDelete( xHandles[task_index] );
	
}


void app_main(void)
{
	
	//Allow other core to finish initialization
    vTaskDelay(pdMS_TO_TICKS(100));
	
	// activate random number genneration without wifi
	// deactivate before the use of wifi or I2C
	ESP_LOGI(TAG,"Init Random without RF");
	bootloader_random_enable();
				
				
	// init the semaphor
	xSemaphore = xSemaphoreCreateBinary();
	if( xSemaphore == NULL )
	{ 
		ESP_LOGE(TAG,"Error during creation of semaphor");
	} else {
		ESP_LOGI(TAG,"Semaphor created!");
	}
				
				
	//* init switches and set interupt routines
	for(uint32_t i = 0; i <5;i++){
			
		char buf [20];
		sprintf(buf, "Task_%i",i+1);
			
		ESP_LOGI(TAG,"Creating %s", buf);
		
		//start gpio task to handle the buttons
		xTaskCreate(task, buf, 2048, i, 10, &xHandles[i]);
			
	}
	
	// GO GO GO ...
	// before you can use the semaphor you have to release it once
	xSemaphoreGive( xSemaphore );	
}

Zu den rekursiven Muteces sei noch gesagt, diese können immer wieder vom Besitzer-Task belegt werden. Die Anzahl der Belegungen wird mitgezählt und muss sich auch in der Anzahl der Freigaben widerspiegeln. Diese können beispielsweise für Rekursionen verwendet werden, bei denen die gleiche Funktion immer wieder, indem sie sich selbst aufruft, auf die selben Daten zugreifen muss.

Event Groups

Library

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h""

Bit setzen:

EventBits_t xEventGroupSetBits(
                  ,
                  
);;

When reusing an existing group, the bits must be reset.

EventBits_t xEventGroupClearBits(
                   , 
                   
);;

Warten auf gesetzte Bits, dies geschieht mit:

xEventGroupWaitBits(
                    , 
                    ,  
                    ,
                    ,
                    
 ); 
// Auswertung 
if( xEventGroup == NULL )
 {
 // EVG konnte nicht erstellt werden
    ESP_LOGE(TAG,"Event groupe not created");
} else if(  (uxBits & ( BIT_0 | BIT_1| ... | BIT_n) ) == ( BIT_0 | BIT_1| BIT_2| ... | BIT_n ) )
 {
 // alle bits wurden gesetzt
        ESP_LOGI(TAG,"Program finshed successfull");
} else {
 // nicht alle Bits wurden gesetzt, timeout
       ESP_LOGW(TAG,"Timeout");
}

Hier ein komplettes Beispiel mit mehreren Tasks. Der Haupttask wartet darauf, dass alle Bits gesetzt werden. Sind alle vorhanden, arbeitet der Haupttask weiter.

Source code main.c (GitHubLink status: OK)

/** idf example
 *
 * This should be an example for tasks ... 
 * this example is not finished jet
 *
 * Some parts are copied from the official idf examples found on 
 * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/
 * 
 * Modified by SteFam @ http://www.fambach.net
 *
 * GPLv3 
 * Blah blah blah, free to use. 
 * I invested some time and money in those examples if you want to delete this header 
 * please do not forget to mention at least my website, thanks!
*/


#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_system.h"
#include "bootloader_random.h"


static const char* TAG = "L6_Task";

// event groupe things
static TaskHandle_t xHandles[5];
static EventGroupHandle_t xEventGroup;
static EventBits_t uxBits;

#define BIT_0    ( 1 << 0 )
#define BIT_1    ( 1 << 1 )
#define BIT_2    ( 1 << 2 )
#define BIT_3    ( 1 << 3 )
#define BIT_4    ( 1 << 4 )


/** tasks 
 *  Simply produce some output
*/
static void task(void* arg)
{
	// get back task index
    uint32_t task_index = (uint32_t*) arg;
		
	// run at minimum 2 times 
	int count = 2 +  esp_random()%10;
			
	for(int i = 0; i < count ; i++){
				
		ESP_LOGD(TAG,"Task %i (run %i / %i )", task_index+1, i, count);
		
		// random delay 250 - 500 ms
		int delay = 250 +  esp_random()%250;
		
		vTaskDelay(delay);
	}
	
	uxBits = xEventGroupSetBits(
                         xEventGroup,    // The event group being updated.
                        (1<<task_index)); // The bits being set.
	
	// Do not forgett to stop the task.
	// If delete is missing an exception will be thrown
	ESP_LOGD(TAG,"Stopping %s ", pcTaskGetName(xHandles[task_index]));
	vTaskDelete( xHandles[task_index] );
	
}


void app_main(void)
{
	
	//Allow other core to finish initialization
    vTaskDelay(pdMS_TO_TICKS(100));
	
	// activate random number genneration without wifi
	// deactivate before the use of wifi or I2C
	ESP_LOGD(TAG,"Init Random without RF");
	bootloader_random_enable();
				
	//* init switches and set interupt routines
	for(uint32_t i = 0; i <5;i++){
			
		char buf [20];
		sprintf(buf, "Task_%i",i+1);
			
		ESP_LOGD(TAG,"Creating %s", buf);
		
		//start gpio task to handle the buttons
		xTaskCreate(task, buf, 2048, i, 10, &xHandles[i]);
			
	}
		
	xEventGroup = xEventGroupCreate();
	if(xEventGroup == NULL){
		ESP_LOGE(TAG,"Can not create event group");
	}
	xEventGroupClearBits(xEventGroup, 0xff);	
	
	uxBits = xEventGroupWaitBits(
                 xEventGroup,    // The event group being tested.
                 BIT_0 | BIT_1| BIT_2| BIT_3 | BIT_4,  // The bits within the event group to wait for.
                 pdFALSE,         // BIT_0 ... and BIT_4 should be cleared before returning.
                 pdTRUE,         // wait for all bits
                 portMAX_DELAY ); // Wait a maximum of 100ms for either bit to be set.
	
	if( xEventGroup == NULL )
	{
		ESP_LOGE(TAG,"Event groupe not created");
	} else if(  (uxBits & ( BIT_0 | BIT_1| BIT_2| BIT_3 | BIT_4) ) == ( BIT_0 | BIT_1| BIT_2| BIT_3 | BIT_4 ) )
    {
        ESP_LOGI(TAG,"Program finshed successfull");
    } else {
        ESP_LOGW(TAG,"Timeout");
    }
	
}

Stream Buffer

Library

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/stream_buffer.h""

Create:

Triggerlevel und MaxAnzBytes sind beide vom Typ const size_t. Ersteres gibt an, wie groß der Bufferstream ist. Zweiteres beschreibt, ab wie viel Bytes der Empfänger aus seiner Wartehaltung erlöst wird.

xStreamBufferCreate(, )

Function to send:

The function takes a stream buffer and the data to be sent. The return value provides the number of bytes sent.

size_t xStreamBufferSend(
           , 
           , 
           , 
           )

Received:

size_t  xStreamBufferReceive( 
             ,
             ( void * )&,
             ,
             );;

Beispielprogramm

Source code main.c (GitHubLink status: OK)

/** idf example
 *
 * Tasks and Stream buffer Example
 * Two tasks, one is writing to the stream buffer and one is reading from stream buffer.
 * The writing task is faster than the reader. This has an build in errors, 
 * when the buffer is full the writer cannot write to the buffer and gets a timeout for writing. 
 * This errors can be prevented by increasing the buffer size or the timeout of the writer.
 *
 * Some parts are copied from the official idf examples found on 
 * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/
 * 
 * Modified by SteFam @ http://www.fambach.net
 *
 * GPLv3 
 * Blah blah blah, free to use. 
 * I invested some time and money in those examples if you want to delete this header 
 * please do not forget to mention at least my website, thanks!
*/


#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/stream_buffer.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_system.h"
#include "bootloader_random.h"


static const char* TAG = "L6_Task";

// event groupe things
static TaskHandle_t senderHandle;
static TaskHandle_t receiverHandle;
static StreamBufferHandle_t streamBuffer;
TickType_t x5s;

static const char sendString[40] = "Go hang a salami - I'm a Lasagna Hog!";


/** tasks 
 *  Simply produce some output
*/
static void send(void* arg)
{
	while (1) {
		size_t xBytesSent;

		xBytesSent =  xStreamBufferSend( streamBuffer, ( void * ) sendString , sizeof( sendString ), x5s );
		if(xBytesSent != sizeof( sendString )){
			ESP_LOGE(TAG, "Error while sending only sent %i o %i bytes",xBytesSent,sizeof( sendString ));
		} else {
			ESP_LOGI(TAG,"Send Bytes %i" ,xBytesSent);
		}
		
		// delay 100 - 500 ms					
		vTaskDelay(pdMS_TO_TICKS(100+esp_random()%400));				
	}
	// Do not forgett to stop the task.
	// If delete is missing an exception will be thrown
	ESP_LOGD(TAG,"Stopping sender");
	vTaskDelete( senderHandle);
	
}

static void receive(void* arg){
	
	char rxBuffer[40];
	while(1){
		size_t xReceivedBytes = xStreamBufferReceive( streamBuffer,
											( void * ) rxBuffer,
											sizeof( rxBuffer ),
											x5s );
											
		if( xReceivedBytes < 40){
			ESP_LOGI(TAG,"ERROR Received Bytes %i Content: %s" ,xReceivedBytes,rxBuffer);
		} else if(xReceivedBytes > 0 ){ 
			ESP_LOGI(TAG,"Received Bytes %i Content: %s" ,xReceivedBytes,rxBuffer);
		} else {
			ESP_LOGW(TAG,"No data received");
		}
	
		// delay 500 - 1000 ms
		vTaskDelay(pdMS_TO_TICKS(500+esp_random()%1000));
	}

	// Do not forgett to stop the task.
	// If delete is missing an exception will be thrown
	ESP_LOGD(TAG,"Stopping receiver ");
	vTaskDelete( receiverHandle);
}


void app_main(void)
{
	
	//Allow other core to finish initialization
    vTaskDelay(pdMS_TO_TICKS(100));
	x5s = pdMS_TO_TICKS( 500 );

	
	// activate random number genneration without wifi
	// deactivate before the use of wifi or I2C
	ESP_LOGD(TAG,"Init Random without RF");
	bootloader_random_enable();
	
	// create stream buffer
	ESP_LOGD(TAG,"Creating stream buffer!");
	
	// buffer can contain 4 bytes maximum, this is very small let's see if it works
	const size_t xStreamBufferSizeBytes = 40;
	// trigger receiver if 1 byte received (10 = 10 Bytes received)
	const size_t xTriggerLevel = 1;
	
	streamBuffer = xStreamBufferCreate( xStreamBufferSizeBytes, xTriggerLevel );

        if( streamBuffer == NULL )
        {
           ESP_LOGE(TAG,"FAILED to create stream buffer!");
        }
        else
        {
            ESP_LOGI(TAG,"Streambuffer created");
        }
	
	
	ESP_LOGD(TAG,"Creating receiver");
	xTaskCreate(receive, "Receiver", 2048, NULL, 10, &receiverHandle);
	configASSERT( receiverHandle );
	
				
	ESP_LOGD(TAG,"Creating sender");
	xTaskCreate(send, "Sender", 2048, NULL, 10, &senderHandle);	
	configASSERT( senderHandle );
		
			
	
			
		
	
}

Message Buffer

Der Messagebuffer baut auf dem Streambuffer auf. Zusätzlich können die Daten in Nachrichten gekapselt werden. Eine Nachricht wird durch ihre Länge definiert. Vor Empfang einer Nachricht kann die Länge vom Puffer erfragt werden.

Library

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/message_buffer.h""

Create:

xMessageBufferCreate(
    
);

Write

size_t xMessageBufferSend( 
             ,
             ( void * ) , 
             , 
             
);;

Reading a message:

First, I'll ask about the length of the next message:

size_t xMessageBufferNextLengthBytes(<BufferHandle>);

Für den eigentlichen Empfang wird die folgende Funktion verwendet

size_t xStreamBufferReceive( 
               <BufferHandle>,
               ( void * ) <EmpfangsBuffer>,
               <LaengeDerNachricht>,
	       <TimeOut>
);

So sieht der Gesamtcode aus. Dieser unterscheidet sich nur geringfügig vom Code des StreamBuffers.

Source code main.c (GitHubLink status: OK)

/** idf example
 *
 * Playing with tasks and message buffer.
 * This example uses two taks and a message buffer to transfer messages from one task to another in a safe manner.
 * Only one reader and one writer is allowed, in case of more than one writer the send function should be part of 
 * a critical ares safed by a semaphor or mutex
 *
 * Some parts are copied from the official idf examples found on 
 * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/
 * 
 * Modified by SteFam @ http://www.fambach.net
 *
 * GPLv3 
 * Blah blah blah, free to use. 
 * I invested some time and money in those examples if you want to delete this header 
 * please do not forget to mention at least my website, thanks!
*/


#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/message_buffer.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_system.h"
#include "bootloader_random.h"


static const char* TAG = "L6_Task";

// event groupe things
static TaskHandle_t senderHandle;
static TaskHandle_t receiverHandle;

uint16_t recDelay = 500;


#define MESSAGE_BUFFER_SIZE 1000
static MessageBufferHandle_t bufferHandle;
TickType_t timeOut;



// some messages ... 
static const char* sendString[12] = {
"Sore was I ere I saw Eros",
"A man, a plan, a canal -- Panama",
"Never a foot too far, even.",
"Euston saw I was not Sue.",
"Live on evasions? No, I save no evil.",
"Red Roses run no risk, sir, on nurses order.",
"Salisbury moor, sir, is roomy. Rub Silas.",
"Marge, let's went. I await news telegram.",
"A new order began, a more Roman age bred Rowena.",
"I, man, am regal; a German am I.",
"Tracy, no panic in a pony-cart.",
"Egad! Loretta has Adams as mad as a hatter. Old age!"};


/** tasks 
 *  Simply produce some output
*/
static void send(void* arg)
{
	while (1) {
		size_t xBytesSent;
		
		for(int i = 0 ; i < 12; i++){
			 
			xBytesSent =  xMessageBufferSend( bufferHandle, ( void * ) sendString[i], strlen( sendString[i]), timeOut );
			if(xBytesSent < 1){
				ESP_LOGE(TAG, "Error while sending only sent %i o %i bytes",xBytesSent,strlen( sendString[i] ));
				recDelay -= 50;
			} else {
				ESP_LOGI(TAG,"Send Bytes %i Content: %s" ,xBytesSent, sendString[i] );
			}
		
			// delay 100 - 500 ms					
			vTaskDelay(pdMS_TO_TICKS(100+esp_random()%400));				
		}
	}
	// Do not forgett to stop the task.
	// If delete is missing an exception will be thrown
	ESP_LOGD(TAG,"Stopping sender");
	vTaskDelete( senderHandle);
	
}

static void receive(void* arg){
	
	char rxBuffer[50];
	while(1){
		
		int length = xMessageBufferNextLengthBytes(bufferHandle);
		size_t xReceivedBytes = xMessageBufferReceive( bufferHandle,
											( void * ) rxBuffer,
											length,
											timeOut );
		
		// fix null termination		
		rxBuffer[length]='\0';
		
		if(xReceivedBytes > 0 ){ 
			ESP_LOGI(TAG,"Received Bytes %i Content: %s" ,xReceivedBytes,(char*)rxBuffer);
		} else {
			ESP_LOGW(TAG,"No data received");
		}
		
		// delay 500 - 1000 ms
		vTaskDelay(pdMS_TO_TICKS(recDelay+esp_random()%500));
	}

	// Do not forgett to stop the task.
	// If delete is missing an exception will be thrown
	ESP_LOGD(TAG,"Stopping receiver ");
	vTaskDelete( receiverHandle);
}


void app_main(void)
{
	//Allow other core to finish initialization
    vTaskDelay(pdMS_TO_TICKS(100));
	timeOut = pdMS_TO_TICKS( 500 );
	
	// activate random number genneration without wifi
	// deactivate before the use of wifi or I2C
	ESP_LOGD(TAG,"Init Random without RF");
	bootloader_random_enable();
	
	// create stream buffer
	ESP_LOGD(TAG,"Creating stream buffer!");

	bufferHandle = xMessageBufferCreate( MESSAGE_BUFFER_SIZE);

        if( bufferHandle == NULL )
        {
           ESP_LOGE(TAG,"FAILED to create buffer!");
        }
        else
        {
           ESP_LOGI(TAG,"Buffer created");
        }
	
	ESP_LOGD(TAG,"Creating receiver");
	xTaskCreate(receive, "Receiver", 2048, NULL, 10, &receiverHandle);
	configASSERT( receiverHandle );
	
				
	ESP_LOGD(TAG,"Creating sender");
	xTaskCreate(send, "Sender", 2048, NULL, 10, &senderHandle);	
	configASSERT( senderHandle );
	
}

Hooks

Es gibt Idle und Tick Hooks. Idle Hooks werden ausgelöst, wenn die CPU im Leerlauf ist. Tickhooks werden über den Tickinterrupt ausgelöst. Zweiteres soll zeitlich nicht sehr zuverlässig sein. Für zeitliche Abfolgen sollten eher Timer oder PWM Signale verwendet werden.

Weiterhin gibt es die Standard-Hooks des FreeRtos und eigene Varianten der IDF.

FreeRTOS Implementation

  • Zur Nutzung CONFIG_FREERTOS_LEGACY_HOOKSLink status: OK aktivieren
  • Es kann nur einen Idle und einen Tick Hook geben
  • FreeRtos Implementierung ist auf single core ausgelegt.

IDF Implementation

  • Es können mehrere Hooks/Ticks registriert werden.
  • Ein Hook/Tick muss seiner CPU zugewiesen werden
  • Die registrierten Hooks/Ticks werden rotierend ausgeführt.

Es dürfen niemals blockierende Codestellen in Hooks und Ticks verwendet werden. Diese müssen immer komplett durchlaufen werden und sollten möglichst kurz sein, da sie sonst andere Tasks behindern könnten.

In Ticks dürfen zusätzlich keine API-Funktionen ohne „FromIsr“ verwendet werden. Log-Ausgaben sind ebenfalls nicht möglich.

Library

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_freertos_hooks.h"

Idle Hook erstellen

esp_err_t esp_register_freertos_idle_hook_for_cpu(
    <Funktion für Callback>, 
    <CPU ID>
)
// oder 
esp_err_t esp_register_freertos_idle_hook_for_cpu(
    <Funktion für Callback>
)
 
// Funktionsprototyp
static bool hook1(void)
{}

Tick erstellen

esp_err_t esp_register_freertos_tick_hook(
     <tickFunctionName>
     <CPU ID>
);

// oder 
esp_err_t esp_register_freertos_tick_hook(
     <tickFunctionName>
);

// Funktionsprototyp
static void tick2(void)
{}

Fehler

  • ESP_OK: Hat geklappt
  • ESP_ERR_NO_MEM: Kein Speicher für den Prozessorkern, um einen Hook zu registrieren
  • ESP_ERR_INVALID_ARG: Ungültige CPU

CPU ID abfragen

// core des aktuellen Tasks
const uint32_t core_id = xPortGetCoreID(); 

// bei zwei Kernen den anderen bekommen 
const uint32_t core_id = !xPortGetCoreID(); 

Hier das Beispielprogramm.

Source code main.c (GitHubLink status: OK)

/** Example
 *
 * Play with tasks, ticks and hooks
 * This example ist the frist I wrote myself, 
 * because of the missing espressif examples
 * 
 * Created by SteFam @ http://www.fambach.net
 *
 * GPLv3 
 * Blah blah blah, free to use. 
 * I invested some time and money in those examples if you want to delete this header 
 * please do not forget to mention at least my website, thanks!
 *
 *
*/


#include <stdio.h>
#include <string.h>
#include <esp_err.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_freertos_hooks.h"
#include "esp_log.h"
#include "esp_system.h"
#include "bootloader_random.h"

static TaskHandle_t calcTask;
static TaskHandle_t tickTask1;
static TaskHandle_t tickTask2;
static const char* TAG = "L6_Task";


/******************** the hooks *************************/

/** Hook 1*/
static bool hook1(void)
{
	ESP_LOGI(TAG,"Hook 1");
	return true;
	
}

/** Hook 2*/
static bool hook2(void)
{
	ESP_LOGI(TAG,"Hook 2");
	return true;
	
}


/******************** the tick *************************/

// tick counters
static int ctick1 = 0;
static int ctick2 = 0;

/** the tick */
static void tick1(void)
{
	if(ctick1 %100 == 0){
		xTaskResumeFromISR(tickTask1);		
	}
	ctick1++;
}


static void tick2(void)
{
	if(ctick2 %100 == 0){
		xTaskResumeFromISR(tickTask2);
		
	}
	ctick2++;
}


static void tick_task(void* args){
	while(1){
		// suspend our selves
		vTaskSuspend(NULL);
		
		// print the tick to log.
		uint32_t i = (uint32_t) args;
		ESP_LOGI(TAG,"100x Tick %i",i);
	}
}

/******************** calc task *************************/
/**
 * Another calculation task
 * so some calculation to keep the cpu bussy
*/
static void calc_task (void* arg)
{
	while(1){
		uint32_t result = 10;
		for(uint32_t i = 0 ; i < 9000; i++){

			result += i+i*1;
			vTaskDelay(pdMS_TO_TICKS(1));	
		}
		ESP_LOGI(TAG,"2 - result generated");
		vTaskDelay(pdMS_TO_TICKS(esp_random()%100));
	}
}

/******************** main task *************************/

void app_main(void)
{
	
	bootloader_random_enable();
	
	// register two hooks
	ESP_ERROR_CHECK(esp_register_freertos_idle_hook(hook1));
	ESP_ERROR_CHECK(esp_register_freertos_idle_hook(hook2));
	
	// register two ticks
	ESP_ERROR_CHECK(esp_register_freertos_tick_hook(tick1));
	ESP_ERROR_CHECK(esp_register_freertos_tick_hook(tick2));
	
	// create tick tasks
	xTaskCreate(tick_task, "Tick Task1", 2048, 1 , 9000, &tickTask1);
	configASSERT( tickTask1 );
	xTaskCreate(tick_task, "Tick Task2", 2048, 2 , 9000, &tickTask2);
	configASSERT( tickTask2 );
	
	
	
	//ESP_LOGI(TAG,"Create calculation task");
	xTaskCreate(calc_task, "Calc Task", 3048, NULL , 9000000, &calcTask);
	configASSERT( calcTask );
	
	while(1){
		
		uint32_t result = 10;
		for(uint32_t i = 0 ; i < 9000000; i++){

			result += i+i*1;
			
			
		}
		ESP_LOGI(TAG,"1 - result generated");
		vTaskDelay(pdMS_TO_TICKS(esp_random()%100));
	}
}

Ring buffer

Der Ringbuffer der IDF ist eine „interessante“ Sache. Ich spare ihn mir an dieser Stelle mal für einen anderen Zeitpunkt auf, an dem ich ihn wirklich brauche ;).

Library

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

Conclusion

Jede Aktion in einen eigenen Task zu verpacken bedarf einiger Übung und Planung. Umso wichtiger ist es, die richtigen Bibliotheksfunktionen zur Verfügung zu haben. Ob diese Funktionalitäten ausreichend sind, muss sich in der Praxis zeigen. Grundsätzlich scheint aber alles, was man braucht, zur Verfügung zu stehen.

Related Posts

Main contributionLink status: Broken

Sources

IDF – Programming GuideLink status: OK

IDF – FreeRtosLink status: OK

IDF – FreeRtos Task ApiLink status: Unknown

IDF – Hooks Link status: Unknown

IDF Beispiel – Task Watchdog (GitHub)Link status: Unknown

Infos zu Ticks und Hooks im Espressif ForumLink status: Unknown

DateChangeAuthor
15.10.2026Beispielcode als Code-Block eingefügt (Einbettung über gist-it.appspot.com funktionierte nicht mehr)Suffocation
03.10.2026Rechtschreibung korrigiert: Kommas, Tippfehler, Groß-/Kleinschreibung, Grammatik
Log-Liste eingefügt
Suffocation
08.01.2021Post createdSuffocation
Changelog

2 thoughts on “Espressif IDF – Tasks”

  1. Hello Stefan,

    vielen Dank für die Mühe die du dir hier gibst! Dieser Blog zum Thema Tasks mit der IDF ist echt genial und hat mir super weiter geholfen.
    5 *****

    Gruß – Markus

Leave a Reply

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