Firmware & OTA
Builds serveur, firmware custom dans le navigateur, flash et mises à jour OTA
PNeX compile, flashe et met à jour le firmware sans quitter la plateforme.
Trois familles d'appareils
| Générique PneX | Custom (IDE) | Prédéfinie (prête à l'emploi) | |
|---|---|---|---|
| Ce que vous faites | Configurer entrées et sorties depuis l'UI | Écrire votre propre main.cpp dans l'éditeur intégré | Brancher une carte dont PNeX garantit la compatibilité |
| Code | Aucun | Le vôtre, versionné dans PNeX | Aucun : un firmware dédié maintenu par PNeX, imposé par le modèle — aucun choix de firmware |
| Valeurs | États des pins (numérique, analogique, PWM) | Vos propres métriques (temp, humidity…) et commandes | Fonctions prédéfinies (humidité du sol, vidéo live…) |
| Usage type | Relais, boutons, entrées analogiques | Capteurs I2C/SPI/1-Wire, logique locale, valeurs calculées | Un capteur ou une caméra clé en main |
Les trois sont compilées par appareil côté serveur, partagent le même transport chiffré et la même OTA, et se flashent de la même façon. Un firmware custom tourne sur la carte d'un modèle générique : vous choisissez son projet au provisioning de l'appareil. Les ordinateurs et Raspberry Pi ne sont pas des cartes : ils rejoignent PNeX comme agents edge.
Service de build
Le serveur compile le firmware à la demande par appareil : les sources PlatformIO sont embarquées dans le binaire serveur, et le résultat est stocké comme artefact (PostgreSQL ou S3-compatible). SoCs supportés : ESP8266, ESP32, ESP32-C3, ESP32-S3, ESP32-C6.
Valeurs injectées en env de build, propres à l'appareil :
- Wi-Fi (depuis le référentiel de l'organisation), nom du point d'entrée devices, identifiant de l'appareil et token ;
- clé partagée du lien Noise ;
- certificat et clé client (
PNEX_CLIENT_CERT/PNEX_CLIENT_KEY), émis par la CA de l'organisation à chaque build ; - CA épinglée du point d'entrée devices (
PNEX_CA_CERT), obligatoire : le firmware n'a plus desetInsecureet le serveur refuse un build sans CA ; - clé publique OTA de l'instance (
PNEX_OTA_PUBKEY), qui vérifie la signature des mises à jour.
Le firmware ne parle que wss:// et https:// : il n'existe plus de build en clair
(l'option WS_SSL a disparu).
Les builds passent par une file de jobs, exécutée par un builder dédié ; le serveur
d'API n'embarque aucune toolchain. Le mot de passe Wi-Fi vit dans le
coffre de secrets : un job en file ne porte
qu'une référence, et le builder résout la valeur au moment de compiler. Le Wi-Fi
vient toujours du référentiel de l'organisation, jamais d'une saisie dans le build :
l'API de build prend device_id, wifi_credential_id et pnex_host.
Chaque binaire est propre à son appareil — il n'existe pas d'image générique réutilisable. Changer un mot de passe Wi-Fi ne recompile donc rien : les appareils déjà flashés gardent l'ancien identifiant jusqu'à ce que vous les recompiliez et reflashiez, un par un.
Firmware custom
Devices › Firmware custom est un éditeur C++ dans le navigateur. Vous
écrivez un seul main.cpp sur la bibliothèque PNeX ; la carte vient de l'appareil,
et la bibliothèque est toujours la version livrée avec votre serveur — rien à
installer.
L'API PNeX
#include <Pnex.h>
#include <Adafruit_BME280.h>
PnexDevice pnex;
Adafruit_BME280 bme;
void setup() {
Wire.begin(21, 22);
bme.begin(0x76);
// Announced to the server; each metric becomes its own telemetry series.
pnex.addMetric("temp", "°C");
pnex.addMetric("humidity", "%");
// Callable from PNeX; the return value becomes the acknowledgement.
pnex.onCommand("calibrate", [](JsonVariantConst args) {
return true;
});
pnex.begin();
}
unsigned long last = 0;
void loop() {
pnex.loop(); // every iteration: heartbeat, OTA, commands
if (millis() - last >= 10000) {
last = millis();
pnex.publish("temp", bme.readTemperature());
pnex.publish("humidity", bme.readHumidity());
}
}addMetric(id, unité)— déclare une métrique avantbegin()(16 au plus). Le serveur ne stocke que les métriques annoncées par le firmware ; le reste est jeté.publish(id, valeur)— envoie une valeur (float, entier,boolou chaîne courte). Elle arrive dans la télémétrie de l'appareil comme n'importe quelle mesure, prête pour les flows, les tableaux de bord et les alertes.onCommand(nom, callback)— déclare une commande (8 au plus). L'éditeur de firmware liste les commandes annoncées par chaque appareil et peut les envoyer ; une commande inconnue du firmware reçoitunknown_command. Depuis un flow, les commandes custom s'envoient avec le nœud Appareil (écriture) (pas de nœud dédié).
Le menu API PneX de l'éditeur insère ces appels avec leur documentation.
Appelez pnex.loop() à chaque itération et ne bloquez jamais dans un long
delay() : passé le délai du heartbeat, le serveur marque l'appareil hors ligne et
ses sorties retombent dans leur état de sûreté. Utilisez le motif non bloquant
millis() ci-dessus.
Identifiants Wi-Fi, token, clé du lien chiffré et certificat client n'apparaissent jamais dans votre sketch : le builder les injecte pour l'appareil ciblé.
Bibliothèques
Les bibliothèques additionnelles viennent d'un catalogue curé aux versions épinglées : BME280, BMP280, SHT3x, AHT20, BH1750, INA219, ADS1x15, OneWire, DS18B20, DHT, NeoPixel, ESP32Servo, SSD1306 et Adafruit Unified Sensor. Ajoutez-les depuis le menu Lib ; le serveur refuse toute bibliothèque hors catalogue. Les clients réseau (MQTT, SDK HTTP cloud…) sont exclus volontairement : le transport, c'est le rôle de PNeX.
Révisions
Chaque enregistrement crée une révision immuable, avec son auteur et son message ; un contenu identique ne crée rien. L'historique permet de comparer et de restaurer. Chaque build garde la révision qu'il a compilée : vous savez toujours quel code tourne sur quel appareil — et pousser en OTA un build plus ancien, c'est un rollback.
Vérifier, puis déployer
- Vérifier compile votre code sans aucun vrai secret et ne garde aucun
artefact. Les erreurs du compilateur sont projetées sur les lignes de votre
main.cpp; celles levées dans une bibliothèque sont listées avec leur fichier. - Provisioning : dans l'assistant appareil, l'étape Modèle propose les projets firmware compatibles avec la puce de la carte. Un projet peut servir plusieurs appareils.
- Recompiler compile la dernière révision du projet pour cet appareil ; flashez ensuite en USB ou poussez en OTA.
Un projet ne peut pas être supprimé tant que des appareils l'utilisent.
Sécurité du build
Compiler du C++, c'est exécuter un outillage contrôlé par l'utilisateur sur le builder ; les builds custom sont donc encadrés :
- le
platformio.iniest généré par le serveur et jamais éditable : il porte les secrets et la politique de bibliothèques, et un script de build déclaré là exécuterait du code arbitraire ; - le
main.cppest contrôlé avant compilation : pas d'include absolu ni en.., pas de#embed, pas d'incbin— rien qui puisse faire entrer un fichier du builder dans le binaire que vous téléchargez. Taille plafonnée à 256 Ko ; - seules les bibliothèques du catalogue sont acceptées ;
- un sandbox bubblewrap optionnel exécute le compilateur sans réseau, dans son propre espace de processus, avec l'hôte en lecture seule hors du dossier du job et un répertoire personnel vide.
Le firmware custom est désactivé par défaut (PNEX_FIRMWARE_CUSTOM_ENABLED).
Activez le sandbox (PNEX_FIRMWARE_SANDBOX=bwrap) avant de l'ouvrir à des personnes
en qui vous n'avez pas entièrement confiance ; sans lui, réservez la fonction aux
installations mono-utilisateur. Les bibliothèques du catalogue sont préchargées
dans l'image du builder (depuis la 0.1.0-beta.4) : les builds compilent sans
réseau.
Flash USB
Flash en un clic, sans aucun toolchain sur votre machine :
- App web (Chrome, Edge) : Web Serial avec esptool-js. Le navigateur choisit le port série par un geste utilisateur, télécharge le binaire mergé depuis le store d'artefacts et flashe la carte.
- App desktop (Linux, Windows) : un esptool embarqué, à version épinglée, flashe la carte ; le port série est détecté automatiquement.
- App Android : pas de flash USB — flashez depuis un ordinateur, puis mettez à jour en OTA.

OTA
Les mises à jour sont un état désiré poussé aux appareils en ligne par WebSocket, ou
récupéré à la prochaine annonce. L'appareil télécharge l'image en HTTPS sur le point
d'entrée devices, avec son token dans l'en-tête Authorization: Bearer et son
certificat client, en vérifiant l'autorité épinglée.
- Intégrité : il calcule le SHA-256 pendant le téléchargement et le compare avant de basculer de partition.
- Signature : chaque image est signée en Ed25519 par l'instance, sur l'identifiant de l'appareil, la version et le SHA-256 ; l'appareil vérifie avec la clé publique compilée dans son firmware. Une image non signée ou mal signée est refusée ; lier l'appareil et la version empêche de rejouer l'image d'un autre appareil ou de ré-étiqueter une vieille version.
- Anti-rétrogradation : une version inférieure à celle qui tourne est refusée (une version égale reste admise pour un redéploiement).
- Clé de signature : dans le coffre de l'instance, jamais tournée automatiquement. La perdre impose de reflasher en USB tous les appareils.
En cas d'échec, la mise à jour est abandonnée et l'ancien firmware continue de tourner. Elle n'est réussie qu'une fois l'appareil redémarré sur la nouvelle version annoncée. Des gardes refusent les assignations dupliquées ou en cours, et les payloads surdimensionnés sur les SoCs contraints.

Écrans
La lib firmware supporte SSD1306 OLED et ST7735 TFT avec barre d'état, timeline de connexion et redraws à changement seulement. L'écran affiche l'état propre de l'appareil (connexion, valeurs live des pins, progression d'une mise à jour) ; aucun nœud de flow n'y écrit.



