Skip to content

Commit 60b9f49

Browse files
committed
doc/guides/advanced_tutorials: added event queue guide
1 parent 4f7b172 commit 60b9f49

File tree

7 files changed

+304
-0
lines changed

7 files changed

+304
-0
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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+
:::

doc/starlight/astro.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export default defineConfig({
137137
"advanced_tutorials/creating_modules",
138138
"advanced_tutorials/device_drivers",
139139
"advanced_tutorials/porting_boards",
140+
"advanced_tutorials/event_queue",
140141
],
141142
},
142143
],

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,4 @@ Here is a quick overview of the examples available in the RIOT:
155155
| [saul](./guides/saul/README.md) | Teaches you how to interact with sensors and actuators through the SAUL interface. [SAUL](https://guide.riot-os.org/c_tutorials/saul/) tutorial |
156156
| [threads](./guides/threads/README.md) | Teaches you how to create and manage multiple execution threads in your RIOT application. [Threads](https://guide.riot-os.org/c_tutorials/threads/) tutorial |
157157
| [timers](./guides/timers/README.md) | Teaches you how to use timers for periodic tasks and time measurement in RIOT. [Timers](https://guide.riot-os.org/c_tutorials/timers/) tutorial |
158+
| [event_queue](./guides/event_queue/README.md) | Teaches you how to use the event queue. [Event Queue](https://guide.riot-os.org/advanced_tutorials/event_queue/) guide |
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# name of your application
2+
APPLICATION = event_queue_example
3+
4+
# Change this to your board if you want to build for a different board
5+
BOARD ?= native
6+
7+
# This has to be the absolute path to the RIOT base directory:
8+
# If you are following the tutorial, your RIOT base directory will
9+
# most likely be something like RIOTBASE ?= $(CURDIR)/RIOT
10+
# instead of this
11+
RIOTBASE ?= $(CURDIR)/../../..
12+
13+
# Add the e
14+
USEMODULE += event_callback
15+
USEMODULE += event_periodic
16+
USEMODULE += event_thread
17+
18+
USEMODULE += ztimer_sec
19+
20+
# Comment this out to disable code in RIOT that does safety checking
21+
# which is not needed in a production environment but helps in the
22+
# development process:
23+
DEVELHELP ?= 1
24+
25+
# Change this to 0 show compiler invocation lines by default:
26+
QUIET ?= 1
27+
28+
include $(RIOTBASE)/Makefile.include
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
BOARD_INSUFFICIENT_MEMORY := \
2+
arduino-duemilanove \
3+
arduino-nano \
4+
arduino-uno \
5+
atmega328p \
6+
atmega328p-xplained-mini \
7+
atmega8 \
8+
nucleo-f031k6 \
9+
nucleo-l011k4 \
10+
samd10-xmini \
11+
stm32f030f4-demo \
12+
#
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Event Queue Guide
2+
This guide demonstrates how to use the event queue system to handle events in a structured manner.
3+
Please refer to `doc/guides/advanced_tutorials/event_queue.md` for a more detailed explanation.

examples/guides/event_queue/main.c

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 TU Dresden
3+
* SPDX-License-Identifier: LGPL-2.1-only
4+
*/
5+
6+
#include <stdio.h>
7+
#include "event.h"
8+
#include "event/thread.h"
9+
#include "event/periodic.h"
10+
#include "ztimer.h"
11+
12+
/* A custom event structure that extends the base event structure
13+
* the custom event carries an additional text field.
14+
*/
15+
typedef struct {
16+
event_t super;
17+
const char *text;
18+
} custom_event_t;
19+
20+
/* Function to handle regular events */
21+
static void regular_handler(event_t *event)
22+
{
23+
(void) event;
24+
printf("\tTriggered regular event.\n");
25+
}
26+
27+
/* Function to handle custom events */
28+
static void custom_handler(event_t *event)
29+
{
30+
/* The handler receives a pointer to the base event structure.
31+
* We need to get the pointer to our custom event structure.
32+
*/
33+
custom_event_t *custom_event = container_of(event, custom_event_t, super);
34+
printf("\tTriggered custom event with text: \"%s\".\n", custom_event->text);
35+
}
36+
37+
/* Defining a regular event and setting its handler */
38+
static event_t event = { .handler = regular_handler };
39+
/* Defining a custom event and setting its handler and text */
40+
static custom_event_t custom_event = { .super.handler = custom_handler,
41+
.text = "CUSTOM EVENT" };
42+
/* Declaring a periodic event */
43+
static event_periodic_t periodic_event;
44+
/* Declaring an event queue */
45+
static event_queue_t my_queue;
46+
47+
/* This thread will handle events from the event queue */
48+
void *event_handler_thread(void *arg)
49+
{
50+
assert(arg != NULL);
51+
event_queue_t *queue = (event_queue_t *)arg;
52+
53+
/* A thread must own an event queue to process its events.
54+
* Ownership is initially assigned to the thread that initializes the queue.
55+
* It can later be transferred by calling `event_queue_claim`.
56+
*/
57+
event_queue_init(queue);
58+
59+
puts("Event handler thread listening for events...");
60+
event_loop(queue);
61+
return NULL;
62+
}
63+
64+
/* Stack for the event handler thread */
65+
char _event_handler_thread_stack[THREAD_STACKSIZE_MAIN];
66+
67+
int main(void)
68+
{
69+
70+
/* Starting the event handler thread */
71+
thread_create(_event_handler_thread_stack, sizeof(_event_handler_thread_stack),
72+
THREAD_PRIORITY_MAIN - 1, 0, event_handler_thread, &my_queue,
73+
"Event Handler Thread");
74+
75+
puts("Posting regular event...");
76+
/* Posting a regular event to the queue handled by our event handler thread */
77+
event_post(&my_queue, &event);
78+
ztimer_sleep(ZTIMER_SEC, 1);
79+
80+
puts("Posting custom event...");
81+
/* Posting a custom event to the queue handled by our event handler thread */
82+
event_post(&my_queue, &custom_event.super);
83+
ztimer_sleep(ZTIMER_SEC, 1);
84+
85+
puts("Posting regular event to the medium priority event thread...");
86+
/* Posting a regular event to medium priority queue.
87+
* This queue will initialized and handled by the `event_thread` module.
88+
*/
89+
event_post(EVENT_PRIO_MEDIUM, &event);
90+
ztimer_sleep(ZTIMER_SEC, 1);
91+
92+
puts("Starting periodic event that posts regular events every second...");
93+
/* This initializes the periodic event. We set the timer it should use,
94+
* the queue it should post to, and the event it should post.
95+
*/
96+
event_periodic_init(&periodic_event, ZTIMER_SEC, EVENT_PRIO_MEDIUM, &event);
97+
/* This starts the periodic event with a period of 1 second. */
98+
event_periodic_start(&periodic_event, 1);
99+
100+
return 0;
101+
}

0 commit comments

Comments
 (0)