Como adicionar um novo produto baseado em idryer-core¶
Use este guia quando criar um novo produto sobre idryer-core: um secador de filamento, um bloco de aquecimento, uma iluminação, um sensor ou outro módulo. Mostra o que o core faz por si e o que o código do produto tem de acrescentar.
Um exemplo completo que compila é o armário de armazenamento aquecido da documentação Build-Your-Own-iDryer (example/09-cabinet): percorre tudo o que está nesta página.
Em que assenta um produto¶
Um produto fala com o core através de 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 da aplicação iDryer pelo ar (ESPTouch) ou do instalador web por USB (Improv) e mantém a ligação;
- associa o dispositivo a uma conta: espera um token de vinculação de uso único — da aplicação pela rede local ou pela porta série (
PAIR_TOKEN:<token>) — e troca-o no portal por um segredo permanente; - liga-se ao MQTT e publica
telemetryestatuscom os períodos deConfig; - anuncia-se na rede local (mDNS
_idryer._tcp) e aceita comandos da aplicação por WebSocket; - publica o card manifest do cartão do dispositivo.
As classes de nível inferior (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¶
idryer-core vai para lib/idryer-core/ (cópia ou ligação simbólica); o PlatformIO obtém as bibliotecas do core a partir do seu library.json. platformio.ini mínimo:
[env:my-device]
platform = espressif32
framework = arduino
board = esp32-c3-devkitm-1
; transporte ESP8266 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 a compilação 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 é um 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 desassociado 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(); // as suas funções de sensores
s_link.telemetry.airHumidityPct[0] = readHumidity();
}
s_link.begin() arranca o Wi-Fi, a vinculação, o MQTT e o acesso local; s_link.loop() tem de correr sempre, sem delay(). O comando revoke chega do portal quando o dispositivo é desassociado 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 contém a palavra-passe da rede nem dados da conta. O utilizador liga o dispositivo na aplicação iDryer: Ligar novo dispositivo → passo Wi-Fi (a aplicação envia a rede por ESPTouch) → passo Vinculação (a aplicação encontra o dispositivo por mDNS, obtém do portal um token de uso único e entrega-o ao dispositivo; o próprio dispositivo ativa o token no portal). Enquanto o Wi-Fi não está ativo, a porta série fica em silêncio: o core reserva-a para o instalador web (Improv).
Passo a passo e com o log esperado — Build-Your-Own-iDryer, capítulo «Arranque do firmware no core».
4. Dados: telemetria e estado¶
- As flags
has*emConfigdefinem que campos do vocabulário vão para a telemetria e que células aparecem no cartão. - Escreva os valores em
s_link.telemetry(airTempC,airHumidityPct,heaterTempC,heaterPower01,fanOn,servoOpen) es_link.status(mode,targetTempC,durationS,elapsedS); o core publica-os com os períodos deConfiges_link.publishStatusNow()envia o estado de imediato. - Um campo próprio — através de
s_link.onTelemetryPublish(), no cartão — através des_link.card().sensor(); ver Cartão do dispositivo.
5. Definições: o menu¶
As definições descrevem-se em src/menu/menu.yaml; o gerador menu_gen.py produz o código C++, o armazenamento em 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-o com
menu_buildFullJson()es_link.devicePublisher()->publishConfigRaw()— ao ficar online e com o 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 que operações lança declara-se com s_link.card() — Cartão do dispositivo: o card manifest.
7. Contrato¶
Quando adicionar topics novos ou alterar payloads:
- atualize
contracts/mqtt_contract.yaml; - execute
contracts/regen.she faça commit dos ficheiros gerados.
Dispositivos de dois chips¶
Para um ESP32 que trabalha com um controlador separado (por exemplo, RP2040) por UART, o core tem a ponte UART idryer_uart.h; a referência funcional é o firmware idryer-link.