Compare commits

2 Commits

6 changed files with 52 additions and 20 deletions

View File

@@ -26,6 +26,10 @@ LVGLHandler::~LVGLHandler() {
lv_draw_buf_destroy(lvgl_draw_buf_); lv_draw_buf_destroy(lvgl_draw_buf_);
lvgl_draw_buf_ = nullptr; lvgl_draw_buf_ = nullptr;
} }
if (lvgl_draw_buf_2_ != nullptr) {
lv_draw_buf_destroy(lvgl_draw_buf_2_);
lvgl_draw_buf_2_ = nullptr;
}
} }
esp_err_t LVGLHandler::initLVGL(EventGroupHandle_t system_event_group) { esp_err_t LVGLHandler::initLVGL(EventGroupHandle_t system_event_group) {
@@ -217,17 +221,31 @@ esp_err_t LVGLHandler::initLVGLDisplay_() {
return ESP_FAIL; return ESP_FAIL;
} }
// Create a draw buffer covering the entire display // Create two draw buffers for double buffering to improve performance
lvgl_draw_buf_ = lv_draw_buf_create(DISPLAY_WIDTH, DISPLAY_HEIGHT, LV_COLOR_FORMAT_I1, LV_STRIDE_AUTO); lvgl_draw_buf_ = lv_draw_buf_create(DISPLAY_WIDTH, DISPLAY_HEIGHT, LV_COLOR_FORMAT_I1, LV_STRIDE_AUTO);
if (lvgl_draw_buf_ == nullptr) { if (lvgl_draw_buf_ == nullptr) {
ESP_LOGE(TAG, "Failed to create LVGL draw buffer"); ESP_LOGE(TAG, "Failed to create LVGL draw buffer 1");
lv_display_delete(lvgl_display_); lv_display_delete(lvgl_display_);
lvgl_display_ = nullptr; lvgl_display_ = nullptr;
lvgl_port_unlock(); lvgl_port_unlock();
return ESP_FAIL; return ESP_FAIL;
} }
lv_display_set_draw_buffers(lvgl_display_, lvgl_draw_buf_, nullptr);
lvgl_draw_buf_2_ = lv_draw_buf_create(DISPLAY_WIDTH, DISPLAY_HEIGHT, LV_COLOR_FORMAT_I1, LV_STRIDE_AUTO);
if (lvgl_draw_buf_2_ == nullptr) {
ESP_LOGE(TAG, "Failed to create LVGL draw buffer 2");
lv_draw_buf_destroy(lvgl_draw_buf_);
lvgl_draw_buf_ = nullptr;
lv_display_delete(lvgl_display_);
lvgl_display_ = nullptr;
lvgl_port_unlock();
return ESP_FAIL;
}
// Set both buffers for double buffering
lv_display_set_draw_buffers(lvgl_display_, lvgl_draw_buf_, lvgl_draw_buf_2_);
lv_display_set_render_mode(lvgl_display_, LV_DISPLAY_RENDER_MODE); lv_display_set_render_mode(lvgl_display_, LV_DISPLAY_RENDER_MODE);
// //
// Configure LVGL display // Configure LVGL display
lv_display_set_color_format(lvgl_display_, LV_COLOR_FORMAT_I1); lv_display_set_color_format(lvgl_display_, LV_COLOR_FORMAT_I1);

View File

@@ -31,4 +31,5 @@ private:
lv_display_t* lvgl_display_ = nullptr; lv_display_t* lvgl_display_ = nullptr;
lv_indev_t* lvgl_touch_indev_ = nullptr; lv_indev_t* lvgl_touch_indev_ = nullptr;
lv_draw_buf_t* lvgl_draw_buf_ = nullptr; lv_draw_buf_t* lvgl_draw_buf_ = nullptr;
lv_draw_buf_t* lvgl_draw_buf_2_ = nullptr;
}; };

View File

