1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-13 16:43:52 +01:00

doc/guides: Riot Tutorial

examples/guides: Introduce tutorial references

doc/guides/tutorial: Introduce code_folder for all tutorials
This commit is contained in:
AnnsAnn 2025-04-24 15:01:57 +02:00
parent a1816a7a7c
commit 8cdb2d9411
108 changed files with 1484 additions and 4 deletions

View File

@ -0,0 +1,191 @@
---
title: Creating a Project
description: This tutorial will guide you through creating a new project with a simple hello world program.
code_folder: examples/guides/creating_project/
---
import Contact from '@components/contact.astro';
import GitSetup from '@components/gitsetup.mdx';
Now that we have played around with the examples and have a basic understanding of how to use RIOT,
let's create a new project from scratch.
We will create a simple hello world program that will print "Hello World!" to the console.
Lets start with the basic git setup,
if you already have a git repository set up,
you can skip to the next section.
## Step 1: The Basics of Git and Submodules
<GitSetup />
## Step 2: Creating our hello world program
Now that we have added RIOT as a submodule to our project,
we can start writing our hello world program.
To do this, we create a new file called `main.c` in the `hello_world` directory.
You can use any text editor to create this file.
We will use Visual Studio Code in this example.
To open Visual Studio Code in the directory, you can use the following command:
```bash
code .
```
Now that Visual Studio Code is open,
we create a new file called `main.c` and add the following code:
```c title="hello_world/main.c"
/*
* For many printing related things, such as the puts function here
* we import stdio, depending on your board, platform or form of output
* it then includes the right definitions without the need to
* worry about the specific details.
*/
#include <stdio.h>
/*
* This is the main function of the program.
* It serves as the entry point for the program and gets called once your CPU is
* initialized.
*
* The function returns an integer value, which is the exit status
* of the program. A return value of 0 indicates that the program has finished
* successfully.
*/
int main(void) {
puts("Hello World!");
return 0;
}
```
![The hello world program in Visual Studio Code](img/create_project/03_main_c.png)
This program will print "Hello World!" to the console when it is run.
The `#include <stdio.h>` line includes the standard input/output library,
which allows us to use the `puts` function to print to the console.
## Step 3: Creating the Makefile
Now that we have created our hello world program,
we need to create a Makefile to build our program.
The Makefile is a build automation tool that allows us to define how our program should be built.
We create a new file called `Makefile` in the `hello_world` directory and add the following code:
```makefile title="hello_world/Makefile"
# name of your application
APPLICATION = hello-world
# Change this to your board if you want to build for a different board
BOARD ?= native
# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/RIOT
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include
```
![The Makefile in Visual Studio Code](img/create_project/04_makefile.png)
Congratulations! You have now created a new project with a simple hello world program.
In the next step, we will build and run our program just like we did in the "Getting Started" guide.
## Step 4: Building and running the program
<Contact />
To build our program, we use the following command:
```bash
BUILD_IN_DOCKER=1 make all
```
:::note
The `BUILD_IN_DOCKER=1` flag tells the build system to use the docker image provided by RIOT
to build our program.
This ensures that we have all the necessary dependencies to build our program.
If you have already built RIOT on your system,
you can omit this flag and the build system will use the toolchain installed on your system.
Do note that building in Docker will take quite a bit longer on the first run,
as it needs to download a fairly large Docker image.
Alteratively, you can simply run:
```bash
make all
```
:::
![Building the program](img/create_project/05_make.png)
After building the program,
we can run it using the following command to start the RIOT shell:
```bash
make term
```
:::note
Depending on your board, the hello world might appear before the RIOT shell starts.
If you want to see the output of the hello world program,
you can run `make flash term` instead,
which will flash the program to the board and then start the RIOT shell.
This does not guarantee that the hello world program will run before the RIOT shell starts
but should increase the chances of it appearing before the shell starts.
:::
If everything went well,
you should see our hello world program printing "Hello World!" to the console after a few seconds.
![The hello world program running in the RIOT shell](img/create_project/06_output.png)
Hooraay! You have successfully created a new project with a simple hello world program.
:::tip
Before you push your project to a git hosting service such as Github,
make sure to add a `.gitignore` file to your project to exclude
unnecessary files from being tracked by git.
For this project, a `.gitignore` file could look like this:
```bash title=".gitignore"
# Ignore build artifacts
bin/
*.bin
*.elf
*.hex
*.map
*.lst
*.o
*.d
*.a
*.out
```
:::
## Conclusion
In this tutorial, we have created a new project with a simple hello world program.
We have added RIOT as a submodule to our project, created a hello world program,
and built and run the program using the RIOT build system.
You can now start building your own applications using RIOT
and explore the vast possibilities that RIOT has to offer.
:::note
The source code for this tutorial can be found
[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/creating_project).
If your project is not working as expected,
you can compare your code with the code in this repository to see if you missed anything.
:::

View File

