Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions firmware/lv_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

/* Memory */
#define LV_MEM_CUSTOM 1
#define LV_MEM_CUSTOM_INCLUDE <stdlib.h>
#define LV_MEM_CUSTOM_ALLOC malloc
#define LV_MEM_CUSTOM_INCLUDE "lv_psram_alloc.h"
#define LV_MEM_CUSTOM_ALLOC lv_psram_malloc
#define LV_MEM_CUSTOM_FREE free
#define LV_MEM_CUSTOM_REALLOC realloc
#define LV_MEM_CUSTOM_REALLOC lv_psram_realloc

/* Display — sized to the larger of supported boards (T-Watch Ultra 410x502).
T-Deck Plus (320x240) uses less than the cap; the unused PSRAM is wasted
Expand Down
21 changes: 21 additions & 0 deletions firmware/lv_psram_alloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once
// LVGL memory allocator redirected to PSRAM on boards that have it.
// Falls back to DRAM if PSRAM alloc fails (fragmentation / OOM safety net).
// On targets without PSRAM (native_test) this is a transparent passthrough.
#ifdef BOARD_HAS_PSRAM
# include <esp_heap_caps.h>
static inline void* lv_psram_malloc(size_t size) {
void* p = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (!p) p = malloc(size);
return p;
}
static inline void* lv_psram_realloc(void* ptr, size_t size) {
void* p = heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
if (!p && size > 0) p = realloc(ptr, size);
return p;
}
#else
# include <stdlib.h>
static inline void* lv_psram_malloc(size_t size) { return malloc(size); }
static inline void* lv_psram_realloc(void* ptr, size_t size) { return realloc(ptr, size); }
#endif
4 changes: 4 additions & 0 deletions firmware/src/config/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ bool ConfigManager::parseJson(const String& json) {

// Debug — missing block defaults to all-off
_config.debug.screenshots = doc["debug"]["screenshots"] | defaults::SCREENSHOTS_ENABLED;
_config.debug.showMemory = doc["debug"]["show_memory"] | false;

LOGF("[Config] Loaded: device=%s, contacts=%d, channels=%d\n",
_config.deviceName.c_str(),
Expand Down Expand Up @@ -560,6 +561,9 @@ String ConfigManager::toJson() const {
if (_config.debug.screenshots) {
doc["debug"]["screenshots"] = _config.debug.screenshots;
}
if (_config.debug.showMemory) {
doc["debug"]["show_memory"] = _config.debug.showMemory;
}

String output;
serializeJsonPretty(doc, output);
Expand Down
1 change: 1 addition & 0 deletions firmware/src/config/ConfigManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ struct BleConfig {
};

struct DebugConfig {
bool showMemory = false; // Enable display of memory usage in status bar.
bool screenshots = false; // Enable save-screen-to-SD (/screenshots/*.bmp). Debug aid, default off.
};

Expand Down
1 change: 1 addition & 0 deletions firmware/src/i18n/I18n.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const DefaultString DEFAULT_STRINGS[] = {
{"lbl_lock_mode", "Lock Mode"},
{"lbl_auto_lock", "Auto-Lock"},
{"lbl_screenshots", "Save Screenshots"},
{"lbl_show_memory", "Show Memory Usage"},
{"lbl_theme", "Theme"},
{"theme_dark", "Dark"},
{"theme_light", "Light"},
Expand Down
1 change: 1 addition & 0 deletions firmware/src/ui/ChatScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ void ChatScreen::hide() {
if (_cannedBtn) lv_group_remove_obj(_cannedBtn);
}
}
lv_obj_clean(_chatArea);
lv_obj_add_flag(_screen, LV_OBJ_FLAG_HIDDEN);
}

Expand Down
12 changes: 10 additions & 2 deletions firmware/src/ui/SettingsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,9 @@ void SettingsScreen::buildDisplay() {
1, 255, cfg.display.kbdBrightness, String(cfg.display.kbdBrightness), true);
addSwitchRowGated(t("lbl_emoji"), cfg.display.emoji, emojiToggleCb, nullptr, false);

// Screenshots fold into Display (no separate Debug screen / header).
addSwitchRowGated(t("lbl_screenshots"), cfg.debug.screenshots, screenshotsToggleCb, nullptr, false);
// Screenshots and debug overlays fold into Display (no separate Debug screen / header).
addSwitchRowGated(t("lbl_screenshots"), cfg.debug.screenshots, screenshotsToggleCb, nullptr, false);
addSwitchRowGated(t("lbl_show_memory"), cfg.debug.showMemory, showMemoryToggleCb, nullptr, false);
}

void SettingsScreen::buildMessaging() {
Expand Down Expand Up @@ -2912,4 +2913,11 @@ void SettingsScreen::screenshotsToggleCb(lv_event_t* e) {
g_dsDirty = true;
}

void SettingsScreen::showMemoryToggleCb(lv_event_t* e) {
auto& mgr = ConfigManager::instance();
lv_obj_t* sw = lv_event_get_target(e);
mgr.config().debug.showMemory = lv_obj_has_state(sw, LV_STATE_CHECKED);
g_dsDirty = true;
}

} // namespace mclite
1 change: 1 addition & 0 deletions firmware/src/ui/SettingsScreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ class SettingsScreen {
static void themeRowCb(lv_event_t* e); // opens the theme picker (reboots to apply)
static void emojiToggleCb(lv_event_t* e);
static void screenshotsToggleCb(lv_event_t* e);
static void showMemoryToggleCb(lv_event_t* e);
};

} // namespace mclite
42 changes: 42 additions & 0 deletions firmware/src/ui/StatusBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
#include "../hal/Speaker.h"
#include "../config/ConfigManager.h"
#include "../util/TimeHelper.h"
#include "../util/log.h"
#include "../net/WiFiManager.h"
#include "../companion/CompanionService.h"
#include <esp_heap_caps.h>

namespace mclite {

Expand Down Expand Up @@ -120,6 +122,11 @@ void StatusBar::create(lv_obj_t* parent) {
_lblTime = lv_label_create(_footer);
lv_obj_set_style_text_font(_lblTime, FONT_STATUSBAR_ICON, 0);
lv_obj_set_style_text_color(_lblTime, theme::TEXT_PRIMARY(), 0);

_lblMem = lv_label_create(_footer);
lv_obj_set_style_text_font(_lblMem, FONT_SMALL, 0);
lv_obj_set_style_text_color(_lblMem, theme::TEXT_SECONDARY(), 0);
lv_label_set_text(_lblMem, "");
#else
// T-Deck: single flex-row, device name left (grow), icons right.
lv_obj_set_flex_flow(_bar, LV_FLEX_FLOW_ROW);
Expand Down Expand Up @@ -147,6 +154,11 @@ void StatusBar::create(lv_obj_t* parent) {

// --- Everything below is right-aligned (no flex_grow) ---

_lblMem = lv_label_create(_bar);
lv_obj_set_style_text_font(_lblMem, FONT_SMALL, 0);
lv_obj_set_style_text_color(_lblMem, theme::TEXT_SECONDARY(), 0);
lv_label_set_text(_lblMem, "");

// Sound icon — clickable label, larger font for tap target
_soundIcon = lv_label_create(_bar);
lv_obj_set_style_text_font(_soundIcon, FONT_NORMAL, 0);
Expand Down Expand Up @@ -307,6 +319,36 @@ void StatusBar::update() {
lv_label_set_text(_lblTime, "");
}

#if defined(ESP32)
// Memory usage — PSRAM and internal DRAM, logged every update and shown in status bar
{
if (_lblMem) {
if (cfg.debug.showMemory) {
const size_t dram_free = heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
const size_t dram_tot = heap_caps_get_total_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
const size_t dram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
const size_t ps_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
const size_t ps_tot = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
const uint8_t ramPct = dram_tot > 0 ? (uint8_t)(100 - (dram_free * 100 / dram_tot)) : 0;
const uint8_t psrPct = ps_tot > 0 ? (uint8_t)(100 - (ps_free * 100 / ps_tot)) : 0;
char memBuf[16];
if (ps_tot > 0)
snprintf(memBuf, sizeof(memBuf), "P%u%% R%u%%", (unsigned)psrPct, (unsigned)ramPct);
else
snprintf(memBuf, sizeof(memBuf), "R%u%%", (unsigned)ramPct);
lv_label_set_text(_lblMem, memBuf);

LOGF("[Mem] DRAM %u/%u KB (min %u KB), PSRAM %u/%u KB\n",
(unsigned)(dram_free / 1024), (unsigned)(dram_tot / 1024),
(unsigned)(dram_min / 1024),
(unsigned)(ps_free / 1024), (unsigned)(ps_tot / 1024));
} else {
lv_label_set_text(_lblMem, "");
}
}
}
#endif

// GPS indicator — green=live, amber=last known, gray=no fix
if (!cfg.gpsEnabled) {
// GPS disabled in config: keep the icon present but dimmed so the
Expand Down
1 change: 1 addition & 0 deletions firmware/src/ui/StatusBar.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class StatusBar {
lv_obj_t* _gpsIcon = nullptr;
lv_obj_t* _wifiIcon = nullptr; // shown only while WiFi is connected
lv_obj_t* _bleIcon = nullptr; // shown only while BLE companion is active
lv_obj_t* _lblMem = nullptr; // "P{X}% R{Y}%" PSRAM/RAM usage

static void soundClickCb(lv_event_t* e);
static void gpsClickCb(lv_event_t* e);
Expand Down
1 change: 1 addition & 0 deletions sdcard/mclite/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"lbl_kbd_backlight": "Tastenhelligkeit",
"lbl_emoji": "Emoji-Auswahl",
"lbl_screenshots": "Screenshots speichern",
"lbl_show_memory": "Speichernutzung zeigen",
"lbl_boot_text": "Boot-Text",
"lbl_history": "Verlauf speichern",
"lbl_max_per_chat": "Max pro Chat",
Expand Down
1 change: 1 addition & 0 deletions sdcard/mclite/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"lbl_kbd_backlight": "Lumi clavier",
"lbl_emoji": "Emoji",
"lbl_screenshots": "Sauver captures",
"lbl_show_memory": "Afficher memoire",
"lbl_boot_text": "Texte demarrage",
"lbl_history": "Sauver historique",
"lbl_max_per_chat": "Max par chat",
Expand Down
1 change: 1 addition & 0 deletions sdcard/mclite/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"lbl_kbd_backlight": "Lumin tastiera",
"lbl_emoji": "Emoji",
"lbl_screenshots": "Salva screenshot",
"lbl_show_memory": "Mostra memoria",
"lbl_boot_text": "Testo avvio",
"lbl_history": "Salva cronologia",
"lbl_max_per_chat": "Max per chat",
Expand Down
Loading