diff --git a/Modules/lvgl-module/Source/symbols.c b/Modules/lvgl-module/Source/symbols.c index 37b859f3..69de48df 100644 --- a/Modules/lvgl-module/Source/symbols.c +++ b/Modules/lvgl-module/Source/symbols.c @@ -134,6 +134,8 @@ const struct ModuleSymbol lvgl_module_symbols[] = { DEFINE_MODULE_SYMBOL(lv_obj_event_base), DEFINE_MODULE_SYMBOL(lv_obj_class_create_obj), DEFINE_MODULE_SYMBOL(lv_obj_class_init_obj), + DEFINE_MODULE_SYMBOL(lv_obj_move_foreground), + DEFINE_MODULE_SYMBOL(lv_obj_move_to_index), // lv_font DEFINE_MODULE_SYMBOL(lv_font_get_default), // lv_theme diff --git a/Tactility/Include/Tactility/settings/WebServerSettings.h b/Tactility/Include/Tactility/settings/WebServerSettings.h index 96abdcfc..59dabf25 100644 --- a/Tactility/Include/Tactility/settings/WebServerSettings.h +++ b/Tactility/Include/Tactility/settings/WebServerSettings.h @@ -63,4 +63,11 @@ bool save(const WebServerSettings& settings); */ std::string generateDefaultApSsid(); +/** + * @brief Generate a cryptographically secure random string for credentials + * @param length The desired length of the string + * @return A random alphanumeric string + */ +std::string generateRandomCredential(size_t length); + } diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 621c2076..45c8afac 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -109,6 +109,7 @@ namespace app { namespace wifimanage { extern const AppManifest manifest; } #ifdef ESP_PLATFORM + namespace apwebserver { extern const AppManifest manifest; } namespace crashdiagnostics { extern const AppManifest manifest; } namespace webserversettings { extern const AppManifest manifest; } #if CONFIG_TT_TDECK_WORKAROUND == 1 @@ -157,6 +158,7 @@ static void registerInternalApps() { addAppManifest(app::wifimanage::manifest); #ifdef ESP_PLATFORM + addAppManifest(app::apwebserver::manifest); addAppManifest(app::webserversettings::manifest); addAppManifest(app::crashdiagnostics::manifest); addAppManifest(app::development::manifest); diff --git a/Tactility/Source/app/apwebserver/ApWebServer.cpp b/Tactility/Source/app/apwebserver/ApWebServer.cpp new file mode 100644 index 00000000..ae81c1ab --- /dev/null +++ b/Tactility/Source/app/apwebserver/ApWebServer.cpp @@ -0,0 +1,120 @@ +#ifdef ESP_PLATFORM + +#include +#include +#include +#include +#include +#include + +#include + +namespace tt::app::apwebserver { + +static const auto LOGGER = tt::Logger("ApWebServerApp"); + +class ApWebServerApp final : public App { + lv_obj_t* labelSsidValue = nullptr; + lv_obj_t* labelPasswordValue = nullptr; + lv_obj_t* labelIpValue = nullptr; + + bool webServerEnabledChanged = false; + settings::webserver::WebServerSettings wsSettings; + +public: + void onCreate(AppContext& app) override { + wsSettings = settings::webserver::loadOrGetDefault(); + } + + void onShow(AppContext& app, lv_obj_t* parent) override { + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + lv_obj_remove_flag(parent, LV_OBJ_FLAG_SCROLLABLE); + + lvgl::toolbar_create(parent, app); + + lv_obj_t* wrapper = lv_obj_create(parent); + lv_obj_set_width(wrapper, LV_PCT(100)); + lv_obj_set_style_pad_all(wrapper, 0, LV_PART_MAIN); + lv_obj_set_style_pad_row(wrapper, 4, LV_PART_MAIN); + lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(wrapper, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + + lv_obj_t* labelSsid = lv_label_create(wrapper); + lv_label_set_text(labelSsid, "SSID:"); + lv_obj_set_style_text_color(labelSsid, lv_palette_main(LV_PALETTE_GREY), LV_PART_MAIN); + + labelSsidValue = lv_label_create(wrapper); + + lv_obj_t* labelPassword = lv_label_create(wrapper); + lv_label_set_text(labelPassword, "Pass:"); + lv_obj_set_style_text_color(labelPassword, lv_palette_main(LV_PALETTE_GREY), LV_PART_MAIN); + + labelPasswordValue = lv_label_create(wrapper); + + lv_obj_t* labelIp = lv_label_create(wrapper); + lv_label_set_text(labelIp, "IP:"); + lv_obj_set_style_text_color(labelIp, lv_palette_main(LV_PALETTE_GREY), LV_PART_MAIN); + + labelIpValue = lv_label_create(wrapper); + + // Start AP Mode and WebServer + settings::webserver::WebServerSettings apSettings = wsSettings; + apSettings.wifiMode = settings::webserver::WiFiMode::AccessPoint; + apSettings.webServerEnabled = true; + + if (apSettings.apSsid.empty()) { + apSettings.apSsid = settings::webserver::generateDefaultApSsid(); + } + + // Generate password if it's an open network or if password is empty + if (apSettings.apOpenNetwork || apSettings.apPassword.empty()) { + apSettings.apPassword = settings::webserver::generateRandomCredential(12); + apSettings.apOpenNetwork = false; + } + + lv_label_set_text(labelSsidValue, apSettings.apSsid.c_str()); + lv_label_set_text(labelPasswordValue, apSettings.apPassword.c_str()); + lv_label_set_text(labelIpValue, "192.168.4.1"); + + // Apply settings and start services + getMainDispatcher().dispatch([apSettings] { + if (!settings::webserver::save(apSettings)) { + LOGGER.error("Failed to save AP settings"); + return; + } + service::webserver::getPubsub()->publish(service::webserver::WebServerEvent::WebServerSettingsChanged); + service::webserver::setWebServerEnabled(true); + }); + webServerEnabledChanged = true; + } + + void onHide(AppContext& app) override { + const auto copy = wsSettings; + const bool webServerChanged = webServerEnabledChanged; + + getMainDispatcher().dispatch([copy, webServerChanged] { + 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); + + if (webServerChanged) { + LOGGER.info("WebServer {}", copy.webServerEnabled ? "enabling..." : "disabling..."); + service::webserver::setWebServerEnabled(copy.webServerEnabled); + } + }); + } +}; + +extern const AppManifest manifest = { + .appId = "ApWebServer", + .appName = "AP Web Server", + .appCategory = Category::System, + .appFlags = AppManifest::Flags::Hidden, + .createApp = create +}; + +} // namespace tt::app::apwebserver + +#endif diff --git a/Tactility/Source/settings/WebServerSettings.cpp b/Tactility/Source/settings/WebServerSettings.cpp index 4029fbf9..6b9ecff8 100644 --- a/Tactility/Source/settings/WebServerSettings.cpp +++ b/Tactility/Source/settings/WebServerSettings.cpp @@ -52,7 +52,7 @@ std::string generateDefaultApSsid() { * @param length The desired length of the string * @return A random alphanumeric string */ -static std::string generateRandomCredential(size_t length) { +std::string generateRandomCredential(size_t length) { static constexpr char charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; static constexpr size_t charsetSize = sizeof(charset) - 1; diff --git a/TactilityC/Source/tt_init.cpp b/TactilityC/Source/tt_init.cpp index 8a56c17b..71727e45 100644 --- a/TactilityC/Source/tt_init.cpp +++ b/TactilityC/Source/tt_init.cpp @@ -123,6 +123,8 @@ const esp_elfsym main_symbols[] { ESP_ELFSYM_EXPORT(fmaxf), ESP_ELFSYM_EXPORT(fmin), ESP_ELFSYM_EXPORT(fminf), + ESP_ELFSYM_EXPORT(round), + ESP_ELFSYM_EXPORT(roundf), #ifndef _REENT_ONLY ESP_ELFSYM_EXPORT(acos), ESP_ELFSYM_EXPORT(acosf),