Aller au contenu

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 :

MAJOR.MINOR.PATCH+BUILD_METADATA
  1  .  4  .  2  +esp32s3.20250415.abc1234
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. 1 % des appareils reçoivent la mise à jour — surveillance des métriques d'erreur pendant 24 h.
  2. 10 % si les métriques sont nominales.
  3. 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.