API pública: iDryer::Link¶
iDryer::Link é o único ponto de entrada para o programador do sistema embarcado. A fachada esconde toda a pilha SDK: WiFi/Improv, máquina de estados da nuvem, claim HTTP, MQTT, WebSocket local, NVS. O produto apenas precisa preencher os campos telemetry/status, registar retornos de chamada e invocar 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 utilizado
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 DEPOIS de begin(): begin() instala o seu próprio despachante
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 ao construtor de Link. Todos os campos utilizam inicialização agregada (inicializadores designados em C++).
| Campo | Tipo | Fim | 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 de JSON |
hasAirHumidity |
bool |
Sensor de humidade presente | false = campo omitido de JSON |
hasHeaterTemp |
bool |
Sensor de temperatura do aquecedor presente | — |
hasHeaterPower |
bool |
Sensor de potência do aquecedor presente | — |
hasFanStatus |
bool |
Estado da ventoinha 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* |
Cadeia de versão de hardware | obrigatório |
firmwareVersion |
const char* |
Cadeia 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¶
Toma a configuração por referência const. CFG deve existir durante toda a vida do objeto (tipicamente static const).
Métodos¶
begin()¶
Inicia toda a pilha SDK: WiFi/Improv, máquina de estados da nuvem, claim HTTP, MQTT, WebSocket local, persistência NVS.
Invocar uma única vez em setup(). Retorna true na inicialização bem-sucedida.
loop()¶
O único tick necessário. Atende WiFi/MQTT/LocalAccess e publica automaticamente telemetria e estado nos seus temporizadores.
Invocar em cada iteração de loop(). Sem esta invocação 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. Utilizar após processar um comando quando o novo estado tem de ser refletido no portal imediatamente.
raiseEvent()¶
Publica um evento para o tópico idryer/{serial}/events. Enviado imediatamente.
| Parâmetro | Tipo | Fim |
|---|---|---|
severity |
EventKind |
Info / Warning / Error |
event |
const char* |
Código de 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()¶
Regista 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. Devolve false em caso de
transbordo (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 intercetados. Ainda assim pode registar
um tratador com o mesmo nome — corre depois do da biblioteca.
Importante: se runtime()->setCommandHandler(...) estiver definido, o
despachante completo interceta tudo e os tratadores registados aqui não são
chamados.
onIntegrationStatus()¶
Invocado quando o estado de conexão de uma integração muda (HA, Bambu, Moonraker). Retorno de chamada opcional.
IntegrationStatusCallback = std::function<void(const iDryer::IntegrationStatus&)>
onClaimComplete()¶
Chamado uma vez, quando o backend confirma a associação — antes de o MQTT subir.
ClaimCompleteCallback = void (*)(const char* deviceId)
isBound()¶
true — o aparelho tem um segredo permanente e está associado a uma conta.
false — modo de configuração: o aparelho espera um token de associação vindo
da aplicação ou do flasher. O produto precisa disto 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 da associação, o do controlador depois.
isOnline()¶
Retorna true se o dispositivo está registado e a sessão MQTT está ativa.
serial()¶
Número de série do dispositivo (cadeia de NVS, atribuído durante claim). Cadeia vazia antes do claim ser concluído.
seedWifiCredentialsIfEmpty()¶
Escreve credenciais WiFi em NVS apenas se ainda não estiverem definidas. Invocar antes de begin(). Utilizado em ambientes de desenvolvimento com credenciais codificadas.
setWifiCredentials()¶
Sempre sobrescreve credenciais WiFi em NVS. Auxiliar de desenvolvimento e re-aprovisionamento forçado.
eraseClaimAndRestart()¶
Remove o token de dispositivo de NVS e reinicia o chip. Após reinicialização o dispositivo não está registado — o fluxo de claim automático recomeça. Esta função não retorna.
integrationsManager()¶
Saída para o gestor de integrações — para cablagem do lado do produto (Moonraker chamber target callbacks, estado 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 os seus próprios tópicos ou se integram no encaminhamento 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. Utilizar para respostas de produto que têm de chegar ao cliente LAN da mesma forma que a telemetria publicada automaticamente.
runtime()¶
Saída para o runtime do SDK — utilizado para definir um manipulador de comando completo em vez do despachante da fachada. Após setCommandHandler(...) os tratadores registados via onCommand() já não são invocados pela via MQTT.
Importante: invocar estritamente após begin() — begin() instala o seu próprio despachante, que tem de 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 lê-os no temporizador telemetryPeriodMs e publica para MQTT e Local WS.
| Campo | Tipo | Sinalizador de configuração | Fim |
|---|---|---|---|
telemetry.airTempC[unitId] |
float |
hasAirTemp |
Temperatura do ar, °C |
telemetry.airHumidityPct[unitId] |
float |
hasAirHumidity |
Humidade, % |
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 |
Estado da ventoinha |
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 | Fim |
|---|---|---|
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 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();
Registo de retorno de chamada via runtime¶
Se for necessário controlo 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);
// Registo — estritamente após link.begin()
link.runtime()->setCommandHandler(handleCommand);
cmd — cadeia de comando ("set", "invoke", "ping", "get_config").
data — ArduinoJson JsonObjectConst com carga.
Com esta abordagem, os tratadores de onCommand() não são invocados pela via MQTT — o produto trata os comandos por si.
Enumerações¶
iDryer::DeviceType¶
| Valor | Numérico | Fim |
|---|---|---|
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 ir mais fundo¶
A fachada é suficiente para a maioria das tarefas. Se precisares de trabalhar abaixo do nível da fachada — com idryer::IdryerRuntime, idryer::MqttClient, idryer::cloud::LinkIntegrationsManager — consulta a secção Arquitetura.