Build, release et OTA¶
Du commit au déploiement sur une flotte de 10 000 appareils — versioning, signature cryptographique et pipeline CI/CD firmware.
Du code au binaire déployé¶
Le développement d'un firmware ne s'arrête pas à la compilation. Le cycle de vie complet comprend la gestion des versions, la traçabilité des artefacts, la signature pour l'intégrité, et le déploiement OTA (Over-The-Air) sur des appareils en production. Ces étapes sont souvent négligées en début de projet — et leur absence crée des problèmes critiques dès les premiers déploiements terrain.
Versioning sémantique¶
Le versioning sémantique (SemVer 2.0.0) s'applique naturellement au firmware :
| Composant | Signification firmware | Exemple |
|---|---|---|
| MAJOR | Changement incompatible (format EEPROM, protocole réseau) | 1 → 2 |
| MINOR | Nouvelle fonctionnalité rétrocompatible | 4 → 5 |
| PATCH | Correctif de bug, pas de nouvelle feature | 2 → 3 |
| Build metadata | Cible, date, hash commit | esp32s3.20250415.abc1234 |
Règle critique : le numéro de version doit être inclu dans le binaire et lisible sans flasher. Il est stocké dans une section flash dédiée et exposé via l'interface de diagnostic.
// version.h — généré automatiquement par le build system
#define FW_VERSION_MAJOR 1
#define FW_VERSION_MINOR 4
#define FW_VERSION_PATCH 2
#define FW_VERSION_BUILD "esp32s3.20250415.abc1234"
#define FW_VERSION_STRING "1.4.2+esp32s3.20250415.abc1234"
// Stockage en flash (section non effacée lors des mises à jour)
const __attribute__((section(".version_info")))
FirmwareVersion fw_version = {
.magic = 0xDEADC0DE,
.major = FW_VERSION_MAJOR,
.minor = FW_VERSION_MINOR,
.patch = FW_VERSION_PATCH,
.crc32 = 0 // calculé à la compilation par un script post-build
};
Build reproductible¶
Un build reproductible produit des binaires bit pour bit identiques à partir des mêmes sources, quelle que soit la machine de build et le moment de la compilation. C'est un prérequis pour la traçabilité en production industrielle.
Pièges courants de non-reproductibilité¶
- Timestamps compilés dans le binaire (
__DATE__,__TIME__) — les supprimer ou les fixer. - Versions non pinnées des dépendances — une mise à jour silencieuse change le binaire.
- Chemins absolus dans les symboles debug — utiliser
-fdebug-prefix-map. - Compilateur non versionné — une mise à jour de GCC change l'optimisation.
Docker pour l'isolation de toolchain¶
# Dockerfile.build — toolchain ESP-IDF fixée
FROM espressif/idf:v5.2.1
WORKDIR /projet
COPY . .
RUN idf.py build
# L'image Docker elle-même est le "lock" de la toolchain
# Build reproductible via Docker
docker build -f Dockerfile.build -t firmware-builder:v5.2.1 .
docker run --rm -v $(pwd)/build:/projet/build firmware-builder:v5.2.1
Nix — le build reproductible absolu¶
Nix garantit la reproductibilité à un niveau supérieur — chaque dépendance est identifiée par son hash, le build est hermétique (pas d'accès réseau pendant la compilation). C'est le choix des projets avec exigences de certification.
# flake.nix — environnement de build fixé
{
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
outputs = { nixpkgs, ... }: {
devShells.default = nixpkgs.legacyPackages.x86_64-linux.mkShell {
packages = with nixpkgs.legacyPackages.x86_64-linux; [
gcc-arm-embedded # version fixée par nixpkgs
cmake ninja openocd
];
};
};
}
Formats d'artefacts¶
| Format | Contenu | Quand l'utiliser |
|---|---|---|
.elf | Binaire avec symboles debug, sections DWARF | Debug avec GDB, profiling, analyse post-mortem |
.bin | Image binaire brute à une adresse fixe | Flashage direct via OpenOCD, esptool, dfu-util |
.hex | Format Intel HEX (ASCII, adresses absolues) | Outils anciens (MPLAB, Keil), production |
.uf2 | Format USB Flashing (Microsoft) | RP2040, drag-and-drop sur MSC USB |
.signed.bin | .bin + signature ECDSA/RSA en footer | OTA avec vérification d'intégrité |
.ota | Archive (.bin + manifest JSON) | Package complet pour serveur OTA |
# Génération des artefacts depuis l'ELF
arm-none-eabi-objcopy -O binary firmware.elf firmware.bin
arm-none-eabi-objcopy -O ihex firmware.elf firmware.hex
arm-none-eabi-size firmware.elf # analyse des sections (.text, .data, .bss)
Préparation OTA — signature et manifest¶
Un déploiement OTA sans signature cryptographique est une vulnérabilité critique : n'importe qui interceptant la mise à jour peut y substituer un firmware malveillant.
Signature ECDSA (ESP-IDF Secure Boot v2)¶
ESP-IDF implémente le Secure Boot v2 avec signature ECDSA-P256 :
# Génération de la clé de signature (une seule fois, stocker en HSM)
espsecure.py generate_signing_key --version 2 signing_key.pem
# Signature du binaire
espsecure.py sign_data \
--version 2 \
--keyfile signing_key.pem \
--output firmware_signed.bin \
firmware.bin
La clé publique est brûlée dans les eFuses de l'ESP32 — l'appareil refusera tout firmware non signé avec la clé correspondante.
Manifest OTA¶
Le manifest décrit le package de mise à jour et permet à l'appareil de valider l'authenticité avant de télécharger le binaire complet.
{
"version": "1.4.2",
"target": "esp32s3-produit-v2",
"build_date": "2025-04-15T14:32:00Z",
"git_commit": "abc1234",
"artifacts": [
{
"type": "application",
"file": "firmware_signed.bin",
"size": 892416,
"sha256": "a3f9c12d8b4e7f1a2c5d9e0b3f6a8c1d4e7f2a5b8c3d6e9f0a1b4c7d0e3f6a9",
"address": "0x10000"
}
],
"min_version": "1.2.0",
"rollback_version": "1.3.1",
"changelog_url": "https://firmware.monproduit.fr/v1.4.2/CHANGELOG.md"
}
Le champ min_version empêche les downgrade vers des versions avec vulnérabilités connues. rollback_version indique la version de repli en cas d'échec de mise à jour.
Changelog automatisé¶
Les Conventional Commits définissent un format de message de commit qui permet la génération automatique du changelog et le calcul du prochain numéro de version SemVer.
feat(capteur): ajouter la lecture de l'humidité relative
^ ^ ^
| | └─ description
| └─ scope (composant)
└─ type : feat, fix, chore, docs, refactor, test, perf
| Type commit | Impact SemVer |
|---|---|
feat: | MINOR++ |
fix: | PATCH++ |
feat!: ou BREAKING CHANGE: | MAJOR++ |
chore:, docs:, test: | Aucun |
Outils : semantic-release, release-please (GitHub), git-cliff (Rust, rapide).
# .github/workflows/release.yml
- name: Calculer le prochain numéro de version
uses: google-github-actions/release-please-action@v4
with:
release-type: simple
package-name: firmware-monproduit
Pipeline CI/CD firmware complet¶
flowchart TD
A[git push\nmain / PR] --> B[Build firmware\nDocker toolchain fixée]
B --> C[Tests unitaires\nsur host + QEMU]
C --> D{Tests OK ?}
D -- Non --> E[Notification\néchec PR]
D -- Oui --> F[Analyse statique\ncppcheck / clang-tidy]
F --> G[Génération artefacts\n.elf .bin .hex]
G --> H[Signature\nECDSA / RSA]
H --> I[Package OTA\n.bin + manifest.json]
I --> J{Branche main ?}
J -- Non --> K[Artefacts\nPR artifacts]
J -- Oui --> L[Release\ntag git + changelog]
L --> M[Serveur OTA\nstaging]
M --> N[Tests HIL\nautomatisés]
N --> O{HIL OK ?}
O -- Non --> P[Rollback\nalerte Ops]
O -- Oui --> Q[OTA production\ndéploiement progressif\n1% → 10% → 100%]
style A fill:#2d4a6e,color:#fff
style Q fill:#1a6e3a,color:#fff
style E fill:#6e1a1a,color:#fff
style P fill:#6e1a1a,color:#fff Déploiement progressif (canary release)¶
Le déploiement progressif limite l'impact d'un bug de régression en production :
- 1 % des appareils reçoivent la mise à jour — surveillance des métriques d'erreur pendant 24 h.
- 10 % si les métriques sont nominales.
- 100 % après 48 h de stabilité confirmée.
Des plateformes comme Mender, Balena Cloud ou AWS IoT Jobs gèrent ce déploiement progressif avec rollback automatique sur critères d'erreur.
Outils OTA courants¶
| Outil | Cible | Protocole | Signature | Rollback | Licence |
|---|---|---|---|---|---|
| ESP-IDF OTA | ESP32 | HTTPS | Secure Boot v2 | Oui | Apache 2.0 |
| MCUboot | Cortex-M (Zephyr, Mbed) | Divers | ECDSA, RSA | Oui | Apache 2.0 |
| Mender | Linux embarqué | HTTPS | TLS + artefact | Oui | Apache 2.0 + Commercial |
| Balena Cloud | Linux (Docker) | HTTPS | TLS | Oui | Commercial |
| AWS IoT Jobs | Multi | MQTT / HTTPS | AWS Signing | Manuel | Commercial |
| RAUC | Linux embarqué | HTTPS / CoAP | X.509 | Oui | LGPL |
Ce qu'il faut retenir¶
- Versionner le firmware dès le premier commit — changer de schéma en cours de projet est douloureux.
- Un build reproductible nécessite de pinner toolchain, dépendances et timestamps — Docker ou Nix sont les outils naturels.
- La signature cryptographique du firmware n'est pas optionnelle en production industrielle.
- Le manifest OTA contient version, hash SHA-256, taille et version minimale — il est signé séparément du binaire.
- Le déploiement progressif (canary) limite l'impact des bugs de régression sur la flotte.
Chapitre suivant : Protocoles et communication — du capteur à la gateway, tous les protocoles IoT industriels et sans fil.