From 8755a658a8598ed530b5aba178f46f4435c04ac8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=B9=8F?= <52451470+txp666@users.noreply.github.com> Date: Sun, 7 Jun 2026 16:08:46 +0800 Subject: [PATCH] fix(electron-bot): show chat subtitle on display (#2042) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Enhance Otto Robot camera support by adding configuration for OV3660. Updated config.h to define camera types and GPIO settings, modified config.json to include new camera options, and refactored otto_robot.cc for improved camera detection and initialization logic. * fix: 移除 OttoEmojiDisplay 构造函数中的 SetTheme 调用以修复 LoadProhibited 崩溃 Made-with: Cursor * refactor: improve audio service error handling and codec timeout management - Updated AudioService to prevent input task termination on read timeout, introducing a delay instead. - Enhanced NoAudioCodec to implement a read timeout for I2S channel reads. - Adjusted WebSocketControlServer to set a control port for improved socket management. - Added manufacturer information to the config.json for waveshare ESP32-Touch-LCD-3.5. * fix(otto): WebSocket direct clients not receiving MCP responses When a browser connects directly to the WebSocket control server (port 8080) and sends a JSON-RPC request, the MCP response was routed through Application::SendMcpMessage -> protocol_->SendMcpMessage, which sends it to the cloud protocol channel. As a result, the direct WebSocket client never received the response, while the WeChat mini-program could because it communicates via the cloud. Fix: - Add BroadcastMessage() to WebSocketControlServer, using httpd_queue_work + httpd_ws_send_frame_async to asynchronously send responses back to all connected clients on port 8080 - Add RegisterMcpBroadcastCallback() to Application, allowing an additional MCP send callback to be registered; SendMcpMessage() now invokes it alongside the cloud protocol - Register the broadcast callback in OttoRobot after the WebSocket server starts successfully Also add WebSocket direct-connect API documentation to README.md with complete JSON-RPC 2.0 command examples. * fix(otto-robot): migrate camera backend and set safe dark default theme - Migrate `otto-robot` camera backend from `EspVideo` to `Esp32Camera` to improve capture stability after reboot/power cycle. - Keep runtime sensor detection for both OV2640 and OV3660, and rename PID macros with `OTTO_` prefix to avoid symbol conflicts. - Configure camera output as `RGB565 + FRAMESIZE_240X240` to match the 240x240 display. - Rotate OV2640 output by 180 degrees (`VFlip + HMirror`) for correct orientation. - Simplify `otto-robot` camera sdkconfig options by keeping only: - `CONFIG_CAMERA_OV2640=y` - `CONFIG_CAMERA_OV3660=y` - Move Otto default dark theme setup into `OttoEmojiDisplay::SetupUI()` (after base UI init) to avoid boot-time LVGL null-object crash caused by calling `SetTheme()` too early. * feat(motion): smooth action ending and adaptive homing on otto/electron-bot Improve servo motion transitions to reduce abrupt returns to home pose. Replace linear interpolation with ease-out movement, make homing duration adaptive to angle delta, and skip intermediate homing when queued actions are pending to keep multi-action sequences fluid. * fix(electron-bot): show chat subtitle on display ElectronEmojiDisplay overrode SetupChatLabel() to delete the parent's chat_message_label_ (which lives inside bottom_bar_) and recreate it on the bottom-layer container_ without alignment. As a result the real subtitle label was hidden behind emoji_box_/top_bar_, while SetChatMessage() only toggled the now-empty bottom_bar_, so chat subtitles were never visible. Reuse the parent label created in bottom_bar_ (matching otto-robot) and only switch to the dark theme in SetupUI(), removing the custom SetupChatLabel(). Co-authored-by: Cursor * fix(electron-bot): restore status icons on standby and clean up display - SetStatus() did not re-show network_label_/battery_label_ when returning to STANDBY, so the WiFi and battery icons stayed hidden after listening or speaking. Restore them on standby to match otto-robot. - Remove the unused InitializeElectronEmojis() method and its declaration. - Drop unused includes (vector, assets.h, emoji_collection.h, lvgl_image.h) and add the missing trailing newline in the header. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- .../electron-bot/electron_emoji_display.cc | 43 ++++--------------- .../electron-bot/electron_emoji_display.h | 6 +-- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/main/boards/electron-bot/electron_emoji_display.cc b/main/boards/electron-bot/electron_emoji_display.cc index 7020a354..a5b0b956 100644 --- a/main/boards/electron-bot/electron_emoji_display.cc +++ b/main/boards/electron-bot/electron_emoji_display.cc @@ -3,12 +3,8 @@ #include #include -#include -#include "assets.h" #include "assets/lang_config.h" -#include "display/lvgl_display/emoji_collection.h" -#include "display/lvgl_display/lvgl_image.h" #include "display/lvgl_display/lvgl_theme.h" #define TAG "ElectronEmojiDisplay" @@ -24,43 +20,20 @@ void ElectronEmojiDisplay::SetupUI() { return; } - // Call parent SetupUI() first to create all lvgl objects (including container_) + // Call parent SetupUI() first to create all lvgl objects (including container_, + // bottom_bar_ and chat_message_label_) SpiLcdDisplay::SetupUI(); - // Setup chat label after parent UI is initialized so that container_ is valid - SetupChatLabel(); + // UI 对象创建完成后切换主题(复用父类创建好的字幕标签,不再重建) + auto* dark_theme = LvglThemeManager::GetInstance().GetTheme("dark"); + if (dark_theme != nullptr) { + SetTheme(dark_theme); + } // 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 - // Note: Default emotion is now set in SetupUI() after LVGL objects are created -} - -void ElectronEmojiDisplay::SetupChatLabel() { - // Create/recreate the chat label under the display lock - { - DisplayLockGuard lock(this); - - if (chat_message_label_) { - lv_obj_del(chat_message_label_); - } - - chat_message_label_ = lv_label_create(container_); - lv_label_set_text(chat_message_label_, ""); - lv_obj_set_width(chat_message_label_, width_ * 0.9); - lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); - lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_set_style_text_color(chat_message_label_, lv_color_white(), 0); - } - // SetTheme acquires DisplayLockGuard internally, so call it after releasing the lock above - SetTheme(LvglThemeManager::GetInstance().GetTheme("dark")); -} - LV_FONT_DECLARE(OTTO_ICON_FONT); void ElectronEmojiDisplay::SetStatus(const char* status) { auto lvgl_theme = static_cast(current_theme_); @@ -94,6 +67,8 @@ void ElectronEmojiDisplay::SetStatus(const char* status) { lv_obj_set_style_text_font(status_label_, text_font, 0); lv_label_set_text(status_label_, ""); lv_obj_clear_flag(status_label_, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(network_label_, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(battery_label_, LV_OBJ_FLAG_HIDDEN); return; } diff --git a/main/boards/electron-bot/electron_emoji_display.h b/main/boards/electron-bot/electron_emoji_display.h index da5f945c..73be59a5 100644 --- a/main/boards/electron-bot/electron_emoji_display.h +++ b/main/boards/electron-bot/electron_emoji_display.h @@ -16,8 +16,4 @@ class ElectronEmojiDisplay : public SpiLcdDisplay { virtual ~ElectronEmojiDisplay() = default; virtual void SetStatus(const char* status) override; virtual void SetupUI() override; - - private: - void InitializeElectronEmojis(); - void SetupChatLabel(); -}; \ No newline at end of file +};