Fehlerbehebung¶
Häufige Symptome bei der Arbeit mit idryer-core, ihre Ursachen und Lösungen.
Stellen Sie sicher, dass HAL-Protokolle aktiviert sind (idryer::hal::initArduinoHal(&Serial)) und dass -DCORE_DEBUG_LEVEL=3 oder höher in platformio.ini gesetzt ist.
WiFi¶
State Machine steckt in WifiConnecting¶
Symptome: Protokoll wiederholt state: WifiConnecting, Übergang zu Provisioning geschieht nie.
Mögliche Ursachen:
- Falsche SSID/Passwort. Überprüfen Sie
WIFI_SSID/WIFI_PASSWORDinsecrets.h. Nach Improv-Provisioning kommen Anmeldedaten aus NVS, nicht vonsecrets.h. - 5-GHz-Netzwerk. ESP32 unterstützt nur 2,4 GHz.
- Verstecktes Netzwerk oder MAC-Filter auf dem Router.
WiFi.begin()aufgerufen voridryer::hal::initArduinoHal(...)— Keine Protokollausgabe, aber dies ist nicht die Ursache des Hängens, nur Blindheit.
Was zu überprüfen ist:
WiFi verbindet sich, bricht aber nach 30–60 Sekunden ab¶
Typischerweise: schwaches Signal (RSSI < -80 dBm), ESP32-C3 wird von einem USB-Hub ohne dediziertes 5V/1A-Netzteil betrieben, Konflikt mit FreeRTOS-Aufgaben.
Protokollieren Sie RSSI in der Produkt-Loop:
if (millis() - lastRssi > 30000) { lastRssi = millis(); HAL_LOG_INFO("WIFI", "RSSI: %d dBm", WiFi.RSSI()); }
Provisioning und Claiming¶
State Machine steckt in Provisioning¶
Symptome: state: Provisioning ohne Übergang zu Registering oder AwaitingClaim.
Ursachen:
- Falsche
IDRYER_API_BASEin Build-Flags. Musshttps://portal.idryer.org/api(Produktion) oderhttps://staging.idryer.org/api(Staging) sein. - Fehlendes TLS-Zertifikat (Let's Encrypt ISRG Root X1). Eingebettet in
root_ca.h, aber wenn ohneMQTT_USE_TLSerstellt, benötigt der HTTP-Client auch TLS — das Root CA wird benötigt für die HTTP API auch. - Gerätezeit nicht synchronisiert (TLS-Handshake benötigt ein gültiges Datum). Überprüfen Sie, dass
configTime(...)insetStateChangeCallbacknach dem ersten Verlassen vonWifiConnectingaufgerufen wird (wie in Storage Link).
State Machine steckt in AwaitingClaim¶
Dies ist der normale Zustand, während der Benutzer die PIN im Portal nicht eingegeben hat. Die PIN wird über setClaimPinCallback ins Protokoll ausgegeben.
Für automatisches Claiming (eigenständige Geräte ohne UI):
Nach requestClaim() gibt das Backend eine PIN aus, die der Benutzer ins Portal eingeben muss.
seedSerialFromMac() generierte eine Seriennummer, aber eine andere wurde ins Portal eingegeben¶
Die in NVS gespeicherte Seriennummer hat Vorrang vor MAC-Generierung. seedSerialFromMac() schreibt in NVS nur, wenn noch keine Seriennummer vorhanden ist. Um die Seriennummer zu ändern, löschen Sie NVS:
MQTT¶
State Machine betrat MqttConnecting, aber erreicht Online nicht¶
Ursachen:
- Broker unerreichbar. Produktion:
mqtt.idryer.org:8883, Staging:staging.idryer.org:1884. MQTT_USE_TLS=1ohne korrektes Root CA — Handshake schlägt stumm fehl.setBufferSize(16384)nicht angewendet —PubSubClientPuffer ist Standard 256 Bytes.MqttClientsetzt bereits 16384, aber wenn SiePubSubClientdirekt verwenden — setzen Sie den Puffer selbst.- Persistente Sitzung "steckt" auf dem Broker mit einer anderen Client-ID. Löschen Sie NVS und reflashen Sie.
Befehle vom Backend kommen nicht an¶
Überprüfen Sie das Abonnement — MqttClient abonniert idryer/{serial}/commands/# mit QoS 1. Wenn das Abonnement fehlschlug, zeigt das Protokoll:
Überprüfen Sie, dass setCommandHandler() vor runtime.begin() aufgerufen wird — sonst wird die erste Batch von Befehlen möglicherweise verpasst.
PubSubClient trennt sich genau alle 60 Sekunden¶
Dies ist ein Keep-Alive-Timeout. Ihre MQTT-Loop wird möglicherweise nicht häufig genug aufgerufen — s_runtime.loop() muss ohne lange Blöcke spinnen. Überprüfen Sie, dass loop() kein delay(>500ms) und keine blockierenden Netzwerkaufrufe hat.
Befehle und Handler¶
commands/invoke kommt an, aber ActionDispatcher wird nicht aufgerufen¶
Wenn Sie setCommandHandler() registrierten, ist der eingebaute Fallback zu ActionDispatcher deaktiviert. IdryerRuntime leitet alles (außer ping) zu Ihrem CommandHandler. Sie müssen dort explizit s_dispatcher.handleInvoke(data) für invoke Befehle aufrufen.
Vorlage:
static void handleCommand(const char* cmd, JsonObjectConst data) {
if (strcmp(cmd, "invoke") == 0) { s_dispatcher.handleInvoke(data); return; }
if (strcmp(cmd, "set") == 0) { s_dispatcher.handleSet(data); return; }
// ... product commands ...
}
commands/set empfangen, aber Konfiguration nicht angewendet¶
ActionDispatcher::handleSet extrahiert id und val und leitet sie an den registrierten SetCallback weiter. Überprüfen Sie:
dispatcher.setSetCallback(onSetCommand, nullptr)wird insetup()aufgerufen.onSetCommandruft tatsächlichs_profile.applyConfig(id, val)auf.applyConfiggibttruefür bekannteidWerte zurück. Für unbekannte gibt esfalsezurück und Änderungen werden ignoriert.
Telemetrie¶
Telemetrie wird nicht veröffentlicht¶
idryer-core veröffentlicht Telemetrie nicht automatisch. Der Produktcode tut dies immer.
Überprüfen Sie:
pub.publishTelemetry(doc)(oders_mqtt.publishTelemetry(doc), falls LocalAccess nicht verwendet wird) wird tatsächlich inloop()aufgerufen.- Die Ratenbedingung blockiert nicht alle Aufrufe. Ein häufiger Fehler:
Beim ersten Durchlauf ist
lastTm == 0undmillis()ist noch klein — der Zweig wird nie ausgeführt. Verwenden Sie>=und initialisieren SielastTmbeim ersten Durchlauf. s_runtime.isOnline() == true. MQTT ist getrennt, bevor Online — Veröffentlichung wird nicht durchgehen.JsonDocumentGröße ist ausreichend für die Payload. Überprüfen Siedoc.overflowed()nachserializeJson.
publishTelemetry gibt false zurück¶
Ursachen:
- Nicht mit Broker verbunden (
MqttClient::isConnected() == false). - Puffer überschritten — Payload größer als
MQTT_BUFFER_SIZE(16384 Bytes). Für große Daten verwenden SiepublishConfigRaw(mit Chunks) oder reduzieren Sie die Payload.
DevicePublisher::publishTelemetry erreicht den WS-Client nicht¶
DevicePublisher gibt keinen Fehler zurück, wenn der WS-Client nicht verbunden ist — es überspringt einfach den WS-Teil. Überprüfen Sie s_local.isClientConnected(). Falls false — der Client ist nicht authentifiziert oder nicht verbunden.
NTP und Systemzeit¶
Gerätezeit ist nicht synchronisiert¶
NTP-Synchronisierung wird in setStateChangeCallback nach dem ersten Verlassen von WifiConnecting gestartet: