diff --git a/main/boards/atoms3-echo-base/atoms3_echo_base.cc b/main/boards/atoms3-echo-base/atoms3_echo_base.cc index 2206076..bbff9ed 100644 --- a/main/boards/atoms3-echo-base/atoms3_echo_base.cc +++ b/main/boards/atoms3-echo-base/atoms3_echo_base.cc @@ -106,6 +106,10 @@ private: InitializeGc9107Display(); InitializeButtons(); GetBacklight()->SetBrightness(100); + + // Ensure UI is set up before displaying error + display_->SetupUI(); + display_->SetStatus(Lang::Strings::ERROR); display_->SetEmotion("triangle_exclamation"); display_->SetChatMessage("system", "Echo Base\nnot connected"); diff --git a/main/boards/atoms3r-echo-base/atoms3r_echo_base.cc b/main/boards/atoms3r-echo-base/atoms3r_echo_base.cc index b2ca565..fed0a24 100644 --- a/main/boards/atoms3r-echo-base/atoms3r_echo_base.cc +++ b/main/boards/atoms3r-echo-base/atoms3r_echo_base.cc @@ -173,6 +173,10 @@ private: InitializeGc9107Display(); InitializeButtons(); GetBacklight()->SetBrightness(100); + + // Ensure UI is set up before displaying error + display_->SetupUI(); + display_->SetStatus(Lang::Strings::ERROR); display_->SetEmotion("triangle_exclamation"); display_->SetChatMessage("system", "Echo Base\nnot connected"); diff --git a/main/boards/electron-bot/electron_emoji_display.cc b/main/boards/electron-bot/electron_emoji_display.cc index 4d86f26..5876ca5 100644 --- a/main/boards/electron-bot/electron_emoji_display.cc +++ b/main/boards/electron-bot/electron_emoji_display.cc @@ -15,17 +15,28 @@ ElectronEmojiDisplay::ElectronEmojiDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy) : SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { - InitializeElectronEmojis(); SetupChatLabel(); } +void ElectronEmojiDisplay::SetupUI() { + // Prevent duplicate calls - parent SetupUI() will also check, but check here for early return + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetupUI() called multiple times, skipping duplicate call"); + return; + } + + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + + // Set default emotion after UI is initialized + SetEmotion("staticstate"); +} + void ElectronEmojiDisplay::InitializeElectronEmojis() { ESP_LOGI(TAG, "Electron表情初始化将由Assets系统处理"); // 表情初始化已移至assets系统,通过DEFAULT_EMOJI_COLLECTION=otto-gif配置 // assets.cc会从assets分区加载GIF表情并设置到theme - - // 设置默认表情为staticstate - SetEmotion("staticstate"); + // Note: Default emotion is now set in SetupUI() after LVGL objects are created } void ElectronEmojiDisplay::SetupChatLabel() { diff --git a/main/boards/electron-bot/electron_emoji_display.h b/main/boards/electron-bot/electron_emoji_display.h index 50a289a..da5f945 100644 --- a/main/boards/electron-bot/electron_emoji_display.h +++ b/main/boards/electron-bot/electron_emoji_display.h @@ -15,6 +15,7 @@ class ElectronEmojiDisplay : public SpiLcdDisplay { virtual ~ElectronEmojiDisplay() = default; virtual void SetStatus(const char* status) override; + virtual void SetupUI() override; private: void InitializeElectronEmojis(); diff --git a/main/boards/jiuchuan-s3/jiuchuan_dev_board.cc b/main/boards/jiuchuan-s3/jiuchuan_dev_board.cc index bb832aa..405a95b 100644 --- a/main/boards/jiuchuan-s3/jiuchuan_dev_board.cc +++ b/main/boards/jiuchuan-s3/jiuchuan_dev_board.cc @@ -39,6 +39,13 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); DisplayLockGuard lock(this); @@ -49,7 +56,6 @@ public: lv_obj_align(emoji_box_, LV_ALIGN_CENTER, 0, -50); // 向上偏移50 // 消息栏适配 lv_obj_align(bottom_bar_, LV_ALIGN_BOTTOM_MID, 0, -40); // 向上偏移40 - } }; diff --git a/main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc b/main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc index 07e6d81..a1a993b 100644 --- a/main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc +++ b/main/boards/movecall-moji-esp32s3/movecall_moji_esp32s3.cc @@ -31,6 +31,13 @@ public: bool mirror_y, bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); DisplayLockGuard lock(this); // 由于屏幕是圆的,所以状态栏需要增加左右内边距 diff --git a/main/boards/otto-robot/otto_emoji_display.cc b/main/boards/otto-robot/otto_emoji_display.cc index ed59dbb..b4fedd2 100644 --- a/main/boards/otto-robot/otto_emoji_display.cc +++ b/main/boards/otto-robot/otto_emoji_display.cc @@ -14,13 +14,33 @@ #define TAG "OttoEmojiDisplay" OttoEmojiDisplay::OttoEmojiDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy) : SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { - InitializeOttoEmojis(); - SetupPreviewImage(); SetTheme(LvglThemeManager::GetInstance().GetTheme("dark")); } +void OttoEmojiDisplay::SetupUI() { + // Prevent duplicate calls - parent SetupUI() will also check, but check here for early return + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetupUI() called multiple times, skipping duplicate call"); + return; + } + + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + + // Setup preview image after UI is initialized + DisplayLockGuard lock(this); + lv_obj_set_size(preview_image_, width_ , height_ ); + + // Set default emotion after UI is initialized + SetEmotion("staticstate"); +} + void OttoEmojiDisplay::SetupPreviewImage() { DisplayLockGuard lock(this); + if (preview_image_ == nullptr) { + ESP_LOGW(TAG, "SetupPreviewImage called but preview_image_ is nullptr (UI not initialized yet)"); + return; + } lv_obj_set_size(preview_image_, width_ , height_ ); } @@ -28,9 +48,7 @@ void OttoEmojiDisplay::InitializeOttoEmojis() { ESP_LOGI(TAG, "Otto表情初始化将由Assets系统处理"); // 表情初始化已移至assets系统,通过DEFAULT_EMOJI_COLLECTION=otto-gif配置 // assets.cc会从assets分区加载GIF表情并设置到theme - - // 设置默认表情为staticstate - SetEmotion("staticstate"); + // Note: Default emotion is now set in SetupUI() after LVGL objects are created } LV_FONT_DECLARE(OTTO_ICON_FONT); diff --git a/main/boards/otto-robot/otto_emoji_display.h b/main/boards/otto-robot/otto_emoji_display.h index 207ed8a..0216704 100644 --- a/main/boards/otto-robot/otto_emoji_display.h +++ b/main/boards/otto-robot/otto_emoji_display.h @@ -16,6 +16,7 @@ class OttoEmojiDisplay : public SpiLcdDisplay { virtual ~OttoEmojiDisplay() = default; virtual void SetStatus(const char* status) override; virtual void SetPreviewImage(std::unique_ptr image) override; + virtual void SetupUI() override; private: void InitializeOttoEmojis(); diff --git a/main/boards/sensecap-watcher/sensecap_watcher.cc b/main/boards/sensecap-watcher/sensecap_watcher.cc index 5a1273a..f069591 100644 --- a/main/boards/sensecap-watcher/sensecap_watcher.cc +++ b/main/boards/sensecap-watcher/sensecap_watcher.cc @@ -45,7 +45,14 @@ class CustomLcdDisplay : public SpiLcdDisplay { bool mirror_y, bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { - + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); auto lvgl_theme = static_cast(current_theme_); auto text_font = lvgl_theme->text_font()->font(); diff --git a/main/boards/sp-esp32-s3-1.28-box/sp-esp32-s3-1.28-box.cc b/main/boards/sp-esp32-s3-1.28-box/sp-esp32-s3-1.28-box.cc index 0f5eb45..12822ef 100644 --- a/main/boards/sp-esp32-s3-1.28-box/sp-esp32-s3-1.28-box.cc +++ b/main/boards/sp-esp32-s3-1.28-box/sp-esp32-s3-1.28-box.cc @@ -115,6 +115,13 @@ public: bool mirror_y, bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); DisplayLockGuard lock(this); // 由于屏幕是圆的,所以状态栏需要增加左右内边距 diff --git a/main/boards/waveshare/esp32-c6-lcd-1.69/esp32-c6-lcd-1.69.cc b/main/boards/waveshare/esp32-c6-lcd-1.69/esp32-c6-lcd-1.69.cc index ce8fdf2..09dbf87 100644 --- a/main/boards/waveshare/esp32-c6-lcd-1.69/esp32-c6-lcd-1.69.cc +++ b/main/boards/waveshare/esp32-c6-lcd-1.69/esp32-c6-lcd-1.69.cc @@ -32,6 +32,14 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_obj_set_style_pad_left(status_bar_, LV_HOR_RES * 0.1, 0); lv_obj_set_style_pad_right(status_bar_, LV_HOR_RES * 0.1, 0); diff --git a/main/boards/waveshare/esp32-c6-touch-amoled-1.32/esp32-c6-touch-amoled-1.32.cc b/main/boards/waveshare/esp32-c6-touch-amoled-1.32/esp32-c6-touch-amoled-1.32.cc index 0664b79..a6d5e4e 100644 --- a/main/boards/waveshare/esp32-c6-touch-amoled-1.32/esp32-c6-touch-amoled-1.32.cc +++ b/main/boards/waveshare/esp32-c6-touch-amoled-1.32/esp32-c6-touch-amoled-1.32.cc @@ -61,9 +61,17 @@ class CustomLcdDisplay : public SpiLcdDisplay { CustomLcdDisplay(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t panel_handle, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy), io_handle_(io_handle) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + SetMIRROR_XY(0xC0); // Rotate 180 degrees - this is safe as it only operates on hardware + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_display_add_event_cb(display_, my_draw_event_cb, LV_EVENT_INVALIDATE_AREA, NULL); - SetMIRROR_XY(0xC0); // Rotate 180 degrees lv_obj_invalidate(lv_screen_active()); } }; diff --git a/main/boards/waveshare/esp32-c6-touch-amoled-1.43/esp32-c6-touch-amoled-1.43.cc b/main/boards/waveshare/esp32-c6-touch-amoled-1.43/esp32-c6-touch-amoled-1.43.cc index 0c1a7dd..ae8e145 100644 --- a/main/boards/waveshare/esp32-c6-touch-amoled-1.43/esp32-c6-touch-amoled-1.43.cc +++ b/main/boards/waveshare/esp32-c6-touch-amoled-1.43/esp32-c6-touch-amoled-1.43.cc @@ -54,6 +54,14 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_display_add_event_cb(display_, MyDrawEventCb, LV_EVENT_INVALIDATE_AREA, NULL); } diff --git a/main/boards/waveshare/esp32-c6-touch-amoled-1.8/esp32-c6-touch-amoled-1.8.cc b/main/boards/waveshare/esp32-c6-touch-amoled-1.8/esp32-c6-touch-amoled-1.8.cc index d3f7dc1..5f56386 100644 --- a/main/boards/waveshare/esp32-c6-touch-amoled-1.8/esp32-c6-touch-amoled-1.8.cc +++ b/main/boards/waveshare/esp32-c6-touch-amoled-1.8/esp32-c6-touch-amoled-1.8.cc @@ -83,6 +83,14 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_obj_set_style_pad_left(status_bar_, LV_HOR_RES * 0.1, 0); lv_obj_set_style_pad_right(status_bar_, LV_HOR_RES * 0.1, 0); diff --git a/main/boards/waveshare/esp32-c6-touch-amoled-2.06/esp32-c6-touch-amoled-2.06.cc b/main/boards/waveshare/esp32-c6-touch-amoled-2.06/esp32-c6-touch-amoled-2.06.cc index c9e5c14..4e224d1 100644 --- a/main/boards/waveshare/esp32-c6-touch-amoled-2.06/esp32-c6-touch-amoled-2.06.cc +++ b/main/boards/waveshare/esp32-c6-touch-amoled-2.06/esp32-c6-touch-amoled-2.06.cc @@ -102,6 +102,14 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_obj_set_style_pad_left(status_bar_, LV_HOR_RES* 0.1, 0); lv_obj_set_style_pad_right(status_bar_, LV_HOR_RES* 0.1, 0); diff --git a/main/boards/waveshare/esp32-s3-epaper-1.54/custom_lcd_display.cc b/main/boards/waveshare/esp32-s3-epaper-1.54/custom_lcd_display.cc index 1169c9e..45cc610 100644 --- a/main/boards/waveshare/esp32-s3-epaper-1.54/custom_lcd_display.cc +++ b/main/boards/waveshare/esp32-s3-epaper-1.54/custom_lcd_display.cc @@ -120,9 +120,8 @@ CustomLcdDisplay::CustomLcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_p return; } - ESP_LOGI(TAG, "ui start"); - - SetupUI(); + // Note: SetupUI() should be called by Application::Initialize(), not in constructor + // to ensure lvgl objects are created after the display is fully initialized. } CustomLcdDisplay::~CustomLcdDisplay() { diff --git a/main/boards/waveshare/esp32-s3-rlcd-4.2/custom_lcd_display.cc b/main/boards/waveshare/esp32-s3-rlcd-4.2/custom_lcd_display.cc index 668e27c..ba5d81d 100644 --- a/main/boards/waveshare/esp32-s3-rlcd-4.2/custom_lcd_display.cc +++ b/main/boards/waveshare/esp32-s3-rlcd-4.2/custom_lcd_display.cc @@ -117,9 +117,8 @@ height_(height) return; } - ESP_LOGI(TAG, "ui start"); - - SetupUI(); + // Note: SetupUI() should be called by Application::Initialize(), not in constructor + // to ensure lvgl objects are created after the display is fully initialized. } CustomLcdDisplay::~CustomLcdDisplay() { diff --git a/main/boards/waveshare/esp32-s3-touch-amoled-1.32/esp32-s3-touch-amoled-1.32.cc b/main/boards/waveshare/esp32-s3-touch-amoled-1.32/esp32-s3-touch-amoled-1.32.cc index 148a243..031eaa0 100644 --- a/main/boards/waveshare/esp32-s3-touch-amoled-1.32/esp32-s3-touch-amoled-1.32.cc +++ b/main/boards/waveshare/esp32-s3-touch-amoled-1.32/esp32-s3-touch-amoled-1.32.cc @@ -61,9 +61,17 @@ class CustomLcdDisplay : public SpiLcdDisplay { CustomLcdDisplay(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t panel_handle, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy), io_handle_(io_handle) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + SetMIRROR_XY(0xC0); // Rotate 180 degrees - this is safe as it only operates on hardware + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_display_add_event_cb(display_, my_draw_event_cb, LV_EVENT_INVALIDATE_AREA, NULL); - SetMIRROR_XY(0xC0); // Rotate 180 degrees lv_obj_invalidate(lv_screen_active()); } }; diff --git a/main/boards/waveshare/esp32-s3-touch-amoled-1.75/esp32-s3-touch-amoled-1.75.cc b/main/boards/waveshare/esp32-s3-touch-amoled-1.75/esp32-s3-touch-amoled-1.75.cc index f0c3c31..5abfe73 100644 --- a/main/boards/waveshare/esp32-s3-touch-amoled-1.75/esp32-s3-touch-amoled-1.75.cc +++ b/main/boards/waveshare/esp32-s3-touch-amoled-1.75/esp32-s3-touch-amoled-1.75.cc @@ -107,6 +107,14 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_obj_set_style_pad_left(status_bar_, LV_HOR_RES* 0.1, 0); lv_obj_set_style_pad_right(status_bar_, LV_HOR_RES* 0.1, 0); diff --git a/main/boards/waveshare/esp32-s3-touch-amoled-1.8/esp32-s3-touch-amoled-1.8.cc b/main/boards/waveshare/esp32-s3-touch-amoled-1.8/esp32-s3-touch-amoled-1.8.cc index b0f28f1..5767ac5 100644 --- a/main/boards/waveshare/esp32-s3-touch-amoled-1.8/esp32-s3-touch-amoled-1.8.cc +++ b/main/boards/waveshare/esp32-s3-touch-amoled-1.8/esp32-s3-touch-amoled-1.8.cc @@ -83,6 +83,14 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_obj_set_style_pad_left(status_bar_, LV_HOR_RES * 0.1, 0); lv_obj_set_style_pad_right(status_bar_, LV_HOR_RES * 0.1, 0); diff --git a/main/boards/waveshare/esp32-s3-touch-amoled-2.06/esp32-s3-touch-amoled-2.06.cc b/main/boards/waveshare/esp32-s3-touch-amoled-2.06/esp32-s3-touch-amoled-2.06.cc index a885521..1d48756 100644 --- a/main/boards/waveshare/esp32-s3-touch-amoled-2.06/esp32-s3-touch-amoled-2.06.cc +++ b/main/boards/waveshare/esp32-s3-touch-amoled-2.06/esp32-s3-touch-amoled-2.06.cc @@ -103,6 +103,14 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_obj_set_style_pad_left(status_bar_, LV_HOR_RES* 0.1, 0); lv_obj_set_style_pad_right(status_bar_, LV_HOR_RES* 0.1, 0); diff --git a/main/boards/waveshare/esp32-s3-touch-lcd-1.46/esp32-s3-touch-lcd-1.46.cc b/main/boards/waveshare/esp32-s3-touch-lcd-1.46/esp32-s3-touch-lcd-1.46.cc index 4592b3c..ea8b1b4 100644 --- a/main/boards/waveshare/esp32-s3-touch-lcd-1.46/esp32-s3-touch-lcd-1.46.cc +++ b/main/boards/waveshare/esp32-s3-touch-lcd-1.46/esp32-s3-touch-lcd-1.46.cc @@ -43,6 +43,14 @@ public: bool swap_xy) : SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) { + // Note: UI customization should be done in SetupUI(), not in constructor + // to ensure lvgl objects are created before accessing them + } + + virtual void SetupUI() override { + // Call parent SetupUI() first to create all lvgl objects + SpiLcdDisplay::SetupUI(); + DisplayLockGuard lock(this); lv_display_add_event_cb(display_, rounder_event_cb, LV_EVENT_INVALIDATE_AREA, NULL); } diff --git a/main/boards/waveshare/esp32-s3-touch-lcd-3.49/custom_lcd_display.cc b/main/boards/waveshare/esp32-s3-touch-lcd-3.49/custom_lcd_display.cc index a5a466f..14eeecc 100644 --- a/main/boards/waveshare/esp32-s3-touch-lcd-3.49/custom_lcd_display.cc +++ b/main/boards/waveshare/esp32-s3-touch-lcd-3.49/custom_lcd_display.cc @@ -141,5 +141,7 @@ CustomLcdDisplay::CustomLcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_p if (offset_x != 0 || offset_y != 0) { lv_display_set_offset(display_, offset_x, offset_y); } - SetupUI(); + + // Note: SetupUI() should be called by Application::Initialize(), not in constructor + // to ensure lvgl objects are created after the display is fully initialized. } \ No newline at end of file diff --git a/main/boards/waveshare/esp32-s3-touch-lcd-3.5b/custom_lcd_display.cc b/main/boards/waveshare/esp32-s3-touch-lcd-3.5b/custom_lcd_display.cc index 11d492f..366bf0b 100644 --- a/main/boards/waveshare/esp32-s3-touch-lcd-3.5b/custom_lcd_display.cc +++ b/main/boards/waveshare/esp32-s3-touch-lcd-3.5b/custom_lcd_display.cc @@ -281,6 +281,6 @@ CustomLcdDisplay::CustomLcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_p lv_display_set_offset(display_, offset_x, offset_y); } - - SetupUI(); + // Note: SetupUI() should be called by Application::Initialize(), not in constructor + // to ensure lvgl objects are created after the display is fully initialized. } \ No newline at end of file diff --git a/main/display/display.h b/main/display/display.h index cc34a07..9e73014 100644 --- a/main/display/display.h +++ b/main/display/display.h @@ -40,14 +40,18 @@ public: virtual Theme* GetTheme() { return current_theme_; } virtual void UpdateStatusBar(bool update_all = false); virtual void SetPowerSaveMode(bool on); - virtual void SetupUI() { } + virtual void SetupUI() { + setup_ui_called_ = true; + } inline int width() const { return width_; } inline int height() const { return height_; } + inline bool IsSetupUICalled() const { return setup_ui_called_; } protected: int width_ = 0; int height_ = 0; + bool setup_ui_called_ = false; // Track if SetupUI() has been called Theme* current_theme_ = nullptr; diff --git a/main/display/lcd_display.cc b/main/display/lcd_display.cc index bd79e19..d226052 100644 --- a/main/display/lcd_display.cc +++ b/main/display/lcd_display.cc @@ -352,6 +352,13 @@ void LcdDisplay::Unlock() { #if CONFIG_USE_WECHAT_MESSAGE_STYLE void LcdDisplay::SetupUI() { + // Prevent duplicate calls - if already called, return early + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetupUI() called multiple times, skipping duplicate call"); + return; + } + + Display::SetupUI(); // Mark SetupUI as called DisplayLockGuard lock(this); auto lvgl_theme = static_cast(current_theme_); @@ -495,8 +502,14 @@ void LcdDisplay::SetupUI() { #define MAX_MESSAGES 20 #endif void LcdDisplay::SetChatMessage(const char* role, const char* content) { + if (!setup_ui_called_) { + ESP_LOGW(TAG, "SetChatMessage('%s', '%s') called before SetupUI() - message will be lost!", role, content); + } DisplayLockGuard lock(this); if (content_ == nullptr) { + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetChatMessage('%s', '%s') failed: content_ is nullptr (SetupUI() was called but container not created)", role, content); + } return; } @@ -789,6 +802,13 @@ void LcdDisplay::ClearChatMessages() { } #else void LcdDisplay::SetupUI() { + // Prevent duplicate calls - if already called, return early + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetupUI() called multiple times, skipping duplicate call"); + return; + } + + Display::SetupUI(); // Mark SetupUI as called DisplayLockGuard lock(this); LvglTheme* lvgl_theme = static_cast(current_theme_); auto text_font = lvgl_theme->text_font()->font(); @@ -985,8 +1005,14 @@ void LcdDisplay::SetPreviewImage(std::unique_ptr image) { } void LcdDisplay::SetChatMessage(const char* role, const char* content) { + if (!setup_ui_called_) { + ESP_LOGW(TAG, "SetChatMessage('%s', '%s') called before SetupUI() - message will be lost!", role, content); + } DisplayLockGuard lock(this); if (chat_message_label_ == nullptr) { + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetChatMessage('%s', '%s') failed: chat_message_label_ is nullptr (SetupUI() was called but label not created)", role, content); + } return; } lv_label_set_text(chat_message_label_, content); @@ -1002,6 +1028,9 @@ void LcdDisplay::ClearChatMessages() { #endif void LcdDisplay::SetEmotion(const char* emotion) { + if (!setup_ui_called_) { + ESP_LOGW(TAG, "SetEmotion('%s') called before SetupUI() - emotion will not be displayed!", emotion); + } // Stop any running GIF animation if (gif_controller_) { DisplayLockGuard lock(this); @@ -1010,6 +1039,9 @@ void LcdDisplay::SetEmotion(const char* emotion) { } if (emoji_image_ == nullptr) { + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetEmotion('%s') failed: emoji_image_ is nullptr (SetupUI() was called but emoji image not created)", emotion); + } return; } diff --git a/main/display/lvgl_display/lvgl_display.cc b/main/display/lvgl_display/lvgl_display.cc index 8942a59..25f72e5 100644 --- a/main/display/lvgl_display/lvgl_display.cc +++ b/main/display/lvgl_display/lvgl_display.cc @@ -70,8 +70,14 @@ LvglDisplay::~LvglDisplay() { } void LvglDisplay::SetStatus(const char* status) { + if (!setup_ui_called_) { + ESP_LOGW(TAG, "SetStatus('%s') called before SetupUI() - message will be lost!", status); + } DisplayLockGuard lock(this); if (status_label_ == nullptr) { + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetStatus('%s') failed: status_label_ is nullptr (SetupUI() was called but label not created)", status); + } return; } lv_label_set_text(status_label_, status); @@ -86,8 +92,14 @@ void LvglDisplay::ShowNotification(const std::string ¬ification, int duration } void LvglDisplay::ShowNotification(const char* notification, int duration_ms) { + if (!setup_ui_called_) { + ESP_LOGW(TAG, "ShowNotification('%s') called before SetupUI() - message will be lost!", notification); + } DisplayLockGuard lock(this); if (notification_label_ == nullptr) { + if (setup_ui_called_) { + ESP_LOGW(TAG, "ShowNotification('%s') failed: notification_label_ is nullptr (SetupUI() was called but label not created)", notification); + } return; } lv_label_set_text(notification_label_, notification); diff --git a/main/display/oled_display.cc b/main/display/oled_display.cc index 7446914..2083a50 100644 --- a/main/display/oled_display.cc +++ b/main/display/oled_display.cc @@ -76,6 +76,18 @@ OledDisplay::OledDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handl return; } + // Note: SetupUI() should be called by Application::Initialize(), not in constructor + // to ensure lvgl objects are created after the display is fully initialized. +} + +void OledDisplay::SetupUI() { + // Prevent duplicate calls - if already called, return early + if (setup_ui_called_) { + ESP_LOGW(TAG, "SetupUI() called multiple times, skipping duplicate call"); + return; + } + + Display::SetupUI(); // Mark SetupUI as called if (height_ == 64) { SetupUI_128x64(); } else { diff --git a/main/display/oled_display.h b/main/display/oled_display.h index c7f4077..40dd184 100644 --- a/main/display/oled_display.h +++ b/main/display/oled_display.h @@ -32,6 +32,7 @@ public: OledDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, int width, int height, bool mirror_x, bool mirror_y); ~OledDisplay(); + virtual void SetupUI() override; virtual void SetChatMessage(const char* role, const char* content) override; virtual void SetEmotion(const char* emotion) override; virtual void SetTheme(Theme* theme) override; diff --git a/main/idf_component.yml b/main/idf_component.yml index 539badb..3a80f4c 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -24,7 +24,7 @@ dependencies: espressif/esp_audio_codec: ~2.4.1 78/esp-ml307: ~3.6.4 78/uart-eth-modem: - version: ~0.3.2 + version: ~0.3.3 rules: - if: target not in [esp32] 78/xiaozhi-fonts: ~1.6.0