Montréal JUG · Présentations éclairs · 5 novembre 2026

From Backend to Hardware

D’une idée à un objet fabriqué en série, vu par un développeur backend.

Alexis Couvreur · CTO, Reeveel

Qui suis-je

Alexis Couvreur

  • Développeur backend · Montréal
  • Platform engineer · Banque Nationale
  • Cofondateur et CTO · Reeveel
  • Mainteneur · Sablier
Chapitre 1 / 5

Le produit

Ce qu’on fabrique, et ce qu’on exige de lui.

Le produit final

Inkolor

par Reeveel

Écrane-paper couleur, 13,3"
RadioBluetooth, Wi-Fi
Batterie10 000 mAh
NFCavec le téléphone
Capteuraccéléromètre
Boutontactile, caché
Stockagecarte microSD
Passe-partoutaimanté
Le produit final

Le e-paper, comme une liseuse

  • Comme un Kindle, en couleur
  • Pas de rétroéclairage : il réfléchit la lumière
  • Une image fixe ne consomme rien
Photo : WrS.tm.pl, domaine public (Wikimedia Commons)
Le cahier des charges

Nos exigences

  • Un an sur une charge
  • Bluetooth d’abord, Wi-Fi en option
  • Mise à jour à distance
  • Des périphériques : NFC, accéléromètre, bouton caché, microSD
Chapitre 2 / 5

Les prototypes

Du Raspberry Pi à notre propre carte.

L’idée

Raspberry Pi

Python, et un écran e-paper : la preuve que l’image s’affiche.

Prototype 1

ESP32, fait main

Une puce qui sait dormir profondément.

Prototype 3

Avec notre fabricant

Notre propre carte, dans le boîtier imprimé en 3D, puis usiné en CNC.

Chapitre 3 / 5

Le firmware

Organiser un logiciel stateful, en C.

La stack technique

ESP-IDF

Le framework d’Espressif : bas niveau, en C, au plus près de la puce.

Pas de Linux, pas de processus, pas de garbage collector.

Le firmware

Une journée de dev

Coderen C
Compilermake dev-build
Flasherla carte, sur le bureau
Interagirl’écran, le bouton, le téléphone

En backend, on lit un JSON. Ici, on regarde l’objet réagir.

Le firmware

Architecturer un firmware

components/features/
├── display/
│   ├── ctrl_ble_display      Bluetooth
│   ├── ctrl_http_display     HTTP
│   ├── ctrl_mqtt_display     MQTT
│   ├── ctrl_cli_display      console
│   ├── svc_display           le cœur, et sa state machine
│   ├── drv_epd_el133uf3      l’écran UF3
│   └── drv_epd_el133uf1      l’écran UF1, une autre variante
└── playlist/
    ├── ctrl_ble_playlist     Bluetooth
    ├── ctrl_http_playlist    HTTP
    ├── svc_playlist          le cœur
    └── repo_playlist         les playlists, sur la carte SD

Une feature, un dossier : controllers, service, repository, drivers.

Le firmware

Le socle : la plateforme

displaynfcplaylistbatteryota…

chaque feature s’enregistre sur

  1. ble_hostBluetooth
  2. http_hostserveur HTTP
  3. console_hostconsole
  4. storage · sdcardcarte SD
  5. powerénergie
Le firmware

Features et socle : comment ils se parlent

display/ controllers svc_display drv_epd_el133uf3 playlist/ controllers svc_playlist repo_playlist nfc/ controllers svc_nfc drv_fm11nt083v bus d’événements DISPLAYED écoute MOUNTED plateforme ble_host http_host console_host storage · sdcard power

Dans une feature, les appels descendent. Entre features, des événements.

Le firmware

Comme dans Spring, plus un Driver

  1. ControllerBluetooth, HTTP, MQTT, consoleaucune logique
  2. Serviceles règles métier, la state machine
  3. Repositoryle stockage : flash, carte SD
  4. Driverla puce, l’écran, les capteursnouveau
appels
événements
Le firmware

Des state machines partout

Bluetooth HTTP console ✓ accepté ✗ refusé ✗ refusé svc_display fsm_try IDLE QUEUED RENDERING I SVC_DISPLAY: IDLE, prêt I SVC_DISPLAY: IDLE --show(src=ble)--> QUEUED I SVC_DISPLAY: QUEUED --show(src=ble)--> RENDERING W SVC_DISPLAY: show(src=http) rejected: still RENDERING W SVC_DISPLAY: show(src=cli) rejected: still RENDERING I SVC_DISPLAY: RENDERING --ESP_OK(src=ble)--> IDLE I SVC_DISPLAY: IDLE --show(src=http)--> QUEUED I SVC_DISPLAY: QUEUED --show(src=http)--> RENDERING I SVC_DISPLAY: RENDERING --ESP_OK(src=http)--> IDLE
if (!fsm_try(&s_fsm, QUEUED, "show", src)) return ESP_ERR_NOT_FINISHED;

