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