feat(ui-lvgl): WiFi icon in the status bar

Shown while WiFi is switched on (Settings > WiFi), bright when connected
(live tiles, map download, update), dim otherwise. The switch is read
from NVS once and cached. The status icon signature buffer grew so
changes to the last icons aren't missed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-26 23:16:47 +02:00
co-authored by Claude Opus 5.5
parent eb201bc4fd
commit 708fde7225
4 changed files with 24 additions and 14 deletions
+2 -1
View File
@@ -257,5 +257,6 @@ User list, second batch (2026-09-26):
default; off: only the top button wakes it). default; off: only the top button wakes it).
- [x] Favourites as their own screen in the menu rather than a tile: a - [x] Favourites as their own screen in the menu rather than a tile: a
Home card left of the main page (six slots, unread badges). Home card left of the main page (six slots, unread badges).
- [ ] WiFi indicator icon in the status bar. - [x] WiFi indicator icon in the status bar: while WiFi is switched on,
bright when connected, dim otherwise.
+16 -9
View File
@@ -140,14 +140,18 @@ static void saveWifi(const char* ssid, const char* pass) {
} }
// Settings > WiFi's switch: off keeps the radio off for everything (scan, map // Settings > WiFi's switch: off keeps the radio off for everything (scan, map
// download). Kept with the credentials. // download). Kept with the credentials.
static int8_t s_wifi_allowed = -1; // read once (the status bar asks every second)
static bool wifiAllowed() { static bool wifiAllowed() {
Preferences p; if (s_wifi_allowed < 0) {
if (!p.begin("mc_wifi", true)) return true; Preferences p;
bool on = p.getBool("on", true); bool on = true;
p.end(); if (p.begin("mc_wifi", true)) { on = p.getBool("on", true); p.end(); }
return on; s_wifi_allowed = on;
}
return s_wifi_allowed;
} }
static void setWifiAllowed(bool on) { static void setWifiAllowed(bool on) {
s_wifi_allowed = on;
Preferences p; Preferences p;
if (!p.begin("mc_wifi", false)) return; if (!p.begin("mc_wifi", false)) return;
p.putBool("on", on); p.putBool("on", on);
@@ -316,8 +320,9 @@ static void netEnd() {
WiFi.disconnect(true); WiFi.disconnect(true);
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
} }
// One line for the download popup: link details and the internal heap (TLS, // For the status bar: NET_UP once connected, else NET_OFF (radio off, a scan,
// WiFi and BLE all compete for it). // still connecting).
static int netRadio() { return WiFi.getMode() != WIFI_OFF && WiFi.status() == WL_CONNECTED ? NET_UP : NET_OFF; }
// Async scan: scanStart(), then scanResults() returns -1 while running, else // Async scan: scanStart(), then scanResults() returns -1 while running, else
// the count, filling `names` (strongest first, as the driver reports them). // the count, filling `names` (strongest first, as the driver reports them).
static void scanStart() { static void scanStart() {
@@ -545,9 +550,11 @@ static void setWifiAllowed(bool on) { s_wifi_on = on; }
static bool s_live_tiles = true; static bool s_live_tiles = true;
static bool liveTiles() { return s_live_tiles; } static bool liveTiles() { return s_live_tiles; }
static void setLiveTiles(bool on) { s_live_tiles = on; } static void setLiveTiles(bool on) { s_live_tiles = on; }
static void netBegin(const char*, const char*) {} static bool s_net_on = false;
static void netBegin(const char*, const char*) { s_net_on = true; }
static int netState() { return NET_UP; } static int netState() { return NET_UP; }
static void netEnd() {} static void netEnd() { s_net_on = false; }
static int netRadio() { return s_net_on ? NET_UP : NET_OFF; }
static uint32_t s_scan_at = 0; static uint32_t s_scan_at = 0;
static void scanStart() { s_scan_at = millis(); } static void scanStart() { s_scan_at = millis(); }
static int scanResults(char names[][33], int max) { // a pretend scan, for the UI static int scanResults(char names[][33], int max) { // a pretend scan, for the UI
+5 -3
View File
@@ -624,14 +624,16 @@ void UITask::refreshStatusBar() {
lv_label_set_text_fmt(_status_batt, "%s%s%s", _board->isExternalPowered() ? LV_SYMBOL_CHARGE : "", batt, level); lv_label_set_text_fmt(_status_batt, "%s%s%s", _board->isExternalPowered() ? LV_SYMBOL_CHARGE : "", batt, level);
// Status icons, as the original's status bar (ui-new): Bluetooth (bright // Status icons, as the original's status bar (ui-new): Bluetooth (bright
// when the app is connected), GPS (green with a fix), the alarm, mute, then // when the app is connected), WiFi (when switched on), GPS (green with a fix), the alarm, mute, then
// the modes that keep running in the background -- auto-advert, trail, live // the modes that keep running in the background -- auto-advert, trail, live
// share, repeater, arrival alert -- in the accent colour. Right to left in // share, repeater, arrival alert -- in the accent colour. Right to left in
// that order, next to the battery. // that order, next to the battery.
struct Icon { const char* sym; uint32_t col; }; struct Icon { const char* sym; uint32_t col; };
Icon icons[10]; Icon icons[11];
int n = 0; int n = 0;
if (isSerialEnabled()) icons[n++] = { LV_SYMBOL_BLUETOOTH, hasConnection() ? theme::TEXT : theme::TEXT_MUTED }; if (isSerialEnabled()) icons[n++] = { LV_SYMBOL_BLUETOOTH, hasConnection() ? theme::TEXT : theme::TEXT_MUTED };
if (lvport::wifiAllowed()) // WiFi switched on: bright while connected (map tiles, update)
icons[n++] = { LV_SYMBOL_WIFI, lvport::netRadio() == lvport::NET_UP ? theme::TEXT : theme::TEXT_MUTED };
int32_t lat, lon; int32_t lat, lon;
bool fix = _core->course.currentLocation(lat, lon); bool fix = _core->course.currentLocation(lat, lon);
if (_core->gpsEnabled() || fix) icons[n++] = { LV_SYMBOL_GPS, fix ? theme::OK : theme::TEXT_MUTED }; if (_core->gpsEnabled() || fix) icons[n++] = { LV_SYMBOL_GPS, fix ? theme::OK : theme::TEXT_MUTED };
@@ -644,7 +646,7 @@ void UITask::refreshStatusBar() {
if (_prefs && _prefs->loc_share_enabled) icons[n++] = { UI_SYMBOL_PIN, theme::ACCENT }; if (_prefs && _prefs->loc_share_enabled) icons[n++] = { UI_SYMBOL_PIN, theme::ACCENT };
if (_prefs && _prefs->client_repeat) icons[n++] = { LV_SYMBOL_LOOP, theme::ACCENT }; if (_prefs && _prefs->client_repeat) icons[n++] = { LV_SYMBOL_LOOP, theme::ACCENT };
if (_prefs && _prefs->locator_enabled && _prefs->locator_has_target) icons[n++] = { UI_SYMBOL_FLAG, theme::ACCENT }; if (_prefs && _prefs->locator_enabled && _prefs->locator_has_target) icons[n++] = { UI_SYMBOL_FLAG, theme::ACCENT };
char sig[48]; char sig[sizeof(_status_sig)];
int o = 0; int o = 0;
for (int i = 0; i < n && o < (int)sizeof(sig) - 12; i++) o += snprintf(sig + o, sizeof(sig) - o, "%s%lx", icons[i].sym, (unsigned long)icons[i].col); for (int i = 0; i < n && o < (int)sizeof(sig) - 12; i++) o += snprintf(sig + o, sizeof(sig) - o, "%s%lx", icons[i].sym, (unsigned long)icons[i].col);
sig[o] = '\0'; sig[o] = '\0';
+1 -1
View File
@@ -427,7 +427,7 @@ private:
lv_obj_t* _status_time = nullptr; lv_obj_t* _status_time = nullptr;
lv_obj_t* _status_icons = nullptr; // a row of icon labels, rebuilt with the state lv_obj_t* _status_icons = nullptr; // a row of icon labels, rebuilt with the state
lv_obj_t* _status_batt = nullptr; lv_obj_t* _status_batt = nullptr;
char _status_sig[48] = ""; // what the icon row shows, to skip unchanged rebuilds char _status_sig[128] = ""; // what the icon row shows, to skip unchanged rebuilds
lv_obj_t* _toast = nullptr; lv_obj_t* _toast = nullptr;
lv_timer_t* _toast_timer = nullptr; lv_timer_t* _toast_timer = nullptr;
lv_obj_t* _home_clock = nullptr; lv_obj_t* _home_clock = nullptr;