FreeRTOS Event Groups – Tasks Synchronization Example with Arduino

In this tutorial, we will learn to use Event groups. Events groups are also an important feature that is provided by FreeRTOS. Firstly, we will see an Introduction to Events groups that shows how and where to use it. After that, we will see a demo example with Arduino

Events Groups Introduction 

In the preceding tutorials, we have learned that FreeRTOS provides semaphores and mutex events. These events are implemented for task synchronization such as a task waiting in the blocked state for an event to occur, and unblocked the task when a particular event has occurred. Similarly, event groups are used to communicate event occurrence to tasks. In contrast to queues and semaphores, event groups permit synchronization of a task with one or more events. Further, it can be used to unblocked multiple tasks that are waiting for the event or multiple events. 

In other words, it can be used to synchronize multiple tasks by using a single event or multiple events. Additionally, it can also block a single task waiting for the multiple events to occur and unblock the task when the event set occurs. 

Why use Event Groups? 

As you know, embedded real-time applications are memory-constrained devices. The efficient use of microcontroller memory is required to host maximum features on a single microcontroller.  By using event groups, we can minimize the RAM use of RTOS based applications. Because instead of using multiple binary semaphores, we can simply replace them with a single event group. 

Note: To use the event groups feature of FreeRTOS, simply add event_groups.c library in your Arduino source file. 

How to use FreeRTOS event groups 

Event groups have mainly two important terms such as event flag and event bits. The event group is basically a set of event flags. The event flag is a boolean value which is either ‘0’ or ‘1’. These boolean values show the occurrence or not-occurrence of an event. In FreeRTOS, EventBits_t. Variable is used to store the state of the event flag. If the value of EventBits_t is 1 that means an event related to this specific bit has occurred. Otherwise, if EventBits_t = 0, the event has not occurred. 

The point here to note is that we can store the state of every flag with a  single bit only and we can store a set of flags inside a single variable which is an event group. As you can see in the figure below, each bit relates to a specific event flag. 

FreeRTOS event group example with Arduino

For example, if the event group value is 0x92 which is equivalent to 1001 0010 that means only event one, four and 7 has occurred. 

Set Event Group Size

Now the next thing is to define the number of flag bits inside a single event group. We can select by using configUSE_16_BIT_TICKS  configuration constant of  FreeRTOSConfig.h. 

  • If configUSE_16_BIT_TICKS = 1, the event group contains 8 flags.
  • Else if configUSE_16_BIT_TICKS = 0, the event group consists of 24 flags.

FreeRTOS Event groups API functions

Create Event Groups Example with Arduino

In this section, we will see an example to create an event group and unblock a task with the help of three events. The demo code is given below. In this arduino demo code, we create three input tasks and each input task makes the respective event flag active high or ‘1’. There is only one output task. This output task remains in the blocked state until all three event flags set to 1. In other words, all events have occurred. 

Arduin Code

// Include header files of FreeRTOS and event groups 
#include <Arduino_FreeRTOS.h>
#include "event_groups.h"

// define three event flag bit variable 
#define TASK1_BIT   (1UL << 0UL) // zero shift for bit0
#define TASK2_BIT   (1UL << 1UL) // 1 shift for flag  bit 1
#define TASK3_BIT   (1UL << 2UL) // 2 shift for flag bit 2

//  declare a event grounp handler variable 
EventGroupHandle_t  xEventGroup;

// A constant to define 500ms delay
//because we use to periodically execute each task
//after every 500ms 
const TickType_t xDelay500ms =  pdMS_TO_TICKS(500);

void setup()
{
   // intialize uart module with baud rate of 9600
   Serial.begin(9600);
   //create event group and assign it a earlier created referene handler
   xEventGroup  =  xEventGroupCreate();
   // Create three tasks which are used to release event with
   // the help of above defined event flags 
   xTaskCreate(InputTask1, "Input Task 1", 100,NULL,1,NULL);
   xTaskCreate(InputTask2, "Input Task 2", 100, NULL, 1, NULL );
   xTaskCreate(InputTask3, "Input Task 3", 100, NULL, 1,NULL);
   //Create output task that will execute only when all three events occured
   xTaskCreate(OutputTask, "Output Task", 100, NULL, 1, NULL);
   
}

// defintion of input task1 
void InputTask1(void *pvParameters)
{
  while(1)
  {
     // set flag bit TASK1_BIT
     xEventGroupSetBits(xEventGroup, TASK1_BIT);
     //delay this task for 500ms
     vTaskDelay(xDelay500ms);
  }
}

// defintion of input task2

void InputTask2(void *pvParameters)
{
  while(1)
  {
     // set flag bit TASK2_BIT
    xEventGroupSetBits(xEventGroup, TASK2_BIT);
     //delay this task for 500ms
    vTaskDelay(xDelay500ms);
  }
}

// defintion of input task3

void InputTask3(void *pvParameters)
{
  while(1)
  {
     // set flag bit TASK2_BIT
    xEventGroupSetBits(xEventGroup,TASK3_BIT);
    //delay this task for 500ms
    vTaskDelay(xDelay500ms);
  }
}

//  Definition of output task
void OutputTask(void *pvParameters)
{ 
  // define a variable which holds the state of events 
  const EventBits_t xBitsToWaitFor  = (TASK1_BIT | TASK2_BIT | TASK3_BIT);
  EventBits_t xEventGroupValue;
  while(1)
  {
    xEventGroupValue  = xEventGroupWaitBits(xEventGroup,
                                            xBitsToWaitFor,
                                            pdTRUE,
                                            pdTRUE,
                                            portMAX_DELAY
                                            );
   if((xEventGroupValue & TASK1_BIT) !=0)
   {
   Serial.println("Task1 event occured");
   }
   if((xEventGroupValue & TASK2_BIT !=0))
   {
   Serial.println("Task2 event occured");
   }
   if((xEventGroupValue & TASK3_BIT !=0))
   {
   Serial.println("Task3 event occured");
   }
  }
}
void loop(){}

Copy above code and upload it to Arduino.

Arduino Serial monitor Output

This figure shows the output of Arduino serial monitor. As expected, output task executes only on the occurence of all three events and output task display strings by comparing flag bit state.

FreeRTOS event groups example with arduino

fFurthermore, we can use FreeRTOS event groups for tasks Synchronization or a single task syncchronization with multiple events.

Other Articles:

Leave a Comment