Require SD card or >4MB flash (#545)
This commit is contained in:
committed by
GitHub
parent
90afba647e
commit
05720821f8
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <Tactility/TactilityCore.h>
|
||||
#include <Tactility/TactilityPrivate.h>
|
||||
#include <Tactility/app/alertdialog/AlertDialog.h>
|
||||
#include <Tactility/app/AppContext.h>
|
||||
#include <Tactility/app/AppPaths.h>
|
||||
#include <Tactility/CpuAffinity.h>
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <Tactility/kernel/SystemEvents.h>
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/lvgl/Style.h>
|
||||
#include <Tactility/Paths.h>
|
||||
#include <Tactility/service/loader/Loader.h>
|
||||
#include <Tactility/settings/BootSettings.h>
|
||||
#include <Tactility/settings/DisplaySettings.h>
|
||||
@@ -44,6 +46,10 @@ class BootApp : public App {
|
||||
// onShow() reads this instead of the live flag to avoid a race between the two.
|
||||
static std::atomic<bool> isUsbBootSplash;
|
||||
|
||||
// Set by bootThreadCallback() when CONFIG_TT_USER_DATA_LOCATION_SD is defined but no SD card is mounted.
|
||||
// onShow() reads this to show an error instead of the normal splash, and boot halts instead of starting the launcher.
|
||||
static std::atomic<bool> sdCardMissing;
|
||||
|
||||
Thread thread = Thread(
|
||||
"boot",
|
||||
5120,
|
||||
@@ -122,11 +128,20 @@ class BootApp : public App {
|
||||
setupDisplay(); // Set backlight
|
||||
prepareFileSystems();
|
||||
|
||||
#ifdef CONFIG_TT_USER_DATA_LOCATION_SD
|
||||
std::string sd_path;
|
||||
if (!findFirstMountedSdCardPath(sd_path)) {
|
||||
LOGGER.error("SD card not found");
|
||||
sdCardMissing = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!setupUsbBootMode()) {
|
||||
LOGGER.info("initFromBootApp");
|
||||
registerApps();
|
||||
waitForMinimalSplashDuration(start_time);
|
||||
stop(manifest.appId);
|
||||
// When SD card is missing, wait for dialog result
|
||||
if (!sdCardMissing) stop(manifest.appId);
|
||||
startNextApp();
|
||||
}
|
||||
|
||||
@@ -162,6 +177,11 @@ class BootApp : public App {
|
||||
}
|
||||
|
||||
static void startNextApp() {
|
||||
if (sdCardMissing) {
|
||||
alertdialog::start("Error", "SD card not found.\nPlease insert one and reboot.", std::vector<const char*> { "Reboot" });
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
if (esp_reset_reason() == ESP_RST_PANIC) {
|
||||
crashdiagnostics::start();
|
||||
@@ -195,6 +215,12 @@ public:
|
||||
thread.join();
|
||||
}
|
||||
|
||||
void onResult(AppContext& /*app*/, LaunchId /*launchId*/, Result /*result*/, std::unique_ptr<Bundle> /*bundle*/) override {
|
||||
#ifdef ESP_PLATFORM
|
||||
esp_restart();
|
||||
#endif
|
||||
}
|
||||
|
||||
void onShow(AppContext& app, lv_obj_t* parent) override {
|
||||
lvgl::obj_set_style_bg_blacken(parent);
|
||||
lv_obj_set_style_border_width(parent, 0, LV_STATE_DEFAULT);
|
||||
@@ -232,6 +258,7 @@ public:
|
||||
};
|
||||
|
||||
std::atomic<bool> BootApp::isUsbBootSplash = false;
|
||||
std::atomic<bool> BootApp::sdCardMissing = false;
|
||||
|
||||
extern const AppManifest manifest = {
|
||||
.appId = "Boot",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <Tactility/crypt/Crypt.h>
|
||||
#include <Tactility/file/PropertiesFile.h>
|
||||
#include <Tactility/Logger.h>
|
||||
#include <Tactility/Paths.h>
|
||||
|
||||
#include <esp_random.h>
|
||||
|
||||
@@ -24,6 +25,10 @@ namespace tt::app::chat {
|
||||
|
||||
static const auto LOGGER = Logger("ChatSettings");
|
||||
|
||||
static std::string getSettingsFilePath() {
|
||||
return getUserDataPath() + "/settings/chat.properties";
|
||||
}
|
||||
|
||||
constexpr auto* KEY_SENDER_ID = "senderId";
|
||||
constexpr auto* KEY_NICKNAME = "nickname";
|
||||
constexpr auto* KEY_ENCRYPTION_KEY = "encryptionKey";
|
||||
@@ -120,7 +125,7 @@ ChatSettingsData loadSettings() {
|
||||
ChatSettingsData settings = getDefaultSettings();
|
||||
|
||||
std::map<std::string, std::string> map;
|
||||
if (!file::loadPropertiesFile(CHAT_SETTINGS_FILE, map)) {
|
||||
if (!file::loadPropertiesFile(getSettingsFilePath(), map)) {
|
||||
settings.senderId = generateSenderId();
|
||||
return settings;
|
||||
}
|
||||
@@ -171,11 +176,11 @@ bool saveSettings(const ChatSettingsData& settings) {
|
||||
map[KEY_ENCRYPTION_KEY] = "";
|
||||
}
|
||||
|
||||
return file::savePropertiesFile(CHAT_SETTINGS_FILE, map);
|
||||
return file::savePropertiesFile(getSettingsFilePath(), map);
|
||||
}
|
||||
|
||||
bool settingsFileExists() {
|
||||
return access(CHAT_SETTINGS_FILE, F_OK) == 0;
|
||||
return access(getSettingsFilePath().c_str(), F_OK) == 0;
|
||||
}
|
||||
|
||||
} // namespace tt::app::chat
|
||||
|
||||
@@ -29,7 +29,6 @@ class WebServerSettingsApp final : public App {
|
||||
settings::webserver::WebServerSettings originalSettings;
|
||||
bool updated = false;
|
||||
bool wifiSettingsChanged = false;
|
||||
bool webServerEnabledChanged = false;
|
||||
lv_obj_t* dropdownWifiMode = nullptr;
|
||||
lv_obj_t* textAreaApPassword = nullptr;
|
||||
lv_obj_t* switchApOpenNetwork = nullptr;
|
||||
@@ -61,11 +60,19 @@ class WebServerSettingsApp final : public App {
|
||||
getMainDispatcher().dispatch([app, enabled] {
|
||||
app->wsSettings.webServerEnabled = enabled;
|
||||
app->updated = true;
|
||||
app->webServerEnabledChanged = true;
|
||||
if (lvgl::lock(100)) {
|
||||
app->updateUrlDisplay();
|
||||
lvgl::unlock();
|
||||
}
|
||||
|
||||
// Apply immediately instead of waiting for app exit
|
||||
const auto copy = app->wsSettings;
|
||||
if (!settings::webserver::save(copy)) {
|
||||
LOGGER.warn("Failed to persist WebServer settings; changes may be lost on reboot");
|
||||
}
|
||||
service::webserver::getPubsub()->publish(service::webserver::WebServerEvent::WebServerSettingsChanged);
|
||||
LOGGER.info("WebServer {}", enabled ? "enabling..." : "disabling...");
|
||||
service::webserver::setWebServerEnabled(enabled);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -131,30 +138,6 @@ class WebServerSettingsApp final : public App {
|
||||
});
|
||||
}
|
||||
|
||||
static void onSyncAssets(lv_event_t* e) {
|
||||
auto* app = static_cast<WebServerSettingsApp*>(lv_event_get_user_data(e));
|
||||
auto* btn = static_cast<lv_obj_t*>(lv_event_get_target_obj(e));
|
||||
lv_obj_add_state(btn, LV_STATE_DISABLED);
|
||||
LOGGER.info("Manual asset sync triggered");
|
||||
|
||||
getMainDispatcher().dispatch([app, btn]{
|
||||
bool success = service::webserver::syncAssets();
|
||||
if (success) {
|
||||
LOGGER.info("Asset sync completed successfully");
|
||||
} else {
|
||||
LOGGER.error("Asset sync failed");
|
||||
}
|
||||
// Only re-enable if button still exists (user hasn't navigated away)
|
||||
// Must acquire LVGL lock since we're not in an LVGL event callback context
|
||||
if (lvgl::lock(1000)) {
|
||||
if (lv_obj_is_valid(btn)) {
|
||||
lv_obj_remove_state(btn, LV_STATE_DISABLED);
|
||||
}
|
||||
lvgl::unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void updateUrlDisplay() {
|
||||
if (!labelUrlValue) return;
|
||||
|
||||
@@ -197,6 +180,8 @@ class WebServerSettingsApp final : public App {
|
||||
public:
|
||||
void onCreate(AppContext& app) override {
|
||||
wsSettings = settings::webserver::loadOrGetDefault();
|
||||
// Reflect the server's actual running state, in case it differs from the persisted setting
|
||||
wsSettings.webServerEnabled = service::webserver::isWebServerEnabled();
|
||||
originalSettings = wsSettings;
|
||||
}
|
||||
|
||||
@@ -341,32 +326,6 @@ public:
|
||||
|
||||
updateUrlDisplay();
|
||||
|
||||
// Sync Assets button
|
||||
auto* sync_wrapper = lv_obj_create(main_wrapper);
|
||||
lv_obj_set_size(sync_wrapper, LV_PCT(100), LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_all(sync_wrapper, 10, LV_STATE_DEFAULT);
|
||||
lv_obj_set_style_border_width(sync_wrapper, 1, LV_STATE_DEFAULT);
|
||||
lv_obj_set_flex_flow(sync_wrapper, LV_FLEX_FLOW_COLUMN);
|
||||
lv_obj_set_style_flex_cross_place(sync_wrapper, LV_FLEX_ALIGN_START, 0);
|
||||
|
||||
auto* sync_label = lv_label_create(sync_wrapper);
|
||||
lv_label_set_text(sync_label, "Asset Synchronization");
|
||||
|
||||
auto* sync_info = lv_label_create(sync_wrapper);
|
||||
lv_label_set_long_mode(sync_info, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_width(sync_info, LV_PCT(95));
|
||||
if (lv_display_get_color_format(lv_obj_get_display(parent)) != LV_COLOR_FORMAT_L8) {
|
||||
lv_obj_set_style_text_color(sync_info, lv_palette_main(LV_PALETTE_GREY), 0);
|
||||
}
|
||||
lv_label_set_text(sync_info, "Sync web assets between Data partition and SD card backup");
|
||||
|
||||
auto* sync_button = lv_btn_create(sync_wrapper);
|
||||
lv_obj_set_width(sync_button, LV_SIZE_CONTENT);
|
||||
auto* sync_button_label = lv_label_create(sync_button);
|
||||
lv_label_set_text(sync_button_label, "Sync Assets Now");
|
||||
lv_obj_center(sync_button_label);
|
||||
lv_obj_add_event_cb(sync_button, onSyncAssets, LV_EVENT_CLICKED, this);
|
||||
|
||||
// Info text
|
||||
auto* info_label = lv_label_create(main_wrapper);
|
||||
lv_label_set_long_mode(info_label, LV_LABEL_LONG_WRAP);
|
||||
@@ -394,11 +353,11 @@ public:
|
||||
}
|
||||
|
||||
// Save to flash only (settings sync at boot handles SD restore)
|
||||
// Note: the enable/disable toggle already saved and applied itself immediately
|
||||
const auto copy = wsSettings;
|
||||
const bool wifiChanged = wifiSettingsChanged;
|
||||
const bool webServerChanged = webServerEnabledChanged;
|
||||
|
||||
getMainDispatcher().dispatch([copy, wifiChanged, webServerChanged]{
|
||||
getMainDispatcher().dispatch([copy, wifiChanged]{
|
||||
// Save to flash (fast, low memory pressure)
|
||||
if (!settings::webserver::save(copy)) {
|
||||
LOGGER.warn("Failed to persist WebServer settings; changes may be lost on reboot");
|
||||
@@ -411,12 +370,6 @@ public:
|
||||
if (wifiChanged) {
|
||||
LOGGER.info("WiFi mode changed to {}", copy.wifiMode == settings::webserver::WiFiMode::AccessPoint ? "AP" : "Station");
|
||||
}
|
||||
|
||||
// Control WebServer service immediately
|
||||
if (webServerChanged) {
|
||||
LOGGER.info("WebServer {}", copy.webServerEnabled ? "enabling..." : "disabling...");
|
||||
service::webserver::setWebServerEnabled(copy.webServerEnabled);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user