@@ -4,6 +4,7 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_lvgl_port.h" #include "esp_lvgl_port.h"
#define LVGL_LOCK_TIMEOUT 2000 // milliseconds
static const char* TAG = "MainUI"; static const char* TAG = "MainUI";
MainUI::~MainUI() { MainUI::~MainUI() {
@@ -34,7 +35,7 @@ void MainUI::create_ui_(lv_obj_t* parent) {
return; return;
} }
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) { if (!lvgl_port_lock(pdMS_TO_TICKS(LVGL_LOCK_TIMEOUT))) {
ESP_LOGE(TAG, "Failed to acquire LVGL lock for UI creation"); ESP_LOGE(TAG, "Failed to acquire LVGL lock for UI creation");
return; return;
} }
@@ -83,10 +84,13 @@ void MainUI::create_ui_(lv_obj_t* parent) {
// Mute button // Mute button
mute_button_ = lv_btn_create(center_container); mute_button_ = lv_btn_create(center_container);
lv_obj_set_size(mute_button_, 200, 60); lv_obj_set_size(mute_button_, 200, 60);
lv_obj_set_style_bg_color(mute_button_, lv_color_white(), 0);
lv_obj_set_style_border_color(mute_button_, lv_color_black(), 0);
lv_obj_set_style_border_width(mute_button_, 2, 0);
lv_obj_t* mute_label = lv_label_create(mute_button_); lv_obj_t* mute_label = lv_label_create(mute_button_);
lv_label_set_text(mute_label, "MUTE"); lv_label_set_text(mute_label, "MUTE");
lv_obj_center(mute_label); lv_obj_center(mute_label);
lv_obj_set_style_text_color(mute_label, lv_color_black(), 0);
// === Bottom Section: Settings and Config Prompt === // === Bottom Section: Settings and Config Prompt ===
lv_obj_t* bottom_container = lv_obj_create(parent); lv_obj_t* bottom_container = lv_obj_create(parent);
@@ -105,10 +109,14 @@ void MainUI::create_ui_(lv_obj_t* parent) {
// Settings button (right side) // Settings button (right side)
settings_button_ = lv_btn_create(bottom_container); settings_button_ = lv_btn_create(bottom_container);
lv_obj_set_size(settings_button_, 60, 60); lv_obj_set_size(settings_button_, 60, 60);
lv_obj_set_style_bg_color(settings_button_, lv_color_white(), 0);
lv_obj_set_style_border_color(settings_button_, lv_color_black(), 0);
lv_obj_set_style_border_width(settings_button_, 2, 0);
lv_obj_t* settings_icon = lv_label_create(settings_button_); lv_obj_t* settings_icon = lv_label_create(settings_button_);
lv_label_set_text(settings_icon, LV_SYMBOL_SETTINGS); lv_label_set_text(settings_icon, LV_SYMBOL_SETTINGS);
lv_obj_center(settings_icon); lv_obj_center(settings_icon);
lv_obj_set_style_text_color(settings_icon, lv_color_black(), 0);
ESP_LOGI(TAG, "Main UI created"); ESP_LOGI(TAG, "Main UI created");
lvgl_port_unlock(); lvgl_port_unlock();
@@ -130,14 +138,14 @@ esp_err_t MainUI::register_on_mute_button_clicked(lv_event_cb_t cb, void* user_d
return ESP_OK; return ESP_OK;
} }
void MainUI::update_status(VoiceState state) { bool MainUI::update_status(VoiceState state) {
if (!status_icon_label_ || !status_text_label_) { if (!status_icon_label_ || !status_text_label_) {
return; return false;
} }
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) { if (!lvgl_port_lock(pdMS_TO_TICKS(LVGL_LOCK_TIMEOUT))) {
ESP_LOGW(TAG, "Failed to acquire LVGL lock for status update"); ESP_LOGW(TAG, "Failed to acquire LVGL lock for status update");
return; return false;
} }
switch (state) { switch (state) {
@@ -167,6 +175,7 @@ void MainUI::update_status(VoiceState state) {
break; break;
} }
lvgl_port_unlock(); lvgl_port_unlock();
return true;
} }
void MainUI::show_error_notification(bool show) { void MainUI::show_error_notification(bool show) {
@@ -174,7 +183,7 @@ void MainUI::show_error_notification(bool show) {
return; return;
} }
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) { if (!lvgl_port_lock(pdMS_TO_TICKS(LVGL_LOCK_TIMEOUT))) {
ESP_LOGW(TAG, "Failed to acquire LVGL lock for error notification update"); ESP_LOGW(TAG, "Failed to acquire LVGL lock for error notification update");
return; return;
} }
@@ -191,7 +200,7 @@ void MainUI::update_config_prompt(bool configured) {
return; return;
} }
if (!lvgl_port_lock(pdMS_TO_TICKS(100))) { if (!lvgl_port_lock(pdMS_TO_TICKS(LVGL_LOCK_TIMEOUT))) {
ESP_LOGW(TAG, "Failed to acquire LVGL lock for config prompt update"); ESP_LOGW(TAG, "Failed to acquire LVGL lock for config prompt update");
return; return;
} }