@ -0,0 +1,312 @@
---
title: GPIO & Real Boards
description: This tutorial explains how to use GPIO in RIOT to control LEDs or read button presses.
code_folder: examples/guides/gpio/
---
import Contact from '@components/contact.astro';
import WorkingVideo from './img/gpio/05_working_video.mp4';
import ButtonVideo from './img/gpio/08_buttons.mp4';
So far we have been running all our code using the `arduino-feather-nrf52840-sense` board.
In this tutorial, we will learn how to use the GPIO pins on a real board
to control LEDs or read button presses.
For this tutorial we will be using a the `arduino-feather-nrf52840-sense` board
with the [Teamagochi PCB](https://github.com/smartuni/teamagochi),
but you can use any board that has GPIO pins.
## Step 1: Configuring our Board
<Contact />
First, we need to inform RIOT about the board we are using.
To do this we adapt the `BOARD` variable in our `Makefile` to the board we are using.
```make
BOARD ?= arduino-feather-nrf52840-sense
```
:::tip
If you ever want to just quickly test something
on a board without having to change the `Makefile`,
you can also set the `BOARD` variable in the terminal before running `make`.
```bash
BOARD=arduino-feather-nrf52840-sense make all
```
![The board variable in Visual Studio Code](img/gpio/01_define_board.png)
First we want to make sure that we can build the code for the board.
To do this we can simply run `make all` in the terminal,
this will build the code for the board and check if everything is set up correctly.
If everything is set up correctly,
we should now be able to move on to flashing the board.
:::tip
It might return something like:
```
/bin/sh: line 1: arm-none-eabi-gcc: command not found
Compiler arm-none-eabi-gcc is required but not found in PATH. Aborting.
```
This means that you need to install the ARM toolchain,
you can do this by running `sudo apt install gcc-arm-none-eabi` on Ubuntu
or `sudo pacman -S arm-none-eabi-gcc` on Arch Linux.
If it still doesn't work,
consider running it via the Docker container using `BUILD_IN_DOCKER=1 make flash`.
Please refer to the [Flashing a RIOT Application](/getting-started/flashing)
guide for more information.
:::
Try flashing the board with `make flash` and see if it works,
it should still execute the same code as before when running RIOT natively on your
own hardware but now using the actual board.
![Flash output in Visual Studio Code](img/gpio/02_flash_output.png)
Now if we type `make term` we should see the output of the board in the terminal.
Make sure that you have the board connected to your computer via USB and
that your user has the necessary permissions to access the serial port.
:::tip
On Arch Linux, you might need to add your user to the `uucp` group
to access the serial port.
```bash
sudo usermod -a -G uucp $USER
```
:::
## Step 2: Controlling LEDs
Now that we have the board working, let's try to control the LEDs on the board.
The exact pins that control the LEDs might vary depending on the board you are using,
but in the case of the `arduino-feather-nrf52840-sense` board,
the LED would be connected on Port 1 Pin 9.
You can often find these defines either in the board schematics or in the board
configuration headers in RIOT.
First we need to include the necessary modules in our projects `Makefile`.
```make
# Add the gpio module to the build
USEMODULE += periph_gpio
USEMODULE += periph_gpio_irq
# Enable the milliseconds timer.
USEMODULE += ztimer
USEMODULE += ztimer_msec
```
![The Makefile with the GPIO modules](img/gpio/03_gpio_modules.png)
This allows us to both control the GPIO pins and timers.
Specifically, we need the `periph_gpio` module to control the GPIO pins,
`periph_gpio_irq` to handle GPIO interrupts,
and `ztimer` to use the timer functionality for blinking the LED.
In this case, we also need the `ztimer_msec` module to use the millisecond timer.
Now we need to actually define the pin that we want to control in our code.
To do this include the following lines **before** the `main` function.
{/*<!--skip ci-->*/}
```c
#include "board.h"
#include "periph/gpio.h"
#include "ztimer.h"
/* Define the LED0 pin and mode */
gpio_t led0 = GPIO_PIN(1, 9);
gpio_mode_t led0_mode = GPIO_OUT;
```
Now we can control the LED. First we initialize the pin
and afterwards we turn the LED off by clearing the pin.
{/*<!--skip ci-->*/}
```c
int main(void) {
/* Initialize the LED0 pin */
gpio_init(led0, led0_mode);
/* Turn off the LED0 pin */
gpio_clear(led0);
/* Loop forever */
while (1) {
}
}
```
Turning the LED off when the board starts is quite boring,
so let's make it blink by adding a delay and toggling the LED.
```c
/* Loop forever */
while (1) {
/* Toggle the LED0 pin every 500 milliseconds */
gpio_toggle(led0);
ztimer_sleep(ZTIMER_MSEC, 500);
}
```
![The Code in Visual Studio Code](img/gpio/04_code.png)
If we now `make flash` and then `make term` we should see the LED blinking.
<video controls>
<source src={WorkingVideo} type="video/mp4" />
Your browser does not support the video tag.
</video>
## Step 3: Reading Button Presses
If you remember what we did in the timers tutorial,
we can use quite similar code to read button presses.
On a constrained device you usually don't want to poll the button state,
which is why we will use an interrupt to detect the button press,
that way we can drastically reduce the power consumption of the device.
First we need to define the callback function
that will be called when the button is pressed.
```c title="Define the button callback function"
/* This callback function will be called when the button state changes */
void button_callback(void *arg) {
/* the argument is not used */
(void)arg;
/* Toggle the LED1 pin based on the button state */
if (gpio_read(button)) {
gpio_clear(led1);
} else {
gpio_set(led1);
}
}
```
Now we need to define the button and led1 pin and mode and initialize it.
```c {1-6, 27-33}
/* Define the LED1 pin and mode */
gpio_t led1 = GPIO_PIN(1, 10);
gpio_mode_t led1_mode = GPIO_OUT;
/* Define the button pin */
gpio_t button = GPIO_PIN(1, 2);
/* This callback function will be called when the button state changes */
void button_callback(void *arg) {
/* the argument is not used */
(void)arg;
/* Toggle the LED1 pin based on the button state */
if (gpio_read(button)) {
gpio_clear(led1);
} else {
gpio_set(led1);
}
}
int main(void) {
/* Initialize the LED0 pin */
gpio_init(led0, led0_mode);
/* Turn off the LED0 pin */
gpio_clear(led0);
/* Initialize the LED1 pin */
gpio_init(led1, led1_mode);
/* Turn off the LED1 pin */
gpio_clear(led1);
/* Initialize the button pin */
gpio_init_int(button, GPIO_IN_PU, GPIO_BOTH, button_callback, NULL);
/* Loop forever */
while (1) {
/* Toggle the LED0 pin every 500 milliseconds */
gpio_toggle(led0);
ztimer_sleep(ZTIMER_MSEC, 500);
}
}
```
This code will initialize the button pin and call the `button_callback`
function whenever the button is pressed.
![Full Code in Visual Studio Code](img/gpio/07_full_code.png)
If we now `make flash` and then `make term` we should see the LED turn on
when the button is pressed.
:::tip
If you are using the `arduino-feather-nrf52840-sense` board,
the button is on the back of the board.
Be careful to not press the reset button by mistake 😉
:::
<video controls>
<source src={ButtonVideo} type="video/mp4" />
Your browser does not support the video tag.
</video>
## Extra: Board specific defines
Most boards (if they have LEDs or buttons) will have
specific defines for the GPIO pins that are used.
For example, the `arduino-feather-nrf52840-sense` board has the `LED0_TOGGLE`
define that allows you to toggle the LED without having to define the pin yourself.
In most cases using gpio like we did in this tutorial is the best way to go,
it's more flexible, documented and standardized for all boards.
However, using these defines can often be easier at the start,
esp. when the GPIO pins are not documented well or the board support is not complete yet.
Using this define, we can simplify our code to the following:
{/*<!--skip ci-->*/}
```c title="Using the LED0_TOGGLE define"
int main(void) {
/* Initialize the LED0 pin */
gpio_init(led0, led0_mode);
/* Turn off the LED0 pin */
gpio_clear(led0);
/* Loop forever */
while (1) {
/* Toggle the LED0 pin every 500 milliseconds */
LED0_TOGGLE;
ztimer_sleep(ZTIMER_MSEC, 500);
}
}
```
## Conclusion
In this tutorial we learned how to use the GPIO pins on a real board to
control LEDs and read button presses.
This is a very basic example, but it should give you a good starting point
to build more complex applications.
<Contact />
:::note
The source code for this tutorial can be found
[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/gpio).
If your project is not working as expected,
you can compare your code with the code in this repository to see if you missed anything.
:::

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -0,0 +1,174 @@
---
title: Sensors/Actuators using SAUL
description: Learn how to use sensors and actuators with RIOT
code_folder: examples/guides/saul/
---
In the previous chapter we learned how to interact with the GPIOs directly,
but RIOT provides a more abstract way to interact with sensors and actuators.
RIOT calls this the SAUL (Sensors/Actuators Abstraction Layer) system.
The availability of sensors and actuators can vary greatly between different boards,
so the SAUL system provides a way to interact with them in a uniform way,
regardless of the underlying hardware.
So consulting the [documentation](https://doc.riot-os.org/group__drivers__saul.html)
is always a good idea.
## The Makefile
First we need to include the necessary module in the `Makefile`, to do this
add the following line to the `Makefile`:
```makefile
USEMODULE += saul
USEMODULE += saul_default
```
![Makefile in VSCode](img/saul/01_makefile.png)
## Including the Headers
Next we need to include the necessary headers in our `main.c` file.
Add the following lines to the top of the file:
```c
#include <stdio.h>
#include "board.h"
#include "saul_reg.h"
#include "ztimer.h"
```
We need:
- `stdio.h` for the `printf` function,
- `board.h` for the board specific configuration,
- `ztimer.h` for the ztimer module so we can sleep for a while,
- and `saul_reg.h` for the SAUL registry and related functions.
![main.c in VSCode](img/saul/02_main_c.png)
## Registering a Sensor
To create a SAUL registry entry RIOT provides a function called `saul_reg_find_type`
which searches for the first device on our board that matches the description we provide.
In this example we will register a temperature sensor, as such we need to simply tell it to
search for `SAUL_SENSE_TEMP` devices.
```c
/* Define our temperature sensor */
saul_reg_t *temperature_sensor = saul_reg_find_type(SAUL_SENSE_TEMP);
```
This doesn't actually guarantee that the sensor is available, which is why we also need to
check if the sensor truly exists. To do this we create a simple if statement that checks
whether the result of the function was `NULL` or not.
```c
/* Exit if we can't find a temperature sensor */
if (!temperature_sensor) {
puts("No temperature sensor found");
return 1;
} else {
/*
* Otherwise print the name of the temperature sensor
* and continue the program
*/
printf("Temperature sensor found: %s\n", temperature_sensor->name);
}
```
![Code to register sensor in VSCode](img/saul/03_register.png)
Congratulations, by this point your program should be able to find
a temperature sensor on your board.
## Reading the Sensor
Here is where SAUL really shines,
to read the sensor we simply call the `saul_reg_read` function
which then stores the result in a `phydat_t` struct we provide.
```c
/* We start an infinite loop to continuously read the temperature */
while (1) {
/* Define a variable to store the temperature */
phydat_t temperature;
/*
* Read the temperature sensor
* and store the result in the temperature variable
* saul_reg_read returns the dimension of the data read (1 in this case)
*/
int dimension = saul_reg_read(temperature_sensor, &temperature);
```
Once again, since C doesn't have exceptions,
we need to check if the sensor was read correctly.
In this case we simply need to check if the dimension is greater than 0.
```c
/* If the read was successful (1+ Dimensions), print the temperature */
if (dimension <= 0) {
puts("Error reading temperature sensor");
return 1;
}
```
Now all that is left is to print the temperature to the console and go to sleep.
RIOT provides a simple function to solve this problem,
`phydat_dump` which prints the data in a `phydat_t` struct to the console.
```c
/* Dump the temperature to the console */
phydat_dump(&temperature, dimension);
/* Sleep for 1 seconds */
ztimer_sleep(ZTIMER_MSEC, 1000);
```
![Code to read sensor in VSCode](img/saul/04_read.png)
## Building and Running the Program
As always, we need to build and run our program.
To do this we use the following commands:
```bash
make flash
```
and then to see the output:
```bash
make term
```
If everything went well,
you should see the temperature being printed to the console every second like this:
```log
2024-10-14 15:31:29,610 # Data: 24.50 °C
2024-10-14 15:31:30,134 # Data: 24.50 °C
2024-10-14 15:31:31,134 # Data: 24.50 °C
2024-10-14 15:31:32,134 # Data: 24.50 °C
2024-10-14 15:31:33,135 # Data: 24.50 °C
2024-10-14 15:31:34,135 # Data: 24.50 °C
2024-10-14 15:31:35,136 # Data: 24.50 °C
```
## Conclusion
Congratulations! You have now learned how to use the SAUL system
to interact with sensors and actuators in RIOT
and how to read the temperature from a temperature sensor. 🎉
:::note
The source code for this tutorial can be found
[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/saul).
If your project is not working as expected, you can compare your code
with the code in this repository to see if you missed anything.
:::

View File

@ -0,0 +1,133 @@
---
title: Shell Commands
description: This tutorial explains how to use the RIOT shell.
code_folder: examples/guides/shell/
---
The shell is a powerful tool in RIOT that allows you to interact
with your application at runtime.
You can use the shell to inspect the state of your application,
change configuration parameters, and even run tests.
In this tutorial, we will take a look at how to use the shell in RIOT
and how to create custom shell commands.
## Step 1: Using the Shell
We need to include the shell module in our application to use the shell.
As we did in the previous tutorials, we will have to go into the `Makefile`
of our application and add the following line:
```make
USEMODULE += shell
```
This line tells the build system to include the shell module in our application.
Now we can use the shell in our application.
![The Makefile in Visual Studio Code](img/shell/01_makefile.png)
Now we can start the shell in our main function by calling the `shell_init` function.
Go into your `main.c` file and include the following code:
```c title="The include so we can use the shell"
#include "shell.h"
```
```c title="The main function"
int main(void) {
/* Buffer to store command line input */
char buffer[SHELL_DEFAULT_BUFSIZE];
/* Start the shell */
shell_run(NULL, buffer, SHELL_DEFAULT_BUFSIZE);
/* Return 0 to indicate that the program has finished successfully */
return 0;
}
```
There are two importants things to note here:
First, we have to provide a buffer to the shell,
the shell will then use this buffer to store your input.
For this example,
we use the `SHELL_DEFAULT_BUFSIZE` macro to define the size of the buffer
but you can use any size you want (as long as it is big enough to store your input).
The second thing to note is that we pass `NULL` as the first argument to the `shell_run` function.
This argument is used to pass a list of shell commands to the shell,
but we will take a look at that in the next step.
Now we can build and run our program.
Compile the program and flash it to your board using `make flash`.
If you look into the terminal via `make term` you should see the shell prompt.
You can now type shell commands and interact with your application.
For example, you can type `help` to get a list of available commands
... which is not very exciting at the moment because we don't have any custom commands.
![The shell prompt](img/shell/02_shell.png)
## Step 2: Creating Custom Shell Commands
Creating custom shell commands is quite simple.
You just have to define a function that implements the command and register it with the shell.
RIOT provides a set of macros to make this process easier.
Let's create a simple shell command that echoes back the input.
Go into your `main.c` file and include the following code:
```c title="The include so we can print to the console"
#include <stdio.h>
```
```c title="The function that implements the echo command"
/* Our command we want to register to the shell */
int echo_command(int argc, char **argv) {
/* We take the arguments passed to the command */
for (int i = 1; i < argc; i++) {
/* We print each argument followed by a space */
printf("%s ", argv[i]);
}
/* Finally, we print a newline character to end the line */
printf("\n");
/* Return 0 to indicate success */
return 0;
}
```
This function takes two arguments:
`argc` and `argv`. `argc` is the number of arguments passed to the command
and `argv` is an array of strings containing the arguments.
Now all that is left is to register the command with the shell.
Go back to your `main.c` file and include the following code _outside_ of the `main` function:
```c title="Register the command with the shell"
/* This little macro registers the command so we can use it in the shell */
SHELL_COMMAND(echo, "Echo a message", echo_command);
```
This macro takes three arguments: the name of the command,
a short description of the command, and the function that implements the command.
And that's it! You have created a custom shell command.
Now you can build and run your program again and type
`echo hello world` in the shell to see the command in action.
![The echo command in action](img/shell/03_echo.png)
## Conclusion
In this tutorial, we have learned how to use the shell in RIOT
and how to create custom shell commands.
:::note
The source code for this tutorial can be found
[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/shell).
If your project is not working as expected,
you can compare your code with the code in this repository to see if you missed anything.
:::

View File

@ -0,0 +1,115 @@
---
title: Threads
description: This tutorial explains how to use threads in RIOT.
code_folder: examples/guides/threads/
---
import Contact from '@components/contact.astro';
Threads allow you to run multiple tasks concurrently in your application.
## Step 1: Creating a Thread Function
To create a thread in RIOT,
you need to define a function that implements the thread and then start the thread.
The signature of the thread function should be `void *thread_function_name(void *arg)`.
The argument `arg` is a pointer to any data that you want to pass to the thread.
Let's create a simple thread that prints a message to the console.
Go into your `main.c` file and add the following code:
```c
void *my_first_thread(void *arg) {
/* The argument we receive will not be used */
(void)arg;
/* We print a simple message from the thread */
puts("Hello, from the thread!");
/* We return NULL to indicate that the thread has finished */
return NULL;
}
```
![The thread function in Visual Studio Code](img/threads/01_thread_function.png)
This function takes a single argument,
a pointer to any data, and prints "Hello, from the thread!" to the console.
Now all that is left is to start the thread.
:::caution
You need to specify `void *arg` even if you don't use it,
otherwise the compiler will throw an error.
:::
## Step 2: Starting the Thread
To start a thread we need two things:
a stack for the thread and a call to `thread_create`.
First, we need to define a stack for the thread.
The stack size should be at least `THREAD_STACKSIZE_MAIN` bytes.
You can define the stack as a global variable
or as a local variable in your `main` function.
The stack size can vary depending on the complexity of your thread.
For example, depending of the amount of local variables you use,
the stack size might need to be larger.
Lets define the stack as a global variable:
```c
char my_thread_stack[THREAD_STACKSIZE_MAIN];
```
![The thread stack in Visual Studio Code](img/threads/02_stack.png)
Lastly, we need to actually start the thread.
Go into your `main` function and include the following code:
```c
int main(void) {
/* Create a thread with the stack we defined above */
thread_create(my_thread_stack, sizeof(my_thread_stack),
THREAD_PRIORITY_MAIN - 1, 0, my_first_thread, NULL,
"My first thread");
/* The main thread can continue doing its work (e.g., printing a message) */
puts("Hello, from the main thread!");
}
```
`thread_creates` takes the following arguments:
- `my_thread_stack`: The stack for the thread
- `sizeof(my_thread_stack)`: The size of the stack
- `THREAD_PRIORITY_MAIN - 1`: The priority of the thread
- `0`: The flags for the thread
- `my_first_thread`: The thread function
- `NULL`: The argument for the thread function
- `"My first thread"`: The name of the thread
## Step 3: Building and Running the Program
Now that we have created our thread, we can build and run our program.
Compile the program and flash it to your board using `make flash`.
If we now look into the terminal via `make term` we should see the message
"Hello, from the main thread!" printed to the console followed by
"Hello, from the thread!" printed by the thread.
![The output](img/threads/03_output.png)
## Conclusion
Congratulations! You have successfully created and started a thread in RIOT.
Threads are a powerful tool but remember that you are on a resource-constrained device,
so don't create too many threads or use too much stack space.
:::note
The source code for this tutorial can be found
[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/threads).
If your project is not working as expected,
you can compare your code with the code in this repository to see if you missed anything.
:::

View File

@ -0,0 +1,111 @@
---
title: Timers and Callbacks
description: This tutorial explains how to use timers and callbacs in RIOT.
code_folder: examples/guides/timers/
---
Timers and interrupts are essential concepts in embedded systems programming.
In this tutorial, we will take a look at how to use timers and interrupts in RIOT.
There are two different approaches to timers,
you can either use interrupts or polling.
Both have their advantages and disadvantages, but in general,
using interrupts is more efficient and allows you to do other things while
waiting for the timer to expire instead of constantly checking if the timer has expired.
## Step 1: Using Timers
In the hello world program we created in the previous tutorial,
we used the `ztimer` module to sleep for 3 seconds before printing "Hello World!" to the console.
But what if we want to do something else while waiting for the timer to expire?
Let's take a closer look at how we can use timers in RIOT.
### Step 1.1: Creating a Callback
Timers are a way to schedule tasks to be executed at a later time.
In RIOT, you have to tell the timers two things:
the time when the timer should expire and the function that should be called
when the timer expires.
Lets start by creating the callback that the timer will call when it expires.
Go into your `main.c` file and add the following code:
```c
void timer_callback(void *arg) {
/* Cast the received pointer "arg" to a C String type */
char *message = (char *)arg;
/* Print the message */
puts(message);
}
```
This function takes a single argument, a pointer to a string,
and prints the string to the console. Congrats, you have created a callback function,
now all that is left is to create the timer and schedule it.
To do that lets restructure our `main` function to use the timer.
### Step 1.2: Scheduling the Timer
Go into your `main` function and include the following code:
```c
int main(void) {
/* Create a timer */
ztimer_t timer = {.callback = timer_callback,
.arg = "3 seconds have passed!"};
```
This code creates a timer and initializes it with the callback function we
created earlier and a message that will be printed when the timer expires.
Now all that is left is to actually start the timer. To do that,
we need to simply call the `ztimer_set` function with the timer we created as an argument.
```c
/* Set the timer to fire in 3 seconds */
ztimer_set(ZTIMER_SEC, &timer, 3);
```
This code tells the timer to fire in 3 seconds.
The first argument specifies which type of clock we want to use,
the second argument is the timer we created, and the third argument is the time in seconds.
### Step 1.3: Running the Program
Now that we have created the timer and scheduled it, we can run the program.
Compile the program using `make` and flash it to your board using `make flash`.
If you look into the terminal via `make term` you should see the message
"3 seconds have passed!" printed to the console after 3 seconds.
That's all there is to it! You have successfully used a timer in RIOT.
Now you can do other things while waiting for the timer to expire,
instead of constantly checking if the timer has expired. For example,
lets use our knowledge from before to let the main thread go to sleep for 5 seconds
and then print a message.
![The output](img/timers/01_output.png)
```c
/* Sleep for 5 seconds */
ztimer_sleep(ZTIMER_SEC, 5);
puts("5 seconds have passed!");
return 0;
```
## Conclusion
In this tutorial, we learned how to use timers in RIOT.
We created a callback function that prints a message to the console
and scheduled a timer to call the callback function after 3 seconds.
:::note
The source code for this tutorial can be found
[HERE](https://github.com/RIOT-OS/RIOT/tree/master/examples/guides/timers).
If your project is not working as expected,
you can compare your code with the code in this repository to see if you missed anything.
:::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 766 KiB

After

Width:  |  Height:  |  Size: 680 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 KiB

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 678 KiB

After

Width:  |  Height:  |  Size: 605 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 772 KiB

After

Width:  |  Height:  |  Size: 684 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 KiB

After

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 KiB

After

Width:  |  Height:  |  Size: 635 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 765 KiB

After

Width:  |  Height:  |  Size: 677 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 KiB

After

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -112,6 +112,8 @@ One last manual step is to add the new guide to the navigation. This is done in
Compared to the Doxygen output we want to be extremely careful about the navigation and only show the guides that are relevant to the user and in a logical order that does not overwhelm the user with too many options at once.
It is also important to note that the place in each section is directly related to the order in which the guides are listed in the `sidebar` array in the `astro.config.mjs` file. The order of the items in the array determines the order in which they will be displayed in the navigation. So if you want to place your guide at the top of a section, you should add it to the beginning of the `items` array for that section.
Generally, the less information you declare within the config the better, given that ideally your frontmatter information is enough to generate the navigation.
You can either add your guide to an existing section or create a new section. In the event that you want to simply append your guide to an existing section, you can simply add the file name to the list of files in the section. The root of the so called `slug` of your file is the guides folder itself, so a guide under `doc/guides/test/hello_world.md` will have the slug `test/hello_world.md`.

View File

@ -61,6 +61,17 @@ export default defineConfig({
"getting-started/finding_modules",
],
},
{
label: "C Basics",
items: [
"c_tutorials/create_project",
"c_tutorials/timers",
"c_tutorials/shell",
"c_tutorials/threads",
"c_tutorials/gpio",
"c_tutorials/saul",
],
},
],
},
{

View File

@ -3,9 +3,7 @@ This tutorial assumes that you have already set up your development environment
as described in the [Getting Started](/getting-started/installing/) guide.
:::
Now that we have played around with the examples and have a basic understanding of how to use RIOT, let's create a new project from scratch. We will create a simple hello world program that will print "Hello World!" to the console.
#### Step 1: Create a new git repository
#### Create a new git repository
We start by creating a new git repository for our project. If you have never worked with git before, you can find a good introduction [here](https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control) or [here](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git).
@ -25,7 +23,7 @@ git init
Congratulations! You have now created a new empty git repository. In the next step, we will add RIOT as a submodule to our project.
#### Step 2: Add RIOT as a submodule
#### Add RIOT as a submodule
We want to import RIOT as a submodule to our project. This will allow us to easily update to newer versions of RIOT and also allows us to easily share our project with others on GitHub, Gitlab, or any other git hosting service.

View File

@ -142,3 +142,16 @@ Here is a quick overview of the examples available in the RIOT:
| [twr_aloha](./advanced/twr_aloha/README.md) | This example allows testing different two-way ranging algorithms between two boards supporting a dw1000 device. This makes use of the uwb-core pkg. |
| [senml_saul](./advanced/senml_saul/README.md) | This example demonstrates the usage of the SAUL (Sensor Actuator Uber Layer) module with the SenML (Sensor Measurement Lists) format. |
| [opendsme](./advanced/opendsme/README.md) | This example demonstrates the usage of the OpenDSME module in RIOT. |
## Examples from Guides
[Our guides](https://guide.riot-os.org/) walk you through small tutorials to get started. The following examples are the resulting code from completing their respective guide.
| Example | Description |
|---------|-------------|
| [creating_project](./guides/creating_project/README.md) | Teaches you the very first steps. How to setup a RIOT project, build and run a Hello World in it. [Create Project](https://guide.riot-os.org/c_tutorials/create_project/) tutorial |
| [shell](./guides/shell/README.md) | Teaches you how to use the interactive RIOT shell for your application. [Shell](https://guide.riot-os.org/c_tutorials/shell/) tutorial |
| [gpio](./guides/gpio/README.md) | Teaches you how to configure and use GPIO pins for external hardware interaction. [GPIO](https://guide.riot-os.org/c_tutorials/gpio/) tutorial |
| [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 |
| [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 |
| [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 |

View File

@ -0,0 +1,21 @@
# name of your application
APPLICATION = creating_project_example
# Change this to your board if you want to build for a different board
BOARD ?= native
# This has to be the absolute path to the RIOT base directory:
# If you are following the tutorial, your RIOT base directory will
# most likely be something like RIOTBASE ?= $(CURDIR)/RIOT
# instead of this
RIOTBASE ?= $(CURDIR)/../../..
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,5 @@
# RIOT Tutorial Replica
This example is a replica of the output of the RIOT tutorial. The tutorial is available at [RIOT Tutorial](https://guide.riot-os.org).
The tutorial itself is located within [doc/guides/c_tutorials](/doc/guides/c_tutorials/). Please refer to the original tutorial for more information.

View File

@ -0,0 +1,28 @@
/*
* SPDX-FileCopyrightText: 2025 Tom Hert <git@annsann.eu>
* SPDX-FileCopyrightText: 2025 HAW Hamburg
* SPDX-License-Identifier: LGPL-2.1-only
*/
/*
* For many printing related things, such as the puts function here
* we import stdio, depending on your board, platform or form of output
* it then includes the right definitions without the need to
* worry about the specific details.
*/
#include <stdio.h>
/*
* This is the main function of the program.
* It serves as the entry point for the program and gets called once your CPU is
* initialized.
*
* The function returns an integer value, which is the exit status
* of the program. A return value of 0 indicates that the program has finished
* successfully.
*/
int main(void) {
puts("Hello World!");
return 0;
}

View File

@ -0,0 +1,29 @@
# name of your application
APPLICATION = gpio_example
# Change this to your board if you want to build for a different board
BOARD ?= arduino-feather-nrf52840-sense
# This has to be the absolute path to the RIOT base directory:
# If you are following the tutorial, your RIOT base directory will
# most likely be something like RIOTBASE ?= $(CURDIR)/RIOT
# instead of this
RIOTBASE ?= $(CURDIR)/../../..
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1
# Add the gpio module to the build
USEMODULE += periph_gpio
USEMODULE += periph_gpio_irq
# Enable the milliseconds timer.
USEMODULE += ztimer
USEMODULE += ztimer_msec
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,4 @@
BOARD_INSUFFICIENT_MEMORY := \
atmega8 \
nucleo-l011k4 \
#

View File

@ -0,0 +1 @@
../creating_project/README.md

View File

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2025 Tom Hert <git@annsann.eu>
* SPDX-FileCopyrightText: 2025 HAW Hamburg
* SPDX-License-Identifier: LGPL-2.1-only
*/
#include "board.h"
#include "periph/gpio.h"
#include "ztimer.h"
/* Define the LED0 pin and mode */
gpio_t led0 = GPIO_PIN(1, 9);
gpio_mode_t led0_mode = GPIO_OUT;
/* Define the LED1 pin and mode */
gpio_t led1 = GPIO_PIN(1, 10);
gpio_mode_t led1_mode = GPIO_OUT;
/* Define the button pin */
gpio_t button = GPIO_PIN(1, 2);
/* This callback function will be called when the button state changes */
void button_callback(void *arg) {
/* the argument is not used */
(void)arg;
/* Toggle the LED1 pin based on the button state */
if (gpio_read(button)) {
gpio_clear(led1);
} else {
gpio_set(led1);
}
}
int main(void) {
/* Initialize the LED0 pin */
gpio_init(led0, led0_mode);
/* Turn off the LED0 pin */
gpio_clear(led0);
/* Initialize the LED1 pin */
gpio_init(led1, led1_mode);
/* Turn off the LED1 pin */
gpio_clear(led1);
/* Initialize the button pin */
gpio_init_int(button, GPIO_IN_PU, GPIO_BOTH, button_callback, NULL);
/* Loop forever */
while (1) {
/* Toggle the LED0 pin every 500 milliseconds */
gpio_toggle(led0);
ztimer_sleep(ZTIMER_MSEC, 500);
}
}

View File

@ -0,0 +1,32 @@
# name of your application
APPLICATION = saul_example
# Change this to your board if you want to build for a different board
BOARD ?= arduino-feather-nrf52840-sense
# This has to be the absolute path to the RIOT base directory:
# If you are following the tutorial, your RIOT base directory will
# most likely be something like RIOTBASE ?= $(CURDIR)/RIOT
# instead of this
RIOTBASE ?= $(CURDIR)/../../..
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1
# This board requires a start sleep to actually catch the printed output
USEMODULE += shell
# Add the SAUL module to the application
USEMODULE += saul
USEMODULE += saul_default
# Enable the milliseconds timer.
USEMODULE += ztimer
USEMODULE += ztimer_msec
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,3 @@
BOARD_INSUFFICIENT_MEMORY := \
atmega8 \
#

View File

@ -0,0 +1 @@
../creating_project/README.md

View File

@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2025 Tom Hert <git@annsann.eu>
* SPDX-FileCopyrightText: 2025 HAW Hamburg
* SPDX-License-Identifier: LGPL-2.1-only
*/
#include <stdio.h>
#include "board.h"
#include "saul_reg.h"
#include "ztimer.h"
int main(void) {
/* We sleep for 5 seconds to allow the system to initialize */
ztimer_sleep(ZTIMER_MSEC, 5000);
puts("Welcome to SAUL magic!");
/* Define our temperature sensor */
saul_reg_t *temperature_sensor = saul_reg_find_type(SAUL_SENSE_TEMP);
/* Exit if we can't find a temperature sensor */
if (!temperature_sensor) {
puts("No temperature sensor found");
return 1;
} else {
/*
* Otherwise print the name of the temperature sensor
* and continue the program
*/
printf("Temperature sensor found: %s\n", temperature_sensor->name);
}
/* We start an infinite loop to continuously read the temperature */
while (1) {
/* Define a variable to store the temperature */
phydat_t temperature;
/*
* Read the temperature sensor
* and store the result in the temperature variable
* saul_reg_read returns the dimension of the data read (1 in this case)
*/
int dimension = saul_reg_read(temperature_sensor, &temperature);
/* If the read was successful (1+ Dimensions), print the temperature */
if (dimension <= 0) {
puts("Error reading temperature sensor");
return 1;
}
/* Dump the temperature to the console */
phydat_dump(&temperature, dimension);
/* Sleep for 1 seconds */
ztimer_sleep(ZTIMER_MSEC, 1000);
}
}

View File

@ -0,0 +1,24 @@
# name of your application
APPLICATION = shell_example
# Change this to your board if you want to build for a different board
BOARD ?= native
# This has to be the absolute path to the RIOT base directory:
# If you are following the tutorial, your RIOT base directory will
# most likely be something like RIOTBASE ?= $(CURDIR)/RIOT
# instead of this
RIOTBASE ?= $(CURDIR)/../../..
# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1
# This board requires a start sleep to actually catch the printed output
USEMODULE += shell
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1 @@
../creating_project/README.md

View File

@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2025 Tom Hert <git@annsann.eu>
* SPDX-FileCopyrightText: 2025 HAW Hamburg
* SPDX-License-Identifier: LGPL-2.1-only
*/
#include <stdio.h>
#include "shell.h"
/* Our command we want to register to the shell */
int echo_command(int argc, char **argv) {
/* We take the arguments passed to the command */
for (int i = 1; i < argc; i++) {
/* We print each argument followed by a space */
printf("%s ", argv[i]);
}
/* Finally, we print a newline character to end the line */
printf("\n");
/* Return 0 to indicate success */
return 0;
}
/* This little macro registers the command so we can use it in the shell */
SHELL_COMMAND(echo, "Echo a message", echo_command);
int main(void) {
/* Buffer to store command line input */
char buffer[SHELL_DEFAULT_BUFSIZE];
/* Start the shell */
shell_run(NULL, buffer, SHELL_DEFAULT_BUFSIZE);
/* Return 0 to indicate that the program has finished successfully */
return 0;
}

Some files were not shown because too many files have changed in this diff Show More