Last Updated on 15. Oktober 2026 by sfambachLink-Status: OK
Ein Task ist eine (wiederkehrende) Aufgabe für den Prozessor. Mehrere Tasks ringen um die Aufmerksamkeit der CPU. Wie diese erstellt werden und welche Datenstrukturen zur Datenübermittlung und Synchronisation vorhanden sind, wird in diesem Beitrag kurz umrissen.
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 verwiesen.
Grundsätzliches
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
Bibliotheken:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
Erstellt werden Tasks mit dem Befehl:
xTaskCreate(
<pointer zur Task Funktion,
<Task Name>,
<Speicher für Task>,
<Task parameter>,
<Priotität>,
<Referenz für Handle>);
Gute Programmierpraxis ist es, einen Task immer nur 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:
// speziellen Task anhalten
void vTaskSuspend(<TaskHandle>);
// Task aus interupt routine anhalten
BaseType_t xTaskResumeFromISR(<TaskHandle>)
// eigenen Task anhalten
vTaskSuspend(NULL);
In diesem Fall wartet der Task, bis er wieder von außen aktiviert wird. Dies geschieht mit dem folgenden Befehl:
// normale Freigabe
void xTaskResume(<TaskHandle>);
// aus Interrupt Routine
void xTaskResumeFromISR(<TaskHandle>);
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:
Quellcode 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.
- Semaphoren – Werden genutzt, um Tasks miteinander zu synchronisieren oder gemeinsam genutzte Ressourcen vor Datenfehlern zu schützen.
- Event Gruppen – 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 BeitragLink-Status: OK erstellt.
Werden diese Datenstrukturen aus Interruptroutinen heraus benutzt, müssen die gleichnamigen ISR Funktionen zum Aufruf verwendet werden.
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.
Bibliothek:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
Queue erstellen:
QueueHandle_t xQueue;
xQueue = xQueueCreate( <Länge Queue>, <größe der Fehler>);
if( xQueue == 0 )
{
// Fehler Erkennung
ESP_LOGE(TAG,"Error during queue creation");
}
Daten senden:
if(!xQueueSendToBack(<queue>, (void*)&<wert>, <timeout>)) {
// queue voll
ESP_LOGW(TAG,"q - Queue Full");
}
Daten empfangen:
if( ! xQueueReceive( <queu>, <value>, <timeout>) ){
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.
Quellcode 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 );
}
Semaphoren
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.
Bibliothek:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
Semaphor erstellen:
SemaphoreHandle_t xSemaphore = NULL;
xSemaphore = xSemaphoreCreateBinary();
if( xSemaphore == NULL ){
// Fehler
}
Vor der ersten Nutzung muss der Semaphor einmal freigegeben werden.
Semaphor holen:
if( xSemaphoreTake( <Semaphor Handle>, timeout) == pdTRUE )
{
//hat geklapp ...
xSemaphoreGive( <Semaphor Handle> );
} else {
// hat nicht geklappt
}
Freigeben des Semaphors:
xSemaphoreGive( <Semaphor Handle>);
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)activat this to dis-/en-able the semaphores
#define enable_semaphore
Quellcode 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
Bibliothek:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
Bit setzen:
EventBits_t xEventGroupSetBits(
<Eventgruppen Handle>,
<Bit welches gesetzt werden soll>
);
Bei der Wiederverwendung einer bestehenden Gruppe müssen die Bits zurückgesetzt werden.
EventBits_t xEventGroupClearBits(
<Gruppen Handle>,
<Bits zum Rücksetzten>
);
Warten auf gesetzte Bits, dies geschieht mit:
xEventGroupWaitBits(
<Gruppen Handle>,
<BIT_0 | BIT_1| ... | BIT_n>,
<Bit Löschen vor Rückgabe>,
<Ein- od. Alle Bits>,
<Wie lange soll auf die Bits gewartet werden>
);
// 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.
Quellcode 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
Bibliothek:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/stream_buffer.h"
Erstellen:
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(<MaxAnzBytes>, <TriggerLevel>)
Funktion zum Senden:
Die Funktion nimmt einen Streambuffer und die zu sendenden Daten auf. Der Rückgabewert liefert die Anzahl der gesendeten Daten zurück.
size_t xStreamBufferSend(
<StreamBuffer>,
<SendeDaten>,
<LängeDerSendeDaten>,
<Timeout>)
Empfangen:
size_t xStreamBufferReceive(
<Stream Buffer>,
( void * )&<Buffer für Empfangsdaten>,
<Göße Emfpangsbuffer>,
<Timeout> );
Beispielprogramm
Quellcode 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.
Bibliothek:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/message_buffer.h"
Erstellen:
MessageBufferHandle_t xMessageBufferCreate(
<size_t GreoesseInBytes>
);
Schreiben:
size_t xMessageBufferSend(
<BufferHandle>,
( void * ) <Nachricht>,
<laengeDerNachricht>,
<TimeOut>
);
Lesen einer Nachricht:
Vorab die Länge der nächsten Nachricht erfragen:
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.
Quellcode 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 Implementierung
- 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 Implementierung
- 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.
Bibliothek:
#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.
Quellcode 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));
}
}
Ringbuffer
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 ;).
Bibliothek:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
Fazit
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.
Verwandte Beiträge
HauptbeitragLink-Status: Kaputt
Quellen
IDF – Programming GuideLink-Status: OK
IDF – FreeRtosLink-Status: OK
IDF – FreeRtos Task ApiLink-Status: OK
IDF – Hooks Link-Status: OK
IDF Beispiel – Task Watchdog (GitHub)Link-Status: OK
Infos zu Ticks und Hooks im Espressif ForumLink-Status: OK
| Datum | Änderung | Autor |
|---|---|---|
| 15.10.2026 | Beispielcode als Code-Block eingefügt (Einbettung über gist-it.appspot.com funktionierte nicht mehr) | sfambach |
| 03.10.2026 | Rechtschreibung korrigiert: Kommas, Tippfehler, Groß-/Kleinschreibung, Grammatik Log-Liste eingefügt | sfambach |
| 08.01.2021 | Beitrag erstellt | sfambach |
Hallo Markus,
Danke für deinen Kommentar, es freut mich immer wieder wenn der Blog hilft.
Grusd
Stefan
Hallo 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