#include #include #include #include #include #include "lua/lua_app.h" #include "settings.h" // SD card is on a separate bus from the display/touch (board schematic) static constexpr int SD_CS = 5; static constexpr int SD_SCK = 18; static constexpr int SD_MOSI = 23; static constexpr int SD_MISO = 19; static constexpr int TOUCH_CS = 33; static const char* LAUNCHER = "/apps/launcher/main.lua"; TFT_eSPI tft; SPIClass touchSpi(HSPI); SPIClass& sdSpi = SPI; XPT2046_Touchscreen touch(TOUCH_CS); LuaApp app(tft, touch); bool halted = false; String nextApp; // Last resort only: the UI lives in Lua, so this exists purely to explain why // nothing else could run. void fallbackScreen(const char* message) { tft.setRotation(0); tft.fillScreen(TFT_WHITE); tft.setTextColor(TFT_RED, TFT_WHITE); tft.drawString(message, 10, 10); tft.setTextColor(TFT_BLACK, TFT_WHITE); tft.drawString("expected /apps/launcher/main.lua", 10, 30); Serial.printf("halted: %s\n", message); halted = true; } void startApp(const String& path) { tft.setRotation(settings.rotationIndex()); // apps may have rotated the frame Serial.printf("launching %s\n", path.c_str()); if (app.load(path.c_str())) return; if (path == LAUNCHER) fallbackScreen("launcher failed to start"); } void setup() { Serial.begin(115200); tft.begin(); tft.setRotation(0); tft.fillScreen(TFT_WHITE); touchSpi.begin(14, 12, 13, TOUCH_CS); touch.begin(touchSpi); sdSpi.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS); if (!SD.begin(SD_CS, sdSpi)) { fallbackScreen("SD card mount failed"); return; } settings.load(); // absent file keeps the built-in defaults WiFi.persistent(false); if (settings.wifiSsid.length()) { WiFi.mode(WIFI_STA); WiFi.begin(settings.wifiSsid.c_str(), settings.wifiPassword.c_str()); } startApp(LAUNCHER); } void loop() { if (halted) return; if (app.running()) { app.loop(); if (!app.running()) nextApp = app.takePendingLaunch(); return; } String path = nextApp.length() > 0 ? nextApp : String(LAUNCHER); nextApp = ""; startApp(path); delay(10); }