diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 52dc8c3..07d655a 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -3,4 +3,4 @@ file(GLOB SRCS "main.cpp" "*.cpp" "*.c" "**/*.cpp" "**/*.c") idf_component_register(SRCS ${SRCS} PRIV_REQUIRES ${requires} - INCLUDE_DIRS "." "display" "touch" "network" "ui" "io" "common") + INCLUDE_DIRS "." "display" "network" "ui" "io" "common") diff --git a/main/io/io.h b/main/io/io.h index ad396f9..4f46722 100644 --- a/main/io/io.h +++ b/main/io/io.h @@ -3,8 +3,8 @@ #include "freertos/event_groups.h" #include -typedef bool(*FilterFunc)(const char* const& key); -typedef void (*KeyValueProcessor)(void* arg, const char* const& key, const char* const& value); +typedef bool(*FilterFunc)(const std::string& key); +typedef void (*KeyValueProcessor)(void* arg, const std::string& key, const std::string& value); class KVStorageHandler { public: @@ -13,15 +13,14 @@ public: virtual void init(const EventGroupHandle_t& system_event_group) = 0; // Store a key-value pair - virtual void put(const char* const& key, const char* const& value) = 0; + virtual void put(const std::string& key, const std::string& value) = 0; - // Retrieve a value by key, returns nullptr if key not found - // The caller is responsible for freeing the returned memory - virtual std::unique_ptr get(const char* const& key) const = 0; + // Retrieve a value by key, returns empty string if key not found + virtual std::string get(const std::string& key) const = 0; virtual esp_err_t process_all(KeyValueProcessor processor, void* arg) const = 0; - virtual esp_err_t process_filtered(const char* const& key_prefix, KeyValueProcessor processor, void* arg) const = 0; + virtual esp_err_t process_filtered(const std::string& key_prefix, KeyValueProcessor processor, void* arg) const = 0; virtual esp_err_t process_filtered(FilterFunc filter_func, KeyValueProcessor processor, void* arg) const = 0; // Delete a key-value pair - virtual void remove(const char* const& key) = 0; + virtual void remove(const std::string& key) = 0; }; \ No newline at end of file diff --git a/main/io/nvs_handler.cpp b/main/io/nvs_handler.cpp index 2a0ad9a..187b6cf 100644 --- a/main/io/nvs_handler.cpp +++ b/main/io/nvs_handler.cpp @@ -2,6 +2,9 @@ #include "io/nvs_handler.h" #include "nvs_flash.h" #include "string.h" +#include "esp_log.h" + +#define TAG "NVSStorageHandler" NVSStorageHandler::NVSStorageHandler( const char* name_space @@ -24,49 +27,51 @@ void NVSStorageHandler::init(const EventGroupHandle_t& system_event_group) { err = nvs_open(this->name_space, NVS_READWRITE, &this->nvsHandle); if (err != ESP_OK) { - printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err)); } else { - xEventGroupSetBits(system_event_group, STORAGE_READY_BIT); - printf("NVS Storage initialized.\n"); + if (system_event_group != nullptr) { + xEventGroupSetBits(system_event_group, STORAGE_READY_BIT); + } + ESP_LOGI(TAG, "NVS Storage initialized."); } } -void NVSStorageHandler::put(const char* const& key, const char* const& value) { +void NVSStorageHandler::put(const std::string& key, const std::string& value) { if (this->nvsHandle == 0) { - printf("NVS handle is not initialized.\n"); + ESP_LOGE(TAG, "NVS handle is not initialized."); return; } - esp_err_t err = nvs_set_str(this->nvsHandle, key, value); + esp_err_t err = nvs_set_str(this->nvsHandle, key.c_str(), value.c_str()); if (err != ESP_OK) { - printf("Error (%s) setting key-value pair in NVS!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) setting key-value pair in NVS!", esp_err_to_name(err)); } else { nvs_commit(this->nvsHandle); - printf("Key-value pair (%s, %s) stored in NVS.\n", key, value); + ESP_LOGI(TAG, "Key-value pair (%s, %s) stored in NVS.", key.c_str(), value.c_str()); } } -std::unique_ptr NVSStorageHandler::get(const char* const& key) const { +std::string NVSStorageHandler::get(const std::string& key) const { if (this->nvsHandle == 0) { - printf("NVS handle is not initialized.\n"); - return nullptr; + ESP_LOGE(TAG, "NVS handle is not initialized."); + return ""; } size_t required_size = 0; - esp_err_t err = nvs_get_str(this->nvsHandle, key, nullptr, &required_size); + esp_err_t err = nvs_get_str(this->nvsHandle, key.c_str(), nullptr, &required_size); if (err == ESP_ERR_NVS_NOT_FOUND) { - printf("Key %s not found in NVS.\n", key); - return nullptr; + ESP_LOGW(TAG, "Key %s not found in NVS.", key.c_str()); + return ""; } else if (err != ESP_OK) { - printf("Error (%s) getting size for key %s from NVS!\n", esp_err_to_name(err), key); - return nullptr; + ESP_LOGE(TAG, "Error (%s) getting size for key %s from NVS!", esp_err_to_name(err), key.c_str()); + return ""; } - std::unique_ptr value(new char[required_size]); - err = nvs_get_str(this->nvsHandle, key, value.get(), &required_size); + std::string value; + err = nvs_get_str(this->nvsHandle, key.c_str(), value.data(), &required_size); if (err != ESP_OK) { - printf("Error (%s) getting value for key %s from NVS!\n", esp_err_to_name(err), key); - return nullptr; + ESP_LOGE(TAG, "Error (%s) getting value for key %s from NVS!", esp_err_to_name(err), key.c_str()); + return ""; } return value; @@ -76,7 +81,7 @@ NVSIteratorGuard NVSStorageHandler::create_iterator() const { nvs_iterator_t it = nullptr; esp_err_t err = nvs_entry_find(NVS_DEFAULT_PART_NAME, this->name_space, NVS_TYPE_ANY, &it); if (err != ESP_OK) { - printf("Error (%s) creating NVS iterator!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) creating NVS iterator!", esp_err_to_name(err)); return NVSIteratorGuard(nullptr, err); } @@ -94,22 +99,23 @@ esp_err_t NVSStorageHandler::process_all(KeyValueProcessor processor, void* arg) nvs_entry_info_t info; esp_err_t err = nvs_entry_info(it, &info); if (err != ESP_OK) { - printf("Error (%s) getting NVS entry info!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) getting NVS entry info!", esp_err_to_name(err)); return err; } nvs_handle_t temp_handle; err = nvs_open(this->name_space, NVS_READONLY, &temp_handle); if (err != ESP_OK) { - printf("Error (%s) opening NVS handle for reading!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) opening NVS handle for reading!", esp_err_to_name(err)); return err; } // call the processor with the key and value - processor(arg, info.key, this->get(info.key).get()); + std::string key_str = info.key; + processor(arg, key_str, this->get(key_str)); } return ESP_OK; } -esp_err_t NVSStorageHandler::process_filtered(const char* const& key_prefix, KeyValueProcessor processor, void* arg) const { +esp_err_t NVSStorageHandler::process_filtered(const std::string& key_prefix, KeyValueProcessor processor, void* arg) const { NVSIteratorGuard iterator_guard = this->create_iterator(); if (!iterator_guard.is_valid()) { return iterator_guard.get_error(); @@ -120,19 +126,19 @@ esp_err_t NVSStorageHandler::process_filtered(const char* const& key_prefix, Key nvs_entry_info_t info; esp_err_t err = nvs_entry_info(it, &info); if (err != ESP_OK) { - printf("Error (%s) getting NVS entry info!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) getting NVS entry info!", esp_err_to_name(err)); return err; } // check if the key matches the prefix - if (strncmp(info.key, key_prefix, strlen(key_prefix)) == 0) { + if (strncmp(info.key, key_prefix.c_str(), key_prefix.length()) == 0) { nvs_handle_t temp_handle; err = nvs_open(this->name_space, NVS_READONLY, &temp_handle); if (err != ESP_OK) { - printf("Error (%s) opening NVS handle for reading!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) opening NVS handle for reading!", esp_err_to_name(err)); return err; } // call the processor with the key and value - processor(arg, info.key, this->get(info.key).get()); + processor(arg, std::string(info.key), this->get(std::string(info.key))); } } return ESP_OK; @@ -149,35 +155,36 @@ esp_err_t NVSStorageHandler::process_filtered(FilterFunc filter_func, KeyValuePr nvs_entry_info_t info; esp_err_t err = nvs_entry_info(it, &info); if (err != ESP_OK) { - printf("Error (%s) getting NVS entry info!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) getting NVS entry info!", esp_err_to_name(err)); return err; } // check if the key matches the filter function - if (filter_func(info.key)) { + std::string key_str(info.key); + if (filter_func(key_str)) { nvs_handle_t temp_handle; err = nvs_open(this->name_space, NVS_READONLY, &temp_handle); if (err != ESP_OK) { - printf("Error (%s) opening NVS handle for reading!\n", esp_err_to_name(err)); + ESP_LOGE(TAG, "Error (%s) opening NVS handle for reading!", esp_err_to_name(err)); return err; } // call the processor with the key and value - processor(arg, info.key, this->get(info.key).get()); + processor(arg, key_str, this->get(key_str)); } } return ESP_OK; } -void NVSStorageHandler::remove(const char* const& key) { +void NVSStorageHandler::remove(const std::string& key) { if (this->nvsHandle == 0) { - printf("NVS handle is not initialized.\n"); + ESP_LOGE(TAG, "NVS handle is not initialized."); return; } - esp_err_t err = nvs_erase_key(this->nvsHandle, key); + esp_err_t err = nvs_erase_key(this->nvsHandle, key.c_str()); if (err != ESP_OK) { - printf("Error (%s) deleting key %s from NVS!\n", esp_err_to_name(err), key); + ESP_LOGE(TAG, "Error (%s) deleting key %s from NVS!", esp_err_to_name(err), key.c_str()); } else { nvs_commit(this->nvsHandle); - printf("Key %s deleted from NVS.\n", key); + ESP_LOGI(TAG, "Key %s deleted from NVS.", key.c_str()); } } diff --git a/main/io/nvs_handler.h b/main/io/nvs_handler.h index 2761551..3b20e39 100644 --- a/main/io/nvs_handler.h +++ b/main/io/nvs_handler.h @@ -53,14 +53,14 @@ public: void init(const EventGroupHandle_t& system_event_group) override; - void put(const char* const& key, const char* const& value) override; + void put(const std::string& key, const std::string& value) override; - std::unique_ptr get(const char* const& key) const override; + std::string get(const std::string& key) const override; esp_err_t process_all(KeyValueProcessor processor, void* arg) const override; - esp_err_t process_filtered(const char* const& key_prefix, KeyValueProcessor processor, void* arg) const override; + esp_err_t process_filtered(const std::string& key_prefix, KeyValueProcessor processor, void* arg) const override; esp_err_t process_filtered(FilterFunc filter_func, KeyValueProcessor processor, void* arg) const override; - void remove(const char* const& key) override; + void remove(const std::string& key) override; private: NVSIteratorGuard create_iterator() const; diff --git a/main/lv_conf.h b/main/lv_conf.h index c500dd0..cce9f55 100644 --- a/main/lv_conf.h +++ b/main/lv_conf.h @@ -109,7 +109,8 @@ * - LV_OS_MQX * - LV_OS_SDL2 * - LV_OS_CUSTOM */ -#define LV_USE_OS LV_OS_NONE + // #define LV_USE_OS LV_OS_NONE +#define LV_USE_OS LV_OS_FREERTOS #if LV_USE_OS == LV_OS_CUSTOM #define LV_OS_CUSTOM_INCLUDE @@ -574,7 +575,8 @@ /* Enable the multi-touch gesture recognition feature */ /* Gesture recognition requires the use of floats */ -#define LV_USE_GESTURE_RECOGNITION 0 +// #define LV_USE_GESTURE_RECOGNITION 0 +#define LV_USE_GESTURE_RECOGNITION 1 /*===================== * COMPILER SETTINGS @@ -617,7 +619,8 @@ #define LV_ATTRIBUTE_EXTERN_DATA /** Use `float` as `lv_value_precise_t` */ -#define LV_USE_FLOAT 0 +// #define LV_USE_FLOAT 0 +#define LV_USE_FLOAT 1 /** Enable matrix support * - Requires `LV_USE_FLOAT = 1` */ @@ -1189,7 +1192,8 @@ /** 1: Enable Pinyin input method * - Requires: lv_keyboard */ -#define LV_USE_IME_PINYIN 0 + // #define LV_USE_IME_PINYIN 0 +#define LV_USE_IME_PINYIN 1 #if LV_USE_IME_PINYIN /** 1: Use default thesaurus. * @note If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesaurus. */ @@ -1436,10 +1440,12 @@ *======================*/ /** Enable examples to be built with the library. */ -#define LV_BUILD_EXAMPLES 1 +// #define LV_BUILD_EXAMPLES 1 +#define LV_BUILD_EXAMPLES 0 /** Build the demos */ -#define LV_BUILD_DEMOS 1 +// #define LV_BUILD_DEMOS 1 +#define LV_BUILD_DEMOS 0 /*=================== * DEMO USAGE diff --git a/main/main.cpp b/main/main.cpp index fea18b0..c0b76bd 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1,10 +1,3 @@ -/* - * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - - #include #include #include @@ -16,18 +9,19 @@ #include "esp_system.h" #include "esp_log.h" - // +// #include "common/constants.h" #include "common/queue_defs.h" #include "io/nvs_handler.h" #include "info/info.h" #include "display/display.h" -#include "touch/touch.h" #include #include "network.h" +// nvs storage namespaces, 15 characters max #define DEFAULT_STORAGE_NAMESPACE "storage" -#define WIFI_CREDENTIALS_STORAGE_NAMESPACE "wifi_credentials" +#define WIFI_CREDENTIALS_STORAGE_NAMESPACE "wifi_cred" +#define TAG "Main" extern "C" void app_main(void); @@ -50,7 +44,7 @@ void app_main(void) { vTaskDelay(5000 / portTICK_PERIOD_MS); return esp_restart(); } - printf("Queues initialized.\n"); + ESP_LOGI(TAG, "Queues initialized.\n"); SemaphoreHandle_t lvgl_mutex = xSemaphoreCreateMutex(); if (lvgl_mutex == NULL) { ESP_LOGE("Main", "Failed to create LVGL mutex"); @@ -58,28 +52,33 @@ void app_main(void) { return esp_restart(); } // - WifiHandler wifi_handler( - new NVSStorageHandler(WIFI_CREDENTIALS_STORAGE_NAMESPACE) - ); - NetworkHandler* network_handler = new NetworkHandler(std::move(wifi_handler)); KVStorageHandler* kv_storage_handler = new NVSStorageHandler( DEFAULT_STORAGE_NAMESPACE ); - DisplayHandler* display_handler = new EInkDisplayHandler(touch_event_queue, lvgl_mutex); - TouchHandler* touch_handler = new EInkTouchHandler(touch_event_queue); + + auto wifi_handler = std::make_unique( + std::unique_ptr(new NVSStorageHandler(WIFI_CREDENTIALS_STORAGE_NAMESPACE)) + ); + NetworkHandler* network_handler = new NetworkHandler(std::move(wifi_handler)); + // DisplayHandler* display_handler = new EInkDisplayHandler(touch_event_queue, lvgl_mutex); // - network_handler->init(system_event_group); kv_storage_handler->init(system_event_group); - display_handler->init(system_event_group); - touch_handler->init(system_event_group); + network_handler->init(system_event_group); + + + // display_handler->init(system_event_group); // // LVGL tick timer auto lvgl_tick_timer_callback = [](TimerHandle_t xTimer) { lv_tick_inc(5); }; + TickType_t lvgl_tick_period = pdMS_TO_TICKS(5); + if (lvgl_tick_period == 0) { + lvgl_tick_period = 1; // ensure at least 1 tick to avoid FreeRTOS assert + } TimerHandle_t lvgl_tick_timer = xTimerCreate( "lvgl_tick_timer", - pdMS_TO_TICKS(5), + lvgl_tick_period, pdTRUE, NULL, lvgl_tick_timer_callback @@ -92,20 +91,21 @@ void app_main(void) { xTimerStart(lvgl_tick_timer, 0); // - printf("Waiting for system to be ready...\n"); + ESP_LOGI(TAG, "Waiting for system to be ready...\n"); xEventGroupWaitBits( system_event_group, - DISPLAY_READY_BIT | TOUCH_CALIBRATED_BIT | STORAGE_READY_BIT | NETWORK_READY_BIT, + // DISPLAY_READY_BIT | TOUCH_CALIBRATED_BIT | + STORAGE_READY_BIT | NETWORK_READY_BIT, // do not clear on exit, require explicit reset pdFALSE, pdTRUE, portMAX_DELAY ); - printf("System is ready. Starting main application...\n"); + ESP_LOGI(TAG, "System is ready. Starting main application...\n"); // starting event loops - display_handler->start_event_loop(); - touch_handler->start_event_loop(); + // display_handler->start_event_loop(); // wait for shutdown signal + ESP_LOGI(TAG, "Waiting for shutdown signal...\n"); EventBits_t bits = xEventGroupWaitBits( system_lifecycle_event_group, SYSTEM_SHUTDOWN_BIT | SYSTEM_RESTART_BIT, @@ -114,27 +114,26 @@ void app_main(void) { pdFALSE, portMAX_DELAY ); - printf("Shutdown signal received. Cleaning up...\n"); + ESP_LOGI(TAG, "Shutdown signal received. Cleaning up...\n"); // cleanup - shutdown_display_handlerFunc shutdown_display_handler = display_handler->get_shutdown_display_handler(); - restart_display_handlerFunc restart_display_handler = display_handler->get_restart_display_handler(); - delete display_handler; - delete touch_handler; + // shutdown_display_handlerFunc shutdown_display_handler = display_handler->get_shutdown_display_handler(); + // restart_display_handlerFunc restart_display_handler = display_handler->get_restart_display_handler(); + // delete display_handler; vSemaphoreDelete(lvgl_mutex); vEventGroupDelete(system_event_group); vQueueDelete(touch_event_queue); - printf("Cleanup complete.\n"); + ESP_LOGI(TAG, "Cleanup complete.\n"); // handle shutdown or restart if (bits & SYSTEM_SHUTDOWN_BIT) { - if (shutdown_display_handler != nullptr) { - printf("Calling display shutdown handler...\n"); - shutdown_display_handler(); - } else { - printf("No display shutdown handler to call.\n"); - } - printf("System is shutting down.\n"); + // if (shutdown_display_handler != nullptr) { + // ESP_LOGI(TAG, "Calling display shutdown handler...\n"); + // shutdown_display_handler(); + // } else { + // ESP_LOGI(TAG, "No display shutdown handler to call.\n"); + // } + ESP_LOGI(TAG, "System is shutting down.\n"); fflush(stdout); // wait for start bit to be set again if future restart is desired, else expect manual power cycle EventBits_t bits = xEventGroupWaitBits( @@ -145,24 +144,24 @@ void app_main(void) { portMAX_DELAY ); if (bits & SYSTEM_START_BIT) { - printf("SYSTEM_START_BIT received, restarting system.\n"); + ESP_LOGI(TAG, "SYSTEM_START_BIT received, restarting system.\n"); } else { - printf("No restart signal received, waiting for manual power cycle.\n"); + ESP_LOGW(TAG, "No restart signal received, waiting for manual power cycle.\n"); while (true) { vTaskDelay(portMAX_DELAY); } } } else if (bits & SYSTEM_RESTART_BIT) { - if (restart_display_handler != nullptr) { - printf("Calling display restart handler...\n"); - restart_display_handler(); - } else { - printf("No display restart handler to call.\n"); - } - printf("System is restarting.\n"); + // if (restart_display_handler != nullptr) { + // ESP_LOGI(TAG, "Calling display restart handler...\n"); + // restart_display_handler(); + // } else { + // ESP_LOGI(TAG, "No display restart handler to call.\n"); + // } + ESP_LOGI(TAG, "System is restarting.\n"); fflush(stdout); } else { - printf("Unknown shutdown signal received. Restarting by default.\n"); + ESP_LOGW(TAG, "Unknown shutdown signal received. Restarting by default.\n"); fflush(stdout); } diff --git a/main/network/network.cpp b/main/network/network.cpp index 432d6a7..d02a717 100644 --- a/main/network/network.cpp +++ b/main/network/network.cpp @@ -4,7 +4,7 @@ #include "common/constants.h" NetworkHandler::NetworkHandler( - WifiHandler&& wifiHandler + std::unique_ptr wifiHandler ) : wifiHandler(std::move(wifiHandler)) { } NetworkHandler::~NetworkHandler() { } @@ -14,7 +14,7 @@ void NetworkHandler::init(EventGroupHandle_t system_event_group) { ESP_LOGW("NetworkHandler", "Already initialized, skipping"); return; } - this->wifiHandler.init(); + this->wifiHandler->init(); this->initialized = true; xEventGroupSetBits( system_event_group, @@ -23,10 +23,10 @@ void NetworkHandler::init(EventGroupHandle_t system_event_group) { } WifiHandler& NetworkHandler::get_wifi_handler() { - return this->wifiHandler; + return *this->wifiHandler; } std::unique_ptr NetworkHandler::get_http_handler(const esp_http_client_config_t&& config) { - return std::unique_ptr(new HttpHandler(std::move(config), &this->wifiHandler)); + return std::unique_ptr(new HttpHandler(std::move(config), this->wifiHandler.get())); } diff --git a/main/network/network.h b/main/network/network.h index ba7eaac..edcc46c 100644 --- a/main/network/network.h +++ b/main/network/network.h @@ -11,7 +11,7 @@ class HttpHandler; class NetworkHandler { public: NetworkHandler( - WifiHandler&& wifiHandler + std::unique_ptr wifiHandler ); ~NetworkHandler(); @@ -22,6 +22,6 @@ public: private: - WifiHandler wifiHandler; + std::unique_ptr wifiHandler; bool initialized = false; }; diff --git a/main/network/wifi_handler.cpp b/main/network/wifi_handler.cpp index 5a70a2a..38d8d01 100644 --- a/main/network/wifi_handler.cpp +++ b/main/network/wifi_handler.cpp @@ -6,38 +6,39 @@ #include "esp_log.h" #include "freertos/semphr.h" #include "common/semaphore_guard.h" +#include "cJSON.h" static const char* TAG = "WifiHandler"; -static const char* WIFI_SSID_KEY = "wifi_ssid"; -static const char* WIFI_PASSWORD_KEY = "wifi_password"; +static const char* WIFI_SSID_KEY = "ssid"; +static const char* WIFI_PASSWORD_STORE_KEY = "psw"; WifiHandler::WifiHandler( // this handler is used to store/retrieve WiFi credentials // should have a unique namespace for WiFi credentials // it will be owned by WifiHandler and deleted in its destructor - KVStorageHandler* kvs -) : kvs(kvs) { + std::unique_ptr kvs +) : kvs(std::move(kvs)) { this->s_wifi_event_group = xEventGroupCreate(); + if (!this->s_wifi_event_group) { + ESP_LOGE(TAG, "Failed to create WiFi event group"); + } this->scan_mutex = xSemaphoreCreateMutex(); + if (!this->scan_mutex) { + ESP_LOGE(TAG, "Failed to create scan mutex"); + } this->connection_mutex = xSemaphoreCreateMutex(); -} - -// Move constructor: transfer ownership of resources -WifiHandler::WifiHandler(WifiHandler&& other) noexcept - : initialized(other.initialized), - kvs(other.kvs), - s_wifi_event_group(other.s_wifi_event_group), - scan_mutex(other.scan_mutex), - connection_mutex(other.connection_mutex), - current_ssid(other.current_ssid), - expect_disconnected(other.expect_disconnected) { - other.kvs = nullptr; - other.initialized = false; - other.s_wifi_event_group = 0; - other.scan_mutex = nullptr; - other.connection_mutex = nullptr; - other.current_ssid = nullptr; - other.expect_disconnected = false; + if (!this->connection_mutex) { + ESP_LOGE(TAG, "Failed to create connection mutex"); + } + this->credential_mutex = xSemaphoreCreateMutex(); + if (!this->credential_mutex) { + ESP_LOGE(TAG, "Failed to create credential mutex"); + } + if (this->kvs == nullptr) { + ESP_LOGW(TAG, "KVStorageHandler is null, WiFi credentials will not be stored"); + } else { + this->kvs->init(nullptr); + } } WifiHandler::~WifiHandler() { @@ -46,20 +47,23 @@ WifiHandler::~WifiHandler() { // Check if it should be called esp_wifi_deinit(); vEventGroupDelete(this->s_wifi_event_group); - if (this->current_ssid) { - delete[] this->current_ssid; + if (!this->current_ssid.empty()) { + this->current_ssid.clear(); } vSemaphoreDelete(this->scan_mutex); vSemaphoreDelete(this->connection_mutex); esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &WifiHandler::wifi_event_handler); esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &WifiHandler::wifi_event_handler); + this->initialized = false; + // unique_ptr will automatically delete the object + this->kvs = nullptr; } } -void WifiHandler::init() { +esp_err_t WifiHandler::init() { if (this->initialized) { ESP_LOGW(TAG, "Already initialized, skipping"); - return; + return ESP_OK; } esp_err_t err; @@ -67,13 +71,13 @@ void WifiHandler::init() { err = esp_netif_init(); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_netif_init failed: %s", esp_err_to_name(err)); - return; + return err; } err = esp_event_loop_create_default(); if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) { ESP_LOGE(TAG, "esp_event_loop_create_default failed: %s", esp_err_to_name(err)); - return; + return err; } // create default WiFi station @@ -84,32 +88,40 @@ void WifiHandler::init() { err = esp_wifi_init(&cfg); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_init failed: %s", esp_err_to_name(err)); - return; + return err; } // register event handlers for WiFi and IP events - esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WifiHandler::wifi_event_handler, this); - esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &WifiHandler::wifi_event_handler, this); + err = esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WifiHandler::wifi_event_handler, this); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_event_handler_register failed: %s", esp_err_to_name(err)); + return err; + } + err = esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &WifiHandler::wifi_event_handler, this); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_event_handler_register failed: %s", esp_err_to_name(err)); + return err; + } err = esp_wifi_set_mode(WIFI_MODE_STA); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_set_mode failed: %s", esp_err_to_name(err)); - return; + return err; } err = esp_wifi_start(); if (err != ESP_OK) { ESP_LOGE(TAG, "esp_wifi_start failed: %s", esp_err_to_name(err)); - return; + return err; } // get WiFi credentials from KV storage if available - char* ssid = nullptr; - char* password = nullptr; + std::string ssid; + std::string password; this->get_wifi_credentials(ssid, password); - if (ssid && password) { - ESP_LOGI(TAG, "Found stored WiFi credentials, connecting to SSID: %s", ssid); + if (!ssid.empty() && !password.empty()) { + ESP_LOGI(TAG, "Found stored WiFi credentials, connecting to SSID: %s", ssid.c_str()); err = this->connect(ssid, password); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to connect to stored WiFi credentials: %s", esp_err_to_name(err)); @@ -118,13 +130,11 @@ void WifiHandler::init() { ESP_LOGI(TAG, "No stored WiFi credentials found, not connecting"); } - delete[] ssid; - delete[] password; - initialized = true; + return ESP_OK; } -esp_err_t WifiHandler::connect(const char* ssid, const char* password) { +esp_err_t WifiHandler::connect(const std::string& ssid, const std::string& password) { SemaphoreGuard guard(this->connection_mutex); // wait up to 5 seconds to take the mutex if (!guard.take(5000 / portTICK_PERIOD_MS)) { @@ -133,24 +143,21 @@ esp_err_t WifiHandler::connect(const char* ssid, const char* password) { } expect_disconnected = false; - if (this->current_ssid) { - delete[] this->current_ssid; + if (!this->current_ssid.empty()) { + this->current_ssid.clear(); } - size_t ssid_len = strlen(ssid); - this->current_ssid = new char[ssid_len + 1]; - strncpy(this->current_ssid, ssid, ssid_len + 1); - this->current_ssid[ssid_len] = '\0'; + this->current_ssid = ssid; // wifi_config_t wifi_config = {}; - strncpy((char*)wifi_config.sta.ssid, this->current_ssid, sizeof(wifi_config.sta.ssid)); + strncpy((char*)wifi_config.sta.ssid, this->current_ssid.c_str(), sizeof(wifi_config.sta.ssid)); wifi_config.sta.ssid[sizeof(wifi_config.sta.ssid) - 1] = '\0'; - strncpy((char*)wifi_config.sta.password, password, sizeof(wifi_config.sta.password)); + strncpy((char*)wifi_config.sta.password, password.c_str(), sizeof(wifi_config.sta.password)); wifi_config.sta.password[sizeof(wifi_config.sta.password) - 1] = '\0'; // set auth mode to WPA2_PSK minimum wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; - ESP_LOGI(TAG, "Connecting to SSID: %s", this->current_ssid); + ESP_LOGI(TAG, "Connecting to SSID: %s", this->current_ssid.c_str()); esp_err_t err = esp_wifi_set_config(wifi_interface_t::WIFI_IF_STA, &wifi_config); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to set WiFi config: %s", esp_err_to_name(err)); @@ -162,40 +169,26 @@ esp_err_t WifiHandler::connect(const char* ssid, const char* password) { return err; } - // store credentials - this->kvs->put(WIFI_SSID_KEY, this->current_ssid); - // store password under key derived from SSID - char* password_key = this->build_password_key(this->current_ssid); - this->kvs->put(password_key, password); - delete[] password_key; - - // set connected bit on successful connection - xEventGroupSetBits( - this->s_wifi_event_group, - WIFI_CONNECTED_BIT - ); + // store credentials after successful connection attempt + this->store_wifi_credentials(this->current_ssid, password); return ESP_OK; } -esp_err_t WifiHandler::connect(const char* ssid) { - char* stored_ssid = nullptr; - char* stored_password = nullptr; +esp_err_t WifiHandler::connect(const std::string& ssid) { + std::string stored_ssid; + std::string stored_password; this->get_wifi_credentials(stored_ssid, stored_password); - if (!stored_ssid || strcmp(stored_ssid, ssid) != 0) { - ESP_LOGE(TAG, "No stored credentials for SSID: %s", ssid); - delete[] stored_ssid; - delete[] stored_password; + if (stored_ssid.empty() || stored_ssid != ssid) { + ESP_LOGE(TAG, "No stored credentials for SSID: %s", ssid.c_str()); return ESP_FAIL; } - esp_err_t err = this->connect(stored_ssid, stored_password ? stored_password : ""); - delete[] stored_ssid; - delete[] stored_password; + esp_err_t err = this->connect(stored_ssid, stored_password); return err; } esp_err_t WifiHandler::reconnect() { - if (!this->current_ssid) { + if (this->current_ssid.empty()) { ESP_LOGE(TAG, "No current SSID set, cannot reconnect"); return ESP_FAIL; } @@ -270,10 +263,15 @@ void WifiHandler::wifi_event_handler(void* arg, esp_event_base_t event_base, int case WIFI_EVENT_STA_START: // When the station starts, attempt to connect ESP_LOGI(TAG, "WIFI_EVENT_STA_START"); - if (!self->expect_disconnected && self->current_ssid) { - ESP_LOGI(TAG, "Station started, attempting to connect to SSID: %s", self->current_ssid); + if (!self->expect_disconnected && !self->current_ssid.empty()) { + ESP_LOGI(TAG, "Station started, attempting to connect to SSID: %s", self->current_ssid.c_str()); self->reconnect(); } + // set the event bit to indicate started + xEventGroupSetBits( + self->s_wifi_event_group, + WIFI_STARTED_BIT + ); break; case WIFI_EVENT_STA_DISCONNECTED: ESP_LOGI(TAG, "WIFI_EVENT_STA_DISCONNECTED"); @@ -306,29 +304,106 @@ void WifiHandler::wifi_event_handler(void* arg, esp_event_base_t event_base, int // private methods // -char* WifiHandler::build_password_key(const char* ssid) { - // `{WIFI_PASSWORD_KEY}_{ssid}` - size_t password_key_len = strlen(WIFI_PASSWORD_KEY) + 1 + strlen(ssid) + 1; - char* password_key_buff = new char[password_key_len]; - snprintf(password_key_buff, password_key_len, "%s_%s", WIFI_PASSWORD_KEY, ssid); - return password_key_buff; +void WifiHandler::store_wifi_credentials(const std::string& ssid, const std::string& password) { + if (!kvs) { + ESP_LOGW(TAG, "KVStorageHandler not set, cannot store WiFi credentials"); + return; + } + SemaphoreGuard guard(this->credential_mutex); + // wait up to 5 seconds to take the mutex + if (!guard.take(5000 / portTICK_PERIOD_MS)) { + ESP_LOGE(TAG, "Failed to take credential mutex"); + return; + } + // store the password according to the JSON structure + std::string password_key_store = kvs->get(WIFI_PASSWORD_STORE_KEY); + cJSON* json = nullptr; + if (password_key_store.empty()) { + // create new JSON object + json = cJSON_CreateObject(); + } else { + // parse existing JSON + json = cJSON_Parse(password_key_store.c_str()); + if (json == nullptr) { + ESP_LOGE(TAG, "Failed to parse existing WiFi password JSON, creating new"); + json = cJSON_CreateObject(); + } + } + cJSON* credentials = cJSON_GetObjectItem(json, "credentials"); + if (credentials == nullptr || !cJSON_IsObject(credentials)) { + // create credentials object if it doesn't exist + credentials = cJSON_CreateObject(); + cJSON_AddItemToObject(json, "credentials", credentials); + } + // create SSID object + cJSON* ssid_item = cJSON_CreateObject(); + // add password field + cJSON_AddStringToObject(ssid_item, "password", password.c_str()); + // add SSID object to credentials + cJSON_AddItemToObject(credentials, ssid.c_str(), ssid_item); + // store updated JSON string + char* updated_json_str = cJSON_PrintUnformatted(json); + if (updated_json_str) { + kvs->put(WIFI_PASSWORD_STORE_KEY, std::string(updated_json_str)); + cJSON_free(updated_json_str); + } + cJSON_Delete(json); } -void WifiHandler::get_wifi_credentials(char*& ssid, char*& password) { +void WifiHandler::get_wifi_credentials(std::string& out_ssid, std::string& out_password) { if (!kvs) { ESP_LOGW(TAG, "KVStorageHandler not set, cannot get WiFi credentials"); return; } - ssid = kvs->get(WIFI_SSID_KEY).get(); - if (!ssid) { - ssid = nullptr; - password = nullptr; + SemaphoreGuard guard(this->credential_mutex); + // wait up to 5 seconds to take the mutex + if (!guard.take(5000 / portTICK_PERIOD_MS)) { + ESP_LOGE(TAG, "Failed to take credential mutex"); + return; + } + out_ssid = kvs->get(WIFI_SSID_KEY); + if (out_ssid.empty()) { + out_ssid = ""; + out_password = ""; return; } // password is from KV storage, may be nullptr - char* password_key = this->build_password_key(ssid); - password = kvs->get(password_key).get(); - delete[] password_key; + std::string password_key_store = kvs->get(WIFI_PASSWORD_STORE_KEY); + if (password_key_store.empty()) { + out_password = ""; + return; + } + // parse from json + cJSON* json = cJSON_Parse(password_key_store.c_str()); + if (json == nullptr) { + ESP_LOGE(TAG, "Failed to parse WiFi password JSON"); + out_password = ""; + return; + } + cJSON* credentials = cJSON_GetObjectItem(json, "credentials"); + if (credentials == nullptr || !cJSON_IsObject(credentials)) { + ESP_LOGE(TAG, "WiFi password JSON does not contain valid 'credentials' object"); + out_password = ""; + cJSON_Delete(json); + return; + } + // get the ssid value + cJSON* ssid_item = cJSON_GetObjectItem(credentials, out_ssid.c_str()); + if (ssid_item == nullptr || !cJSON_IsObject(ssid_item)) { + ESP_LOGE(TAG, "WiFi password JSON does not contain valid SSID field for SSID: %s", out_ssid.c_str()); + out_password = ""; + cJSON_Delete(json); + return; + } + cJSON* password = cJSON_GetObjectItem(ssid_item, "password"); + if (password == nullptr || !cJSON_IsString(password)) { + ESP_LOGE(TAG, "WiFi password JSON does not contain valid 'password' field for SSID: %s", out_ssid.c_str()); + out_password = ""; + cJSON_Delete(json); + return; + } + out_password = password->valuestring; + cJSON_Delete(json); } EventBits_t WifiHandler::wait_for_connection(TickType_t ticks_to_wait) { diff --git a/main/network/wifi_handler.h b/main/network/wifi_handler.h index 5614e36..31a0f17 100644 --- a/main/network/wifi_handler.h +++ b/main/network/wifi_handler.h @@ -3,7 +3,9 @@ #include "esp_wifi.h" #include "freertos/event_groups.h" -#define WIFI_CONNECTED_BIT (1 << 0) +#define WIFI_STARTED_BIT (1 << 0) +#define WIFI_CONNECTED_BIT (1 << 1) + class WifiHandler { public: @@ -11,16 +13,13 @@ public: // this handler is used to store/retrieve WiFi credentials // should have a unique namespace for WiFi credentials // it will be owned by WifiHandler and deleted in its destructor - KVStorageHandler* kvs + std::unique_ptr kvs ); ~WifiHandler(); - // move semantics - WifiHandler(WifiHandler&& other) noexcept; - - void init(); - esp_err_t connect(const char* ssid, const char* password); - esp_err_t connect(const char* ssid); // connect using stored password + esp_err_t init(); + esp_err_t connect(const std::string& ssid, const std::string& password); + esp_err_t connect(const std::string& ssid); // connect using stored password esp_err_t reconnect(); // reconnect to current SSID void disconnect(); EventBits_t wait_for_connection(TickType_t ticks_to_wait); @@ -37,17 +36,21 @@ private: // prevent copying WifiHandler(const WifiHandler&) = delete; WifiHandler& operator=(const WifiHandler&) = delete; + // prevent moving + WifiHandler(WifiHandler&& other) = delete; + WifiHandler& operator=(WifiHandler&& other) = delete; - char* build_password_key(const char* ssid); - void get_wifi_credentials(char*& ssid, char*& password); + void store_wifi_credentials(const std::string& ssid, const std::string& password); + void get_wifi_credentials(std::string& out_ssid, std::string& out_password); bool initialized = false; - KVStorageHandler* kvs = nullptr; + std::unique_ptr kvs = nullptr; EventGroupHandle_t s_wifi_event_group = 0; SemaphoreHandle_t scan_mutex = nullptr; SemaphoreHandle_t connection_mutex = nullptr; + SemaphoreHandle_t credential_mutex = nullptr; // current connected / preferred SSID - char* current_ssid = nullptr; + std::string current_ssid; // prevent auto-reconnect on expected disconnection, e.g. when user calls disconnect() // should be reset to false after connect() bool expect_disconnected = false; diff --git a/main/touch/touch.cpp b/main/touch/touch.cpp deleted file mode 100644 index e82a7b4..0000000 --- a/main/touch/touch.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "touch.h" -#include "common/constants.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -// TODO: implement actual touch functionality - -TouchHandler::TouchHandler(QueueHandle_t touch_queue) { - (void)touch_queue; -} - -TouchHandler::~TouchHandler() { } - -EInkTouchHandler::EInkTouchHandler(QueueHandle_t touch_queue) - : TouchHandler(touch_queue) { } - -EInkTouchHandler::~EInkTouchHandler() { } - -void EInkTouchHandler::init(EventGroupHandle_t system_event_group) { - if (system_event_group != NULL) { - xEventGroupSetBits(system_event_group, TOUCH_CALIBRATED_BIT); - } -} - -void EInkTouchHandler::start_event_loop() { - // Minimal background task to represent touch processing - xTaskCreate( - // use static adapter and pass `this` as task parameter - EInkTouchHandler::task_adapter, - "touch_task", - 2048, - this, - tskIDLE_PRIORITY + 1, - nullptr - ); -} - -// static -void EInkTouchHandler::task_adapter(void* arg) { - EInkTouchHandler* self = static_cast(arg); - if (self) { - self->run_event_loop(); - } else { - printf("EInkTouchHandler::task_adapter received null pointer\n"); - } - vTaskDelete(NULL); -} - -void EInkTouchHandler::run_event_loop() { - for (;;) { - vTaskDelay(pdMS_TO_TICKS(1000)); - } -} diff --git a/main/touch/touch.h b/main/touch/touch.h deleted file mode 100644 index a06796c..0000000 --- a/main/touch/touch.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_system.h" - -class TouchHandler { -public: - TouchHandler(QueueHandle_t touch_queue); - // the system_event_group is used to set touch-calibrated bit - virtual void init(EventGroupHandle_t system_event_group) = 0; - virtual void start_event_loop() = 0; - virtual ~TouchHandler() = 0; -private: - TouchHandler(const TouchHandler&) = delete; - TouchHandler& operator=(const TouchHandler&) = delete; -}; - -class EInkTouchHandler : public TouchHandler { -public: - EInkTouchHandler(QueueHandle_t touch_queue); - void init(EventGroupHandle_t system_event_group) override; - void start_event_loop() override; - ~EInkTouchHandler() override; - -private: - // Task adapter used for FreeRTOS task creation. Forwards to - // `run_event_loop()` using the `this` pointer passed as the task param. - static void task_adapter(void* arg); - - // Instance method implementing the touch event loop. - void run_event_loop(); - // prevent copying - EInkTouchHandler(const EInkTouchHandler&) = delete; - EInkTouchHandler& operator=(const EInkTouchHandler&) = delete; -};