---
title: Firmware
lang: en
type: notion
phases:
  - preuve-de-concept
tags:
  - eee
  - notion
prerequis:
  - cpp-en
aa: [RA-PROJET-C03-3/PROJ/5]
draft: false
source_fr: embarque/mcu/firmware.md
source_sha256: b09a8a7d9c23b30480910bc400bd5a10b9ef103f1154edf8439e934c5e9829de
---

The **firmware** is the embedded program that runs on the microcontroller: the code that, once uploaded, *is* the behaviour of the system. This page is not about a language or about one particular board, but about **how to structure** that code so it stays readable, reliable and open to change, from the simple loop of a first build up to the architecture of a real-time system. The concrete techniques (non-blocking loop, state machines, RTOS) each have their own page. Here we situate the whole and choose the architecture that fits the need.

## What is it for?

Firmware is not software in the sense a PC has software. It runs **with no [[systeme-d-exploitation-en|operating system]]** most of the time, on **counted resources** ([[memoire-en|memory]], processing power), in an **endless loop**, and it has to be **robust**: a lock-up out in the field, with no keyboard and no screen to step in, is a dead system.

The challenge is not to "write code" (that is the [[cpp-en|language]]), but to **organise** it. A first sketch fits in a few lines inside `loop()`. A real system piles up dozens of functions, several activities carried on at once, error handling. With no structure, the code soon turns into a "plate of spaghetti" nobody dares touch. Structuring firmware well means choosing, **according to how complex the system is**, the architecture that will keep it manageable.

## How to structure firmware

There is a **progression** of architectures, from the simplest to the most demanding. You adopt the level that is just necessary: you only move up a step when the need forces it.

![Staircase of the five firmware architectures, ranked by increasing complexity: super-loop, non-blocking cooperative loop, state machines, breaking into modules, real-time RTOS.](/ressources/img/firmware/architectures.svg)

1. **The super-loop.** `setup()` sets things up, `loop()` repeats: the whole program inside the loop. Enough for a build that does *one* thing. It is the starting point ([[cpp-en|the C++ language]]).
2. **The non-blocking cooperative loop.** As soon as the system does *more than one thing*, each activity becomes a brief task called on every pass, never blocking. It is the default architecture of nearly every real firmware ([[programmation-non-bloquante-en|non-blocking programming]]).
3. **State machines.** For behaviour with **modes** (waiting → running → done), you model each task as a [[machine-a-etats-en|state machine]]: it reads well, and it spares you cascades of flags.
4. **Breaking into modules.** You separate the code by **responsibility** — sensor acquisition, control, communication — into named [[fonction-informatique-en|functions]] and files. Each module can be tested and read on its own.
5. **The real-time system (RTOS).** When some tasks have **strict deadlines** to meet whatever happens, an RTOS such as [[esp32-freertos-en|FreeRTOS]] **preempts** to guarantee priorities. Powerful but heavy: keep it for the cases that demand it, especially on the [[esp32-en|ESP32]] side.

This ladder is the heart of the craft: most school projects live very well at levels 2 to 4, with the RTOS remaining a horizon.

## Firmware across the families

The **way of structuring** described above is the same everywhere. What changes from one microcontroller family to another is the **tooling** and the **API**:

- **Arduino** (AVR / ARM) — the *Wiring* API, a super-loop, the Arduino [[ide-en|IDE]]: the gentlest way in ([[arduino-en|Arduino hub]]).
- **ESP32** — either the **Arduino core** (same API as the Arduino, so migration is immediate), or **ESP-IDF**, Espressif's native framework built on FreeRTOS ([[esp32-en|ESP32 hub]]).
- **STM32** — the HAL library generated by CubeMX for fine control, or the Arduino core (STM32duino) to stay on familiar ground ([[stm32-en|STM32 hub]]).
- **MicroPython** — an **interpreted** language and a REPL: the tooling changes completely, but the architectures are the same, `while True` standing in for the super-loop, non-blocking code and state machines having their twins ([[micropython-en|MicroPython hub]]).

The cross-cutting lesson: **the architecture concepts do not depend on the board**. Only the development environment and the names of the functions change. Learning to structure firmware once serves you on every family. The tooling specifics are covered in each family's hub.

## Pitfalls

**Piling everything into `loop()`.** The beginner's reflex: adding line after line inside the loop until it is a plate of spaghetti. Break it into functions and tasks as soon as it goes past one activity.

**`delay()` everywhere.** Every `delay()` freezes the whole system. Incompatible with firmware that does several things. Move to non-blocking code.

**No handling of the unexpected.** No default value, no [[chien-de-garde-en|watchdog]]: at the first anomaly out in the field, the system locks up and never restarts. Robustness is designed in, it is not added afterwards.

**Over-architecting too early.** Setting up an RTOS to blink two LEDs adds complexity for no benefit. The architecture follows the need, it does not come before it.

**Not putting the firmware under version control.** The code changes at every attempt. Without Git, you lose track of the version that *worked*. Version control is part of firmware discipline, not a comfort.

## See also

- [[cpp-en|The C++ language]] — writing the code, where firmware is about organising it
- [[programmation-non-bloquante-en|Non-blocking programming]] — the cooperative loop, the default architecture
- [[machine-a-etats-en|State machine]] — structuring behaviour that has modes
- [[fonction-informatique-en|Function]] — the unit you break modules into
- [[esp32-en|ESP32]] — Arduino core against ESP-IDF, and the FreeRTOS horizon
- [[arduino-en|Arduino]] — the super-loop and the Wiring API
- [[stm32-en|STM32]] — HAL generated by CubeMX, or the Arduino core
- [[micropython-en|MicroPython]] — the same architectures, interpreted tooling
- [[chien-de-garde-en|Watchdog]] — robustness in the face of lock-ups
- [[debugger-embarque-en|Debugging an embedded system]] — tracking a bug down in the firmware (messages or hardware debugging)
