API pública: iDryer::Link¶
iDryer::Link é o único ponto de entrada para o desenvolvedor embarcado. A fachada esconde toda a pilha do SDK: WiFi/Improv, máquina de estados em nuvem, reclamação HTTP, MQTT, WebSocket local, NVS. O produto apenas precisa preencher campos telemetry/status, registrar callbacks e chamar begin()/loop().
Ciclo de vida¶
Esqueleto típico de main.cpp:
#include <iDryer.h>
#include <runtime/idryer_runtime.h> // necessário apenas se setCommandHandler for usado
static const iDryer::Config CFG = {
.deviceType = iDryer::DeviceType::StorageLink,
.unitsCount = 1,
.hasAirTemp = true,
.hasAirHumidity = true,
.hasHeaterTemp = false,
.hasHeaterPower = false,
.hasFanStatus = false,
.hasScales = false,
.hasRfid = false,
.telemetryPeriodMs = 10000,
.statusPeriodMs = 0,
.hardwareVersion = "1.0",
.firmwareVersion = "1.0.0",
};
static iDryer::Link link(CFG);
void setup() {
link.begin();
// setCommandHandler — estritamente APÓS begin(): begin() instala seu próprio dispatcher
link.runtime()->setCommandHandler(handleCommand);
}
void loop() {
link.loop();
link.telemetry.airTempC[0] = sensor.readTemp();
link.telemetry.airHumidityPct[0] = sensor.readHumidity();
}
Configuração: iDryer::Config¶
Preenchida uma única vez em main.cpp, passada para o construtor de Link. Todos os campos usam inicialização agregada (inicializadores designados em C++).
| Campo | Tipo | Propósito | Nota |
|---|---|---|---|
deviceType |
DeviceType |
Tipo de dispositivo | obrigatório |
unitsCount |
uint8_t |
Número de unidades (câmaras), 1..MAX_UNITS (4) |
obrigatório |
hasAirTemp |
bool |
Sensor de temperatura do ar presente | false = campo omitido do JSON |
hasAirHumidity |
bool |
Sensor de umidade presente | false = campo omitido do JSON |
hasHeaterTemp |
bool |
Sensor de temperatura do aquecedor presente | — |
hasHeaterPower |
bool |
Sensor de potência do aquecedor presente | — |
hasFanStatus |
bool |
Status do ventilador presente | — |
hasScales |
bool |
Balança presente | — |
hasRfid |
bool |
Leitor RFID presente | — |
telemetryPeriodMs |
uint32_t |
Período de publicação automática de Telemetry (ms) |
0 = não publicar |
statusPeriodMs |
uint32_t |
Período de publicação automática de Status (ms) |
0 = não publicar |
hardwareVersion |
const char* |
String de versão de hardware | obrigatório |
firmwareVersion |
const char* |
String de versão de firmware | obrigatório |
Quais integrações entram na imagem é definido por
IDRYER_WITH_HA,IDRYER_WITH_BAMBUeIDRYER_WITH_MOONRAKERnoplatformio.inido produto. Uma integração desativada não existe na imagem nem emintegrations/status; o comando do portal para ela é rejeitado.
Classe iDryer::Link¶
Construtor¶
Recebe a configuração por referência const. CFG deve existir por toda a vida útil do objeto (normalmente static const).
Métodos¶
begin()¶
Inicializa toda a pilha do SDK: WiFi/Improv, máquina de estados em nuvem, reclamação HTTP, MQTT, WebSocket local, persistência NVS.
Chamar uma única vez em setup(). Retorna true na inicialização bem-sucedida.
loop()¶
O único tick necessário. Atende WiFi/MQTT/LocalAccess e auto-publica telemetria e status em seus temporizadores.
Chamar em cada iteração de loop(). Sem essa chamada a conexão não é mantida.
Fonte: iDryer-Storage/src/main.cpp:253, iHeater-link/src/main.cpp:381.
publishTelemetryNow()¶
Publica imediatamente o estado atual de link.telemetry, independentemente do temporizador telemetryPeriodMs.
publishStatusNow()¶
Publica imediatamente o estado atual de link.status. Use após processar um comando quando o novo estado deve ser refletido no portal imediatamente.
raiseEvent()¶
Publica um evento para o tópico idryer/{serial}/events. Enviado imediatamente.
| Parâmetro | Tipo | Propósito |
|---|---|---|
severity |
EventKind |
Info / Warning / Error |
event |
const char* |
Código do evento, p.ex. "OVERHEAT", "SESSION_COMPLETE" |
message |
const char* |
Texto de depuração arbitrário |
unitId |
uint8_t |
Índice de unidade (0..unitsCount-1) ou 0xFF para dispositivo |
onCommand()¶
Registra um tratador de comando por nome. Dispara quando chega commands/{name}
com JSON arbitrário por MQTT ou Local WS.
CommandCallback = void (*)(JsonObjectConst data)
O nome é arbitrário: drying, stop, storage, get_config, set,
profile, led.pulse — qualquer um serve. Retorna false em caso de estouro
(no máximo 12 comandos) ou argumentos inválidos.
Os comandos internos link_integration, bambu_apply e ping são tratados
pela própria biblioteca e não podem ser interceptados. Ainda assim você pode
registrar um tratador com o mesmo nome — ele roda depois do da biblioteca.
Importante: se runtime()->setCommandHandler(...) estiver definido, o
despachante completo intercepta tudo e os tratadores registrados aqui não são
chamados.
onIntegrationStatus()¶
Chamado quando o estado de conexão de uma integração muda (HA, Bambu, Moonraker). Callback opcional.
IntegrationStatusCallback = std::function<void(const iDryer::IntegrationStatus&)>
onClaimComplete()¶
Chamado uma vez, quando o backend confirma o vínculo — antes de o MQTT subir.
ClaimCompleteCallback = void (*)(const char* deviceId)
isBound()¶
true — o aparelho tem um segredo permanente e está vinculado a uma conta.
false — modo de configuração: o aparelho espera um token de vínculo vindo do
aplicativo ou do flasher. O produto precisa disso para mostrar o estado à
pessoa.
handleRevoke()¶
Trata o comando REVOKE do portal: apaga o segredo e devolve o aparelho à
espera de um token. Chamado a partir de onCommand("revoke").
mqttKey()¶
A chave sob a qual o aparelho publica em MQTT: o número de série do módulo antes do vínculo, o do controlador depois.
isOnline()¶
Retorna true se o dispositivo está registrado e a sessão MQTT está ativa.
serial()¶
Número de série do dispositivo (string do NVS, atribuído durante reclamação). String vazia antes de reclamação ser concluída.
seedWifiCredentialsIfEmpty()¶
Escreve credenciais WiFi em NVS apenas se ainda não forem definidas. Chamar antes de begin(). Usado em ambientes de desenvolvimento com credenciais codificadas.
setWifiCredentials()¶
Sempre sobrescreve credenciais WiFi em NVS. Auxiliar de desenvolvimento e reprovisionamento forçado.
eraseClaimAndRestart()¶
Remove o token do dispositivo do NVS e reinicia o chip. Após reinicialização o dispositivo não é reclamado — o fluxo de reclamação automática é iniciado novamente. Esta função não retorna.
integrationsManager()¶
Saída para o gerenciador de integrações — para fiação do lado do produto (callbacks de câmara virtual Moonraker, status da impressora Bambu, etc.).
Requer #include <integrations/common/link_integrations_manager.h>.
// iHeater-link/src/main.cpp:337
device().integrationsManager()->setVirtualChamberCallback(onVirtualChamberUpdate);
mqttClient()¶
Saída para o cliente MQTT do SDK — para componentes que publicam seus próprios tópicos ou se integram ao roteamento de comandos (p.ex., MenuBridge).
Requer #include <mqtt/mqtt_client.h>.
devicePublisher()¶
Saída para o auxiliar de publicação dupla — envia uma carga para MQTT e Local WS simultaneamente. Use para respostas de produto que precisam chegar ao cliente LAN da mesma forma que a telemetria publicada automaticamente.
runtime()¶
Saída para o runtime do SDK — usado para definir um manipulador de comando completo em vez do dispatcher de fachada. Após setCommandHandler(...) os tratadores registrados via onCommand() não são mais chamados pela via MQTT.
Importante: chamar estritamente após begin() — begin() instala seu próprio dispatcher, que deve ser sobrescrito.
// iDryer-Storage/src/main.cpp:249
link.runtime()->setCommandHandler(handleCommand);
// Assinatura do manipulador:
// void handleCommand(const char* cmd, JsonObjectConst data);
Requer #include <runtime/idryer_runtime.h>.
Campos de telemetria¶
Preenchidos pelo produto em loop(). O SDK os lê no temporizador telemetryPeriodMs e publica para MQTT e Local WS.
| Campo | Tipo | Flag de configuração | Propósito |
|---|---|---|---|
telemetry.airTempC[unitId] |
float |
hasAirTemp |
Temperatura do ar, °C |
telemetry.airHumidityPct[unitId] |
float |
hasAirHumidity |
Umidade, % |
telemetry.heaterTempC[unitId] |
float |
hasHeaterTemp |
Temperatura do aquecedor, °C |
telemetry.heaterPower01[unitId] |
float |
hasHeaterPower |
Potência do aquecedor, 0.0..1.0 |
telemetry.fanOn[unitId] |
bool |
hasFanStatus |
Status do ventilador |
telemetry.weightG[unitId] |
uint16_t |
hasScales |
Peso, gramas |
// iDryer-Storage/src/main.cpp:267
link.telemetry.airTempC[0] = r.temperature;
link.telemetry.airHumidityPct[0] = r.humidity;
unitId = 0 para a primeira (ou única) unidade. O índice deve ser < Config.unitsCount.
Campos Status — mesma estrutura, mas para estado operacional:
| Campo | Tipo | Propósito |
|---|---|---|
status.mode[unitId] |
UnitMode |
Modo atual da unidade |
status.targetTempC[unitId] |
float |
Temperatura alvo |
status.durationS[unitId] |
uint32_t |
Duração solicitada, s (0 = indefinida) |
status.elapsedS[unitId] |
uint32_t |
Tempo decorrido desde o início da sessão, s |
// iHeater-link/src/main.cpp:229
device().status.mode[0] = iDryer::UnitMode::Drying;
device().status.targetTempC[0] = cmd.targetTempC;
device().publishStatusNow();
Registro de callback via runtime¶
Se você precisar de controle total sobre comandos recebidos (p.ex., o produto processa get_config, set, invoke não-padrão):
// Assinatura — de idryer_runtime.h
void handleCommand(const char* cmd, JsonObjectConst data);
// Registro — estritamente após link.begin()
link.runtime()->setCommandHandler(handleCommand);
cmd — string de comando ("set", "invoke", "ping", "get_config").
data — ArduinoJson JsonObjectConst com carga útil.
Com essa abordagem, os tratadores de onCommand() não são chamados pela via MQTT — o produto trata os comandos sozinho.
Enumerações¶
iDryer::DeviceType¶
| Valor | Numérico | Propósito |
|---|---|---|
Unknown |
0 | Nenhum / indefinido |
Dryer |
1 | Secador (iDryer LINK) |
Heater |
2 | Aquecedor |
StorageLink |
4 | Storage Link (ESP32-C3 + LED) |
IHeaterLink |
5 | iHeater Link |
iDryer::UnitMode¶
Idle, Drying, Storage, Profile, Fault, Unknown
iDryer::EventKind¶
Info, Warning, Error
iDryer::RequestKind¶
Start, Stop, Storage, Find, ClearErrors
iDryer::IntegrationKind¶
Ha, Bambu, Moonraker
iDryer::IntegrationState¶
Disabled, Idle, Connecting, Online, ConfigMissing, Error
Quando aprofundar¶
A fachada é suficiente para a maioria das tarefas. Se você precisar trabalhar abaixo do nível da fachada — com idryer::IdryerRuntime, idryer::MqttClient, idryer::cloud::LinkIntegrationsManager — consulte a seção Arquitetura.