2026-02-09 11:41:12 -05:00
|
|
|
#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"
|
2026-02-10 17:05:42 -05:00
|
|
|
#include "../sensors/BatterySensor.h"
|
2026-02-09 11:41:12 -05:00
|
|
|
#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;
|
2026-02-10 17:05:42 -05:00
|
|
|
static BatterySensor battery;
|
2026-02-09 11:41:12 -05:00
|
|
|
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();
|
|
|
|
|
|
2026-02-10 17:05:42 -05:00
|
|
|
Serial.begin(115200);
|
|
|
|
|
delay(100);
|
|
|
|
|
Serial.println("\n\n=== FacePlant Starting ===");
|
|
|
|
|
Serial.print("Firmware: ");
|
|
|
|
|
Serial.println(PB_VERSION);
|
|
|
|
|
|
2026-02-09 11:41:12 -05:00
|
|
|
settings.begin();
|
|
|
|
|
bool forceSetup = BootTrigger::checkAndConsume();
|
|
|
|
|
|
2026-02-10 17:05:42 -05:00
|
|
|
Serial.print("Force setup mode: ");
|
|
|
|
|
Serial.println(forceSetup ? "YES" : "NO");
|
|
|
|
|
Serial.print("Saved WiFi SSID: ");
|
|
|
|
|
Serial.println(settings.hasWiFi() ? settings.wifiSsid() : "(none)");
|
|
|
|
|
|
2026-02-09 11:41:12 -05:00
|
|
|
display.begin();
|
|
|
|
|
display.showStatus("FacePlant", "Starting...");
|
|
|
|
|
|
|
|
|
|
wifi.begin(settings, forceSetup);
|
|
|
|
|
|
|
|
|
|
moisture.begin(settings);
|
2026-02-10 17:05:42 -05:00
|
|
|
battery.begin();
|
2026-02-09 11:41:12 -05:00
|
|
|
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();
|
2026-02-10 17:05:42 -05:00
|
|
|
|
|
|
|
|
Serial.println("=== Setup Complete ===\n");
|
2026-02-09 11:41:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void App::loop() {
|
2026-02-10 17:05:42 -05:00
|
|
|
static bool lastConnected = false;
|
|
|
|
|
static unsigned long lastDisplayUpdate = 0;
|
|
|
|
|
|
2026-02-09 11:41:12 -05:00
|
|
|
BootTrigger::clearAfterStableUptime();
|
|
|
|
|
|
|
|
|
|
wifi.loop();
|
|
|
|
|
web.loop();
|
|
|
|
|
|
2026-02-10 17:05:42 -05:00
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 11:41:12 -05:00
|
|
|
moisture.loop();
|
2026-02-10 17:05:42 -05:00
|
|
|
battery.loop();
|
|
|
|
|
face.loop(moisture, battery);
|
2026-02-09 11:41:12 -05:00
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|