consolidate sketches
This commit is contained in:
576
DoorbellLogic.cpp
Normal file
576
DoorbellLogic.cpp
Normal file
@@ -0,0 +1,576 @@
|
||||
#include "DoorbellLogic.h"
|
||||
|
||||
// =====================================================================
|
||||
// Lifecycle
|
||||
// =====================================================================
|
||||
void DoorbellLogic::begin() {
|
||||
_bootTime = millis();
|
||||
_timeClient = new NTPClient(_ntpUDP, "pool.ntp.org", 0, NTP_SYNC_INTERVAL_MS);
|
||||
|
||||
_screen.debugMode = DEBUG_MODE;
|
||||
_screen.screen = ScreenID::BOOT_SPLASH;
|
||||
updateScreenState();
|
||||
}
|
||||
|
||||
void DoorbellLogic::beginWiFi() {
|
||||
_instance = this;
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.setSleep(false);
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.onEvent(onWiFiEvent);
|
||||
|
||||
for (int i = 0; i < NUM_WIFI; i++) {
|
||||
_wifiMulti.addAP(wifiNetworks[i].ssid, wifiNetworks[i].pass);
|
||||
}
|
||||
|
||||
_screen.screen = ScreenID::WIFI_CONNECTING;
|
||||
updateScreenState();
|
||||
}
|
||||
|
||||
void DoorbellLogic::connectWiFiBlocking() {
|
||||
Serial.println("[WIFI] Connecting...");
|
||||
|
||||
int tries = 0;
|
||||
while (_wifiMulti.run() != WL_CONNECTED && tries++ < 40) {
|
||||
Serial.print(".");
|
||||
delay(500);
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
if (WiFi.isConnected()) {
|
||||
Serial.printf("[WIFI] Connected: %s %s\n",
|
||||
WiFi.SSID().c_str(), WiFi.localIP().toString().c_str());
|
||||
updateScreenState();
|
||||
_screen.screen = ScreenID::WIFI_CONNECTED;
|
||||
} else {
|
||||
Serial.println("[WIFI] FAILED — no networks reachable");
|
||||
_screen.screen = ScreenID::WIFI_FAILED;
|
||||
}
|
||||
updateScreenState();
|
||||
}
|
||||
|
||||
void DoorbellLogic::finishBoot() {
|
||||
if (WiFi.isConnected()) {
|
||||
_timeClient->begin();
|
||||
Serial.println("[NTP] Starting sync...");
|
||||
|
||||
for (int i = 0; i < 5 && !_ntpSynced; i++) {
|
||||
syncNTP();
|
||||
if (!_ntpSynced) delay(500);
|
||||
}
|
||||
|
||||
if (_ntpSynced) {
|
||||
Serial.printf("[NTP] Synced: %s UTC\n",
|
||||
_timeClient->getFormattedTime().c_str());
|
||||
} else {
|
||||
Serial.println("[NTP] Initial sync failed — will retry in update()");
|
||||
}
|
||||
|
||||
checkNetwork();
|
||||
|
||||
char bootMsg[80];
|
||||
snprintf(bootMsg, sizeof(bootMsg), "%s %s RSSI:%d",
|
||||
WiFi.SSID().c_str(),
|
||||
WiFi.localIP().toString().c_str(),
|
||||
WiFi.RSSI());
|
||||
queueStatus("BOOTED", bootMsg);
|
||||
flushStatus();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
transitionTo(DeviceState::SILENT);
|
||||
Serial.printf("[BOOT] Grace period: %d ms\n", BOOT_GRACE_MS);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Main Update Loop
|
||||
// =====================================================================
|
||||
void DoorbellLogic::update() {
|
||||
unsigned long now = millis();
|
||||
|
||||
if (_inBootGrace && (now - _bootTime >= BOOT_GRACE_MS)) {
|
||||
_inBootGrace = false;
|
||||
Serial.println("[BOOT] Grace period ended");
|
||||
}
|
||||
|
||||
syncNTP();
|
||||
|
||||
if (!WiFi.isConnected()) {
|
||||
if (_wifiMulti.run() == WL_CONNECTED) {
|
||||
Serial.println("[WIFI] Reconnected");
|
||||
queueStatus("RECONNECTED", WiFi.SSID().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (now - _lastPoll >= POLL_INTERVAL_MS) {
|
||||
_lastPoll = now;
|
||||
if (WiFi.isConnected() && _ntpSynced) {
|
||||
pollTopics();
|
||||
}
|
||||
}
|
||||
|
||||
flushStatus();
|
||||
|
||||
switch (_state) {
|
||||
case DeviceState::ALERTING:
|
||||
if (now - _lastBlink >= BLINK_INTERVAL_MS) {
|
||||
_lastBlink = now;
|
||||
_blinkState = !_blinkState;
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceState::WAKE:
|
||||
if (now - _wakeStart > WAKE_DISPLAY_MS) {
|
||||
transitionTo(DeviceState::SILENT);
|
||||
}
|
||||
break;
|
||||
|
||||
case DeviceState::SILENT:
|
||||
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 (heap < 20000) {
|
||||
Serial.println("[HEAP] CRITICAL — rebooting!");
|
||||
queueStatus("REBOOTING", "low heap");
|
||||
flushStatus();
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
updateScreenState();
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Input Events
|
||||
// =====================================================================
|
||||
void DoorbellLogic::onTouch(const TouchEvent& evt) {
|
||||
if (!evt.pressed) return;
|
||||
|
||||
static unsigned long lastAction = 0;
|
||||
unsigned long now = millis();
|
||||
if (now - lastAction < TOUCH_DEBOUNCE_MS) return;
|
||||
lastAction = now;
|
||||
|
||||
Serial.printf("[TOUCH] x=%d y=%d state=%d\n", evt.x, evt.y, (int)_state);
|
||||
|
||||
switch (_state) {
|
||||
case DeviceState::ALERTING:
|
||||
handleSilence("touch");
|
||||
break;
|
||||
case DeviceState::SILENT:
|
||||
transitionTo(DeviceState::WAKE);
|
||||
break;
|
||||
case DeviceState::WAKE:
|
||||
transitionTo(DeviceState::SILENT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DoorbellLogic::onSerialCommand(const String& cmd) {
|
||||
if (cmd == "CLEAR_DEDUP") {
|
||||
_lastAlertId = _lastSilenceId = _lastAdminId = "";
|
||||
Serial.println("[CMD] Dedup cleared");
|
||||
} else if (cmd == "NET") {
|
||||
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::ALERTING ? "ALERT" : "WAKE",
|
||||
WiFi.isConnected() ? WiFi.SSID().c_str() : "DOWN",
|
||||
WiFi.RSSI(),
|
||||
ESP.getFreeHeap() / 1024,
|
||||
_ntpSynced ? _timeClient->getFormattedTime().c_str() : "no");
|
||||
} else if (cmd == "WAKE") {
|
||||
transitionTo(DeviceState::WAKE);
|
||||
} else if (cmd == "TEST") {
|
||||
handleAlert("TEST ALERT");
|
||||
} else if (cmd == "REBOOT") {
|
||||
Serial.println("[CMD] Rebooting...");
|
||||
queueStatus("REBOOTING", "serial");
|
||||
flushStatus();
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
} else {
|
||||
Serial.printf("[CMD] Unknown: %s\n", cmd.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// State Transitions
|
||||
// =====================================================================
|
||||
void DoorbellLogic::transitionTo(DeviceState newState) {
|
||||
_state = newState;
|
||||
unsigned long now = millis();
|
||||
|
||||
switch (newState) {
|
||||
case DeviceState::SILENT:
|
||||
_screen.screen = ScreenID::OFF;
|
||||
_alertMsgEpoch = 0;
|
||||
Serial.println("-> SILENT");
|
||||
break;
|
||||
case DeviceState::ALERTING:
|
||||
_alertStart = 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; // ← CHANGED from STATUS
|
||||
Serial.println("-> WAKE (dashboard)"); // ← CHANGED
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Message Handlers
|
||||
// =====================================================================
|
||||
void DoorbellLogic::handleAlert(const String& msg) {
|
||||
if (_state == DeviceState::ALERTING && _currentMessage == msg) return;
|
||||
_currentMessage = msg;
|
||||
_alertMsgEpoch = _lastParsedMsgEpoch;
|
||||
|
||||
for (int i = ALERT_HISTORY_SIZE - 1; i > 0; i--) {
|
||||
_screen.alertHistory[i] = _screen.alertHistory[i - 1];
|
||||
}
|
||||
strncpy(_screen.alertHistory[0].message, msg.c_str(), 63);
|
||||
_screen.alertHistory[0].message[63] = '\0';
|
||||
strncpy(_screen.alertHistory[0].timestamp,
|
||||
_ntpSynced ? _timeClient->getFormattedTime().c_str() : "??:??:??", 11);
|
||||
_screen.alertHistory[0].timestamp[11] = '\0';
|
||||
|
||||
if (_screen.alertHistoryCount < ALERT_HISTORY_SIZE)
|
||||
_screen.alertHistoryCount++;
|
||||
|
||||
Serial.printf("[ALERT] Accepted. ntfy time=%ld history=%d\n",
|
||||
(long)_alertMsgEpoch, _screen.alertHistoryCount);
|
||||
transitionTo(DeviceState::ALERTING);
|
||||
queueStatus("ALERTING", msg);
|
||||
}
|
||||
|
||||
void DoorbellLogic::handleSilence(const String& msg) {
|
||||
if (_state != DeviceState::ALERTING) {
|
||||
Serial.println("[SILENCE] Ignored — not alerting");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lastParsedMsgEpoch > 0 && _alertMsgEpoch > 0 &&
|
||||
_lastParsedMsgEpoch <= _alertMsgEpoch) {
|
||||
Serial.printf("[SILENCE] Ignored — predates alert (silence:%ld <= alert:%ld)\n",
|
||||
(long)_lastParsedMsgEpoch, (long)_alertMsgEpoch);
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("[SILENCE] Accepted (silence:%ld > alert:%ld)\n",
|
||||
(long)_lastParsedMsgEpoch, (long)_alertMsgEpoch);
|
||||
_currentMessage = "";
|
||||
_alertMsgEpoch = 0;
|
||||
transitionTo(DeviceState::SILENT);
|
||||
queueStatus("SILENT", "silenced");
|
||||
}
|
||||
|
||||
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");
|
||||
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::ALERTING ? "ALERT" : "WAKE",
|
||||
WiFi.SSID().c_str(), WiFi.RSSI(), ESP.getFreeHeap() / 1024);
|
||||
queueStatus("STATUS", buf);
|
||||
}
|
||||
else if (msg == "wake") {
|
||||
transitionTo(DeviceState::WAKE);
|
||||
}
|
||||
else if (msg == "REBOOT") {
|
||||
queueStatus("REBOOTING", "admin");
|
||||
flushStatus();
|
||||
delay(200);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Screen State Sync
|
||||
// =====================================================================
|
||||
void DoorbellLogic::updateScreenState() {
|
||||
_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();
|
||||
if (_screen.wifiConnected) {
|
||||
strncpy(_screen.wifiSSID, WiFi.SSID().c_str(), sizeof(_screen.wifiSSID) - 1);
|
||||
_screen.wifiSSID[sizeof(_screen.wifiSSID) - 1] = '\0';
|
||||
strncpy(_screen.wifiIP, WiFi.localIP().toString().c_str(), sizeof(_screen.wifiIP) - 1);
|
||||
_screen.wifiIP[sizeof(_screen.wifiIP) - 1] = '\0';
|
||||
_screen.wifiRSSI = WiFi.RSSI();
|
||||
}
|
||||
|
||||
_screen.ntpSynced = _ntpSynced;
|
||||
if (_ntpSynced) {
|
||||
strncpy(_screen.timeString, _timeClient->getFormattedTime().c_str(),
|
||||
sizeof(_screen.timeString) - 1);
|
||||
_screen.timeString[sizeof(_screen.timeString) - 1] = '\0';
|
||||
}
|
||||
|
||||
_screen.uptimeMinutes = (millis() - _bootTime) / 60000;
|
||||
_screen.freeHeapKB = ESP.getFreeHeap() / 1024;
|
||||
_screen.networkOK = _networkOK;
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// WiFi & Network
|
||||
// =====================================================================
|
||||
void DoorbellLogic::syncNTP() {
|
||||
if (_timeClient->update()) {
|
||||
_ntpSynced = true;
|
||||
_lastEpoch = _timeClient->getEpochTime();
|
||||
}
|
||||
}
|
||||
|
||||
void DoorbellLogic::checkNetwork() {
|
||||
Serial.printf("[NET] WiFi:%s RSSI:%d IP:%s\n",
|
||||
WiFi.SSID().c_str(), WiFi.RSSI(), WiFi.localIP().toString().c_str());
|
||||
|
||||
IPAddress ip;
|
||||
if (!WiFi.hostByName("ntfy.sh", ip)) {
|
||||
Serial.println("[NET] DNS FAILED");
|
||||
_networkOK = false;
|
||||
return;
|
||||
}
|
||||
Serial.printf("[NET] DNS OK: %s\n", ip.toString().c_str());
|
||||
|
||||
WiFiClientSecure tls;
|
||||
tls.setInsecure();
|
||||
if (tls.connect("ntfy.sh", 443, 15000)) {
|
||||
Serial.println("[NET] TLS OK");
|
||||
tls.stop();
|
||||
_networkOK = true;
|
||||
} else {
|
||||
Serial.println("[NET] TLS FAILED");
|
||||
_networkOK = false;
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// ntfy Polling
|
||||
// =====================================================================
|
||||
void DoorbellLogic::pollTopics() {
|
||||
Serial.printf("[POLL] Starting poll cycle... WiFi:%s NTP:%d Grace:%d\n",
|
||||
WiFi.isConnected() ? "OK" : "DOWN", _ntpSynced, _inBootGrace);
|
||||
|
||||
pollTopic(ALERT_URL, &DoorbellLogic::handleAlert, "ALERT", _lastAlertId);
|
||||
yield();
|
||||
pollTopic(SILENCE_URL, &DoorbellLogic::handleSilence, "SILENCE", _lastSilenceId);
|
||||
yield();
|
||||
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) {
|
||||
Serial.printf("[%s] Polling: %s\n", name, url);
|
||||
|
||||
if (!WiFi.isConnected()) {
|
||||
Serial.printf("[%s] SKIPPED — WiFi down\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
client.setTimeout(10);
|
||||
|
||||
HTTPClient http;
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
http.setTimeout(10000);
|
||||
http.setReuse(false);
|
||||
|
||||
if (!http.begin(client, url)) {
|
||||
Serial.printf("[%s] begin() FAILED\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
int code = http.GET();
|
||||
Serial.printf("[%s] HTTP %d\n", name, code);
|
||||
|
||||
if (code == HTTP_CODE_OK) {
|
||||
String response = http.getString();
|
||||
Serial.printf("[%s] %d bytes\n", name, response.length());
|
||||
if (response.length() > 0) {
|
||||
parseMessages(response, name, handler, lastId);
|
||||
} else {
|
||||
Serial.printf("[%s] Empty response\n", name);
|
||||
}
|
||||
} else if (code < 0) {
|
||||
Serial.printf("[%s] ERROR: %s\n", name, http.errorToString(code).c_str());
|
||||
_networkOK = false;
|
||||
} else {
|
||||
Serial.printf("[%s] Unexpected code: %d\n", name, code);
|
||||
}
|
||||
|
||||
http.end();
|
||||
client.stop();
|
||||
yield();
|
||||
}
|
||||
|
||||
void DoorbellLogic::parseMessages(String& response, const char* name,
|
||||
void (DoorbellLogic::*handler)(const String&),
|
||||
String& lastId) {
|
||||
Serial.printf("[%s] parseMessages: grace=%d ntp=%d epoch=%ld\n",
|
||||
name, _inBootGrace, _ntpSynced, (long)_lastEpoch);
|
||||
|
||||
if (_inBootGrace || !_ntpSynced || _lastEpoch == 0) {
|
||||
Serial.printf("[%s] SKIPPED — guard failed\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
int lineStart = 0;
|
||||
int msgCount = 0;
|
||||
while (lineStart < (int)response.length()) {
|
||||
int lineEnd = response.indexOf('\n', lineStart);
|
||||
if (lineEnd == -1) lineEnd = response.length();
|
||||
|
||||
String line = response.substring(lineStart, lineEnd);
|
||||
line.trim();
|
||||
|
||||
if (line.length() > 0 && line.indexOf('{') >= 0) {
|
||||
msgCount++;
|
||||
JsonDocument doc;
|
||||
if (deserializeJson(doc, line)) {
|
||||
Serial.printf("[%s] JSON parse FAILED on line %d\n", name, msgCount);
|
||||
lineStart = lineEnd + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* event = doc["event"];
|
||||
const char* msgId = doc["id"];
|
||||
const char* message = doc["message"];
|
||||
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",
|
||||
(long)msgTime,
|
||||
message ? message : "null");
|
||||
|
||||
if (event && strcmp(event, "message") != 0) {
|
||||
Serial.printf("[%s] SKIP — not a message event (event=%s)\n", name, event);
|
||||
lineStart = lineEnd + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!message || strlen(message) == 0) {
|
||||
Serial.printf("[%s] SKIP — empty message\n", name);
|
||||
lineStart = lineEnd + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
String idStr = msgId ? String(msgId) : "";
|
||||
if (idStr.length() > 0 && idStr == lastId) {
|
||||
Serial.printf("[%s] SKIP — dedup (id=%s)\n", name, msgId);
|
||||
lineStart = lineEnd + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (msgTime > 0 && (_lastEpoch - msgTime) > (time_t)STALE_MSG_THRESHOLD_S) {
|
||||
Serial.printf("[%s] SKIP — stale (age=%llds, threshold=%ds)\n",
|
||||
name, (long long)(_lastEpoch - msgTime), STALE_MSG_THRESHOLD_S);
|
||||
lineStart = lineEnd + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
Serial.printf("[%s] ACCEPTED: %.50s\n", name, message);
|
||||
if (idStr.length() > 0) lastId = idStr;
|
||||
|
||||
_lastParsedMsgEpoch = msgTime;
|
||||
(this->*handler)(String(message));
|
||||
_lastParsedMsgEpoch = 0;
|
||||
}
|
||||
lineStart = lineEnd + 1;
|
||||
}
|
||||
|
||||
Serial.printf("[%s] Parsed %d JSON objects\n", name, msgCount);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Status Publishing
|
||||
// =====================================================================
|
||||
void DoorbellLogic::queueStatus(const char* st, const String& msg) {
|
||||
_pendingStatus = true;
|
||||
_pendStatusState = st;
|
||||
_pendStatusMsg = msg;
|
||||
Serial.printf("[STATUS] Queued: %s — %s\n", st, msg.c_str());
|
||||
}
|
||||
|
||||
void DoorbellLogic::flushStatus() {
|
||||
if (!_pendingStatus || !WiFi.isConnected()) return;
|
||||
_pendingStatus = false;
|
||||
|
||||
JsonDocument doc;
|
||||
doc["state"] = _pendStatusState;
|
||||
doc["message"] = _pendStatusMsg;
|
||||
doc["timestamp"] = _ntpSynced ? (long long)_timeClient->getEpochTime() * 1000LL : 0LL;
|
||||
|
||||
String payload;
|
||||
serializeJson(doc, payload);
|
||||
|
||||
WiFiClientSecure client;
|
||||
client.setInsecure();
|
||||
|
||||
HTTPClient http;
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
http.begin(client, STATUS_URL);
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
int code = http.POST(payload);
|
||||
http.end();
|
||||
|
||||
Serial.printf("[STATUS] Sent (%d): %s\n", code, _pendStatusState.c_str());
|
||||
}
|
||||
|
||||
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");
|
||||
WiFi.reconnect();
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
|
||||
Serial.println("[WIFI] Reconnected to AP");
|
||||
break;
|
||||
case ARDUINO_EVENT_WIFI_STA_GOT_IP:
|
||||
Serial.printf("[WIFI] Got IP: %s\n", WiFi.localIP().toString().c_str());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user