Como adicionar um novo produto baseado em idryer-core¶
Use este guia quando for criar um novo produto sobre o idryer-core: um secador de filamento, um bloco de aquecimento, uma iluminação, um sensor ou outro módulo. Ele mostra o que o core faz por você e o que o código do produto precisa adicionar.
Um exemplo completo que compila é o gabinete de armazenamento aquecido da documentação Build-Your-Own-iDryer (example/09-cabinet): ele passa por tudo o que está nesta página.
Sobre o que um produto é construído¶
Um produto conversa com o core por um único objeto — a fachada iDryer::Link (<iDryer.h>). Dentro de s_link.begin() e s_link.loop(), o core:
- recebe a rede Wi-Fi do app iDryer pelo ar (ESPTouch) ou do instalador web por USB (Improv) e mantém a conexão;
- vincula o dispositivo a uma conta: espera um token de vinculação de uso único — do app pela rede local ou pela porta serial (
PAIR_TOKEN:<token>) — e troca no portal por um segredo permanente; - conecta ao MQTT e publica
telemetryestatusnos períodos deConfig; - se anuncia na rede local (mDNS
_idryer._tcp) e aceita comandos do app por WebSocket; - publica o card manifest do cartão do dispositivo.
As classes de nível mais baixo (IdryerRuntime, CloudStateMachine, LocalAccess e outras) são internas do core: o token de vinculação só chega à parte de nuvem dentro de iDryer::Link. Construa o produto sobre a fachada.
1. Projeto¶
O idryer-core vai em lib/idryer-core/ (cópia ou link simbólico); o PlatformIO pega as bibliotecas do core do library.json dele. platformio.ini mínimo:
[env:my-device]
platform = espressif32
framework = arduino
board = esp32-c3-devkitm-1
; transporte do ESP8266 que vem das dependências do espMqttClient: não compila no 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
Sem lib_ignore = ESPAsyncTCP o build falha em ESPAsyncTCP.cpp; sem MQTT_BROKER e MQTT_PORT o core não compila.
2. Firmware mínimo¶
#include <iDryer.h>
static const iDryer::Config CFG = {
.deviceType = iDryer::DeviceType::Unknown, // não é produto iDryer: o cartão vem do manifesto
.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();
// Dispositivo desvinculado no portal: apagar o segredo e aguardar nova vinculação.
s_link.onCommand("revoke", [](JsonObjectConst) { s_link.handleRevoke(); });
}
void loop() {
s_link.loop();
s_link.telemetry.airTempC[0] = readTemp(); // suas funções de sensores
s_link.telemetry.airHumidityPct[0] = readHumidity();
}
s_link.begin() sobe o Wi-Fi, a vinculação, o MQTT e o acesso local; s_link.loop() precisa rodar o tempo todo, sem delay(). O comando revoke chega do portal quando o dispositivo é desvinculado da conta: handleRevoke() apaga o segredo e o dispositivo espera um novo token de vinculação.
3. Wi-Fi e vinculação — nada no código¶
O firmware não tem a senha da rede nem dados da conta. O usuário conecta o dispositivo no app iDryer: Conectar novo dispositivo → passo Wi-Fi (o app envia a rede por ESPTouch) → passo Vinculação (o app encontra o dispositivo por mDNS, pega no portal um token de uso único e entrega ao dispositivo; o próprio dispositivo ativa o token no portal). Enquanto o Wi-Fi não sobe, a porta serial fica em silêncio: o core a reserva para o instalador web (Improv).
Passo a passo e com o log esperado — Build-Your-Own-iDryer, capítulo “Início do firmware no core”.
4. Dados: telemetria e status¶
- As flags
has*emConfigdefinem quais campos do vocabulário vão para a telemetria e quais células aparecem no cartão. - Grave os valores em
s_link.telemetry(airTempC,airHumidityPct,heaterTempC,heaterPower01,fanOn,servoOpen) es_link.status(mode,targetTempC,durationS,elapsedS); o core os publica nos períodos deConfig, es_link.publishStatusNow()envia o status na hora. - Um campo próprio — pelo
s_link.onTelemetryPublish(), no cartão — pelos_link.card().sensor(); veja Cartão do dispositivo.
5. Configurações: o menu¶
As configurações são descritas em src/menu/menu.yaml; o gerador menu_gen.py produz o código C++, o armazenamento na NVS e o JSON do menu (Menu como Protocolo). O código do produto:
- carrega o menu antes de
s_link.begin():menu.initDefaults(),menu.loadFromNVS(),menu_sync_state_to_cache(); - publica com
menu_buildFullJson()es_link.devicePublisher()->publishConfigRaw()— ao ficar online e no comandoget_config; - aplica o comando
setcommenu_apply_by_bind()(valor, NVS e cache de uma vez) e publica o menu de novo.
O código completo — Build-Your-Own-iDryer, capítulo “Menu a partir de YAML”.
6. Cartão do dispositivo¶
O que o cartão mostra e quais operações ele dispara é declarado com s_link.card() — Cartão do dispositivo: o card manifest.
7. Contrato¶
Quando você adiciona topics novos ou muda payloads:
- atualize
contracts/mqtt_contract.yaml; - rode
contracts/regen.she faça commit dos arquivos gerados.
Dispositivos de dois chips¶
Para um ESP32 que trabalha com um controlador separado (por exemplo, RP2040) via UART, o core tem a ponte UART idryer_uart.h; a referência que funciona é o firmware idryer-link.