Ir para o conteúdo

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,
    .allowHa           = false,
    .allowBambu        = false,
    .allowMoonraker    = 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
allowHa bool Permitir integração Home Assistant false = SDK não cria cliente
allowBambu bool Permitir integração Bambu Lab LAN
allowMoonraker bool Permitir integração Moonraker/Klipper
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

Construtor

explicit Link(const Config& cfg);

Toma a configuração por referência const. CFG deve existir durante toda a vida do objeto (tipicamente static const).

Métodos

begin()

bool 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.

void setup() {
    link.begin();
}

loop()

void 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.

void loop() {
    link.loop();  // primeiro em loop(), antes da lógica do produto
}

Fonte: iDryer-Storage/src/main.cpp:253, iHeater-link/src/main.cpp:381.

publishTelemetryNow()

void publishTelemetryNow();

Publica imediatamente o estado atual de link.telemetry, independentemente do temporizador telemetryPeriodMs.

publishStatusNow()

void 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.

// iHeater-link/src/main.cpp:238
device().publishStatusNow();

raiseEvent()

void raiseEvent(EventKind   severity,
                const char* event,
                const char* message,
                uint8_t     unitId = 0xFF);

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
link.raiseEvent(iDryer::EventKind::Error, "OVERHEAT", "U1 too hot", 0);

onRequest()

void onRequest(RequestCallback cb);

Registra um retorno de chamada para comandos comerciais (Start, Stop, Storage, Find, ClearErrors) chegando sobre MQTT ou Local WS. A fonte do comando é transparente.

RequestCallback = std::function<void(const iDryer::Request&)>

link.onRequest([](const iDryer::Request& r) {
    switch (r.kind) {
        case iDryer::RequestKind::Start: myStart(r.unitId, r.targetTempC); break;
        case iDryer::RequestKind::Stop:  myStop(r.unitId);                 break;
        default: break;
    }
});

Importante: se runtime()->setCommandHandler(...) estiver definido, este retorno de chamada não é invocado — o despachante completo interceta todos os comandos.

onProfile()

void onProfile(ProfileCallback cb);

Registra um retorno de chamada para commands/profile — um programa de secagem em múltiplas etapas.

ProfileCallback = std::function<void(const iDryer::ProfileSchedule&)>

onIntegrationStatus()

void onIntegrationStatus(IntegrationStatusCallback cb);

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&)>

onClaimPin()

void onClaimPin(ClaimPinCallback cb);

Invocado quando o fluxo de claim da nuvem retorna um PIN para entrada no portal.

ClaimPinCallback = std::function<void(const char* pin, uint32_t expiresInSeconds)>

// iHeater-link/src/main.cpp:367
device().onClaimPin([](const char* pin, uint32_t expiresInSeconds) {
    Serial.printf("CLAIM_PIN:%s:%u\n", pin, expiresInSeconds);
});

isOnline()

bool isOnline() const;

Retorna true se o dispositivo está registado e a sessão MQTT está ativa.

// iHeater-link/src/main.cpp:281
if (device().isOnline()) { ... }

serial()

const char* serial() const;

Número de série do dispositivo (cadeia de NVS, atribuído durante claim). Cadeia vazia antes do claim ser concluído.

seedWifiCredentialsIfEmpty()

void seedWifiCredentialsIfEmpty(const char* ssid, const char* password);

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()

void setWifiCredentials(const char* ssid, const char* password);

Sempre sobrescreve credenciais WiFi em NVS. Auxiliar de desenvolvimento e re-aprovisionamento forçado.

// iHeater-link/src/main.cpp:313
device().setWifiCredentials(ssid.c_str(), pass.c_str());

requestClaim()

bool requestClaim();

Inicia manualmente o fluxo de claim da nuvem (provision → register → check-claim). Em caso de sucesso invoca o retorno de chamada registado onClaimPin. Retorna true se o pedido foi aceite.

// iHeater-link/src/main.cpp:284
bool ok = device().requestClaim();

eraseClaimAndRestart()

void 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.

// iHeater-link/src/main.cpp:293
device().eraseClaimAndRestart();

integrationsManager()

idryer::cloud::LinkIntegrationsManager* 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()

idryer::MqttClient* 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()

idryer::DevicePublisher* 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.

// iDryer-Storage/src/main.cpp:175
link.devicePublisher()->publishConfigRaw(buf, len);

runtime()

idryer::IdryerRuntime* 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(...) a fachada onRequest/onProfile já não é invocada 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, onRequest() e onProfile() não são invocadas da via MQTT — o produto processa comandos diretamente.


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.