#include "App.h" #include "../util/Version.h" #include "../util/BootTrigger.h" #include "../settings/Settings.h" #include "../net/WiFiManager.h" #include "../net/WebUI.h" #include "../net/OtaService.h" #include "../net/WebhookService.h" #include "../ui/Display.h" #include "../sensors/MoistureSensor.h" #include "../sensors/BatterySensor.h" #include "../ui/FaceRenderer.h" static Settings settings; static WiFiManager wifi; static WebUI web; static OtaService ota; static WebhookService webhook; static Display display; static MoistureSensor moisture; static BatterySensor battery; static FaceRenderer face; static unsigned long bootMs = 0; static FaceRenderer::Mood lastMood = FaceRenderer::HAPPY; static bool lastDead = false; static PlantEventType moodToEvent(FaceRenderer::Mood m) { if (m == FaceRenderer::DRY) return EVT_DRY; if (m == FaceRenderer::TOO_WET) return EVT_TOO_WET; return EVT_OK; } void App::setup() { bootMs = millis(); Serial.begin(115200); delay(100); Serial.println("\n\n=== FacePlant Starting ==="); Serial.print("Firmware: "); Serial.println(PB_VERSION); settings.begin(); bool forceSetup = BootTrigger::checkAndConsume(); Serial.print("Force setup mode: "); Serial.println(forceSetup ? "YES" : "NO"); Serial.print("Saved WiFi SSID: "); Serial.println(settings.hasWiFi() ? settings.wifiSsid() : "(none)"); display.begin(); display.showStatus("FacePlant", "Starting..."); wifi.begin(settings, forceSetup); moisture.begin(settings); battery.begin(); face.begin(display, settings); webhook.begin(settings); web.begin(settings, wifi, moisture, face, webhook, bootMs); ota.begin(web.server(), &display); lastMood = face.mood(); lastDead = face.isDeadMode(); Serial.println("=== Setup Complete ===\n"); } void App::loop() { static bool lastConnected = false; static unsigned long lastDisplayUpdate = 0; BootTrigger::clearAfterStableUptime(); wifi.loop(); web.loop(); // Show WiFi status on display during connection attempts bool currentConnected = wifi.connected(); if (currentConnected != lastConnected) { if (currentConnected) { Serial.println("[App] WiFi connected - showing on display"); display.showStatus("WiFi Connected!", wifi.ssid().c_str()); delay(2000); } else if (wifi.mode() == NET_STA) { Serial.println("[App] WiFi disconnected"); if (millis() - lastDisplayUpdate > 5000) { display.showStatus("WiFi", "Connecting..."); lastDisplayUpdate = millis(); } } lastConnected = currentConnected; } moisture.loop(); battery.loop(); face.loop(moisture, battery); // Webhook events on state transitions FaceRenderer::Mood m = face.mood(); bool dead = face.isDeadMode(); if (dead && !lastDead) { webhook.send(EVT_DEAD, moisture, dead, bootMs); } else if (!dead && lastDead) { webhook.send(EVT_OK, moisture, dead, bootMs); } else if (!dead && m != lastMood) { webhook.send(moodToEvent(m), moisture, dead, bootMs); } lastMood = m; lastDead = dead; }