#include #include #include #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_chip_info.h" #include "esp_flash.h" #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/eink_display_handler.h" #include "display/lvgl_handler.h" #include "ui/ui_handler.h" #include "ui/app_registry.h" #include "ui/apps/shutdown_app.h" #include "ui/apps/discord_app.h" #include "ui/apps/mtr_app.h" #include #include "esp_lvgl_port.h" #include "lvgl.h" #include "network.h" #include #include "lvgl.h" // nvs storage namespaces, 15 characters max #define DEFAULT_STORAGE_NAMESPACE "storage" #define WIFI_CREDENTIALS_STORAGE_NAMESPACE "wifi_cred" #define TAG "Main" extern "C" void app_main(void); void init_queues( QueueHandle_t& touch_queue, EventGroupHandle_t& system_event_group, EventGroupHandle_t& system_lifecycle_event_group ); void app_main(void) { display_chip_info(); QueueHandle_t touch_event_queue = NULL; EventGroupHandle_t system_event_group = NULL, system_lifecycle_event_group = NULL; init_queues(touch_event_queue, system_event_group, system_lifecycle_event_group); if (touch_event_queue == NULL || system_event_group == NULL || system_lifecycle_event_group == NULL) { ESP_LOGE("Main", "Failed to create one or more queues/event groups"); vTaskDelay(5000 / portTICK_PERIOD_MS); return esp_restart(); } ESP_LOGI(TAG, "Queues initialized.\n"); // KVStorageHandler* kv_storage_handler = new NVSStorageHandler( DEFAULT_STORAGE_NAMESPACE ); auto wifi_handler = std::make_unique( std::unique_ptr(new NVSStorageHandler(WIFI_CREDENTIALS_STORAGE_NAMESPACE)) ); NetworkHandler* network_handler = new NetworkHandler(std::move(wifi_handler)); EInkDisplayHandler* display_handler = new EInkDisplayHandler(); // Initialize display and touch // display_handler->init_devices(system_event_group); display_handler->init_devices(); ESP_LOGI(TAG, "E-Ink display handler initialized.\n"); // LVGL Handler std::unique_ptr display_uptr(display_handler); LVGLHandler lvgl_handler(std::move(display_uptr)); esp_err_t err = lvgl_handler.initLVGL(system_event_group); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize LVGL handler: %s", esp_err_to_name(err)); vTaskDelay(5000 / portTICK_PERIOD_MS); return esp_restart(); } // kv_storage_handler->init(system_event_group); network_handler->init(system_event_group); // ESP_LOGI(TAG, "Waiting for system to be ready...\n"); xEventGroupWaitBits( system_event_group, // DISPLAY_READY_BIT | STORAGE_READY_BIT | NETWORK_READY_BIT, DISPLAY_READY_BIT, // do not clear on exit, require explicit reset pdFALSE, pdTRUE, portMAX_DELAY ); ESP_LOGI(TAG, "System is ready. Starting main application...\n"); DiscordAppDescriptor::instance(); UIHandler ui_handler; err = ui_handler.init(); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize UI handler: %s", esp_err_to_name(err)); vTaskDelay(5000 / portTICK_PERIOD_MS); return esp_restart(); } ESP_LOGI(TAG, "UI handler initialized.\n"); // Allow LVGL system to stabilize before creating objects vTaskDelay(pdMS_TO_TICKS(100)); // Create main screen and button for random rectangle demo // lv_obj_t* scr = lv_scr_act(); // // Create a button // lv_obj_t* btn = lv_btn_create(scr); // lv_obj_set_size(btn, 200, 60); // lv_obj_align(btn, LV_ALIGN_TOP_MID, 0, 20); // lv_obj_set_style_border_width(btn, 2, 0); // lv_obj_set_style_border_color(btn, lv_color_make(0, 0, 0), 0); // // Add label to button // lv_obj_t* label = lv_label_create(btn); // lv_label_set_text(label, "Create Random Rect"); // lv_obj_center(label); // lv_obj_set_style_text_color(label, lv_color_make(0, 0, 0), 0); // // Event handler for button - creates random rectangles // auto btn_event_cb = [](lv_event_t* e) { // lv_obj_t* scr = lv_scr_act(); // // Create a random rectangle // lv_obj_t* rect = lv_obj_create(scr); // // Random size (30-100 pixels) // lv_coord_t width = 30 + (esp_random() % 70); // lv_coord_t height = 30 + (esp_random() % 70); // lv_obj_set_size(rect, width, height); // // Random position (avoid top 100px where button is) // lv_coord_t x = esp_random() % (LV_HOR_RES - width); // lv_coord_t y = 100 + (esp_random() % (LV_VER_RES - 100 - height)); // lv_obj_set_pos(rect, x, y); // lv_obj_set_style_bg_color(rect, lv_color_make(0, 0, 0), 0); // lv_obj_set_style_bg_opa(rect, LV_OPA_COVER, 0); // // Make rectangle clickable // lv_obj_add_flag(rect, LV_OBJ_FLAG_CLICKABLE); // // Event handler to delete rectangle when clicked // auto rect_event_cb = [](lv_event_t* e) { // lv_obj_t* rect = static_cast(lv_event_get_target(e)); // lv_obj_del(rect); // ESP_LOGI(TAG, "Rectangle deleted"); // }; // lv_obj_add_event_cb(rect, rect_event_cb, LV_EVENT_CLICKED, NULL); // ESP_LOGI(TAG, "Created rectangle at (%d, %d) with size %dx%d", x, y, width, height); // }; // lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); // ESP_LOGI(TAG, "Random rectangle demo initialized. Tap button to create rectangles.\n"); // 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, // do not clear on exit, require explicit reset pdFALSE, pdFALSE, portMAX_DELAY ); ESP_LOGI(TAG, "Shutdown signal received. Cleaning up...\n"); // Show shutdown screen using the shutdown descriptor's app instance // ShutdownApp* shutdown_app = dynamic_cast(shutdown_descriptor->get_app_instance()); // if (shutdown_app) { // ui_handler.switch_app(shutdown_app); // } vTaskDelay(1000 / portTICK_PERIOD_MS); // Display shutdown message briefly // Cleanup // ui_handler.deinit(); // delete demo_descriptor; // delete shutdown_descriptor; // delete mtr_descriptor; vEventGroupDelete(system_event_group); vQueueDelete(touch_event_queue); ESP_LOGI(TAG, "Cleanup complete.\n"); // handle shutdown or restart if (bits & SYSTEM_SHUTDOWN_BIT) { // 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( system_lifecycle_event_group, SYSTEM_START_BIT, pdFALSE, pdFALSE, portMAX_DELAY ); if (bits & SYSTEM_START_BIT) { ESP_LOGI(TAG, "SYSTEM_START_BIT received, restarting system.\n"); } else { 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) { // 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 { ESP_LOGW(TAG, "Unknown shutdown signal received. Restarting by default.\n"); fflush(stdout); } return esp_restart(); } void init_queues( QueueHandle_t& touch_queue, EventGroupHandle_t& system_event_group, EventGroupHandle_t& system_lifecycle_event_group ) { // Implementation of queue initialization touch_queue = xQueueCreate(10, sizeof(touch_event_t)); system_event_group = xEventGroupCreate(); system_lifecycle_event_group = xEventGroupCreate(); }