This commit is contained in:
2026-02-12 06:27:57 -08:00
parent 0e7641c261
commit 63db67b9b1

View File

@@ -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;
}