Writing drivers for a CPU peripheral or some sensor is a good way to start IMHO.
In the embedded space, good data sheets are usually readily available.
Depending on your experience, there are possible projects of varying difficulty.
A few suggestions for boards are available here, but those are usually boards with Microcontrollers (MCUs) not yet supported by RIOT.
As with all code bases, it’s usually a good idea to read a few Pull Requests to get a feeling of ‘how things are done’ around here.
Maybe this already gives you an idea for an interesting projects.
Adding support for a board with an MCU already supported by RIOT
This should be the easiest. Get a cheap board from AliExpress, Adafruit, Olimex, Pollin or Seed Studio with a Microcontroller (or Microcontroller family) that is already supported by RIOT.
This will be mostly copy & paste from an existing board where you have to change / add the pin configuration to match the schematic of your new board.
@miri64 wrote an article with more details about this.
If your board comes with sensors / a display / some other component not yet supported by RIOT (e.g. if you get an esp32 based watch), this can also lead to
Adding support for a new sensor
You can also get a sensor break out board to connect to an already supported board. Or maybe you have a board that comes with a sensor that is not yet supported by RIOT.
I2C Temperature / Moisture sensors are easy, usually you can just read a register and then have a formula to convert the register value into a temperature value. Try to avoid floating point arithmetic here.
Make sure to check open / closed PRs if you can build on some existing work that has gone stale.
It can often be helpful to check what other systems are doing if you are stuck with something.
Adding support for a peripheral on a supported MCU
MCUs are composed of a CPU core (often a standard ARM Cortex-M processor) and vendor provided peripherals.
Common peripherals (SPI, I2C, UART, Timers, etc) can be found on every MCU.
RIOT provides a common API so code can be written in a vendor agnostic way.
That means those interfaces have to be implemented for every CPU family.
This is not always complete.
cpu/sam3/periph (found on the Arduino Due) and
cpu/lm4f120/periph (found on TM4C123G LaunchPad) lack an I2C driver, many others lack a Watchdog driver (this is a low hanging fruit).
USB is also only implemented for some STM32, nrf52 and the sam0 family - although that might be a more complex, yet equally rewarding task.
Adding support for a MCU not yet supported by RIOT
There are two variants of this:
- The CPU family is already supported (e.g. add support for STM32H7 to
- The CPU family is not yet supported (e.g. RP2040, Cypress PSoC, Nuvoton NuMicro, …)
Vendors will usually reuse peripherals across their lines of MCUs. That means you can leverage the existing implementation and might only need to do some small modifications to get your new chip running.
Or you discover that some long overdue refactoring is needed before you can add your new processor.
In any case, make sure not to break the existing functionality
In that case starting with a new family can be easier as you don’t have to accommodate for the existing siblings but can start from a clean state. It means however that you will need to provide all the peripheral drivers yourself.
If you are lucky, the vendor provides a good reference implementation that you can use.
If you are unlucky, you are stuck with an opaque HAL and no good data sheet.
New ARM Cortex-M CPUs are easier to support than RISC-V ones, for one simply because the Cortex-M port is much more mature, but also because ARM already provides a standard CPU core with an interrupt controller, so the only thing that will differ across vendors is the clock setup and peripherals.
With RISC-V there is more variety, e.g. the interrupt controller can vary (note: this is also true for ‘classic’ or ‘big’ ARM cores (non -M))
Adding support for a New CPU architecture
This is not for the faint hearted, but I wanted to include it here for completeness’ sake.
This would mean porting RIOT to a new architecture (pic24, stm8, Z80, 8051, …) and will also lead to the previous task.
This will require knowledge of the architecture and assembly to implement proper task switching.