Ajouter un nouveau produit basé sur idryer-core¶
Utilisez ce guide quand vous construisez un nouveau produit sur idryer-core : séchoir à filament, bloc chauffant, éclairage, capteur ou autre module. Il montre ce que le core fait pour vous et ce que le code du produit doit ajouter.
Un exemple complet qui compile est l'armoire de stockage chauffée de la documentation Build-Your-Own-iDryer (example/09-cabinet) : il parcourt tout ce qui figure sur cette page.
Sur quoi repose un produit¶
Un produit parle au core par un seul objet — la façade iDryer::Link (<iDryer.h>). Dans s_link.begin() et s_link.loop(), le core :
- reçoit le réseau Wi-Fi de l'application iDryer par les ondes (ESPTouch) ou de l'installateur web par USB (Improv) et maintient la connexion ;
- associe l'appareil à un compte : attend un jeton d'association à usage unique — venant de l'application par le réseau local ou par le port série (
PAIR_TOKEN:<jeton>) — et l'échange sur le portail contre un secret permanent ; - se connecte à MQTT et publie
telemetryetstatusselon les périodes deConfig; - s'annonce sur le réseau local (mDNS
_idryer._tcp) et accepte les commandes de l'application par WebSocket ; - publie le card manifest de la carte de l'appareil.
Les classes de plus bas niveau (IdryerRuntime, CloudStateMachine, LocalAccess et d'autres) sont des éléments internes du core : le jeton d'association n'atteint la partie cloud qu'à l'intérieur d'iDryer::Link. Construisez le produit sur la façade.
1. Projet¶
idryer-core va dans lib/idryer-core/ (copie ou lien symbolique) ; PlatformIO prend les bibliothèques du core dans son library.json. platformio.ini minimal :
[env:my-device]
platform = espressif32
framework = arduino
board = esp32-c3-devkitm-1
; transport ESP8266 des dépendances d'espMqttClient : ne compile pas sur ESP32
lib_ignore = ESPAsyncTCP
build_flags =
-DIDRYER_API_BASE='"https://portal.idryer.org/api"'
-DMQTT_BROKER='"mqtt.idryer.org"'
-DMQTT_PORT=8883
-DMQTT_USE_TLS=1
Sans lib_ignore = ESPAsyncTCP, la compilation échoue dans ESPAsyncTCP.cpp ; sans MQTT_BROKER et MQTT_PORT, le core ne compile pas.
2. Firmware minimal¶
#include <iDryer.h>
static const iDryer::Config CFG = {
.deviceType = iDryer::DeviceType::Unknown, // pas un produit iDryer : la carte vient du manifeste
.unitsCount = 1,
.hasAirTemp = true,
.hasAirHumidity = true,
.hardwareVersion = "1.0",
.firmwareVersion = "0.1.0",
.model = "My Device",
};
static iDryer::Link s_link(CFG);
void setup() {
s_link.begin();
// Appareil dissocié sur le portail : effacer le secret, attendre une nouvelle association.
s_link.onCommand("revoke", [](JsonObjectConst) { s_link.handleRevoke(); });
}
void loop() {
s_link.loop();
s_link.telemetry.airTempC[0] = readTemp(); // vos fonctions de capteurs
s_link.telemetry.airHumidityPct[0] = readHumidity();
}
s_link.begin() démarre le Wi-Fi, l'association, MQTT et l'accès local ; s_link.loop() doit tourner en permanence, sans delay(). La commande revoke vient du portail quand l'appareil est dissocié du compte : handleRevoke() efface le secret, et l'appareil attend un nouveau jeton d'association.
3. Wi-Fi et association — rien dans le code¶
Le firmware ne contient ni le mot de passe du réseau ni les données du compte. L'utilisateur connecte l'appareil dans l'application iDryer : Connecter un nouvel appareil → étape Wi-Fi (l'application envoie le réseau par ESPTouch) → étape Association (l'application trouve l'appareil par mDNS, obtient un jeton à usage unique auprès du portail et le remet à l'appareil ; l'appareil active lui-même le jeton sur le portail). Tant que le Wi-Fi n'est pas établi, le port série reste muet : le core le réserve à l'installateur web (Improv).
Pas à pas, avec le journal attendu — Build-Your-Own-iDryer, chapitre « Démarrage du firmware sur le core ».
4. Données : télémétrie et statut¶
- Les flags
has*deConfigdéfinissent quels champs du vocabulaire partent dans la télémétrie et quelles cellules apparaissent sur la carte. - Écrivez les valeurs dans
s_link.telemetry(airTempC,airHumidityPct,heaterTempC,heaterPower01,fanOn,servoOpen) ets_link.status(mode,targetTempC,durationS,elapsedS) ; le core les publie selon les périodes deConfig, ets_link.publishStatusNow()envoie le statut tout de suite. - Un champ à vous — via
s_link.onTelemetryPublish(), sur la carte — vias_link.card().sensor(); voir Carte de l'appareil.
5. Réglages : le menu¶
Les réglages sont décrits dans src/menu/menu.yaml ; le générateur menu_gen.py en tire du code C++, le stockage en NVS et le JSON du menu (Le menu comme protocole). Le code du produit :
- charge le menu avant
s_link.begin():menu.initDefaults(),menu.loadFromNVS(),menu_sync_state_to_cache(); - le publie avec
menu_buildFullJson()ets_link.devicePublisher()->publishConfigRaw()— au passage en ligne et sur la commandeget_config; - applique la commande
setavecmenu_apply_by_bind()(valeur, NVS et cache d'un coup) et republie le menu.
Le code complet — Build-Your-Own-iDryer, chapitre « Menu depuis YAML ».
6. Carte de l'appareil¶
Ce que montre la carte et les opérations qu'elle lance se déclarent avec s_link.card() — Carte de l'appareil : le card manifest.
7. Contrat¶
Quand vous ajoutez des topics ou modifiez des payloads :
- mettez à jour
contracts/mqtt_contract.yaml; - lancez
contracts/regen.shet committez les fichiers générés.
Appareils à deux puces¶
Pour un ESP32 qui travaille avec un contrôleur séparé (par exemple RP2040) par UART, le core fournit le pont UART idryer_uart.h ; la référence qui fonctionne est le firmware idryer-link.