// roro9stack — temporary diagnostics screen for M0 hardware checks (replaced by the Launcher in step 7). #include #include "event_bus.h" #include "platform/nvs_store.h" #include "service_manager.h" #include "services/battery_service.h" #include "services/clock_service.h" #include "services/power_service.h" #include "services/storage_service.h" #include "settings.h" #include "version.h" using namespace roro; static EventBus bus; static NvsStore nvs; static Settings settings(nvs, bus); static ServiceManager services; static BatteryService* battery; static StorageService* storage; static PowerService* power; static ClockService* clockService; static String lastKey = "-"; static String lastEvent = "-"; static bool dirty = true; static void draw() { auto& d = M5Cardputer.Display; d.fillScreen(TFT_BLACK); d.setTextSize(1); d.setTextColor(TFT_GREEN); d.drawString(String(kProductName) + " " + versionString(), 4, 2); d.setTextColor(TFT_WHITE); const auto& b = battery->estimator(); const auto& s = storage->state(); int y = 16; auto line = [&](const String& text) { d.drawString(text, 4, y); y += 11; }; line("Battery: " + String(b.percent()) + "% " + String(b.millivolts()) + " mV (raw " + String(battery->lastRawMillivolts()) + ")"); if (s.present) line("SD: " + String(s.usedPercent) + "% of " + String((uint32_t)(s.totalBytes >> 20)) + " MB logs:" + (s.logsAllowed ? "y" : "n") + " capt:" + (s.capturesAllowed ? "y" : "n")); else line("SD: no card"); line("Clock: " + String(clockService->displayTime().c_str()) + " uptime " + String(millis() / 1000) + " s"); line("Heap: " + String(ESP.getFreeHeap() / 1024) + " KB free, min " + String(ESP.getMinFreeHeap() / 1024) + " KB"); line("Bright: " + String(settings.getInt(Setting::Brightness)) + "% dim " + String(settings.getInt(Setting::DimTimeoutS)) + "s off " + String(settings.getInt(Setting::OffTimeoutS)) + "s"); line("Key: " + lastKey); line("Event: " + lastEvent); d.setTextColor(TFT_DARKGREY); line("+/- brightness, d: dim test (5s/10s)"); } static void logEvent(const String& text) { lastEvent = text; dirty = true; Serial.println("event: " + text); } void setup() { auto cfg = M5.config(); M5Cardputer.begin(cfg, true); M5Cardputer.Display.setRotation(1); Serial.begin(115200); nvs.begin(); settings.load(); battery = new BatteryService(bus); storage = new StorageService(bus); power = new PowerService(settings); clockService = new ClockService(settings, bus); services.add(*power); services.add(*clockService); services.add(*battery); services.add(*storage); bus.subscribe(EventType::BatteryChanged, [](const Event& e) { logEvent("battery " + String(e.a) + "% " + String(e.b) + "mV"); }); bus.subscribe(EventType::StorageThreshold, [](const Event& e) { logEvent("storage level " + String(e.b) + " (" + String(e.a) + "%)"); }); bus.subscribe(EventType::Notification, [](const Event& e) { logEvent("notify: " + String(e.text)); }); bus.subscribe(EventType::SettingChanged, [](const Event& e) { logEvent("setting " + String(e.text)); }); services.startAll(millis()); } static void handleKeys() { if (!M5Cardputer.Keyboard.isChange() || !M5Cardputer.Keyboard.isPressed()) return; if (power->onKey(millis())) return; // only woke the screen auto state = M5Cardputer.Keyboard.keysState(); String word; for (auto c : state.word) word += c; lastKey = word + (state.fn ? " +fn" : "") + (state.opt ? " +opt" : "") + (state.ctrl ? " +ctrl" : "") + (state.enter ? " enter" : "") + (state.del ? " del" : ""); dirty = true; int brightness = settings.getInt(Setting::Brightness); if (word == "=" || word == "+") settings.setInt(Setting::Brightness, brightness + 10); if (word == "-") settings.setInt(Setting::Brightness, brightness - 10); if (word == "d") { // Short timeouts to test dimming quickly; persisted, so they survive a reboot. settings.setInt(Setting::DimTimeoutS, 5); settings.setInt(Setting::OffTimeoutS, 10); } } void loop() { static uint32_t lastDraw = 0, lastLog = 0; M5Cardputer.update(); handleKeys(); services.tick(millis()); bus.dispatch(); if (millis() - lastLog > 5000) { lastLog = millis(); const auto& s = storage->state(); Serial.printf("status: batt %d%% %dmV raw %d | sd %s %d%% total %lluMB | heap %u min %u | screen %d\n", battery->estimator().percent(), battery->estimator().millivolts(), battery->lastRawMillivolts(), s.present ? "yes" : "no", s.usedPercent, (unsigned long long)(s.totalBytes >> 20), ESP.getFreeHeap(), ESP.getMinFreeHeap(), (int)power->screen()); } if (power->screen() != ScreenState::Off && (dirty || millis() - lastDraw > 1000)) { lastDraw = millis(); dirty = false; draw(); } }