snapshot
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#include "DoorbellLogic.h"
|
||||
|
||||
// =====================================================================
|
||||
// Lifecycle
|
||||
// Lifecycle
|
||||
// =====================================================================
|
||||
void DoorbellLogic::begin() {
|
||||
_bootTime = millis();
|
||||
@@ -13,12 +13,12 @@ void DoorbellLogic::begin() {
|
||||
}
|
||||
|
||||
void DoorbellLogic::beginWiFi() {
|
||||
_instance = this; // ← MISSING
|
||||
_instance = this;
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.setSleep(false);
|
||||
WiFi.setAutoReconnect(true); // ← MISSING
|
||||
WiFi.onEvent(onWiFiEvent); // ← MISSING
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.onEvent(onWiFiEvent);
|
||||
|
||||
for (int i = 0; i < NUM_WIFI; i++) {
|
||||
_wifiMulti.addAP(wifiNetworks[i].ssid, wifiNetworks[i].pass);
|
||||
@@ -78,16 +78,16 @@ void DoorbellLogic::finishBoot() {
|
||||
flushStatus();
|
||||
}
|
||||
|
||||
Serial.printf("[CONFIG] ALERT_URL: %s\n", ALERT_URL);
|
||||
Serial.printf("[CONFIG] ALERT_URL: %s\n", ALERT_URL);
|
||||
Serial.printf("[CONFIG] SILENCE_URL: %s\n", SILENCE_URL);
|
||||
Serial.printf("[CONFIG] ADMIN_URL: %s\n", ADMIN_URL);
|
||||
Serial.printf("[CONFIG] ADMIN_URL: %s\n", ADMIN_URL);
|
||||
|
||||
transitionTo(DeviceState::SILENT);
|
||||
Serial.printf("[BOOT] Grace period: %d ms\n", BOOT_GRACE_MS);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Main Update Loop
|
||||
// Main Update Loop
|
||||
// =====================================================================
|
||||
void DoorbellLogic::update() {
|
||||
unsigned long now = millis();
|
||||
@@ -117,11 +117,6 @@ void DoorbellLogic::update() {
|
||||
|
||||
switch (_state) {
|
||||
case DeviceState::ALERTING:
|
||||
// if (now - _alertStart > ALERT_TIMEOUT_MS) {
|
||||
// Serial.println("[ALERT] Timeout — auto-silencing");
|
||||
// handleSilence("timeout");
|
||||
// break;
|
||||
// }
|
||||
if (now - _lastBlink >= BLINK_INTERVAL_MS) {
|
||||
_lastBlink = now;
|
||||
_blinkState = !_blinkState;
|
||||
@@ -138,32 +133,32 @@ void DoorbellLogic::update() {
|
||||
break;
|
||||
}
|
||||
|
||||
if (now - _lastHeartbeat >= HEARTBEAT_INTERVAL_MS) {
|
||||
_lastHeartbeat = now;
|
||||
uint32_t heap = ESP.getFreeHeap();
|
||||
Serial.printf("[%lus] %s | WiFi:%s RSSI:%d | heap:%dKB | minHeap:%dKB\n",
|
||||
now / 1000,
|
||||
_state == DeviceState::SILENT ? "SILENT" :
|
||||
_state == DeviceState::ALERTING ? "ALERT" : "WAKE",
|
||||
WiFi.isConnected() ? "OK" : "DOWN",
|
||||
WiFi.RSSI(),
|
||||
heap / 1024,
|
||||
ESP.getMinFreeHeap() / 1024);
|
||||
if (now - _lastHeartbeat >= HEARTBEAT_INTERVAL_MS) {
|
||||
_lastHeartbeat = now;
|
||||
uint32_t heap = ESP.getFreeHeap();
|
||||
Serial.printf("[%lus] %s | WiFi:%s RSSI:%d | heap:%dKB | minHeap:%dKB\n",
|
||||
now / 1000,
|
||||
_state == DeviceState::SILENT ? "SILENT" :
|
||||
_state == DeviceState::ALERTING ? "ALERT" : "WAKE",
|
||||
WiFi.isConnected() ? "OK" : "DOWN",
|
||||
WiFi.RSSI(),
|
||||
heap / 1024,
|
||||
ESP.getMinFreeHeap() / 1024);
|
||||
|
||||
if (heap < 20000) {
|
||||
Serial.println("[HEAP] CRITICAL — rebooting!");
|
||||
queueStatus("REBOOTING", "low heap");
|
||||
flushStatus();
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
if (heap < 20000) {
|
||||
Serial.println("[HEAP] CRITICAL — rebooting!");
|
||||
queueStatus("REBOOTING", "low heap");
|
||||
flushStatus();
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateScreenState();
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Input Events
|
||||
// Input Events
|
||||
// =====================================================================
|
||||
void DoorbellLogic::onTouch(const TouchEvent& evt) {
|
||||
if (!evt.pressed) return;
|
||||
@@ -196,7 +191,7 @@ void DoorbellLogic::onSerialCommand(const String& cmd) {
|
||||
checkNetwork();
|
||||
} else if (cmd == "STATUS") {
|
||||
Serial.printf("[CMD] State:%s WiFi:%s RSSI:%d Heap:%dKB NTP:%s\n",
|
||||
_state == DeviceState::SILENT ? "SILENT" :
|
||||
_state == DeviceState::SILENT ? "SILENT" :
|
||||
_state == DeviceState::ALERTING ? "ALERT" : "WAKE",
|
||||
WiFi.isConnected() ? WiFi.SSID().c_str() : "DOWN",
|
||||
WiFi.RSSI(),
|
||||
@@ -218,7 +213,7 @@ void DoorbellLogic::onSerialCommand(const String& cmd) {
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// State Transitions
|
||||
// State Transitions
|
||||
// =====================================================================
|
||||
void DoorbellLogic::transitionTo(DeviceState newState) {
|
||||
_state = newState;
|
||||
@@ -232,28 +227,27 @@ void DoorbellLogic::transitionTo(DeviceState newState) {
|
||||
break;
|
||||
case DeviceState::ALERTING:
|
||||
_alertStart = now;
|
||||
_lastBlink = now;
|
||||
_lastBlink = now;
|
||||
_blinkState = false;
|
||||
_screen.screen = ScreenID::ALERT;
|
||||
Serial.printf("-> ALERTING: %s\n", _currentMessage.c_str());
|
||||
break;
|
||||
case DeviceState::WAKE:
|
||||
_wakeStart = now;
|
||||
_screen.screen = ScreenID::DASHBOARD;
|
||||
Serial.println("-> WAKE (dashboard)");
|
||||
_screen.screen = ScreenID::DASHBOARD; // ← CHANGED from STATUS
|
||||
Serial.println("-> WAKE (dashboard)"); // ← CHANGED
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Message Handlers
|
||||
// Message Handlers
|
||||
// =====================================================================
|
||||
void DoorbellLogic::handleAlert(const String& msg) {
|
||||
if (_state == DeviceState::ALERTING && _currentMessage == msg) return;
|
||||
_currentMessage = msg;
|
||||
_alertMsgEpoch = _lastParsedMsgEpoch;
|
||||
|
||||
// Push into history (shift older entries down)
|
||||
for (int i = ALERT_HISTORY_SIZE - 1; i > 0; i--) {
|
||||
_screen.alertHistory[i] = _screen.alertHistory[i - 1];
|
||||
}
|
||||
@@ -278,8 +272,6 @@ void DoorbellLogic::handleSilence(const String& msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this came from ntfy poll (_lastParsedMsgEpoch > 0), reject if it
|
||||
// predates or equals the alert. Both timestamps are from ntfy's server clock.
|
||||
if (_lastParsedMsgEpoch > 0 && _alertMsgEpoch > 0 &&
|
||||
_lastParsedMsgEpoch <= _alertMsgEpoch) {
|
||||
Serial.printf("[SILENCE] Ignored — predates alert (silence:%ld <= alert:%ld)\n",
|
||||
@@ -298,13 +290,13 @@ void DoorbellLogic::handleSilence(const String& msg) {
|
||||
void DoorbellLogic::handleAdmin(const String& msg) {
|
||||
Serial.printf("[ADMIN] %s\n", msg.c_str());
|
||||
|
||||
if (msg == "SILENCE") handleSilence("admin");
|
||||
else if (msg == "PING") queueStatus("PONG", "ping");
|
||||
else if (msg == "test") handleAlert("TEST ALERT");
|
||||
if (msg == "SILENCE") handleSilence("admin");
|
||||
else if (msg == "PING") queueStatus("PONG", "ping");
|
||||
else if (msg == "test") handleAlert("TEST ALERT");
|
||||
else if (msg == "status") {
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "State:%s WiFi:%s RSSI:%d Heap:%dKB",
|
||||
_state == DeviceState::SILENT ? "SILENT" :
|
||||
_state == DeviceState::SILENT ? "SILENT" :
|
||||
_state == DeviceState::ALERTING ? "ALERT" : "WAKE",
|
||||
WiFi.SSID().c_str(), WiFi.RSSI(), ESP.getFreeHeap() / 1024);
|
||||
queueStatus("STATUS", buf);
|
||||
@@ -321,15 +313,15 @@ void DoorbellLogic::handleAdmin(const String& msg) {
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Screen State Sync
|
||||
// Screen State Sync
|
||||
// =====================================================================
|
||||
void DoorbellLogic::updateScreenState() {
|
||||
_screen.deviceState = _state;
|
||||
_screen.blinkPhase = _blinkState;
|
||||
_screen.deviceState = _state;
|
||||
_screen.blinkPhase = _blinkState;
|
||||
strncpy(_screen.alertMessage, _currentMessage.c_str(), sizeof(_screen.alertMessage) - 1);
|
||||
_screen.alertMessage[sizeof(_screen.alertMessage) - 1] = '\0';
|
||||
|
||||
_screen.wifiConnected = WiFi.isConnected();
|
||||
_screen.wifiConnected = WiFi.isConnected();
|
||||
if (_screen.wifiConnected) {
|
||||
strncpy(_screen.wifiSSID, WiFi.SSID().c_str(), sizeof(_screen.wifiSSID) - 1);
|
||||
_screen.wifiSSID[sizeof(_screen.wifiSSID) - 1] = '\0';
|
||||
@@ -338,20 +330,20 @@ void DoorbellLogic::updateScreenState() {
|
||||
_screen.wifiRSSI = WiFi.RSSI();
|
||||
}
|
||||
|
||||
_screen.ntpSynced = _ntpSynced;
|
||||
_screen.ntpSynced = _ntpSynced;
|
||||
if (_ntpSynced) {
|
||||
strncpy(_screen.timeString, _timeClient->getFormattedTime().c_str(),
|
||||
sizeof(_screen.timeString) - 1);
|
||||
sizeof(_screen.timeString) - 1);
|
||||
_screen.timeString[sizeof(_screen.timeString) - 1] = '\0';
|
||||
}
|
||||
|
||||
_screen.uptimeMinutes = (millis() - _bootTime) / 60000;
|
||||
_screen.freeHeapKB = ESP.getFreeHeap() / 1024;
|
||||
_screen.networkOK = _networkOK;
|
||||
_screen.uptimeMinutes = (millis() - _bootTime) / 60000;
|
||||
_screen.freeHeapKB = ESP.getFreeHeap() / 1024;
|
||||
_screen.networkOK = _networkOK;
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// WiFi & Network
|
||||
// WiFi & Network
|
||||
// =====================================================================
|
||||
void DoorbellLogic::syncNTP() {
|
||||
if (_timeClient->update()) {
|
||||
@@ -385,7 +377,7 @@ void DoorbellLogic::checkNetwork() {
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// ntfy Polling
|
||||
// ntfy Polling
|
||||
// =====================================================================
|
||||
void DoorbellLogic::pollTopics() {
|
||||
Serial.printf("[POLL] Starting poll cycle... WiFi:%s NTP:%d Grace:%d\n",
|
||||
@@ -393,16 +385,16 @@ void DoorbellLogic::pollTopics() {
|
||||
|
||||
pollTopic(ALERT_URL, &DoorbellLogic::handleAlert, "ALERT", _lastAlertId);
|
||||
yield();
|
||||
pollTopic(SILENCE_URL, &DoorbellLogic::handleSilence, "SILENCE", _lastSilenceId);
|
||||
pollTopic(SILENCE_URL, &DoorbellLogic::handleSilence, "SILENCE", _lastSilenceId);
|
||||
yield();
|
||||
pollTopic(ADMIN_URL, &DoorbellLogic::handleAdmin, "ADMIN", _lastAdminId);
|
||||
pollTopic(ADMIN_URL, &DoorbellLogic::handleAdmin, "ADMIN", _lastAdminId);
|
||||
|
||||
Serial.printf("[POLL] Done. Heap: %dKB\n", ESP.getFreeHeap() / 1024);
|
||||
}
|
||||
|
||||
void DoorbellLogic::pollTopic(const char* url,
|
||||
void (DoorbellLogic::*handler)(const String&),
|
||||
const char* name, String& lastId) {
|
||||
void (DoorbellLogic::*handler)(const String&),
|
||||
const char* name, String& lastId) {
|
||||
Serial.printf("[%s] Polling: %s\n", name, url);
|
||||
|
||||
if (!WiFi.isConnected()) {
|
||||
@@ -444,13 +436,12 @@ void DoorbellLogic::pollTopic(const char* url,
|
||||
|
||||
http.end();
|
||||
client.stop();
|
||||
|
||||
yield();
|
||||
}
|
||||
|
||||
void DoorbellLogic::parseMessages(String& response, const char* name,
|
||||
void (DoorbellLogic::*handler)(const String&),
|
||||
String& lastId) {
|
||||
void (DoorbellLogic::*handler)(const String&),
|
||||
String& lastId) {
|
||||
Serial.printf("[%s] parseMessages: grace=%d ntp=%d epoch=%ld\n",
|
||||
name, _inBootGrace, _ntpSynced, (long)_lastEpoch);
|
||||
|
||||
@@ -480,12 +471,12 @@ void DoorbellLogic::parseMessages(String& response, const char* name,
|
||||
const char* event = doc["event"];
|
||||
const char* msgId = doc["id"];
|
||||
const char* message = doc["message"];
|
||||
time_t msgTime = doc["time"] | 0;
|
||||
time_t msgTime = doc["time"] | 0;
|
||||
|
||||
Serial.printf("[%s] msg#%d: event=%s id=%s time=%ld msg=%.30s\n",
|
||||
name, msgCount,
|
||||
event ? event : "null",
|
||||
msgId ? msgId : "null",
|
||||
event ? event : "null",
|
||||
msgId ? msgId : "null",
|
||||
(long)msgTime,
|
||||
message ? message : "null");
|
||||
|
||||
@@ -529,7 +520,7 @@ void DoorbellLogic::parseMessages(String& response, const char* name,
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Status Publishing (deferred)
|
||||
// Status Publishing
|
||||
// =====================================================================
|
||||
void DoorbellLogic::queueStatus(const char* st, const String& msg) {
|
||||
_pendingStatus = true;
|
||||
@@ -567,7 +558,6 @@ DoorbellLogic* DoorbellLogic::_instance = nullptr;
|
||||
|
||||
void DoorbellLogic::onWiFiEvent(WiFiEvent_t event) {
|
||||
if (!_instance) return;
|
||||
|
||||
switch (event) {
|
||||
case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
|
||||
Serial.println("[WIFI] Disconnected — will reconnect");
|
||||
@@ -584,4 +574,3 @@ void DoorbellLogic::onWiFiEvent(WiFiEvent_t event) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user