Refractored epd handler

This commit is contained in:
GW_MC
2026-01-28 17:35:49 +08:00
parent 38d5facc24
commit fc79e92660
7 changed files with 619 additions and 786 deletions

View File

@@ -44,252 +44,6 @@ void init_queues(
void random_draw_rect(
LVGLHandler* lvgl_handler
) {
// Draw a random rectangle on the display using LVGL every 2 seconds until 100 rectangles have been drawn
static int rect_count = 0;
do {
rect_count++;
LVGLHandler* handler = lvgl_handler;
if (handler == nullptr) {
return;
}
lvgl_port_lock(pdMS_TO_TICKS(5000));
// Create a random rectangle
lv_obj_t* rect = lv_obj_create(lv_scr_act());
int x = esp_random() % (DISPLAY_WIDTH - 50);
int y = esp_random() % (DISPLAY_HEIGHT - 50);
lv_obj_set_pos(rect, x, y);
int w = 20 + (esp_random() % 100);
int h = 20 + (esp_random() % 100);
lv_obj_set_size(rect, w, h);
// white or black fill for the rect
bool is_white = (esp_random() % 2 == 0);
lv_obj_set_style_bg_color(rect, is_white ? lv_color_hex(0xFFFFFF) : lv_color_hex(0x000000), 0);
lvgl_port_unlock();
ESP_LOGI(TAG, "Drawn %s rectangle %d at (%d,%d) size (%d x %d)",
is_white ? "white" : "black",
rect_count, x, y, w, h);
// Schedule next rectangle draw
vTaskDelay(pdMS_TO_TICKS(2000));
} while (rect_count < 100);
}
void EInk_Checkerboard(
EInkDisplayHandler* display_handler
) {
struct CheckerboardTaskParams {
EInkDisplayHandler* display_handler;
};
auto checkerboard_task_fn = [](void* pvParameters) {
CheckerboardTaskParams* params = static_cast<CheckerboardTaskParams*>(pvParameters);
if (params != nullptr && params->display_handler != nullptr) {
// Add this task to the watchdog timer
esp_err_t wdt_err = esp_task_wdt_add(NULL);
if (wdt_err != ESP_OK) {
ESP_LOGW(TAG, "Failed to add checkerboard task to watchdog: %s", esp_err_to_name(wdt_err));
}
EInkDisplayHandler* display_handler = params->display_handler;
const size_t DISPLAY_BUFFER_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT / 8;
uint8_t* framebuffer = new uint8_t[DISPLAY_BUFFER_SIZE];
if (framebuffer == nullptr) {
ESP_LOGE(TAG, "Failed to allocate framebuffer for checkerboard task");
if (wdt_err == ESP_OK) {
esp_task_wdt_delete(NULL);
}
vTaskDelete(NULL);
return;
}
// Create checkerboard pattern
for (size_t y = 0; y < DISPLAY_HEIGHT; y++) {
for (size_t x = 0; x < DISPLAY_WIDTH; x++) {
size_t byte_index = (y * DISPLAY_WIDTH + x) / 8;
size_t bit_index = 7 - (x % 8);
bool is_white = ((x / 20) % 2) == ((y / 20) % 2);
if (is_white) {
framebuffer[byte_index] |= (1 << bit_index); // Set bit to 1 for white
} else {
framebuffer[byte_index] &= ~(1 << bit_index); // Clear bit to 0 for black
}
}
// Yield and reset watchdog periodically
const size_t YIELD_INTERVAL = 16;
if (y % YIELD_INTERVAL == 0) {
if (wdt_err == ESP_OK) {
esp_task_wdt_reset();
}
vTaskDelay(1 / portTICK_PERIOD_MS);
// partial refresh to show progress
int32_t y_start = static_cast<int32_t>((y >= YIELD_INTERVAL - 1) ? (y - (YIELD_INTERVAL - 1)) : 0);
int32_t y_end = static_cast<int32_t>(y);
// get the partial framebuffer for this area
uint8_t* partial_framebuffer = &framebuffer[y_start * (DISPLAY_WIDTH / 8)];
esp_err_t err = display_handler->partial_refresh(partial_framebuffer, RefreshArea { 0, y_start, DISPLAY_WIDTH - 1, y_end });
if (err != ESP_OK) {
ESP_LOGE(TAG, "Partial refresh failed at y=%d: %s", y, esp_err_to_name(err));
}
// wait for 4 seconds to prevent spamming the display
// vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
// Perform full write to display
// esp_err_t err = display_handler->full_write(framebuffer);
// if (err != ESP_OK) {
// ESP_LOGE(TAG, "Checkerboard full write failed: %s", esp_err_to_name(err));
// } else {
// ESP_LOGI(TAG, "Checkerboard pattern displayed successfully.");
// }
delete[] framebuffer;
// Remove task from watchdog before deletion
if (wdt_err == ESP_OK) {
esp_task_wdt_delete(NULL);
}
} else {
ESP_LOGE(TAG, "Invalid parameters for checkerboard task");
}
vTaskDelete(NULL);
};
CheckerboardTaskParams* checker_params = new CheckerboardTaskParams();
checker_params->display_handler = display_handler;
BaseType_t res = xTaskCreate(
checkerboard_task_fn,
"checkerboard_task",
8192,
static_cast<void*>(checker_params),
tskIDLE_PRIORITY + 1,
NULL
);
if (res != pdPASS) {
ESP_LOGE(TAG, "Failed to create checkerboard task");
delete checker_params;
}
}
void LVGL_Checkerboard(
LVGLHandler* lvgl_handler
) {
struct CheckerboardTaskParams {
LVGLHandler* lvgl_handler;
};
auto checkerboard_task_fn = [](void* pvParameters) {
CheckerboardTaskParams* params = static_cast<CheckerboardTaskParams*>(pvParameters);
if (params == nullptr || params->lvgl_handler == nullptr) {
ESP_LOGE(TAG, "Invalid parameters for LVGL checkerboard task");
delete params;
vTaskDelete(NULL);
return;
}
auto* handler = static_cast<LVGLHandler*>(params->lvgl_handler);
// Add safety checks
if (!handler) {
ESP_LOGE("LVGL", "Handler is null!");
delete params;
vTaskDelete(NULL);
return;
}
ESP_LOGI("HEAP", "Free: %d", esp_get_free_heap_size());
// Wait for LVGL system to fully initialize
vTaskDelay(pdMS_TO_TICKS(200));
// Acquire LVGL lock with proper timeout
if (!lvgl_port_lock(pdMS_TO_TICKS(5000))) {
ESP_LOGE(TAG, "Failed to acquire LVGL lock for checkerboard");
delete params;
vTaskDelete(NULL);
return;
}
// Verify LVGL is properly initialized
if (lv_display_get_default() == nullptr) {
ESP_LOGE(TAG, "LVGL default display not available");
lvgl_port_unlock();
delete params;
vTaskDelete(NULL);
return;
}
// Create LVGL objects for checkerboard
lv_obj_t* scr = lv_scr_act();
if (scr == nullptr) {
ESP_LOGE(TAG, "Failed to get active LVGL screen");
lvgl_port_unlock();
delete params;
vTaskDelete(NULL);
return;
}
lv_obj_t* checkerboard = lv_obj_create(scr);
if (checkerboard == nullptr) {
ESP_LOGE(TAG, "Failed to create LVGL checkerboard object");
lvgl_port_unlock();
delete params;
vTaskDelete(NULL);
return;
}
lv_obj_set_size(checkerboard, DISPLAY_WIDTH, DISPLAY_HEIGHT);
// remove border and padding
lv_obj_set_style_pad_all(checkerboard, 0, 0);
lv_obj_set_style_border_width(checkerboard, 0, 0);
const int CELL_SIZE = 40;
lvgl_port_unlock();
// Create checkerboard pattern using LVGL
for (int y = 0; y < DISPLAY_HEIGHT; y += CELL_SIZE) {
lvgl_port_lock(pdMS_TO_TICKS(1000));
for (int x = 0; x < DISPLAY_WIDTH; x += CELL_SIZE) {
lv_color_t color = (((x / CELL_SIZE) % 2) == ((y / CELL_SIZE) % 2)) ? lv_color_hex(0xFFFFFF) : lv_color_hex(0x000000);
lv_obj_t* cell = lv_obj_create(checkerboard);
if (cell == nullptr) {
ESP_LOGE(TAG, "Failed to create LVGL checkerboard cell");
lvgl_port_unlock();
continue;
}
lv_obj_set_size(cell, CELL_SIZE, CELL_SIZE);
lv_obj_set_style_bg_color(cell, color, 0);
lv_obj_set_pos(cell, x, y);
// remove border and padding
lv_obj_set_style_pad_all(cell, 0, 0);
lv_obj_set_style_border_width(cell, 0, 0);
lv_obj_t* label = lv_label_create(cell);
if (label != nullptr) {
lv_label_set_text_fmt(label, "(%d,%d)", x, y);
lv_obj_center(label);
}
}
lvgl_port_unlock();
// Yield to allow LVGL to process rendering
vTaskDelay(500 / portTICK_PERIOD_MS);
}
ESP_LOGI(TAG, "LVGL Checkerboard pattern displayed successfully.");
delete params;
vTaskDelete(NULL);
};
CheckerboardTaskParams* checker_params = new CheckerboardTaskParams();
checker_params->lvgl_handler = lvgl_handler;
BaseType_t res = xTaskCreate(
checkerboard_task_fn,
"lvgl_checkerboard_task0",
8192,
static_cast<void*>(checker_params),
tskIDLE_PRIORITY + 1,
NULL
);
if (res != pdPASS) {
ESP_LOGE(TAG, "Failed to create LVGL checkerboard task");
delete checker_params;
}
}
void app_main(void) {
display_chip_info();
@@ -306,14 +60,14 @@ void app_main(void) {
ESP_LOGI(TAG, "Queues initialized.\n");
//
KVStorageHandler* kv_storage_handler = new NVSStorageHandler(
DEFAULT_STORAGE_NAMESPACE
);
// KVStorageHandler* kv_storage_handler = new NVSStorageHandler(
// DEFAULT_STORAGE_NAMESPACE
// );
auto wifi_handler = std::make_unique<WifiHandler>(
std::unique_ptr<KVStorageHandler>(new NVSStorageHandler(WIFI_CREDENTIALS_STORAGE_NAMESPACE))
);
NetworkHandler* network_handler = new NetworkHandler(std::move(wifi_handler));
// auto wifi_handler = std::make_unique<WifiHandler>(
// std::unique_ptr<KVStorageHandler>(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);
@@ -330,8 +84,8 @@ void app_main(void) {
}
//
kv_storage_handler->init(system_event_group);
network_handler->init(system_event_group);
// kv_storage_handler->init(system_event_group);
// network_handler->init(system_event_group);
//
ESP_LOGI(TAG, "Waiting for system to be ready...\n");
@@ -349,36 +103,60 @@ void app_main(void) {
// Allow LVGL system to stabilize before creating objects
vTaskDelay(pdMS_TO_TICKS(100));
// Show checkerboard pattern on display for testing
// EInk_Checkerboard(display_handler);
// LVGL_Checkerboard(&lvgl_handler);
// Create main screen and button for random rectangle demo
lv_obj_t* scr = lv_scr_act();
// Register apps with AppRegistry by creating their descriptors
// Each descriptor will create and register the app instance
// DemoAppDescriptor* demo_descriptor = new DemoAppDescriptor();
// ShutdownAppDescriptor* shutdown_descriptor = new ShutdownAppDescriptor();
DiscordAppDescriptor::instance(); // Use singleton pattern for Discord app
// MtrAppDescriptor* mtr_descriptor = new MtrAppDescriptor();
// 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);
// Pass network handler to MtrApp so it can fetch arrival data
// MtrApp* mtr_app = dynamic_cast<MtrApp*>(mtr_descriptor->get_app_instance());
// if (mtr_app) {
// mtr_app->set_network_handler(network_handler);
// }
// 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);
ESP_LOGI(TAG, "Apps registered with AppRegistry\n");
// Event handler for button - creates random rectangles
auto btn_event_cb = [](lv_event_t* e) {
lv_obj_t* scr = lv_scr_act();
// Initialize UI Handler (will render app icons from registry)
UIHandler ui_handler;
if (ui_handler.init() != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize UI handler");
vTaskDelay(5000 / portTICK_PERIOD_MS);
return esp_restart();
}
ESP_LOGI(TAG, "UI handler initialized successfully\n");
ESP_LOGI(TAG, "Main screen displayed with app icons. Tap an icon to launch an app.\n");
// 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_obj_t*>(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");