mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-10-23 14:22:43 +00:00
refactor(settings): move status readouts to Status screen, reorganize by use
Settings held live status (GPS fix, storage/RAM/identity) that belongs on the Status screen, plus a per-second tick() doing SPI flash stat reads and label churn mid-scroll — the main cause of laggy scrolling. - GPS section (sats/location/altitude/HDOP/time) -> StatusScreen - System Info (firmware build, storage, RAM) -> StatusScreen, with storage/RAM stat reads throttled to ~5s and stack-buffer snprintfs instead of Arduino String concatenation - Settings gains a Status link row (trackball-reachable) that opens Route::STATUS; the per-second SettingsScreen tick/refresh is deleted - Reordered sections by frequency of use: General (name/brightness/ timeout/kb-light), Notifications, Network (now includes the TCP/Auto/BLE interface switches), Radio (LoRa + params), Delivery, Advanced, DANGER: Transport Mode (still final) - Identity/LXMF hashes shown in Settings were truncated duplicates of the Status screen's full display; removed - main.cpp publishes firmware build + GPS to the Status screen - Contract test for the storage readout follows the code to StatusScreen
This commit is contained in:
1 parent
8c0d38ee2e
commit
28ea218a18
7 files changed
+348
-337
No files matched your search
@@ -8,10 +8,7 @@
|
||||
|
||||
#include <WiFi.h>
|
||||
#include "../LVGL/LVGLLock.h"
|
||||
#include <LittleFS.h>
|
||||
#include <TinyGPSPlus.h>
|
||||
#include <microReticulum/Log.h>
|
||||
#include <microReticulum/Utilities/OS.h>
|
||||
#include "../LVGL/LVGLInit.h"
|
||||
#include "../TextAreaHelper.h"
|
||||
|
||||
@@ -60,9 +57,7 @@ SettingsScreen::SettingsScreen(lv_obj_t* parent)
|
||||
_ta_tcp_host(nullptr), _ta_tcp_port(nullptr), _btn_reconnect(nullptr),
|
||||
_ta_display_name(nullptr),
|
||||
_slider_brightness(nullptr), _label_brightness_value(nullptr), _switch_kb_light(nullptr), _dropdown_timeout(nullptr),
|
||||
_label_gps_sats(nullptr), _label_gps_coords(nullptr), _label_gps_alt(nullptr), _label_gps_hdop(nullptr), _label_gps_time(nullptr),
|
||||
_label_identity_hash(nullptr), _label_lxmf_address(nullptr), _label_firmware(nullptr),
|
||||
_label_storage(nullptr), _label_ram(nullptr),
|
||||
_btn_status(nullptr),
|
||||
_switch_tcp_enabled(nullptr), _switch_lora_enabled(nullptr),
|
||||
_ta_lora_frequency(nullptr), _dropdown_lora_bandwidth(nullptr),
|
||||
_dropdown_lora_sf(nullptr), _dropdown_lora_cr(nullptr),
|
||||
@@ -72,7 +67,7 @@ SettingsScreen::SettingsScreen(lv_obj_t* parent)
|
||||
_switch_transport_enabled(nullptr), _transport_warning_modal(nullptr),
|
||||
_transport_modal_group(nullptr), _transport_enable_confirmed(false),
|
||||
_btn_propagation_nodes(nullptr), _switch_prop_fallback(nullptr), _switch_prop_only(nullptr),
|
||||
_save_state(0U), _apply_retry_at_ms(0U), _gps(nullptr) {
|
||||
_save_state(0U), _apply_retry_at_ms(0U) {
|
||||
LVGL_LOCK();
|
||||
|
||||
// Create screen object
|
||||
@@ -171,15 +166,14 @@ void SettingsScreen::create_content() {
|
||||
lv_obj_set_scroll_dir(_content, LV_DIR_VER);
|
||||
lv_obj_set_scrollbar_mode(_content, LV_SCROLLBAR_MODE_AUTO);
|
||||
|
||||
// Create sections
|
||||
create_network_section(_content);
|
||||
create_identity_section(_content);
|
||||
create_display_section(_content);
|
||||
// Create sections. Order is by frequency of use: the everyday controls
|
||||
// come first, the rare/advanced ones last. Live status readouts no longer
|
||||
// live here — the Status link at the top opens the Status screen (Route::STATUS).
|
||||
create_general_section(_content);
|
||||
create_notifications_section(_content);
|
||||
create_interfaces_section(_content);
|
||||
create_network_section(_content);
|
||||
create_radio_section(_content);
|
||||
create_delivery_section(_content);
|
||||
create_gps_section(_content);
|
||||
create_system_section(_content);
|
||||
create_advanced_section(_content);
|
||||
// This dangerous opt-in must remain the final Settings section.
|
||||
create_transport_mode_section(_content);
|
||||
@@ -293,17 +287,95 @@ void SettingsScreen::create_network_section(lv_obj_t* parent) {
|
||||
lv_obj_center(label_reconnect);
|
||||
lv_obj_set_style_text_color(label_reconnect, Theme::textPrimary(), 0);
|
||||
lv_obj_set_style_text_font(label_reconnect, &lv_font_montserrat_14, 0);
|
||||
// Auto Discovery row
|
||||
lv_obj_t* auto_row = lv_obj_create(parent);
|
||||
lv_obj_set_width(auto_row, LV_PCT(100));
|
||||
lv_obj_set_height(auto_row, 28);
|
||||
lv_obj_set_style_bg_opa(auto_row, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(auto_row, 0, 0);
|
||||
lv_obj_set_style_pad_all(auto_row, 0, 0);
|
||||
lv_obj_clear_flag(auto_row, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t* auto_label = lv_label_create(auto_row);
|
||||
lv_label_set_text(auto_label, "Auto Discovery:");
|
||||
lv_obj_align(auto_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
lv_obj_set_style_text_color(auto_label, Theme::textTertiary(), 0);
|
||||
lv_obj_set_style_text_font(auto_label, &lv_font_montserrat_14, 0);
|
||||
|
||||
_switch_auto_enabled = lv_switch_create(auto_row);
|
||||
lv_obj_set_size(_switch_auto_enabled, 40, 20);
|
||||
lv_obj_align(_switch_auto_enabled, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_set_style_bg_color(_switch_auto_enabled, Theme::border(), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(_switch_auto_enabled, Theme::primary(), LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
|
||||
// BLE P2P row
|
||||
lv_obj_t* ble_row = lv_obj_create(parent);
|
||||
lv_obj_set_width(ble_row, LV_PCT(100));
|
||||
lv_obj_set_height(ble_row, 28);
|
||||
lv_obj_set_style_bg_opa(ble_row, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(ble_row, 0, 0);
|
||||
lv_obj_set_style_pad_all(ble_row, 0, 0);
|
||||
lv_obj_clear_flag(ble_row, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t* ble_label = lv_label_create(ble_row);
|
||||
lv_label_set_text(ble_label, "BLE P2P:");
|
||||
lv_obj_align(ble_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
lv_obj_set_style_text_color(ble_label, Theme::textTertiary(), 0);
|
||||
lv_obj_set_style_text_font(ble_label, &lv_font_montserrat_14, 0);
|
||||
|
||||
_switch_ble_enabled = lv_switch_create(ble_row);
|
||||
lv_obj_set_size(_switch_ble_enabled, 40, 20);
|
||||
lv_obj_align(_switch_ble_enabled, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_set_style_bg_color(_switch_ble_enabled, Theme::border(), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(_switch_ble_enabled, Theme::primary(), LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
|
||||
// TCP Enable row
|
||||
lv_obj_t* tcp_row = lv_obj_create(parent);
|
||||
lv_obj_set_width(tcp_row, LV_PCT(100));
|
||||
lv_obj_set_height(tcp_row, 28);
|
||||
lv_obj_set_style_bg_opa(tcp_row, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(tcp_row, 0, 0);
|
||||
lv_obj_set_style_pad_all(tcp_row, 0, 0);
|
||||
lv_obj_clear_flag(tcp_row, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t* tcp_label = lv_label_create(tcp_row);
|
||||
lv_label_set_text(tcp_label, "TCP Interface:");
|
||||
lv_obj_align(tcp_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
lv_obj_set_style_text_color(tcp_label, Theme::textTertiary(), 0);
|
||||
lv_obj_set_style_text_font(tcp_label, &lv_font_montserrat_14, 0);
|
||||
|
||||
_switch_tcp_enabled = lv_switch_create(tcp_row);
|
||||
lv_obj_set_size(_switch_tcp_enabled, 40, 20);
|
||||
lv_obj_align(_switch_tcp_enabled, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_set_style_bg_color(_switch_tcp_enabled, Theme::border(), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(_switch_tcp_enabled, Theme::primary(), LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void SettingsScreen::create_identity_section(lv_obj_t* parent) {
|
||||
create_section_header(parent, "== Identity ==");
|
||||
void SettingsScreen::create_general_section(lv_obj_t* parent) {
|
||||
// Status link: live readouts (GPS fix, system storage/RAM, network state)
|
||||
// moved to the Status screen (Route::STATUS). Settings stays actionable-only,
|
||||
// which also removes the per-second refresh work from this scroll view.
|
||||
_btn_status = lv_btn_create(parent);
|
||||
lv_obj_set_width(_btn_status, LV_PCT(100));
|
||||
lv_obj_set_height(_btn_status, 30);
|
||||
lv_obj_set_style_bg_color(_btn_status, Theme::surfaceInput(), 0);
|
||||
lv_obj_set_style_bg_color(_btn_status, Theme::surfaceElevated(), LV_STATE_PRESSED);
|
||||
lv_obj_add_event_cb(_btn_status, on_status_clicked, LV_EVENT_CLICKED, this);
|
||||
lv_obj_t* status_label = lv_label_create(_btn_status);
|
||||
lv_label_set_text(status_label, "Status " LV_SYMBOL_RIGHT);
|
||||
lv_obj_align(status_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
lv_obj_set_style_text_color(status_label, Theme::textPrimary(), 0);
|
||||
{
|
||||
lv_group_t* g = LVGL::LVGLInit::get_default_group();
|
||||
if (g) lv_group_add_obj(g, _btn_status);
|
||||
}
|
||||
|
||||
create_section_header(parent, "== General ==");
|
||||
|
||||
create_label_row(parent, "Display Name:");
|
||||
_ta_display_name = create_text_input(parent, "Your name", false, 32);
|
||||
}
|
||||
|
||||
void SettingsScreen::create_display_section(lv_obj_t* parent) {
|
||||
create_section_header(parent, "== Display ==");
|
||||
|
||||
// Brightness row
|
||||
lv_obj_t* brightness_row = lv_obj_create(parent);
|
||||
@@ -436,71 +508,8 @@ void SettingsScreen::create_notifications_section(lv_obj_t* parent) {
|
||||
lv_obj_set_style_text_font(_label_notification_volume_value, &lv_font_montserrat_14, 0);
|
||||
}
|
||||
|
||||
void SettingsScreen::create_interfaces_section(lv_obj_t* parent) {
|
||||
create_section_header(parent, "== Interfaces ==");
|
||||
|
||||
// Auto Discovery row
|
||||
lv_obj_t* auto_row = lv_obj_create(parent);
|
||||
lv_obj_set_width(auto_row, LV_PCT(100));
|
||||
lv_obj_set_height(auto_row, 28);
|
||||
lv_obj_set_style_bg_opa(auto_row, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(auto_row, 0, 0);
|
||||
lv_obj_set_style_pad_all(auto_row, 0, 0);
|
||||
lv_obj_clear_flag(auto_row, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t* auto_label = lv_label_create(auto_row);
|
||||
lv_label_set_text(auto_label, "Auto Discovery:");
|
||||
lv_obj_align(auto_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
lv_obj_set_style_text_color(auto_label, Theme::textTertiary(), 0);
|
||||
lv_obj_set_style_text_font(auto_label, &lv_font_montserrat_14, 0);
|
||||
|
||||
_switch_auto_enabled = lv_switch_create(auto_row);
|
||||
lv_obj_set_size(_switch_auto_enabled, 40, 20);
|
||||
lv_obj_align(_switch_auto_enabled, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_set_style_bg_color(_switch_auto_enabled, Theme::border(), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(_switch_auto_enabled, Theme::primary(), LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
|
||||
// BLE P2P row
|
||||
lv_obj_t* ble_row = lv_obj_create(parent);
|
||||
lv_obj_set_width(ble_row, LV_PCT(100));
|
||||
lv_obj_set_height(ble_row, 28);
|
||||
lv_obj_set_style_bg_opa(ble_row, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(ble_row, 0, 0);
|
||||
lv_obj_set_style_pad_all(ble_row, 0, 0);
|
||||
lv_obj_clear_flag(ble_row, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t* ble_label = lv_label_create(ble_row);
|
||||
lv_label_set_text(ble_label, "BLE P2P:");
|
||||
lv_obj_align(ble_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
lv_obj_set_style_text_color(ble_label, Theme::textTertiary(), 0);
|
||||
lv_obj_set_style_text_font(ble_label, &lv_font_montserrat_14, 0);
|
||||
|
||||
_switch_ble_enabled = lv_switch_create(ble_row);
|
||||
lv_obj_set_size(_switch_ble_enabled, 40, 20);
|
||||
lv_obj_align(_switch_ble_enabled, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_set_style_bg_color(_switch_ble_enabled, Theme::border(), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(_switch_ble_enabled, Theme::primary(), LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
|
||||
// TCP Enable row
|
||||
lv_obj_t* tcp_row = lv_obj_create(parent);
|
||||
lv_obj_set_width(tcp_row, LV_PCT(100));
|
||||
lv_obj_set_height(tcp_row, 28);
|
||||
lv_obj_set_style_bg_opa(tcp_row, LV_OPA_TRANSP, 0);
|
||||
lv_obj_set_style_border_width(tcp_row, 0, 0);
|
||||
lv_obj_set_style_pad_all(tcp_row, 0, 0);
|
||||
lv_obj_clear_flag(tcp_row, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
lv_obj_t* tcp_label = lv_label_create(tcp_row);
|
||||
lv_label_set_text(tcp_label, "TCP Interface:");
|
||||
lv_obj_align(tcp_label, LV_ALIGN_LEFT_MID, 0, 0);
|
||||
lv_obj_set_style_text_color(tcp_label, Theme::textTertiary(), 0);
|
||||
lv_obj_set_style_text_font(tcp_label, &lv_font_montserrat_14, 0);
|
||||
|
||||
_switch_tcp_enabled = lv_switch_create(tcp_row);
|
||||
lv_obj_set_size(_switch_tcp_enabled, 40, 20);
|
||||
lv_obj_align(_switch_tcp_enabled, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||
lv_obj_set_style_bg_color(_switch_tcp_enabled, Theme::border(), LV_PART_MAIN);
|
||||
lv_obj_set_style_bg_color(_switch_tcp_enabled, Theme::primary(), LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
void SettingsScreen::create_radio_section(lv_obj_t* parent) {
|
||||
create_section_header(parent, "== Radio ==");
|
||||
|
||||
// LoRa Enable row
|
||||
lv_obj_t* lora_row = lv_obj_create(parent);
|
||||
@@ -743,26 +752,6 @@ void SettingsScreen::create_delivery_section(lv_obj_t* parent) {
|
||||
lv_obj_set_style_bg_color(_switch_prop_only, Theme::primary(), LV_PART_INDICATOR | LV_STATE_CHECKED);
|
||||
}
|
||||
|
||||
void SettingsScreen::create_gps_section(lv_obj_t* parent) {
|
||||
create_section_header(parent, "== GPS Status ==");
|
||||
|
||||
_label_gps_sats = create_label_row(parent, "Satellites: --");
|
||||
_label_gps_coords = create_label_row(parent, "Location: No fix");
|
||||
_label_gps_alt = create_label_row(parent, "Altitude: --");
|
||||
_label_gps_hdop = create_label_row(parent, "HDOP: --");
|
||||
_label_gps_time = create_label_row(parent, "Time: --");
|
||||
}
|
||||
|
||||
void SettingsScreen::create_system_section(lv_obj_t* parent) {
|
||||
create_section_header(parent, "== System Info ==");
|
||||
|
||||
_label_identity_hash = create_label_row(parent, "Identity: --");
|
||||
_label_lxmf_address = create_label_row(parent, "LXMF: --");
|
||||
_label_firmware = create_label_row(parent, "Firmware: " FIRMWARE_VERSION);
|
||||
_label_storage = create_label_row(parent, "Storage: --");
|
||||
_label_ram = create_label_row(parent, "RAM: --");
|
||||
}
|
||||
|
||||
void SettingsScreen::create_advanced_section(lv_obj_t* parent) {
|
||||
create_section_header(parent, "== Advanced ==");
|
||||
|
||||
@@ -1388,140 +1377,6 @@ void SettingsScreen::update_settings_from_ui() {
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsScreen::update_gps_display() {
|
||||
LVGL_LOCK();
|
||||
|
||||
// Current system clock (independent of the GPS object): verify the time-sync at
|
||||
// a glance. A sane local date means a good fix synced; "not set" means unsynced;
|
||||
// a far-future year would flag a GPS week-rollover slipping through.
|
||||
{
|
||||
time_t now = time(nullptr);
|
||||
struct tm lt;
|
||||
localtime_r(&now, <);
|
||||
if (lt.tm_year + 1900 >= 2024) { // < 2024 == unsynced (1970 or the ESP32 ~2016 boot default)
|
||||
char tbuf[48];
|
||||
strftime(tbuf, sizeof(tbuf), "Time: %Y-%m-%d %H:%M:%S", <);
|
||||
lv_label_set_text(_label_gps_time, tbuf);
|
||||
} else {
|
||||
lv_label_set_text(_label_gps_time, "Time: not set (awaiting GPS)");
|
||||
}
|
||||
}
|
||||
|
||||
if (!_gps) {
|
||||
lv_label_set_text(_label_gps_sats, "Satellites: N/A");
|
||||
lv_label_set_text(_label_gps_coords, "Location: GPS not available");
|
||||
lv_label_set_text(_label_gps_alt, "Altitude: --");
|
||||
lv_label_set_text(_label_gps_hdop, "HDOP: --");
|
||||
return;
|
||||
}
|
||||
|
||||
// Satellites
|
||||
String sats = "Satellites: " + String(_gps->satellites.value());
|
||||
lv_label_set_text(_label_gps_sats, sats.c_str());
|
||||
|
||||
// Coordinates
|
||||
if (_gps->location.isValid()) {
|
||||
String coords = "Location: " +
|
||||
String(_gps->location.lat(), 4) + ", " +
|
||||
String(_gps->location.lng(), 4);
|
||||
lv_label_set_text(_label_gps_coords, coords.c_str());
|
||||
lv_obj_set_style_text_color(_label_gps_coords, Theme::success(), 0);
|
||||
} else {
|
||||
lv_label_set_text(_label_gps_coords, "Location: No fix");
|
||||
lv_obj_set_style_text_color(_label_gps_coords, Theme::error(), 0);
|
||||
}
|
||||
|
||||
// Altitude
|
||||
if (_gps->altitude.isValid()) {
|
||||
String alt = "Altitude: " + String(_gps->altitude.meters(), 1) + "m";
|
||||
lv_label_set_text(_label_gps_alt, alt.c_str());
|
||||
} else {
|
||||
lv_label_set_text(_label_gps_alt, "Altitude: --");
|
||||
}
|
||||
|
||||
// HDOP (fix quality)
|
||||
if (_gps->hdop.isValid()) {
|
||||
double hdop = _gps->hdop.hdop(); // hdop() already returns the true value (raw value()/100)
|
||||
String quality;
|
||||
if (hdop < 1.0) quality = "Ideal";
|
||||
else if (hdop < 2.0) quality = "Excellent";
|
||||
else if (hdop < 5.0) quality = "Good";
|
||||
else if (hdop < 10.0) quality = "Moderate";
|
||||
else quality = "Poor";
|
||||
|
||||
String hdop_str = "HDOP: " + String(hdop, 1) + " (" + quality + ")";
|
||||
lv_label_set_text(_label_gps_hdop, hdop_str.c_str());
|
||||
} else {
|
||||
lv_label_set_text(_label_gps_hdop, "HDOP: --");
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsScreen::update_system_info() {
|
||||
LVGL_LOCK();
|
||||
// Identity hash
|
||||
if (_identity_hash.size() > 0) {
|
||||
String hash = "Identity: " + String(_identity_hash.toHex().substr(0, 16).c_str()) + "...";
|
||||
lv_label_set_text(_label_identity_hash, hash.c_str());
|
||||
}
|
||||
|
||||
// LXMF address
|
||||
if (_lxmf_address.size() > 0) {
|
||||
String addr = "LXMF: " + String(_lxmf_address.toHex().substr(0, 16).c_str()) + "...";
|
||||
lv_label_set_text(_label_lxmf_address, addr.c_str());
|
||||
}
|
||||
|
||||
// Storage
|
||||
size_t total = LittleFS.totalBytes();
|
||||
size_t used = LittleFS.usedBytes();
|
||||
String storage;
|
||||
if (total == 0 || used > total) {
|
||||
storage = "Storage: unavailable";
|
||||
} else {
|
||||
size_t free = total - used;
|
||||
storage = "Storage: " + String(free / 1024) + " KB free";
|
||||
}
|
||||
lv_label_set_text(_label_storage, storage.c_str());
|
||||
|
||||
// RAM
|
||||
size_t free_heap = ESP.getFreeHeap();
|
||||
String ram = "RAM: " + String(free_heap / 1024) + " KB free";
|
||||
lv_label_set_text(_label_ram, ram.c_str());
|
||||
}
|
||||
|
||||
void SettingsScreen::set_identity_hash(const Bytes& hash) {
|
||||
_identity_hash = hash;
|
||||
}
|
||||
|
||||
void SettingsScreen::set_lxmf_address(const Bytes& hash) {
|
||||
_lxmf_address = hash;
|
||||
}
|
||||
|
||||
void SettingsScreen::set_firmware_version(const String& version) {
|
||||
if (_label_firmware) {
|
||||
String text = "Firmware: " + version;
|
||||
lv_label_set_text(_label_firmware, text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsScreen::set_gps(TinyGPSPlus* gps) {
|
||||
_gps = gps;
|
||||
}
|
||||
|
||||
void SettingsScreen::refresh() {
|
||||
update_gps_display();
|
||||
update_system_info();
|
||||
}
|
||||
|
||||
void SettingsScreen::tick() {
|
||||
// Called every main-loop pass while Settings is the active screen. Throttled to
|
||||
// ~1s, it refreshes the live readouts so the clock (and GPS/system info) updates
|
||||
// on screen instead of being a static snapshot from when the screen was opened.
|
||||
uint32_t now = millis();
|
||||
if (now - _last_tick_ms < 1000) return;
|
||||
_last_tick_ms = now;
|
||||
refresh();
|
||||
}
|
||||
|
||||
void SettingsScreen::set_back_callback(BackCallback callback) {
|
||||
_back_callback = callback;
|
||||
}
|
||||
@@ -1542,9 +1397,12 @@ void SettingsScreen::set_propagation_nodes_callback(PropagationNodesCallback cal
|
||||
_propagation_nodes_callback = callback;
|
||||
}
|
||||
|
||||
void SettingsScreen::set_status_callback(StatusScreenCallback callback) {
|
||||
_status_callback = callback;
|
||||
}
|
||||
|
||||
void SettingsScreen::show() {
|
||||
LVGL_LOCK();
|
||||
refresh();
|
||||
lv_obj_clear_flag(_screen, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_move_foreground(_screen);
|
||||
|
||||
@@ -1681,6 +1539,13 @@ void SettingsScreen::on_transport_cancel_enable(lv_event_t* event) {
|
||||
screen->close_transport_warning();
|
||||
}
|
||||
|
||||
void SettingsScreen::on_status_clicked(lv_event_t* event) {
|
||||
SettingsScreen* screen = (SettingsScreen*)lv_event_get_user_data(event);
|
||||
if (screen->_status_callback) {
|
||||
screen->_status_callback();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsScreen::on_propagation_nodes_clicked(lv_event_t* event) {
|
||||
SettingsScreen* screen = (SettingsScreen*)lv_event_get_user_data(event);
|
||||
if (screen->_propagation_nodes_callback) {
|
||||
|
||||
@@ -10,11 +10,6 @@
|
||||
#include <Preferences.h>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <microReticulum/Bytes.h>
|
||||
#include <microReticulum/Identity.h>
|
||||
|
||||
// Forward declaration
|
||||
class TinyGPSPlus;
|
||||
|
||||
namespace UI {
|
||||
namespace LXMF {
|
||||
@@ -97,41 +92,49 @@ struct AppSettings {
|
||||
* Settings Screen
|
||||
*
|
||||
* Allows configuration of WiFi, TCP server, display, and other settings.
|
||||
* Also shows GPS status and system info.
|
||||
* Live GPS/system readouts live on the Status screen (Route::STATUS);
|
||||
* Settings links there and holds only actionable controls.
|
||||
*
|
||||
* Layout:
|
||||
* +---------------------------------------+
|
||||
* | [<] Settings [Save] | 36px
|
||||
* +---------------------------------------+
|
||||
* | == Network == |
|
||||
* | WiFi SSID: [__________________] |
|
||||
* | Password: [******************] |
|
||||
* | TCP Server: [_________________] |
|
||||
* | TCP Port: [____] [Reconnect] |
|
||||
* | Status > (link)
|
||||
* | == General == |
|
||||
* | Display Name: [_______________] |
|
||||
* | Brightness: [=======o------] 180 |
|
||||
* | Keyboard Light: [OFF] |
|
||||
* | Screen Timeout: [1 min v] |
|
||||
* | |
|
||||
* | == Identity == |
|
||||
* | Display Name: [_______________] |
|
||||
* | == Notifications == |
|
||||
* | Message Sound: [ON] |
|
||||
* | Volume: [=====o--------] 10 |
|
||||
* | |
|
||||
* | == Display == |
|
||||
* | Brightness: [=======o------] 180 |
|
||||
* | Timeout: [1 min v] |
|
||||
* | == Network == |
|
||||
* | WiFi SSID: [__________________] |
|
||||
* | Password: [******************] |
|
||||
* | TCP Server: [_________________] |
|
||||
* | Port: [____] [Reconnect] |
|
||||
* | TCP Interface: [ON] |
|
||||
* | Auto Discovery: [OFF] |
|
||||
* | BLE P2P: [OFF] |
|
||||
* | |
|
||||
* | == GPS Status == |
|
||||
* | Satellites: 8 |
|
||||
* | Location: 40.7128, -74.0060 |
|
||||
* | Altitude: 10.5m |
|
||||
* | HDOP: 1.2 (Excellent) |
|
||||
* | == Radio == |
|
||||
* | LoRa Interface: [OFF] |
|
||||
* | (params shown when enabled) |
|
||||
* | |
|
||||
* | == System Info == |
|
||||
* | Identity: a1b2c3d4e5f6... |
|
||||
* | LXMF: f7e8d9c0b1a2... |
|
||||
* | Firmware: v1.0.0 |
|
||||
* | Storage: 1.2 MB free |
|
||||
* | RAM: 145 KB free |
|
||||
* | == Delivery == |
|
||||
* | Propagation Nodes: [View] |
|
||||
* | Fallback to Prop: [ON] |
|
||||
* | Propagation Only: [OFF] |
|
||||
* | |
|
||||
* | == Advanced == |
|
||||
* | Announce: [60] seconds |
|
||||
* | GPS Sync: [ON] |
|
||||
* | == Advanced == |
|
||||
* | Announce Interval (min): [240] |
|
||||
* | Prop Sync Interval (hrs): [4] |
|
||||
* | GPS Time Sync: [ON] |
|
||||
* | |
|
||||
* | == DANGER: Transport Mode == |
|
||||
* | (warning + confirm switch) |
|
||||
* +---------------------------------------+
|
||||
*/
|
||||
class SettingsScreen {
|
||||
@@ -143,6 +146,7 @@ public:
|
||||
using WifiReconnectCallback = std::function<void(const String&, const String&)>;
|
||||
using BrightnessChangeCallback = std::function<void(uint8_t)>;
|
||||
using PropagationNodesCallback = std::function<void()>;
|
||||
using StatusScreenCallback = std::function<void()>;
|
||||
|
||||
/**
|
||||
* Create settings screen
|
||||
@@ -171,30 +175,9 @@ public:
|
||||
*/
|
||||
const AppSettings& get_settings() const { return _settings; }
|
||||
|
||||
/**
|
||||
* Set identity hash for display
|
||||
*/
|
||||
void set_identity_hash(const RNS::Bytes& hash);
|
||||
|
||||
/**
|
||||
* Set LXMF delivery address hash
|
||||
*/
|
||||
void set_lxmf_address(const RNS::Bytes& hash);
|
||||
|
||||
/** Set the exact running firmware build for post-reboot verification. */
|
||||
void set_firmware_version(const String& version);
|
||||
|
||||
/**
|
||||
* Set GPS pointer for status display
|
||||
*/
|
||||
void set_gps(TinyGPSPlus* gps);
|
||||
|
||||
/**
|
||||
* Refresh GPS and system info displays
|
||||
*/
|
||||
void refresh();
|
||||
void tick(); // periodic update while the screen is shown (ticks clock/GPS/system readouts)
|
||||
|
||||
/**
|
||||
* Set callback for back button
|
||||
*/
|
||||
@@ -220,6 +203,11 @@ public:
|
||||
*/
|
||||
void set_propagation_nodes_callback(PropagationNodesCallback callback);
|
||||
|
||||
/**
|
||||
* Set callback for the Status link (opens Route::STATUS)
|
||||
*/
|
||||
void set_status_callback(StatusScreenCallback callback);
|
||||
|
||||
/**
|
||||
* Show the screen
|
||||
*/
|
||||
@@ -270,20 +258,8 @@ private:
|
||||
lv_obj_t* _slider_notification_volume;
|
||||
lv_obj_t* _label_notification_volume_value;
|
||||
|
||||
// GPS status labels (read-only)
|
||||
lv_obj_t* _label_gps_sats;
|
||||
lv_obj_t* _label_gps_coords;
|
||||
lv_obj_t* _label_gps_alt;
|
||||
lv_obj_t* _label_gps_hdop;
|
||||
lv_obj_t* _label_gps_time;
|
||||
uint32_t _last_tick_ms = 0; // throttle for tick()
|
||||
|
||||
// System info labels (read-only)
|
||||
lv_obj_t* _label_identity_hash;
|
||||
lv_obj_t* _label_lxmf_address;
|
||||
lv_obj_t* _label_firmware;
|
||||
lv_obj_t* _label_storage;
|
||||
lv_obj_t* _label_ram;
|
||||
// Status link row (opens the Status screen with the live readouts)
|
||||
lv_obj_t* _btn_status;
|
||||
|
||||
// Interfaces section
|
||||
lv_obj_t* _switch_tcp_enabled;
|
||||
@@ -319,9 +295,6 @@ private:
|
||||
AppSettings _pending_save_settings;
|
||||
std::atomic<std::uint8_t> _save_state; // 0 idle, 1 pending, 2 processing
|
||||
std::uint32_t _apply_retry_at_ms;
|
||||
RNS::Bytes _identity_hash;
|
||||
RNS::Bytes _lxmf_address;
|
||||
TinyGPSPlus* _gps;
|
||||
|
||||
// Callbacks
|
||||
BackCallback _back_callback;
|
||||
@@ -329,20 +302,18 @@ private:
|
||||
WifiReconnectCallback _wifi_reconnect_callback;
|
||||
BrightnessChangeCallback _brightness_change_callback;
|
||||
PropagationNodesCallback _propagation_nodes_callback;
|
||||
StatusScreenCallback _status_callback;
|
||||
|
||||
// UI construction
|
||||
void create_header();
|
||||
void create_content();
|
||||
void create_network_section(lv_obj_t* parent);
|
||||
void create_identity_section(lv_obj_t* parent);
|
||||
void create_display_section(lv_obj_t* parent);
|
||||
void create_general_section(lv_obj_t* parent);
|
||||
void create_notifications_section(lv_obj_t* parent);
|
||||
void create_interfaces_section(lv_obj_t* parent);
|
||||
void create_gps_section(lv_obj_t* parent);
|
||||
void create_system_section(lv_obj_t* parent);
|
||||
void create_network_section(lv_obj_t* parent);
|
||||
void create_radio_section(lv_obj_t* parent);
|
||||
void create_delivery_section(lv_obj_t* parent);
|
||||
void create_advanced_section(lv_obj_t* parent);
|
||||
void create_transport_mode_section(lv_obj_t* parent);
|
||||
void create_delivery_section(lv_obj_t* parent);
|
||||
|
||||
// Helpers
|
||||
lv_obj_t* create_section_header(lv_obj_t* parent, const char* title);
|
||||
@@ -353,12 +324,11 @@ private:
|
||||
// Update UI from settings
|
||||
void update_ui_from_settings();
|
||||
void update_settings_from_ui();
|
||||
void update_gps_display();
|
||||
void update_system_info();
|
||||
|
||||
// Event handlers
|
||||
static void on_back_clicked(lv_event_t* event);
|
||||
static void on_save_clicked(lv_event_t* event);
|
||||
static void on_status_clicked(lv_event_t* event);
|
||||
static void on_reconnect_clicked(lv_event_t* event);
|
||||
static void on_brightness_changed(lv_event_t* event);
|
||||
static void on_lora_enabled_changed(lv_event_t* event);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#ifdef ARDUINO
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <LittleFS.h>
|
||||
#include <TinyGPSPlus.h>
|
||||
#include <microReticulum/Log.h>
|
||||
#include "../LVGL/LVGLInit.h"
|
||||
#include "../LVGL/LVGLLock.h"
|
||||
@@ -22,6 +24,10 @@ StatusScreen::StatusScreen(lv_obj_t* parent)
|
||||
_label_identity_value(nullptr), _label_lxmf_value(nullptr),
|
||||
_label_wifi_status(nullptr), _label_wifi_ip(nullptr), _label_wifi_rssi(nullptr),
|
||||
_label_rns_status(nullptr), _label_prop_node(nullptr), _label_ble_header(nullptr),
|
||||
_label_gps_sats(nullptr), _label_gps_coords(nullptr), _label_gps_alt(nullptr),
|
||||
_label_gps_hdop(nullptr), _label_gps_time(nullptr),
|
||||
_label_firmware(nullptr), _label_storage(nullptr), _label_ram(nullptr),
|
||||
_gps(nullptr), _last_heavy_ms(0U),
|
||||
_rns_connected(false), _ble_peer_count(0) {
|
||||
// Initialize BLE peer labels array
|
||||
for (size_t i = 0; i < MAX_BLE_PEERS; i++) {
|
||||
@@ -191,6 +197,46 @@ void StatusScreen::create_content() {
|
||||
lv_label_set_long_mode(_label_prop_node, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_style_pad_bottom(_label_prop_node, 8, 0);
|
||||
|
||||
// GPS section (read-only live readouts, previously on the Settings screen)
|
||||
_label_gps_sats = lv_label_create(_content);
|
||||
lv_label_set_text(_label_gps_sats, "Satellites: --");
|
||||
lv_obj_set_style_text_color(_label_gps_sats, Theme::textPrimary(), 0);
|
||||
lv_obj_set_style_pad_bottom(_label_gps_sats, 2, 0);
|
||||
|
||||
_label_gps_coords = lv_label_create(_content);
|
||||
lv_label_set_text(_label_gps_coords, "Location: No fix");
|
||||
lv_obj_set_style_text_color(_label_gps_coords, Theme::textPrimary(), 0);
|
||||
lv_obj_set_style_pad_bottom(_label_gps_coords, 2, 0);
|
||||
|
||||
_label_gps_alt = lv_label_create(_content);
|
||||
lv_label_set_text(_label_gps_alt, "Altitude: --");
|
||||
lv_obj_set_style_text_color(_label_gps_alt, Theme::textPrimary(), 0);
|
||||
lv_obj_set_style_pad_bottom(_label_gps_alt, 2, 0);
|
||||
|
||||
_label_gps_hdop = lv_label_create(_content);
|
||||
lv_label_set_text(_label_gps_hdop, "HDOP: --");
|
||||
lv_obj_set_style_text_color(_label_gps_hdop, Theme::textPrimary(), 0);
|
||||
lv_obj_set_style_pad_bottom(_label_gps_hdop, 2, 0);
|
||||
|
||||
_label_gps_time = lv_label_create(_content);
|
||||
lv_label_set_text(_label_gps_time, "Time: --");
|
||||
lv_obj_set_style_text_color(_label_gps_time, Theme::textPrimary(), 0);
|
||||
lv_obj_set_style_pad_bottom(_label_gps_time, 8, 0);
|
||||
|
||||
// System section (firmware build, storage, RAM)
|
||||
_label_firmware = lv_label_create(_content);
|
||||
lv_label_set_text(_label_firmware, "Firmware: " FIRMWARE_VERSION);
|
||||
lv_obj_set_style_text_color(_label_firmware, Theme::textPrimary(), 0);
|
||||
|
||||
_label_storage = lv_label_create(_content);
|
||||
lv_label_set_text(_label_storage, "Storage: --");
|
||||
lv_obj_set_style_text_color(_label_storage, Theme::textPrimary(), 0);
|
||||
|
||||
_label_ram = lv_label_create(_content);
|
||||
lv_label_set_text(_label_ram, "RAM: --");
|
||||
lv_obj_set_style_text_color(_label_ram, Theme::textPrimary(), 0);
|
||||
lv_obj_set_style_pad_bottom(_label_ram, 8, 0);
|
||||
|
||||
// BLE section header
|
||||
_label_ble_header = lv_label_create(_content);
|
||||
lv_label_set_text(_label_ble_header, "BLE: No peers");
|
||||
@@ -205,6 +251,7 @@ void StatusScreen::create_content() {
|
||||
lv_obj_set_style_pad_left(_label_ble_peers[i], 8, 0);
|
||||
lv_obj_add_flag(_label_ble_peers[i], LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void StatusScreen::set_identity_hash(const Bytes& hash) {
|
||||
@@ -242,6 +289,16 @@ void StatusScreen::set_propagation_node(const String& display) {
|
||||
update_labels();
|
||||
}
|
||||
|
||||
void StatusScreen::set_gps(TinyGPSPlus* gps) {
|
||||
_gps = gps;
|
||||
}
|
||||
|
||||
void StatusScreen::set_firmware_version(const String& version) {
|
||||
LVGL_LOCK();
|
||||
_firmware_version = version;
|
||||
update_system_labels();
|
||||
}
|
||||
|
||||
void StatusScreen::refresh() {
|
||||
LVGL_LOCK();
|
||||
update_labels();
|
||||
@@ -357,6 +414,95 @@ void StatusScreen::update_labels() {
|
||||
lv_obj_add_flag(_label_ble_peers[i], LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
update_gps_labels();
|
||||
update_system_labels();
|
||||
}
|
||||
|
||||
void StatusScreen::update_gps_labels() {
|
||||
// Current system clock (independent of the GPS object): verify the time-sync at
|
||||
// a glance. A sane local date means a good fix synced; "not set" means unsynced.
|
||||
{
|
||||
time_t now = time(nullptr);
|
||||
struct tm lt;
|
||||
localtime_r(&now, <);
|
||||
if (lt.tm_year + 1900 >= 2024) {
|
||||
char tbuf[48];
|
||||
strftime(tbuf, sizeof(tbuf), "Time: %Y-%m-%d %H:%M:%S", <);
|
||||
lv_label_set_text(_label_gps_time, tbuf);
|
||||
} else {
|
||||
lv_label_set_text(_label_gps_time, "Time: not set (awaiting GPS)");
|
||||
}
|
||||
}
|
||||
|
||||
if (!_gps) {
|
||||
lv_label_set_text(_label_gps_sats, "Satellites: N/A");
|
||||
lv_label_set_text(_label_gps_coords, "Location: GPS not available");
|
||||
lv_label_set_text(_label_gps_alt, "Altitude: --");
|
||||
lv_label_set_text(_label_gps_hdop, "HDOP: --");
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "Satellites: %d", (int)_gps->satellites.value());
|
||||
lv_label_set_text(_label_gps_sats, buf);
|
||||
|
||||
if (_gps->location.isValid()) {
|
||||
snprintf(buf, sizeof(buf), "Location: %.4f, %.4f",
|
||||
_gps->location.lat(), _gps->location.lng());
|
||||
lv_label_set_text(_label_gps_coords, buf);
|
||||
lv_obj_set_style_text_color(_label_gps_coords, Theme::success(), 0);
|
||||
} else {
|
||||
lv_label_set_text(_label_gps_coords, "Location: No fix");
|
||||
lv_obj_set_style_text_color(_label_gps_coords, Theme::error(), 0);
|
||||
}
|
||||
|
||||
if (_gps->altitude.isValid()) {
|
||||
snprintf(buf, sizeof(buf), "Altitude: %.1fm", _gps->altitude.meters());
|
||||
lv_label_set_text(_label_gps_alt, buf);
|
||||
} else {
|
||||
lv_label_set_text(_label_gps_alt, "Altitude: --");
|
||||
}
|
||||
|
||||
if (_gps->hdop.isValid()) {
|
||||
double hdop = _gps->hdop.hdop();
|
||||
const char* quality;
|
||||
if (hdop < 1.0) quality = "Ideal";
|
||||
else if (hdop < 2.0) quality = "Excellent";
|
||||
else if (hdop < 5.0) quality = "Good";
|
||||
else if (hdop < 10.0) quality = "Moderate";
|
||||
else quality = "Poor";
|
||||
snprintf(buf, sizeof(buf), "HDOP: %.1f (%s)", hdop, quality);
|
||||
lv_label_set_text(_label_gps_hdop, buf);
|
||||
} else {
|
||||
lv_label_set_text(_label_gps_hdop, "HDOP: --");
|
||||
}
|
||||
}
|
||||
|
||||
void StatusScreen::update_system_labels() {
|
||||
if (_label_firmware && _firmware_version.length() > 0) {
|
||||
char fbuf[96];
|
||||
snprintf(fbuf, sizeof(fbuf), "Firmware: %s", _firmware_version.c_str());
|
||||
lv_label_set_text(_label_firmware, fbuf);
|
||||
}
|
||||
|
||||
// Storage/RAM stat reads hit the SPI flash and the allocator; throttle them
|
||||
// to ~5s so the per-second refresh stays cheap.
|
||||
uint32_t now = millis();
|
||||
if (now - _last_heavy_ms < 5000) return;
|
||||
_last_heavy_ms = now;
|
||||
|
||||
char buf[48];
|
||||
size_t total = LittleFS.totalBytes();
|
||||
size_t used = LittleFS.usedBytes();
|
||||
if (total == 0 || used > total) {
|
||||
lv_label_set_text(_label_storage, "Storage: unavailable");
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "Storage: %d KB free", (int)((total - used) / 1024));
|
||||
lv_label_set_text(_label_storage, buf);
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "RAM: %d KB free", (int)(ESP.getFreeHeap() / 1024));
|
||||
lv_label_set_text(_label_ram, buf);
|
||||
}
|
||||
|
||||
void StatusScreen::set_back_callback(BackCallback callback) {
|
||||
@@ -373,7 +519,7 @@ void StatusScreen::set_radio_activity_callback(RadioActivityCallback callback) {
|
||||
|
||||
void StatusScreen::show() {
|
||||
LVGL_LOCK();
|
||||
refresh(); // Update status when shown
|
||||
update_labels(); // Update status when shown
|
||||
lv_obj_clear_flag(_screen, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_move_foreground(_screen);
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
#include <microReticulum/Bytes.h>
|
||||
#include <microReticulum/Identity.h>
|
||||
|
||||
// Forward declaration
|
||||
class TinyGPSPlus;
|
||||
|
||||
namespace UI {
|
||||
namespace LXMF {
|
||||
|
||||
@@ -23,6 +26,8 @@ namespace LXMF {
|
||||
* - LXMF delivery destination hash
|
||||
* - WiFi status and IP
|
||||
* - RNS connection status
|
||||
* - GPS fix (satellites, location, altitude, HDOP, time)
|
||||
* - System (firmware build, storage, RAM)
|
||||
*
|
||||
* Layout:
|
||||
* ┌─────────────────────────────────────┐
|
||||
@@ -77,6 +82,16 @@ public:
|
||||
*/
|
||||
void set_rns_status(bool connected, const String& server_name = "");
|
||||
|
||||
/**
|
||||
* Set the GPS receiver for live fix readouts
|
||||
*/
|
||||
void set_gps(TinyGPSPlus* gps);
|
||||
|
||||
/**
|
||||
* Set the exact running firmware build string (e.g. "v0.3.7 [ota_0]")
|
||||
*/
|
||||
void set_firmware_version(const String& version);
|
||||
|
||||
/**
|
||||
* Set propagation node display string
|
||||
* @param display Formatted string like "Auto (NodeName)" or "None"
|
||||
@@ -153,6 +168,18 @@ private:
|
||||
lv_obj_t* _label_rns_status;
|
||||
lv_obj_t* _label_prop_node;
|
||||
|
||||
// GPS readout labels (read-only)
|
||||
lv_obj_t* _label_gps_sats;
|
||||
lv_obj_t* _label_gps_coords;
|
||||
lv_obj_t* _label_gps_alt;
|
||||
lv_obj_t* _label_gps_hdop;
|
||||
lv_obj_t* _label_gps_time;
|
||||
|
||||
// System readout labels (read-only)
|
||||
lv_obj_t* _label_firmware;
|
||||
lv_obj_t* _label_storage;
|
||||
lv_obj_t* _label_ram;
|
||||
|
||||
// BLE peer labels (pre-allocated, hidden when unused)
|
||||
lv_obj_t* _label_ble_header;
|
||||
lv_obj_t* _label_ble_peers[MAX_BLE_PEERS]; // Each shows identity + rssi + mac
|
||||
@@ -162,6 +189,9 @@ private:
|
||||
bool _rns_connected;
|
||||
String _rns_server;
|
||||
String _prop_node_display;
|
||||
String _firmware_version;
|
||||
TinyGPSPlus* _gps;
|
||||
uint32_t _last_heavy_ms; // throttles storage/RAM stat reads in update_labels()
|
||||
|
||||
// BLE peer data (cached for display)
|
||||
BLEPeerInfo _ble_peers[MAX_BLE_PEERS];
|
||||
@@ -174,6 +204,8 @@ private:
|
||||
void create_header();
|
||||
void create_content();
|
||||
void update_labels();
|
||||
void update_gps_labels();
|
||||
void update_system_labels();
|
||||
|
||||
static void on_back_clicked(lv_event_t* event);
|
||||
static void on_share_clicked(lv_event_t* event);
|
||||
|
||||
@@ -594,6 +594,12 @@ bool UIManager::init() {
|
||||
[this]() { on_back_from_settings(); }
|
||||
);
|
||||
|
||||
// Status link on the Settings screen opens the Status screen; the
|
||||
// navigation stack carries the back button there.
|
||||
_settings_screen->set_status_callback(
|
||||
[this]() { show_status(); }
|
||||
);
|
||||
|
||||
|
||||
// Set up callbacks for propagation nodes screen
|
||||
_propagation_nodes_screen->set_back_callback(
|
||||
@@ -646,10 +652,6 @@ bool UIManager::init() {
|
||||
}
|
||||
}
|
||||
|
||||
// Set identity and LXMF address on settings screen
|
||||
_settings_screen->set_identity_hash(_router.identity().hash());
|
||||
_settings_screen->set_lxmf_address(_router.delivery_destination().hash());
|
||||
|
||||
|
||||
// Set identity hash and LXMF address on status screen
|
||||
_status_screen->set_identity_hash(_router.identity().hash());
|
||||
@@ -813,9 +815,6 @@ void UIManager::update() {
|
||||
if (_navigation.current() == Route::ANNOUNCES && _announce_list_screen) {
|
||||
_announce_list_screen->tick();
|
||||
}
|
||||
if (_navigation.current() == Route::SETTINGS && _settings_screen) {
|
||||
_settings_screen->tick(); // keep the live clock / GPS / system readouts ticking
|
||||
}
|
||||
const uint32_t now = millis();
|
||||
nomad_update_library();
|
||||
nomad_update();
|
||||
@@ -1149,7 +1148,6 @@ void UIManager::render_route(Route route) {
|
||||
_announce_list_screen->show();
|
||||
break;
|
||||
case Route::STATUS:
|
||||
_status_screen->refresh();
|
||||
_status_screen->show();
|
||||
break;
|
||||
case Route::RADIO_ACTIVITY:
|
||||
@@ -1165,7 +1163,6 @@ void UIManager::render_route(Route route) {
|
||||
_nomadnet_screen->show();
|
||||
break;
|
||||
case Route::SETTINGS:
|
||||
_settings_screen->refresh();
|
||||
_settings_screen->show();
|
||||
break;
|
||||
case Route::CALL: if (_call_screen) _call_screen->show(); break;
|
||||
@@ -1874,9 +1871,6 @@ void UIManager::refresh_current_screen() {
|
||||
case Route::STATUS:
|
||||
_status_screen->refresh();
|
||||
break;
|
||||
case Route::SETTINGS:
|
||||
_settings_screen->refresh();
|
||||
break;
|
||||
case Route::PROPAGATION_NODES:
|
||||
_propagation_nodes_screen->refresh();
|
||||
break;
|
||||
|
||||
+8
-6
@@ -1453,9 +1453,8 @@ void setup_ui_manager() {
|
||||
// Set GPS for satellite count display
|
||||
ui_manager->set_gps(&gps);
|
||||
|
||||
// Configure settings screen
|
||||
UI::LXMF::SettingsScreen* settings = ui_manager->get_settings_screen();
|
||||
if (settings) {
|
||||
// Publish the exact running build (with OTA slot) to the Status screen
|
||||
if (ui_manager->get_status_screen()) {
|
||||
String firmware_info = FIRMWARE_VERSION;
|
||||
const esp_partition_t* running_partition = esp_ota_get_running_partition();
|
||||
if (running_partition) {
|
||||
@@ -1463,10 +1462,13 @@ void setup_ui_manager() {
|
||||
firmware_info += running_partition->label;
|
||||
firmware_info += "]";
|
||||
}
|
||||
settings->set_firmware_version(firmware_info);
|
||||
// Pass GPS for status display
|
||||
settings->set_gps(&gps);
|
||||
ui_manager->get_status_screen()->set_firmware_version(firmware_info);
|
||||
ui_manager->get_status_screen()->set_gps(&gps);
|
||||
}
|
||||
|
||||
// Configure settings screen
|
||||
UI::LXMF::SettingsScreen* settings = ui_manager->get_settings_screen();
|
||||
if (settings) {
|
||||
// Set brightness change callback (immediate)
|
||||
settings->set_brightness_change_callback([](uint8_t brightness) {
|
||||
// Apply brightness immediately via display backlight
|
||||
|
||||
@@ -135,7 +135,9 @@ def test_successful_boot_cancels_ota_rollback_after_subsystems_initialize():
|
||||
|
||||
|
||||
def test_system_info_reports_littlefs_not_unmounted_spiffs():
|
||||
source = (REPO_ROOT / "lib/tdeck_ui/UI/LXMF/SettingsScreen.cpp").read_text()
|
||||
# The live system readouts (firmware build, storage, RAM) moved from the
|
||||
# Settings screen to the Status screen; the contract follows them there.
|
||||
source = (REPO_ROOT / "lib/tdeck_ui/UI/LXMF/StatusScreen.cpp").read_text()
|
||||
assert '"Firmware: " FIRMWARE_VERSION' in source
|
||||
assert "LittleFS.totalBytes()" in source
|
||||
assert "LittleFS.usedBytes()" in source
|
||||
|
||||
Reference in new issue
Block a user