View File

@@ -56,7 +56,7 @@ public:
* @brief Update status display with current voice state * @brief Update status display with current voice state
* @param state Current voice state * @param state Current voice state
*/ */
void update_status(VoiceState state); bool update_status(VoiceState state);
/** /**
* @brief Show or hide error notification banner * @brief Show or hide error notification banner

View File

@@ -106,7 +106,7 @@ void MainUIHandler::send_mute_command_() {
} }
void MainUIHandler::on_status_update_(StatusUpdateEventData data) { void MainUIHandler::on_status_update_(StatusUpdateEventData data) {
ESP_LOGI(TAG, "on_status_update_ called with state: %d", data.state); ESP_LOGI(TAG, "on_status_update_ called with state: %d, current_state_: %d", data.state, current_state_);
// Update state in thread-safe manner // Update state in thread-safe manner
bool update_ui = false; bool update_ui = false;
@@ -114,7 +114,6 @@ void MainUIHandler::on_status_update_(StatusUpdateEventData data) {
if (data.state != current_state_) { if (data.state != current_state_) {
update_ui = true; update_ui = true;
} }
current_state_ = data.state;
xSemaphoreGive(state_mutex_); xSemaphoreGive(state_mutex_);
ESP_LOGI(TAG, "State updated in mutex"); ESP_LOGI(TAG, "State updated in mutex");
} }
@@ -125,18 +124,18 @@ void MainUIHandler::on_status_update_(StatusUpdateEventData data) {
return; return;
} }
ESP_LOGI(TAG, "Calling update_ui_()"); ESP_LOGI(TAG, "Calling update_ui_()");
update_ui_(); update_ui_(&data.state);
ESP_LOGI(TAG, "on_status_update_ complete"); ESP_LOGI(TAG, "on_status_update_ complete");
} }
void MainUIHandler::update_ui_() { void MainUIHandler::update_ui_(StatusUpdateEventData::VoiceState* state_ptr) {
ESP_LOGI(TAG, "update_ui_ called"); ESP_LOGI(TAG, "update_ui_ called");
if (main_ui_) { if (main_ui_) {
StatusUpdateEventData::VoiceState state; StatusUpdateEventData::VoiceState state;
if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) { if (state_mutex_ && xSemaphoreTake(state_mutex_, pdMS_TO_TICKS(100)) == pdTRUE) {
state = current_state_; state = state_ptr ? *state_ptr : current_state_;
xSemaphoreGive(state_mutex_); xSemaphoreGive(state_mutex_);
} else { } else {
state = StatusUpdateEventData::VoiceState::UNKNOWN; state = StatusUpdateEventData::VoiceState::UNKNOWN;
@@ -164,7 +163,13 @@ void MainUIHandler::update_ui_() {
ESP_LOGI(TAG, "Calling main_ui_->update_status() with ui_state: %d", ui_state); ESP_LOGI(TAG, "Calling main_ui_->update_status() with ui_state: %d", ui_state);
// Lock LVGL before calling UI functions from another task // Lock LVGL before calling UI functions from another task
main_ui_->update_status(ui_state); bool success = main_ui_->update_status(ui_state);
if (!success) {
ESP_LOGW(TAG, "main_ui_->update_status() failed");
} else {
// Update current state only on successful UI update
current_state_ = state;
}
ESP_LOGI(TAG, "main_ui_->update_status() returned"); ESP_LOGI(TAG, "main_ui_->update_status() returned");
} }

View File

@@ -35,7 +35,7 @@ private:
void on_mute_button_clicked_(); void on_mute_button_clicked_();
void on_status_update_(StatusUpdateEventData data); void on_status_update_(StatusUpdateEventData data);
void send_mute_command_(); void send_mute_command_();
void update_ui_(); void update_ui_(StatusUpdateEventData::VoiceState* state = nullptr);
std::unique_ptr<MainUI> main_ui_ = nullptr; std::unique_ptr<MainUI> main_ui_ = nullptr;
std::unique_ptr<IotDisBridge> bridge_ = nullptr; std::unique_ptr<IotDisBridge> bridge_ = nullptr;
@@ -49,4 +49,3 @@ private:
lv_event_cb_t on_settings_callback_ = nullptr; lv_event_cb_t on_settings_callback_ = nullptr;
void* settings_callback_user_data_ = nullptr; void* settings_callback_user_data_ = nullptr;
}; };