C++ has embedded friendly mechanisms that can help to structure your firmware. While keeping the design resource-lean.
In this post, I use classes, C++ stl containers and algorithms to manage close-to-the-hardware resources with objects. With runtime costs that are similar to a good C design.
example context:
- you want to use PIO state machines (example: you have a set of stepper motors that you want to control with PIO state machines)
- you only want to program the PIOs and state machines that have a stepper motor assigned to it
- you are a C++ fanboy and want to handle the motors with objects
| I'm using stepper motors and Pico PIO co-controllers as real world examples. The mechanism works for many real world designs where you want to process, filter, aggregate ... (I also used these concepts for a GPS data parser) |
In this little post, I use STL algorithms that take care that:
- a PIO is only programmed if at least one of its state machines serves a motor, and
- a state machine is only initialised if it serves a motor.
|
pseudocode: if a PIO is used program it initialise its state machines that you are using post condition 1: PIOs and state machines that are not used, are not touched |
declare a motor handler class
In this post, the class that can handle a motor is called dummy_handler, because this is demo code. For a real class that can handle a stepper motor, check pio_stepper_lib.
struct dummy_handler {
dummy_handler(PIO pio, uint sm) : pio(pio), sm(sm) {}
PIO pio;
uint sm;
};
I tried to make this as small as possible. Just enough to be usable in this example.
declare a container with stepper motor handlers
In my example, I have 6 motors. 4 run on the state machines of the first PIO. 2 others run on the second PIO.

I use an array, because that's a good choice for an embedded application container.
std::array<dummy_handler, 6> handlers {{
{pio0, 0}, {pio0, 1}, {pio0, 2}, {pio0, 3}
, {pio1, 0}, {pio1, 1}
}};
program the PIOs
I only want to load my PIO code in the PIOs that have at least 1 motor assigned to them. In this example I use PIO 1 and 2. If this runs on a Pico2, I don't want to program PIO3, because it doesn't control any stepper motor.
any_of
I use the stl any_if algorithm to check, for each PIO, if my container has at least one motor for that PIO. If yes, I program it.
for (uint u = 0u; u < NUM_PIOS; u++) {
if (std::ranges::any_of(handlers, [u](auto& h){ return h.pio == PIO_INSTANCE(u); })) {
uint offset = pio_add_program(PIO_INSTANCE(u), &run_program);
// more code later :)
}
}
This code loops over the available PIOs on the Pico. only if it finds at least one motor in the array that runs on that PIO, it programs it.
for_each with filter
Then, it'll initiate each used state machine of that PIO:
for(auto &h : handlers | std::views::filter([u](const auto& h){ return h.pio == PIO_INSTANCE(u);})) {
run_program_init(h.pio, h.sm, offset);
pio_add_program() and run_program_init() are functions that any PIO developer will recognise. They are part of the Pico C SDK.
The for will only invoke code on the items in the container that match the current PIO in our loop. The filter takes care that the loop only returns items that match that condition. For Linux users: this is similar to filtering pipes.
everything together
This is all of the code. It takes care that only the used PIOs are programmed. And that only the used state machines of those PIOs are initialised:
// program each PIO, and initialise each sm - only if they are used
for (uint u = 0u; u < NUM_PIOS; u++) {
if (std::ranges::any_of(handlers, [u](auto& h){ return h.pio == PIO_INSTANCE(u); })) {
uint offset = pio_add_program(PIO_INSTANCE(u), &run_program);
for(auto &h : handlers | std::views::filter([u](const auto& h){ return h.pio == PIO_INSTANCE(u);})) {
run_program_init(h.pio, h.sm, offset);
}
}
}
|
This may look odd to a C++98 user, or if you are used to C. It compiles to efficient code though.
There are 4 learning ramps to climb:
|
I put embedded friendly in the title of this post, because this is resource light. If you would write similar functionality in C, your logic would have a comparable cost.
Thank you for reading.







