|
| 1 | +--- |
| 2 | +title: Working with Event Queues |
| 3 | +description: This tutorial provides a quick introduction to using event queues in RIOT OS. |
| 4 | +code_folder: examples/guides/event_queue/ |
| 5 | +--- |
| 6 | + |
| 7 | +## Introduction |
| 8 | +An event queue is a FIFO (First In First Out) data structure that holds events |
| 9 | +to be processed by an event handler. |
| 10 | + |
| 11 | +Each event has exactly one callback function called the `handler`, |
| 12 | +that is executed when the event is processed. |
| 13 | +Posting an event to a queue triggers the execution of the event's handler |
| 14 | +on the thread that owns the event queue. |
| 15 | +Posting an event to a queue can be done from any thread or interrupt context. |
| 16 | + |
| 17 | +This guide will give a quick introduction to using event queues in RIOT OS. |
| 18 | +We will cover the following topics: |
| 19 | +- [Regular Events](#regular-events) |
| 20 | +- [Custom Events](#custom-events) |
| 21 | +- [Handling Events](#handling-events) |
| 22 | +- [Periodic Events](#periodic-events) |
| 23 | + |
| 24 | +:::note |
| 25 | +The whole source code for this guide can be found |
| 26 | +[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/event_queue). |
| 27 | + |
| 28 | +If your project is not working as expected, |
| 29 | +you can compare your code with the code in this repository to see if you missed anything. |
| 30 | +::: |
| 31 | + |
| 32 | +### Regular Events |
| 33 | +Creating and posting regular events is straightforward in RIOT. |
| 34 | +First, we define the event handler function: |
| 35 | +```c title="main.c" |
| 36 | +static void regular_handler(event_t *event) |
| 37 | +{ |
| 38 | + (void) event; |
| 39 | + printf("\tTriggered regular event.\n"); |
| 40 | +} |
| 41 | +``` |
| 42 | +Then, we can create an instance of the event: |
| 43 | +```c title="main.c" |
| 44 | +static event_t event = { .handler = regular_handler }; |
| 45 | +``` |
| 46 | +This event can now be posted to an event queue: |
| 47 | +```c title="main.c" |
| 48 | + event_post(&my_queue, &event); |
| 49 | +``` |
| 50 | +:::tip |
| 51 | +See [Handling Events](#handling-events) for information about setting up an event queue. |
| 52 | +::: |
| 53 | +### Custom Events |
| 54 | +Custom events allow us to pass additional data to the event handler. |
| 55 | +To create a custom event, we need to define a new struct that contains |
| 56 | +an `event_t` member and any additional data we want to pass. |
| 57 | +For example, let's create a custom event that passes a string message: |
| 58 | +```c time="main.c" |
| 59 | +typedef struct { |
| 60 | + event_t super; |
| 61 | + const char *text; |
| 62 | +} custom_event_t; |
| 63 | +``` |
| 64 | +Next, we define the event handler function for the custom event: |
| 65 | +```c title="main.c" |
| 66 | +static void custom_handler(event_t *event) |
| 67 | +{ |
| 68 | + /* The handler receives a pointer to the base event structure. |
| 69 | + * We need to get the pointer to our custom event structure. |
| 70 | + */ |
| 71 | + custom_event_t *custom_event = container_of(event, custom_event_t, super); |
| 72 | + printf("\tTriggered custom event with text: \"%s\".\n", custom_event->text); |
| 73 | +} |
| 74 | +``` |
| 75 | +Notice how we do not get passed the `custom_event_t` pointer directly, |
| 76 | +but rather the embedded `event_t` pointer. |
| 77 | +To access the additional fields of our custom event, we use the `container_of` |
| 78 | +macro to access the parent structure. |
| 79 | +
|
| 80 | +Then, we can create an instance of our custom event: |
| 81 | +```c title="main.c" |
| 82 | +static custom_event_t custom_event = { .super.handler = custom_handler, |
| 83 | + .text = "CUSTOM EVENT" }; |
| 84 | +``` |
| 85 | + |
| 86 | +Posting a custom event means posting the embedded `event_t` member: |
| 87 | +```c title="main.c" |
| 88 | + event_post(&my_queue, &custom_event.super); |
| 89 | +``` |
| 90 | +:::tip |
| 91 | +See [Handling Events](#handling-events) for information about setting up an event queue. |
| 92 | +::: |
| 93 | +### Handling Events |
| 94 | +It is common to have a dedicated thread for handling events. |
| 95 | +A simple thread function that processes incoming events looks like this: |
| 96 | +```c title="main.c" |
| 97 | +void *event_handler_thread(void *arg) |
| 98 | +{ |
| 99 | + assert(arg != NULL); |
| 100 | + event_queue_t *queue = (event_queue_t *)arg; |
| 101 | +
|
| 102 | + /* A thread must own an event queue to process its events. |
| 103 | + * Ownership is initially assigned to the thread that initializes the queue. |
| 104 | + * It can later be transferred by calling `event_queue_claim`. |
| 105 | + */ |
| 106 | + event_queue_init(queue); |
| 107 | +
|
| 108 | + puts("Event handler thread listening for events..."); |
| 109 | + event_loop(queue); |
| 110 | + return NULL; |
| 111 | +} |
| 112 | +``` |
| 113 | +:::note |
| 114 | +The thread calling `event_queue_init` becomes the owner of the event queue. Only the owner can |
| 115 | +process events from the queue. The ownership can be transferred to another thread using the |
| 116 | +`event_queue_claim` function. |
| 117 | +::: |
| 118 | +Once the thread is started and has initialized the event queue, we can post events to it |
| 119 | +from any other thread or interrupt context. |
| 120 | +The handler functions of the posted events will be executed on the event handler thread. |
| 121 | + |
| 122 | +Since using a dedicated thread for handling events is common, |
| 123 | +RIOT provides a module which sets up event queues and a handler thread for us. |
| 124 | + |
| 125 | +To use it, add the module to your application's Makefile: |
| 126 | +```makefile |
| 127 | +USEMODULE += event_thread |
| 128 | +``` |
| 129 | + |
| 130 | +Now we can post to the `EVENT_PRIO_HIGHEST`, `EVENT_PRIO_MEDIUM`, and `EVENT_PRIO_LOWEST` queues. |
| 131 | +A single thread will be automatically created that handles events posted to the queues |
| 132 | +according to their priority. |
| 133 | +Posting an event to one of these queues is as simple as: |
| 134 | +```c title="main.c" |
| 135 | + event_post(EVENT_PRIO_MEDIUM, &event); |
| 136 | +``` |
| 137 | +## Periodic Events |
| 138 | +Periodic events are events that are posted to an event queue at regular intervals. |
| 139 | +First we need to include the module in our application's Makefile: |
| 140 | +```makefile |
| 141 | +USEMODULE += event_periodic |
| 142 | +``` |
| 143 | + |
| 144 | +To create a periodic event, we need to declare and initialize a `event_periodic_t` structure. |
| 145 | +```c title="main.c" |
| 146 | + /* This initializes the periodic event. We set the timer it should use, |
| 147 | + * the queue it should post to, and the event it should post. |
| 148 | + */ |
| 149 | + event_periodic_init(&periodic_event, ZTIMER_SEC, EVENT_PRIO_MEDIUM, &event); |
| 150 | +``` |
| 151 | +Once the periodic event is initialized, we can start it: |
| 152 | +```c title="main.c" |
| 153 | + event_periodic_start(&periodic_event, 1); |
| 154 | +``` |
| 155 | +:::note |
| 156 | +A periodic event can be set to repeat only a certain number of times using the |
| 157 | +`event_periodic_set_count` function. |
| 158 | +::: |
0 commit comments