Files
slate32/src/main.cpp
T
evan eec9b692c6 feat(wifi): scan, connect and persist networks from the settings app
Adds a wifi binding over the Arduino API and a settings flow that scans, picks
the strongest AP per SSID, takes a password from an on-screen keyboard, and
reports connection state. Credentials join /settings.lua and reconnect at boot.

Settings are now written through a temp file and rename, and strings are
Lua-escaped, so a password cannot corrupt the file the firmware parses at boot.
2026-08-01 06:29:38 -04:00

84 lines
2.1 KiB
C++

#include <SD.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include <WiFi.h>
#include <XPT2046_Touchscreen.h>
#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);
}