diff --git a/sketches/doorbell/doorbell.ino b/sketches/doorbell/doorbell.ino index 4f8306e..3236846 100644 --- a/sketches/doorbell/doorbell.ino +++ b/sketches/doorbell/doorbell.ino @@ -8,13 +8,12 @@ const char* ssid = "iot-2GHz"; const char* password = "lesson-greater"; -// CRITICAL: Use ?poll=1 for explicit short-poll, no hanging connection const char* COMMAND_POLL_URL = "http://ntfy.sh/ALERT_klubhaus_topic/json?poll=1"; const char* SILENCE_POLL_URL = "http://ntfy.sh/SILENCE_klubhaus_topic/json?poll=1"; const char* STATUS_POST_URL = "http://ntfy.sh/STATUS_klubhaus_topic"; -const unsigned long COMMAND_POLL_INTERVAL = 30000; -const unsigned long SILENCE_POLL_INTERVAL = 5000; +const unsigned long COMMAND_POLL_INTERVAL = 10000; +const unsigned long SILENCE_POLL_INTERVAL = 15000; const unsigned long BLINK_DURATION = 180000; const unsigned long BLINK_PERIOD = 1000; const unsigned long ERROR_BACKOFF = 60000; @@ -63,6 +62,13 @@ unsigned long long epochOffsetMs = 0; // ============== PROFESSIONAL LOGGING ============== +void formatIsoTimestamp(char* buf, size_t len); + +unsigned long long getEpochMs() { + if (epochOffsetMs == 0) return millis(); + return epochOffsetMs + millis(); +} + void logMessage(LogLevel level, const char* component, const char* message) { if (level < CURRENT_LOG_LEVEL) return; @@ -95,15 +101,15 @@ void logValue(LogLevel level, const char* component, const char* name, long long logMessage(level, component, buf); } +// FIX: Was recursively calling itself with wrong args void logString(LogLevel level, const char* component, const char* name, const String& value) { if (level < CURRENT_LOG_LEVEL) return; char buf[256]; - // Escape newlines for clean logging String escaped = value; escaped.replace("\n", "\\n"); escaped.replace("\r", "\\r"); snprintf(buf, sizeof(buf), "%s=[%s]", name, escaped.c_str()); - logString(LOG_INFO, component, buf); + logMessage(level, component, buf); // FIXED: was logString() before } void formatIsoTimestamp(char* buf, size_t len) { @@ -123,8 +129,8 @@ void setup() { Serial.begin(115200); delay(3000); - logMessage(LOG_INFO, "MAIN", "=== KLUBHAUS DOORBELL v3.8 ==="); - logMessage(LOG_INFO, "MAIN", "Fix: NDJSON parsing, ?poll=1, find non-empty messages"); + logMessage(LOG_INFO, "MAIN", "=== KLUBHAUS DOORBELL v3.8.1 ==="); + logMessage(LOG_INFO, "MAIN", "Fix: logString() recursive call bug"); pinMode(BACKLIGHT_PIN, OUTPUT); setBacklight(false); @@ -201,11 +207,6 @@ void initEpochTime() { } } -unsigned long long getEpochMs() { - if (epochOffsetMs == 0) return millis(); - return epochOffsetMs + millis(); -} - // ============== MAIN LOOP ============== void loop() { @@ -305,7 +306,7 @@ void transitionTo(State newState) { publishStatus(); } -// ============== NDJSON PARSING (CRITICAL FIX) ============== +// ============== NDJSON PARSING ============== void checkCommandTopic() { logMessage(LOG_DEBUG, "HTTP", "GET /ALERT_klubhaus_topic/json?poll=1"); @@ -319,7 +320,6 @@ void checkCommandTopic() { return; } - // Parse ALL lines of NDJSON, find first with non-empty message int lineStart = 0; int lineCount = 0; bool foundMessage = false; @@ -346,26 +346,22 @@ void checkCommandTopic() { lineCount, id.c_str(), message.c_str(), serverTime); logMessage(LOG_DEBUG, "JSON", inspectBuf); - // Check if this line has actual content String testMsg = message; testMsg.trim(); if (testMsg.length() > 0) { - // Found a message with content! logValue(LOG_INFO, "COMMAND", "linesParsed", lineCount); logValue(LOG_INFO, "COMMAND", "durationMs", duration); logString(LOG_INFO, "COMMAND", "id", id); logString(LOG_INFO, "COMMAND", "message", message); logValue(LOG_INFO, "COMMAND", "serverTime", serverTime); - // Deduplication check if (id == lastCommandId && id.length() > 0) { logMessage(LOG_DEBUG, "COMMAND", "Duplicate ID — discarded"); return; } lastCommandId = id; - // Process the message if (message.equalsIgnoreCase("SILENCE")) { logMessage(LOG_INFO, "COMMAND", "SILENCE command — acknowledging"); transitionTo(STATE_SILENT); @@ -376,7 +372,7 @@ void checkCommandTopic() { transitionTo(STATE_ALERT); } foundMessage = true; - break; // Stop after first valid message + break; } } else { char errBuf[128]; @@ -402,7 +398,6 @@ void checkSilenceTopic() { if (response.length() == 0) return; - // Parse ALL lines int lineStart = 0; int lineCount = 0; @@ -439,7 +434,7 @@ void checkSilenceTopic() { transitionTo(STATE_SILENT); flashConfirm("SILENCED", COLOR_MINT, COLOR_BLACK); } - return; // Stop after first valid + return; } } } @@ -451,7 +446,7 @@ void checkSilenceTopic() { String fetchNtfyJson(const char* url, const char* topicName) { HTTPClient http; http.begin(url); - http.setTimeout(12000); // INCREASED: ntfy poll can wait up to 5s, give headroom + http.setTimeout(12000); int code = http.GET(); @@ -487,8 +482,6 @@ String fetchNtfyJson(const char* url, const char* topicName) { String payload = http.getString(); http.end(); - - // DO NOT truncate — return full NDJSON for parsing return payload; }