20 machines, 67 états. Combinés : plus de 5 milliards.

Le firmware

Un code, plusieurs cartes

sdkconfig.defaults                  commun à toutes les cartes
sdkconfig.esp32s3-proto1.EL133UF3   puce · révision · écran
sdkconfig.esp32s3-proto2.EL133UF3   + LED avant, buzzer

make dev-build BOARD=esp32s3-proto2.EL133UF3

Une carte, un profil. Un écran, un driver. Le reste ne bouge pas.

Chapitre 4 / 5

L’énergie

Tenir un an sur une charge.

Exigence · un an sur une charge

D’abord, mesurer

Dix secondes de courant mesuré au PPK2 : un plancher bas et des pics réguliers, un par réveil
4,23 mAen moyenne
72 mAau pic
10 sde mesure

Un Nordic PPK2, en série avec la batterie, USB débranché.

Photo du PPK2 : Thomas Amberg, CC BY-SA 2.0, recadrée (Wikimedia Commons)
Exigence · un an sur une charge

Les leviers, un par un

  1. Sans rienCPU à 160 MHz, qui attend27,6 mA
  2. + CPU adaptatifdescend à 40 MHz13,2 mA÷ 2
  3. + Light sleepdort entre deux tâches240 µA÷ 115
  4. + Deep sleepjusqu’au prochain réveil7 µA÷ 3 900

Et pour y rester : des interruptions plutôt que du polling, et un nom par raison de rester éveillé.

Puce ESP32-S3 seule, au repos, 3,3 V, 25 °C. Source : datasheet ESP32-S3 v2.2, Espressif. Barres à l’échelle.
Chapitre 5 / 5

Les mises à jour

Corriger un cadre déjà installé chez quelqu’un.

Exigence · mise à jour à distance

Mettre à jour : du blue-green dans la puce

1 · Télécharger

A · v1actif

B · v2écriture

2 · Redémarrer

A · v1en attente

B · v2probation

3 · Health check OK

A · v1secours

B · v2actif

3 · Échec

A · v1actif

B · v2rejeté

Par Bluetooth : 1,9 Mo en 30 s. Ou tout seul, en HTTP.

Observer la flotte

Télémétrie : débugger à l’échelle

Le cadrelogs, crashs, métriques
Alloyretire les données perso
GrafanaLoki, Prometheus
Le dashboard Grafana de la flotte : les cadres, leurs versions, leurs raisons de démarrage et leur batterie

Seulement en développement, ou si un client l’active pour qu’on l’aide. Désactivée en production.

Conclusion

Et maintenant ?

D’une idée à un Kickstarter, en 15 mois.

Conclusion

Où on en est

  1. Juil. 2025L’idée
  2. Août – nov.Écran, boîtier, électronique
  3. Déc. 2025Prototype 1
  4. Début 2026Prototype 2
  5. Mai – juil.Prototypes 3 et 4
  6. Sept. 2026Validation en usine
  7. 14 oct. 2026Kickstarteren cours
Questions ?

Merci !

Inkolor est sur Kickstarter en ce moment.

getreeveel.com · Kickstarter · alexiscouvreur.fr

- Ce qui change quand on ne peut plus juste redéployer

- Backend et plateforme : du code déployé en quelques secondes - Platform engineer à la Banque Nationale : GitHub pour des milliers d'ingénieurs - Sablier : open source, plus de 3 000 étoiles - Reeveel depuis 2025 : on fabrique un objet

- Le résultat final d'abord, le chemin ensuite - E-paper couleur 13,3", ESP32, Bluetooth et Wi-Fi - NFC, accéléromètre, bouton caché, microSD, passe-partout aimanté - Et tout ça sur batterie : c'est ce qui rend le reste difficile

- Comme un Kindle, en couleur et en grand - Réfléchit la lumière : lisible en plein soleil - Une image fixe ne consomme rien, en changer coûte (on y revient)

- Un an : dormir presque tout le temps - Bluetooth, sans compte ni cloud. Le Wi-Fi en option : tout marche sans réseau - Mise à jour : on livrera des bugs, sans jamais bloquer un cadre - Périphériques : chacun consomme, chacun peut réveiller le cadre - De l'idée au Kickstarter : 15 mois

- Raspberry Pi et Python : l'écran marche, rien de plus - Un proof of concept, fait pour être jeté

- Une ESP32 câblée à la main - Le vrai critère apparaît : savoir dormir profondément, presque tout le temps

- Boîtier : un cadre du commerce, puis notre imprimante 3D (4 versions en 2 semaines) - Le fabricant prend le relais : impression 3D, puis usinage CNC - Sur la photo : la batterie, notre carte, la nappe de l'écran - Les aimants du passe-partout : premier essai raté, couche trop fine

- Le framework d'Espressif, en C, bas niveau - Pas de Linux, pas de processus, pas de GC : des tâches, la mémoire à la main

- Backend : coder, relancer, curl, lire un JSON - Ici : compiler, flasher en USB, regarder l'écran, tourner le cadre, approcher le téléphone - La console série : on tape une commande, la carte répond - Un build de dev instrumenté, un build de prod pour mesurer - [ressenti : voir un objet réagir à son code] - Transition : comment on organise un vrai logiciel là-dessus ?

- La vraie difficulté : un logiciel stateful, l'état vit dans la puce - Une image peut venir de 5 endroits, parfois en même temps - On range par feature : un dossier - Display : 4 controllers, 1 service, 1 driver par écran (UF3, UF1) - Playlist : un repository sur la carte SD

- Le vertical, les features. L'horizontal, la plateforme - Configuré une fois : Bluetooth, HTTP, console, carte SD, énergie - Chaque feature vient s'y enregistrer - Règle : on n'entre dans la plateforme que quand une 2e feature en a besoin

- Display publie DISPLAYED, la playlist passe à l'image suivante - Storage publie MOUNTED, le repository retrouve ses fichiers - Dans une feature : des appels. Entre features : des événements - Comme un bus de messages, dans la même puce

- Controller, Service, Repository : comme dans Spring - Plus une couche : le Driver, qui parle au matériel - Controller sans logique : une règle métier écrite une seule fois - Les appels descendent, les événements remontent - Vérifié par le build et un linter, pas en review

- Avant : un booléen "busy" quelque part, et on devinait - L'animation : Bluetooth accepté, HTTP et console refusés pendant RENDERING, puis HTTP passe - Chaque refus est loggé, pas de verrou dans le code métier - 20 machines, 67 états : 5 milliards de combinaisons, l'explosion combinatoire - C'est pour ça qu'on éteint et rallume nos appareils - Chaque machine reste petite, testable seule

- Une carte, un profil : la puce, la révision, l'écran - De proto1 à proto2 : 4 lignes (LED avant, buzzer) - Un nouvel écran, c'est un nouveau driver. Le reste ne bouge pas - En backend : un adapter par fournisseur

- En backend, la latence. Ici, des microampères - Un multimètre ne suffit pas : un PPK2 en série avec la batterie - La mesure : 4,23 mA en moyenne, des pics à 72 mA, chacun est un réveil - De 10 µA à 150 mA : 4 ordres de grandeur sur la même courbe - USB débranché : il fausse la mesure

- Le point de départ : la puce allumée qui attend, réglage par défaut - Chaque levier s'ajoute au précédent : ÷ 2, puis ÷ 115, puis ÷ 3 900 - Chiffres de la puce seule : la carte, l'écran et la radio ajoutent le reste - Interruptions : sinon on se réveille pour vérifier les capteurs - Deep sleep : chaque réveil repart de zéro, le "éteindre et rallumer" gratuit - 3 niveaux de présence : connecté, éveillé (s'annonce en Bluetooth), endormi (radio coupée) - Chaque composant qui garde la puce éveillée doit dire pourquoi - Un rafraîchissement coûte 4 fois plus que prévu : 1 ou 2 images par jour, une décision produit

- La mise à jour à distance, c'est du blue-green : vous connaissez déjà - Deux emplacements : on écrit B pendant que A tourne, on redémarre, B en probation - Échec : retour automatique sur A - Pas d'orchestrateur : le firmware se valide lui-même - Deux chemins : le téléphone en Bluetooth, ou le cadre seul en HTTP

- Au bureau, la console série. Chez quelqu'un, impossible - OpenTelemetry, comme vos services : logs, crashs, métriques - Notre propre passerelle : pas de clé d'API dans un firmware public - Seulement en dev, ou si un client l'active pour qu'on l'aide : désactivée en production - Le dashboard : versions, raisons de réveil, crashs, batterie - La télémétrie réveille le cadre une fois par jour au plus - Les versions cohabitent : on débugge plusieurs versions à la fois

- On a vu le comment. Maintenant, où on en est

- Juillet 2025 : l'idée - 4 prototypes, les 2 derniers avec le fabricant - Le Kickstarter est en ligne depuis le 14 octobre - La suite : tests, production cet hiver, livraisons au printemps - Pour moi : mettre à jour des cadres chez des gens que je ne connais pas

- Merci, place aux questions