This commit is contained in:
2026-02-16 03:14:38 -08:00
parent 67fd36bffb
commit 893c50dbc0
7 changed files with 255 additions and 304 deletions

View File

@@ -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) {
}
}