From e86a11b7e2f200a8cf34a350aad1d83b263a1efd Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Wed, 4 Dec 2024 23:06:03 +0100 Subject: [PATCH] App improvements (#105) --- Boards/Simulator/Source/FreeRTOSConfig.h | 2 +- Documentation/pics/screenshot-Desktop.png | Bin 3999 -> 3948 bytes Documentation/pics/screenshot-Settings.png | Bin 3862 -> 4022 bytes Documentation/pics/screenshot-WifiManage.png | Bin 4214 -> 5003 bytes Tactility/Source/Tactility.cpp | 2 + Tactility/Source/app/boot/Boot.cpp | 2 +- Tactility/Source/app/files/FileUtils.cpp | 4 +- Tactility/Source/app/files/Files.cpp | 3 +- Tactility/Source/app/power/Power.cpp | 2 +- .../Source/app/systeminfo/SystemInfo.cpp | 52 +++++ .../Source/app/textviewer/TextViewer.cpp | 7 +- .../app/wifiapsettings/WifiApSettings.cpp | 97 +++++++++ .../app/wifiapsettings/WifiApSettings.h | 11 + Tactility/Source/app/wifimanage/Bindings.h | 2 + Tactility/Source/app/wifimanage/View.cpp | 198 +++++++++++------- Tactility/Source/app/wifimanage/View.h | 6 +- .../Source/app/wifimanage/WifiManage.cpp | 16 +- Tactility/Source/lvgl/Toolbar.cpp | 14 +- Tactility/Source/lvgl/Toolbar.h | 1 + .../Source/service/statusbar/Statusbar.cpp | 1 + TactilityHeadless/Source/service/wifi/Wifi.h | 5 + .../Source/service/wifi/WifiEsp.cpp | 20 +- .../Source/service/wifi/WifiMock.cpp | 10 +- .../Source/service/wifi/WifiSettings.h | 4 +- sdkconfig.board.lilygo_tdeck | 1 + sdkconfig.board.m5stack_core2 | 1 + sdkconfig.board.m5stack_cores3 | 1 + sdkconfig.board.yellow_board | 1 + sdkconfig.defaults | 2 + 29 files changed, 358 insertions(+), 107 deletions(-) create mode 100644 Tactility/Source/app/wifiapsettings/WifiApSettings.cpp create mode 100644 Tactility/Source/app/wifiapsettings/WifiApSettings.h diff --git a/Boards/Simulator/Source/FreeRTOSConfig.h b/Boards/Simulator/Source/FreeRTOSConfig.h index 7d1e36ef..d03bc412 100644 --- a/Boards/Simulator/Source/FreeRTOSConfig.h +++ b/Boards/Simulator/Source/FreeRTOSConfig.h @@ -46,7 +46,7 @@ extern void vAssertCalled(unsigned long line, const char* const file); /* Run time and task stats gathering related definitions. */ #define configGENERATE_RUN_TIME_STATS 0 -#define configUSE_TRACE_FACILITY 0 +#define configUSE_TRACE_FACILITY 1 #define configUSE_STATS_FORMATTING_FUNCTIONS 0 /* Co-routine related definitions. */ diff --git a/Documentation/pics/screenshot-Desktop.png b/Documentation/pics/screenshot-Desktop.png index 2da8a0b993a576cdc012db464d1df614bbf750ac..a1c2fd543221606094f340ffc07e150f67e88b42 100644 GIT binary patch delta 3840 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 OOJ{6gu3x8%jQt<=wp4lm literal 3999 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 delta 3762 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 u$9%JMVzkY#LRpKKvT*<~5K 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 Yn#CqQ{+cwv!SMSS8<^=o)x$^r3(G;50RR91 delta 3814 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 diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 3ca91f4d..4d2ffc00 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -49,6 +49,7 @@ namespace app { namespace selectiondialog { extern const Manifest manifest; } namespace systeminfo { extern const Manifest manifest; } namespace textviewer { extern const Manifest manifest; } + namespace wifiapsettings { extern const Manifest manifest; } namespace wificonnect { extern const Manifest manifest; } namespace wifimanage { extern const Manifest manifest; } } @@ -70,6 +71,7 @@ static const std::vector system_apps = { &app::selectiondialog::manifest, &app::systeminfo::manifest, &app::textviewer::manifest, + &app::wifiapsettings::manifest, &app::wificonnect::manifest, &app::wifimanage::manifest, #ifndef ESP_PLATFORM diff --git a/Tactility/Source/app/boot/Boot.cpp b/Tactility/Source/app/boot/Boot.cpp index 126886d2..f666f606 100644 --- a/Tactility/Source/app/boot/Boot.cpp +++ b/Tactility/Source/app/boot/Boot.cpp @@ -20,7 +20,7 @@ namespace tt::app::boot { static int32_t threadCallback(void* context); struct Data { - Data() : thread("", 4096, threadCallback, this) {} + Data() : thread("boot", 4096, threadCallback, this) {} Thread thread; }; diff --git a/Tactility/Source/app/files/FileUtils.cpp b/Tactility/Source/app/files/FileUtils.cpp index 63deb35e..be713438 100644 --- a/Tactility/Source/app/files/FileUtils.cpp +++ b/Tactility/Source/app/files/FileUtils.cpp @@ -14,8 +14,8 @@ int dirent_filter_dot_entries(const struct dirent* entry) { } int dirent_sort_alpha_and_type(const struct dirent** left, const struct dirent** right) { - bool left_is_dir = (*left)->d_type == TT_DT_DIR; - bool right_is_dir = (*right)->d_type == TT_DT_DIR; + bool left_is_dir = (*left)->d_type == TT_DT_DIR || (*left)->d_type == TT_DT_CHR; + bool right_is_dir = (*right)->d_type == TT_DT_DIR || (*right)->d_type == TT_DT_CHR; if (left_is_dir == right_is_dir) { return strcmp((*left)->d_name, (*right)->d_name); } else { diff --git a/Tactility/Source/app/files/Files.cpp b/Tactility/Source/app/files/Files.cpp index c30514bd..b3627cc5 100644 --- a/Tactility/Source/app/files/Files.cpp +++ b/Tactility/Source/app/files/Files.cpp @@ -133,6 +133,7 @@ static void onFilePressed(lv_event_t* event) { switch (dir_entry->d_type) { case TT_DT_DIR: + case TT_DT_CHR: data_set_entries_for_child_path(files_data, dir_entry->d_name); updateViews(files_data); break; @@ -155,7 +156,7 @@ static void createFileWidget(Data* files_data, lv_obj_t* parent, struct dirent* tt_check(parent); auto* list = (lv_obj_t*)parent; const char* symbol; - if (dir_entry->d_type == TT_DT_DIR) { + if (dir_entry->d_type == TT_DT_DIR || dir_entry->d_type == TT_DT_CHR) { symbol = LV_SYMBOL_DIRECTORY; } else if (isSupportedImageFile(dir_entry->d_name)) { symbol = LV_SYMBOL_IMAGE; diff --git a/Tactility/Source/app/power/Power.cpp b/Tactility/Source/app/power/Power.cpp index 55caad9a..9a07d29a 100644 --- a/Tactility/Source/app/power/Power.cpp +++ b/Tactility/Source/app/power/Power.cpp @@ -78,7 +78,7 @@ static void onShow(App& app, lv_obj_t* parent) { lv_obj_set_align(enable_label, LV_ALIGN_LEFT_MID); lv_obj_t* enable_switch = lv_switch_create(switch_container); - lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_ALL, data); + lv_obj_add_event_cb(enable_switch, onPowerEnabledChanged, LV_EVENT_VALUE_CHANGED, data); lv_obj_set_align(enable_switch, LV_ALIGN_RIGHT_MID); data->enable_switch = enable_switch; diff --git a/Tactility/Source/app/systeminfo/SystemInfo.cpp b/Tactility/Source/app/systeminfo/SystemInfo.cpp index 1065ef83..c74725a0 100644 --- a/Tactility/Source/app/systeminfo/SystemInfo.cpp +++ b/Tactility/Source/app/systeminfo/SystemInfo.cpp @@ -5,6 +5,8 @@ namespace tt::app::systeminfo { +#define TAG "system_info" + static size_t getHeapFree() { #ifdef ESP_PLATFORM return heap_caps_get_free_size(MALLOC_CAP_INTERNAL); @@ -65,6 +67,47 @@ static void addMemoryBar(lv_obj_t* parent, const char* label, size_t used, size_ lv_obj_set_style_text_align(bottom_label, LV_TEXT_ALIGN_RIGHT, 0); } +#if configUSE_TRACE_FACILITY + +static const char* getTaskState(const TaskStatus_t& task) { + switch (task.eCurrentState) { + case eRunning: + return "running"; + case eReady: + return "ready"; + case eBlocked: + return "blocked"; + case eSuspended: + return "suspended"; + case eDeleted: + return "deleted"; + case eInvalid: + default: + return "invalid"; + } +} + +static void addRtosTask(lv_obj_t* parent, const TaskStatus_t& task) { + lv_obj_t* label = lv_label_create(parent); + const char* name = (task.pcTaskName == nullptr || task.pcTaskName[0] == 0) ? "(unnamed)" : task.pcTaskName; + lv_label_set_text_fmt(label, "%s (%s)", name, getTaskState(task)); +} + +static void addRtosTasks(lv_obj_t* parent) { + UBaseType_t count = uxTaskGetNumberOfTasks(); + TaskStatus_t* tasks = (TaskStatus_t*)malloc(sizeof(TaskStatus_t) * count); + uint32_t totalRuntime = 0; + UBaseType_t actual = uxTaskGetSystemState(tasks, count, &totalRuntime); + for (int i = 0; i < actual; ++i) { + const TaskStatus_t& task = tasks[i]; + TT_LOG_I(TAG, "Task: %s", task.pcTaskName); + addRtosTask(parent, task); + } + free(tasks); +} + +#endif + static void onShow(App& app, lv_obj_t* parent) { lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lvgl::toolbar_create(parent, app); @@ -86,6 +129,15 @@ static void onShow(App& app, lv_obj_t* parent) { addMemoryBar(memory_wrapper, "Heap", getHeapTotal() - getHeapFree(), getHeapTotal()); addMemoryBar(memory_wrapper, "SPI", getSpiTotal() - getSpiFree(), getSpiTotal()); +#if configUSE_TRACE_FACILITY + lv_obj_t* tasks_label = lv_label_create(wrapper); + lv_label_set_text(tasks_label, "Tasks"); + lv_obj_t* tasks_wrapper = lv_obj_create(wrapper); + lv_obj_set_flex_flow(tasks_wrapper, LV_FLEX_FLOW_COLUMN); + lv_obj_set_size(tasks_wrapper, LV_PCT(100), LV_SIZE_CONTENT); + addRtosTasks(tasks_wrapper); +#endif + #ifdef ESP_PLATFORM // Build info lv_obj_t* build_info_label = lv_label_create(wrapper); diff --git a/Tactility/Source/app/textviewer/TextViewer.cpp b/Tactility/Source/app/textviewer/TextViewer.cpp index faa8132c..b162c54d 100644 --- a/Tactility/Source/app/textviewer/TextViewer.cpp +++ b/Tactility/Source/app/textviewer/TextViewer.cpp @@ -25,9 +25,10 @@ static void onShow(App& app, lv_obj_t* parent) { const Bundle& bundle = app.getParameters(); std::string file_argument; if (bundle.optString(TEXT_VIEWER_FILE_ARGUMENT, file_argument)) { - std::string prefixed_path = "A:" + file_argument; - TT_LOG_I(TAG, "Opening %s", prefixed_path.c_str()); - lvgl::label_set_text_file(label, prefixed_path.c_str()); + TT_LOG_I(TAG, "Opening %s", file_argument.c_str()); + lvgl::label_set_text_file(label, file_argument.c_str()); + } else { + lv_label_set_text_fmt(label, "Failed to load %s", file_argument.c_str()); } } diff --git a/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp new file mode 100644 index 00000000..d93d35d5 --- /dev/null +++ b/Tactility/Source/app/wifiapsettings/WifiApSettings.cpp @@ -0,0 +1,97 @@ +#include "WifiApSettings.h" +#include "TactilityCore.h" +#include "app/App.h" +#include "lvgl.h" +#include "lvgl/Style.h" +#include "lvgl/Toolbar.h" +#include "service/loader/Loader.h" +#include "service/wifi/WifiSettings.h" + +namespace tt::app::wifiapsettings { + +#define TAG "wifi_ap_settings" + +extern const Manifest manifest; + +void start(const std::string& ssid) { + Bundle bundle; + bundle.putString("ssid", ssid); + service::loader::startApp(manifest.id, false, bundle); +} + +static void onToggleAutoConnect(lv_event_t* event) { + lv_event_code_t code = lv_event_get_code(event); + const Bundle& parameters = *(const Bundle*)lv_event_get_user_data(event); + if (code == LV_EVENT_VALUE_CHANGED) { + auto* enable_switch = static_cast(lv_event_get_target(event)); + bool is_on = lv_obj_has_state(enable_switch, LV_STATE_CHECKED); + std::string ssid = parameters.getString("ssid"); + + service::wifi::settings::WifiApSettings settings {}; + if (service::wifi::settings::load(ssid.c_str(), &settings)) { + settings.auto_connect = is_on; + if (!service::wifi::settings::save(&settings)) { + TT_LOG_E(TAG, "Failed to save settings"); + } + } else { + TT_LOG_E(TAG, "Failed to load settings"); + } + } +} + +static void onShow(App& app, lv_obj_t* parent) { + const Bundle& bundle = app.getParameters(); + std::string ssid = bundle.getString("ssid"); + + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + lvgl::toolbar_create(parent, ssid); + + // Wrappers + + lv_obj_t* secondary_flex = lv_obj_create(parent); + lv_obj_set_width(secondary_flex, LV_PCT(100)); + lv_obj_set_flex_grow(secondary_flex, 1); + lv_obj_set_flex_flow(secondary_flex, LV_FLEX_FLOW_COLUMN); + lv_obj_set_style_border_width(secondary_flex, 0, 0); + lvgl::obj_set_style_no_padding(secondary_flex); + lvgl::obj_set_style_bg_invisible(secondary_flex); + + // align() methods don't work on flex, so we need this extra wrapper + lv_obj_t* wrapper = lv_obj_create(secondary_flex); + lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT); + lvgl::obj_set_style_bg_invisible(wrapper); + lv_obj_set_style_border_width(wrapper, 0, 0); + + // Auto-connect toggle + + lv_obj_t* auto_connect_label = lv_label_create(wrapper); + lv_label_set_text(auto_connect_label, "Auto-connect"); + lv_obj_align(auto_connect_label, LV_ALIGN_TOP_LEFT, 0, 6); + + lv_obj_t* auto_connect_switch = lv_switch_create(wrapper); + lv_obj_add_event_cb(auto_connect_switch, onToggleAutoConnect, LV_EVENT_VALUE_CHANGED, (void*)&bundle); + lv_obj_align(auto_connect_switch, LV_ALIGN_TOP_RIGHT, 0, 0); + + service::wifi::settings::WifiApSettings settings {}; + if (service::wifi::settings::load(ssid.c_str(), &settings)) { + if (settings.auto_connect) { + lv_obj_add_state(auto_connect_switch, LV_STATE_CHECKED); + } else { + lv_obj_remove_state(auto_connect_switch, LV_STATE_CHECKED); + } + + } else { + TT_LOG_E(TAG, "Failed to load settings"); + } +} + +extern const Manifest manifest = { + .id = "WifiApSettings", + .name = "Wi-Fi AP Settings", + .icon = LV_SYMBOL_WIFI, + .type = TypeHidden, + .onShow = onShow +}; + +} // namespace + diff --git a/Tactility/Source/app/wifiapsettings/WifiApSettings.h b/Tactility/Source/app/wifiapsettings/WifiApSettings.h new file mode 100644 index 00000000..da6fed9a --- /dev/null +++ b/Tactility/Source/app/wifiapsettings/WifiApSettings.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Bundle.h" +#include "Mutex.h" +#include "service/wifi/Wifi.h" + +namespace tt::app::wifiapsettings { + +void start(const std::string& ssid); + +} // namespace diff --git a/Tactility/Source/app/wifimanage/Bindings.h b/Tactility/Source/app/wifimanage/Bindings.h index 091de0a2..73b91d79 100644 --- a/Tactility/Source/app/wifimanage/Bindings.h +++ b/Tactility/Source/app/wifimanage/Bindings.h @@ -5,11 +5,13 @@ namespace tt::app::wifimanage { typedef void (*OnWifiToggled)(bool enable); typedef void (*OnConnectSsid)(const char* ssid); typedef void (*OnDisconnect)(); +typedef void (*OnShowApSettings)(const char* ssid); struct Bindings{ OnWifiToggled onWifiToggled; OnConnectSsid onConnectSsid; OnDisconnect onDisconnect; + OnShowApSettings onShowApSettings; }; } // namespace diff --git a/Tactility/Source/app/wifimanage/View.cpp b/Tactility/Source/app/wifimanage/View.cpp index 92852510..99c8dcf8 100644 --- a/Tactility/Source/app/wifimanage/View.cpp +++ b/Tactility/Source/app/wifimanage/View.cpp @@ -13,7 +13,6 @@ namespace tt::app::wifimanage { #define TAG "wifi_main_view" -#define SPINNER_HEIGHT 40 static void on_enable_switch_changed(lv_event_t* event) { lv_event_code_t code = lv_event_get_code(event); @@ -42,41 +41,115 @@ static void on_disconnect_pressed(lv_event_t* event) { // region Secondary updates static void connect(lv_event_t* event) { - lv_obj_t* button = lv_event_get_current_target_obj(event); + lv_obj_t* wrapper = lv_event_get_current_target_obj(event); // Assumes that the second child of the button is a label ... risky - lv_obj_t* label = lv_obj_get_child(button, 1); + lv_obj_t* label = lv_obj_get_child(wrapper, 0); // We get the SSID from the button label because it's safer than alloc'ing // our own and passing it as the event data const char* ssid = lv_label_get_text(label); - TT_LOG_I(TAG, "Clicked AP: %s", ssid); - auto* bindings = static_cast(lv_event_get_user_data(event)); - bindings->onConnectSsid(ssid); + if (ssid != nullptr) { + TT_LOG_I(TAG, "Clicked AP: %s", ssid); + auto* bindings = (Bindings*)lv_event_get_user_data(event); + bindings->onConnectSsid(ssid); + } } -void View::createNetworkButton(Bindings* bindings, const service::wifi::WifiApRecord& record) { - const char* icon = service::statusbar::getWifiStatusIconForRssi(record.rssi, record.auth_mode != WIFI_AUTH_OPEN); - lv_obj_t* ap_button = lv_list_add_btn( - networks_list, - icon, - record.ssid.c_str() - ); - lv_obj_add_event_cb(ap_button, &connect, LV_EVENT_CLICKED, bindings); +static void showDetails(lv_event_t* event) { + lv_obj_t* wrapper = lv_event_get_current_target_obj(event); + // Hack: Get the hidden label with the ssid + lv_obj_t* ssid_label = lv_obj_get_child(wrapper, 1); + const char* ssid = lv_label_get_text(ssid_label); + auto* bindings = (Bindings*)lv_event_get_user_data(event); + bindings->onShowApSettings(ssid); +} + +void View::createSsidListItem(Bindings* bindings, const service::wifi::WifiApRecord& record, bool isConnecting) { + lv_obj_t* wrapper = lv_obj_create(networks_list); + lv_obj_add_event_cb(wrapper, &connect, LV_EVENT_CLICKED, bindings); + lv_obj_set_user_data(wrapper, bindings); + lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT); + lvgl::obj_set_style_no_padding(wrapper); + lv_obj_set_style_margin_all(wrapper, 0, 0); + lv_obj_set_style_border_width(wrapper, 0, 0); + + lv_obj_t* label = lv_label_create(wrapper); + lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0); + lv_label_set_text(label, record.ssid.c_str()); + lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_obj_set_width(label, LV_PCT(70)); + + lv_obj_t* info_wrapper = lv_obj_create(wrapper); + lv_obj_set_style_pad_all(info_wrapper, 0, 0); + lv_obj_set_style_margin_all(info_wrapper, 0, 0); + lv_obj_set_size(info_wrapper, 36, 36); + lv_obj_set_style_border_color(info_wrapper, lv_theme_get_color_primary(info_wrapper), 0); + lv_obj_add_event_cb(info_wrapper, &showDetails, LV_EVENT_CLICKED, bindings); + lv_obj_align(info_wrapper, LV_ALIGN_RIGHT_MID, 0, 0); + + lv_obj_t* info_label = lv_label_create(info_wrapper); + lv_label_set_text(info_label, "i"); + // Hack: Create a hidden label to store data and pass it to the callback + lv_obj_t* ssid_label = lv_label_create(info_wrapper); + lv_label_set_text(ssid_label, record.ssid.c_str()); + lv_obj_add_flag(ssid_label, LV_OBJ_FLAG_HIDDEN); + lv_obj_set_style_text_color(info_label, lv_theme_get_color_primary(info_wrapper), 0); + lv_obj_align(info_label, LV_ALIGN_CENTER, 0, 0); + + if (isConnecting) { + lv_obj_t* connecting_spinner = lv_spinner_create(wrapper); + lv_obj_set_size(connecting_spinner, 40, 40); + lv_spinner_set_anim_params(connecting_spinner, 1000, 60); + lv_obj_set_style_pad_all(connecting_spinner, 4, 0); + lv_obj_align_to(connecting_spinner, info_wrapper, LV_ALIGN_OUT_LEFT_MID, -8, 0); + } else { + const char* icon = service::statusbar::getWifiStatusIconForRssi(record.rssi, record.auth_mode != WIFI_AUTH_OPEN); + lv_obj_t* image = lv_image_create(wrapper); + lv_image_set_src(image, icon); + lv_obj_align(image, LV_ALIGN_RIGHT_MID, -50, 0); + } } void View::updateNetworkList(State* state, Bindings* bindings) { lv_obj_clean(networks_list); + switch (state->getRadioState()) { case service::wifi::WIFI_RADIO_ON_PENDING: case service::wifi::WIFI_RADIO_ON: case service::wifi::WIFI_RADIO_CONNECTION_PENDING: case service::wifi::WIFI_RADIO_CONNECTION_ACTIVE: { - lv_obj_clear_flag(networks_label, LV_OBJ_FLAG_HIDDEN); + + std::string connection_target = service::wifi::getConnectionTarget(); auto& ap_records = state->lockApRecords(); + + bool is_connected = !connection_target.empty() && + state->getRadioState() == service::wifi::WIFI_RADIO_CONNECTION_ACTIVE; + bool added_connected = false; + if (is_connected) { + if (!ap_records.empty()) { + for (auto &record : ap_records) { + if (record.ssid == connection_target) { + lv_list_add_text(networks_list, "Connected"); + createSsidListItem(bindings, record, false); + added_connected = true; + break; + } + } + } + } + + lv_list_add_text(networks_list, "Other networks"); std::set used_ssids; if (!ap_records.empty()) { for (auto& record : ap_records) { if (used_ssids.find(record.ssid) == used_ssids.end()) { - createNetworkButton(bindings, record); + bool connection_target_match = (record.ssid == connection_target); + bool is_connecting = connection_target_match + && state->getRadioState() == service::wifi::WIFI_RADIO_CONNECTION_PENDING && + !connection_target.empty(); + bool skip = connection_target_match && added_connected; + if (!skip) { + createSsidListItem(bindings, record, is_connecting); + } used_ssids.insert(record.ssid); } } @@ -94,7 +167,6 @@ void View::updateNetworkList(State* state, Bindings* bindings) { case service::wifi::WIFI_RADIO_OFF_PENDING: case service::wifi::WIFI_RADIO_OFF: { lv_obj_add_flag(networks_list, LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(networks_label, LV_OBJ_FLAG_HIDDEN); break; } } @@ -138,19 +210,6 @@ void View::updateEnableOnBootToggle() { } } -void View::updateConnectedAp(State* state, TT_UNUSED Bindings* bindings) { - switch (state->getRadioState()) { - case service::wifi::WIFI_RADIO_CONNECTION_PENDING: - case service::wifi::WIFI_RADIO_CONNECTION_ACTIVE: - lv_obj_clear_flag(connected_ap_container, LV_OBJ_FLAG_HIDDEN); - lv_label_set_text(connected_ap_label, state->getConnectSsid().c_str()); - break; - default: - lv_obj_add_flag(connected_ap_container, LV_OBJ_FLAG_HIDDEN); - break; - } -} - // endregion Secondary updates // region Main @@ -158,72 +217,50 @@ void View::updateConnectedAp(State* state, TT_UNUSED Bindings* bindings) { void View::init(const App& app, Bindings* bindings, lv_obj_t* parent) { root = parent; + // Toolbar lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); lv_obj_t* toolbar = lvgl::toolbar_create(parent, app); + scanning_spinner = lvgl::toolbar_add_spinner_action(toolbar); + enable_switch = lvgl::toolbar_add_switch_action(toolbar); - lv_obj_add_event_cb(enable_switch, on_enable_switch_changed, LV_EVENT_ALL, bindings); + lv_obj_add_event_cb(enable_switch, on_enable_switch_changed, LV_EVENT_VALUE_CHANGED, bindings); - lv_obj_t* wrapper = lv_obj_create(parent); - lv_obj_set_width(wrapper, LV_PCT(100)); - lv_obj_set_flex_grow(wrapper, 1); - lv_obj_set_flex_flow(wrapper, LV_FLEX_FLOW_COLUMN); + // Wrappers - // Top row: enable/disable - lv_obj_t* switch_container = lv_obj_create(wrapper); - lv_obj_set_width(switch_container, LV_PCT(100)); - lv_obj_set_height(switch_container, LV_SIZE_CONTENT); - lvgl::obj_set_style_no_padding(switch_container); - lvgl::obj_set_style_bg_invisible(switch_container); + lv_obj_t* secondary_flex = lv_obj_create(parent); + lv_obj_set_width(secondary_flex, LV_PCT(100)); + lv_obj_set_flex_grow(secondary_flex, 1); + lv_obj_set_flex_flow(secondary_flex, LV_FLEX_FLOW_COLUMN); + lv_obj_set_style_border_width(secondary_flex, 0, 0); + lvgl::obj_set_style_no_padding(secondary_flex); + lvgl::obj_set_style_bg_invisible(secondary_flex); - lv_obj_t* enable_label = lv_label_create(switch_container); + // align() methods don't work on flex, so we need this extra wrapper + lv_obj_t* wrapper = lv_obj_create(secondary_flex); + lv_obj_set_size(wrapper, LV_PCT(100), LV_SIZE_CONTENT); + lvgl::obj_set_style_bg_invisible(wrapper); + lv_obj_set_style_border_width(wrapper, 0, 0); + + // Enable on boot + + lv_obj_t* enable_label = lv_label_create(wrapper); lv_label_set_text(enable_label, "Enable on boot"); - lv_obj_set_align(enable_label, LV_ALIGN_LEFT_MID); + lv_obj_align(enable_label, LV_ALIGN_TOP_LEFT, 0, 6); - enable_on_boot_switch = lv_switch_create(switch_container); - lv_obj_add_event_cb(enable_on_boot_switch, on_enable_on_boot_switch_changed, LV_EVENT_ALL, bindings); - lv_obj_set_align(enable_on_boot_switch, LV_ALIGN_RIGHT_MID); - - connected_ap_container = lv_obj_create(wrapper); - lv_obj_set_size(connected_ap_container, LV_PCT(100), LV_SIZE_CONTENT); - lv_obj_set_style_min_height(connected_ap_container, SPINNER_HEIGHT, 0); - lvgl::obj_set_style_no_padding(connected_ap_container); - lv_obj_set_style_border_width(connected_ap_container, 0, 0); - - connected_ap_label = lv_label_create(connected_ap_container); - lv_obj_align(connected_ap_label, LV_ALIGN_LEFT_MID, 0, 0); - - lv_obj_t* disconnect_button = lv_btn_create(connected_ap_container); - lv_obj_add_event_cb(disconnect_button, &on_disconnect_pressed, LV_EVENT_CLICKED, bindings); - lv_obj_t* disconnect_label = lv_label_create(disconnect_button); - lv_label_set_text(disconnect_label, "Disconnect"); - lv_obj_align(disconnect_button, LV_ALIGN_RIGHT_MID, 0, 0); + enable_on_boot_switch = lv_switch_create(wrapper); + lv_obj_add_event_cb(enable_on_boot_switch, on_enable_on_boot_switch_changed, LV_EVENT_VALUE_CHANGED, bindings); + lv_obj_align(enable_on_boot_switch, LV_ALIGN_TOP_RIGHT, 0, 0); // Networks - lv_obj_t* networks_header = lv_obj_create(wrapper); - lv_obj_set_size(networks_header, LV_PCT(100), LV_SIZE_CONTENT); - lv_obj_set_style_min_height(networks_header, SPINNER_HEIGHT, 0); - lvgl::obj_set_style_no_padding(networks_header); - lv_obj_set_style_border_width(networks_header, 0, 0); - - networks_label = lv_label_create(networks_header); - lv_label_set_text(networks_label, "Networks"); - lv_obj_align(networks_label, LV_ALIGN_LEFT_MID, 0, 0); - - scanning_spinner = lv_spinner_create(networks_header); - lv_spinner_set_anim_params(scanning_spinner, 1000, 60); - lv_obj_set_size(scanning_spinner, SPINNER_HEIGHT, SPINNER_HEIGHT); - lv_obj_set_style_pad_top(scanning_spinner, 4, 0); - lv_obj_set_style_pad_bottom(scanning_spinner, 4, 0); - lv_obj_align_to(scanning_spinner, networks_label, LV_ALIGN_OUT_RIGHT_MID, 8, 0); - networks_list = lv_obj_create(wrapper); lv_obj_set_flex_flow(networks_list, LV_FLEX_FLOW_COLUMN); lv_obj_set_width(networks_list, LV_PCT(100)); lv_obj_set_height(networks_list, LV_SIZE_CONTENT); - lv_obj_set_style_pad_top(networks_list, 8, 0); - lv_obj_set_style_pad_bottom(networks_list, 8, 0); + lv_obj_set_style_pad_top(networks_list, 0, 0); + lv_obj_set_style_pad_bottom(networks_list, 0, 0); + lv_obj_align(networks_list, LV_ALIGN_TOP_LEFT, 0, 44); } void View::update(Bindings* bindings, State* state) { @@ -231,7 +268,6 @@ void View::update(Bindings* bindings, State* state) { updateEnableOnBootToggle(); updateScanning(state); updateNetworkList(state, bindings); - updateConnectedAp(state, bindings); } } // namespace diff --git a/Tactility/Source/app/wifimanage/View.h b/Tactility/Source/app/wifimanage/View.h index 65376c78..33c9aa19 100644 --- a/Tactility/Source/app/wifimanage/View.h +++ b/Tactility/Source/app/wifimanage/View.h @@ -13,10 +13,7 @@ private: lv_obj_t* enable_switch = nullptr; lv_obj_t* enable_on_boot_switch = nullptr; lv_obj_t* scanning_spinner = nullptr; - lv_obj_t* networks_label = nullptr; lv_obj_t* networks_list = nullptr; - lv_obj_t* connected_ap_container = nullptr; - lv_obj_t* connected_ap_label = nullptr; public: View() {} void init(const App& app, Bindings* bindings, lv_obj_t* parent); @@ -24,12 +21,11 @@ public: private: - void updateConnectedAp(State* state, TT_UNUSED Bindings* bindings); void updateWifiToggle(State* state); void updateEnableOnBootToggle(); void updateScanning(State* state); void updateNetworkList(State* state, Bindings* bindings); - void createNetworkButton(Bindings* bindings, const service::wifi::WifiApRecord& record); + void createSsidListItem(Bindings* bindings, const service::wifi::WifiApRecord& record, bool isConnecting); }; diff --git a/Tactility/Source/app/wifimanage/WifiManage.cpp b/Tactility/Source/app/wifimanage/WifiManage.cpp index 64b18369..65f8d389 100644 --- a/Tactility/Source/app/wifimanage/WifiManage.cpp +++ b/Tactility/Source/app/wifimanage/WifiManage.cpp @@ -1,13 +1,14 @@ #include "WifiManage.h" +#include "View.h" +#include "State.h" #include "app/App.h" #include "app/wificonnect/Parameters.h" +#include "app/wifiapsettings/WifiApSettings.h" #include "TactilityCore.h" #include "service/loader/Loader.h" #include "service/wifi/WifiSettings.h" #include "lvgl/LvglSync.h" -#include "View.h" -#include "State.h" namespace tt::app::wifimanage { @@ -27,6 +28,10 @@ static void onConnect(const char* ssid) { } } +static void onShowApSettings(const char* ssid) { + wifiapsettings::start(ssid); +} + static void onDisconnect() { service::wifi::disconnect(); } @@ -37,9 +42,10 @@ static void onWifiToggled(bool enabled) { WifiManage::WifiManage() { bindings = (Bindings) { - .onWifiToggled = &onWifiToggled, - .onConnectSsid = &onConnect, - .onDisconnect = &onDisconnect + .onWifiToggled = onWifiToggled, + .onConnectSsid = onConnect, + .onDisconnect = onDisconnect, + .onShowApSettings = onShowApSettings }; } diff --git a/Tactility/Source/lvgl/Toolbar.cpp b/Tactility/Source/lvgl/Toolbar.cpp index b6bd2e50..bc7208a1 100644 --- a/Tactility/Source/lvgl/Toolbar.cpp +++ b/Tactility/Source/lvgl/Toolbar.cpp @@ -6,6 +6,8 @@ #include "lvgl/Spacer.h" #include "lvgl/Style.h" +#define SPINNER_HEIGHT TOOLBAR_HEIGHT + namespace tt::lvgl { typedef struct { @@ -80,6 +82,7 @@ lv_obj_t* toolbar_create(lv_obj_t* parent, const std::string& title) { toolbar->action_container = lv_obj_create(obj); lv_obj_set_width(toolbar->action_container, LV_SIZE_CONTENT); + lv_obj_set_flex_flow(toolbar->action_container, LV_FLEX_FLOW_ROW); lv_obj_set_style_pad_all(toolbar->action_container, 0, 0); lv_obj_set_style_border_width(toolbar->action_container, 0, 0); @@ -123,7 +126,16 @@ uint8_t toolbar_add_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callba lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj) { auto* toolbar = (Toolbar*)obj; lv_obj_t* widget = lv_switch_create(toolbar->action_container); - lv_obj_set_pos(widget, 0, 4); // Because aligning doesn't work + lv_obj_set_style_margin_top(widget, 4, 0); // Because aligning doesn't work + return widget; +} + +lv_obj_t* toolbar_add_spinner_action(lv_obj_t* obj) { + auto* toolbar = (Toolbar*)obj; + lv_obj_t* widget = lv_spinner_create(toolbar->action_container); + lv_obj_set_size(widget, SPINNER_HEIGHT, SPINNER_HEIGHT); + lv_spinner_set_anim_params(widget, 1000, 60); + lv_obj_set_style_pad_all(widget, 4, 0); return widget; } diff --git a/Tactility/Source/lvgl/Toolbar.h b/Tactility/Source/lvgl/Toolbar.h index 17bad633..86dcb710 100644 --- a/Tactility/Source/lvgl/Toolbar.h +++ b/Tactility/Source/lvgl/Toolbar.h @@ -24,4 +24,5 @@ void toolbar_set_title(lv_obj_t* obj, const std::string& title); void toolbar_set_nav_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data); uint8_t toolbar_add_action(lv_obj_t* obj, const char* icon, lv_event_cb_t callback, void* user_data); lv_obj_t* toolbar_add_switch_action(lv_obj_t* obj); +lv_obj_t* toolbar_add_spinner_action(lv_obj_t* obj); } // namespace diff --git a/Tactility/Source/service/statusbar/Statusbar.cpp b/Tactility/Source/service/statusbar/Statusbar.cpp index a6246819..cff1477e 100644 --- a/Tactility/Source/service/statusbar/Statusbar.cpp +++ b/Tactility/Source/service/statusbar/Statusbar.cpp @@ -191,6 +191,7 @@ static void on_start(Service& service) { data->thread->setCallback(service_main, data); data->thread->setPriority(Thread::PriorityLow); data->thread->setStackSize(3000); + data->thread->setName("statusbar"); data->thread->start(); } diff --git a/TactilityHeadless/Source/service/wifi/Wifi.h b/TactilityHeadless/Source/service/wifi/Wifi.h index 992341d8..5ca51b80 100644 --- a/TactilityHeadless/Source/service/wifi/Wifi.h +++ b/TactilityHeadless/Source/service/wifi/Wifi.h @@ -89,6 +89,11 @@ void scan(); */ bool isScanning(); +/** + * @return true the ssid name or empty string + */ +std::string getConnectionTarget(); + /** * @brief Returns the access points from the last scan (if any). It only contains public APs. */ diff --git a/TactilityHeadless/Source/service/wifi/WifiEsp.cpp b/TactilityHeadless/Source/service/wifi/WifiEsp.cpp index e6f438af..4dc5da23 100644 --- a/TactilityHeadless/Source/service/wifi/WifiEsp.cpp +++ b/TactilityHeadless/Source/service/wifi/WifiEsp.cpp @@ -116,6 +116,25 @@ WifiRadioState getRadioState() { return state; } +std::string getConnectionTarget() { + lock(wifi_singleton); + std::string result; + switch (wifi_singleton->radio_state) { + case WIFI_RADIO_CONNECTION_PENDING: + case WIFI_RADIO_CONNECTION_ACTIVE: + result = wifi_singleton->connection_target.ssid; + break; + case WIFI_RADIO_ON: + case WIFI_RADIO_ON_PENDING: + case WIFI_RADIO_OFF_PENDING: + case WIFI_RADIO_OFF: + result = ""; + break; + } + unlock(wifi_singleton); + return result; +} + void scan() { TT_LOG_I(TAG, "scan()"); tt_assert(wifi_singleton); @@ -723,7 +742,6 @@ _Noreturn int32_t wifi_main(TT_UNUSED void* parameter) { WifiMessage message; while (true) { - TT_LOG_I(TAG, "Message queue %ld", queue.getCount()); if (queue.get(&message, 10000 / portTICK_PERIOD_MS) == TtStatusOk) { TT_LOG_I(TAG, "Processing message of type %d", message.type); switch (message.type) { diff --git a/TactilityHeadless/Source/service/wifi/WifiMock.cpp b/TactilityHeadless/Source/service/wifi/WifiMock.cpp index 9a9394e4..5d6f80a6 100644 --- a/TactilityHeadless/Source/service/wifi/WifiMock.cpp +++ b/TactilityHeadless/Source/service/wifi/WifiMock.cpp @@ -76,6 +76,10 @@ WifiRadioState getRadioState() { return wifi->radio_state; } +std::string getConnectionTarget() { + return "Home Wifi"; +} + void scan() { tt_assert(wifi); wifi->scan_active = false; // TODO: enable and then later disable automatically @@ -110,17 +114,17 @@ std::vector getScanResults() { .auth_mode = WIFI_AUTH_WPA2_PSK }); records.push_back((WifiApRecord) { - .ssid = "Living Room", + .ssid = "No place like 127.0.0.1", .rssi = -67, .auth_mode = WIFI_AUTH_WPA2_PSK }); records.push_back((WifiApRecord) { - .ssid = "No place like 127.0.0.1", + .ssid = "Pretty fly for a Wi-Fi", .rssi = -70, .auth_mode = WIFI_AUTH_WPA2_PSK }); records.push_back((WifiApRecord) { - .ssid = "Public Wi-Fi", + .ssid = "An AP with a really, really long name", .rssi = -80, .auth_mode = WIFI_AUTH_WPA2_PSK }); diff --git a/TactilityHeadless/Source/service/wifi/WifiSettings.h b/TactilityHeadless/Source/service/wifi/WifiSettings.h index 0b615007..285226b6 100644 --- a/TactilityHeadless/Source/service/wifi/WifiSettings.h +++ b/TactilityHeadless/Source/service/wifi/WifiSettings.h @@ -10,11 +10,11 @@ namespace tt::service::wifi::settings { * The SSID and secret are increased by 1 byte to facilitate string null termination. * This makes it easier to use the char array as a string in various places. */ -typedef struct { +struct WifiApSettings { char ssid[TT_WIFI_SSID_LIMIT + 1]; char password[TT_WIFI_CREDENTIALS_PASSWORD_LIMIT + 1]; bool auto_connect; -} WifiApSettings; +}; bool contains(const char* ssid); diff --git a/sdkconfig.board.lilygo_tdeck b/sdkconfig.board.lilygo_tdeck index f8d7a90a..883211b0 100644 --- a/sdkconfig.board.lilygo_tdeck +++ b/sdkconfig.board.lilygo_tdeck @@ -14,6 +14,7 @@ CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" diff --git a/sdkconfig.board.m5stack_core2 b/sdkconfig.board.m5stack_core2 index c08aed7b..9bf63768 100644 --- a/sdkconfig.board.m5stack_core2 +++ b/sdkconfig.board.m5stack_core2 @@ -14,6 +14,7 @@ CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" diff --git a/sdkconfig.board.m5stack_cores3 b/sdkconfig.board.m5stack_cores3 index 2164388b..e9e28f25 100644 --- a/sdkconfig.board.m5stack_cores3 +++ b/sdkconfig.board.m5stack_cores3 @@ -14,6 +14,7 @@ CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" diff --git a/sdkconfig.board.yellow_board b/sdkconfig.board.yellow_board index 8f8148b8..cc610426 100644 --- a/sdkconfig.board.yellow_board +++ b/sdkconfig.board.yellow_board @@ -14,6 +14,7 @@ CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" diff --git a/sdkconfig.defaults b/sdkconfig.defaults index ce02a0d7..131bb3ad 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -13,6 +13,8 @@ CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_FREERTOS_SMP=n CONFIG_FREERTOS_UNICORE=n +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"