diff --git a/CMakeLists.txt b/CMakeLists.txt index ea8ffd0f..dccdb48e 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) -set(PROJECT_VER "1.1.2") +set(PROJECT_VER "1.1.9") # Add this line to disable the specific warning add_compile_options(-Wno-missing-field-initializers) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 3873cbc5..2590469a 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -13,8 +13,6 @@ set(SOURCES "audio_codecs/audio_codec.cc" "display/ssd1306_display.cc" "boards/lilygo-t-circle-s3/esp_lcd_gc9d01n.c" "protocols/protocol.cc" - "protocols/mqtt_protocol.cc" - "protocols/websocket_protocol.cc" "iot/thing.cc" "iot/thing_manager.cc" "system_info.cc" @@ -107,6 +105,12 @@ endif() file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc) list(APPEND SOURCES ${BOARD_SOURCES}) +if(CONFIG_CONNECTION_TYPE_MQTT_UDP) + list(APPEND SOURCES "protocols/mqtt_protocol.cc") +elseif(CONFIG_CONNECTION_TYPE_WEBSOCKET) + list(APPEND SOURCES "protocols/websocket_protocol.cc") +endif() + if(CONFIG_USE_AUDIO_PROCESSING) list(APPEND SOURCES "audio_processing/audio_processor.cc" "audio_processing/wake_word_detect.cc") endif() @@ -119,8 +123,10 @@ if(CONFIG_IDF_TARGET_ESP32) "audio_codecs/es8388_audio_codec.cc") endif() +file(GLOB ASSETS ${CMAKE_CURRENT_SOURCE_DIR}/assets/zh/*.p3) + idf_component_register(SRCS ${SOURCES} - EMBED_FILES "assets/err_reg.p3" "assets/err_pin.p3" "assets/wificonfig.p3" "assets/upgrade.p3" + EMBED_FILES ${ASSETS} INCLUDE_DIRS ${INCLUDE_DIRS} WHOLE_ARCHIVE ) diff --git a/main/application.cc b/main/application.cc index c0ba33d7..8ffd4efa 100644 --- a/main/application.cc +++ b/main/application.cc @@ -8,6 +8,7 @@ #include "websocket_protocol.h" #include "font_awesome_symbols.h" #include "iot/thing_manager.h" +#include "assets/zh/binary.h" #include #include @@ -17,14 +18,6 @@ #define TAG "Application" -extern const char p3_err_reg_start[] asm("_binary_err_reg_p3_start"); -extern const char p3_err_reg_end[] asm("_binary_err_reg_p3_end"); -extern const char p3_err_pin_start[] asm("_binary_err_pin_p3_start"); -extern const char p3_err_pin_end[] asm("_binary_err_pin_p3_end"); -extern const char p3_wificonfig_start[] asm("_binary_wificonfig_p3_start"); -extern const char p3_wificonfig_end[] asm("_binary_wificonfig_p3_end"); -extern const char p3_upgrade_start[] asm("_binary_upgrade_p3_start"); -extern const char p3_upgrade_end[] asm("_binary_upgrade_p3_end"); static const char* const STATE_STRINGS[] = { "unknown", @@ -35,6 +28,7 @@ static const char* const STATE_STRINGS[] = { "listening", "speaking", "upgrading", + "activating", "fatal_error", "invalid_state" }; @@ -42,9 +36,6 @@ static const char* const STATE_STRINGS[] = { Application::Application() { event_group_ = xEventGroupCreate(); background_task_ = new BackgroundTask(4096 * 8); - - ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL); - ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str()); } Application::~Application() { @@ -61,13 +52,9 @@ void Application::CheckNewVersion() { ota_.SetPostData(board.GetJson()); while (true) { - bool success = ota_.CheckVersion(); - if (ota_.HasActivationCode()) { - DisplayActivationCode(); - } - if (success) { + if (ota_.CheckVersion()) { if (ota_.HasNewVersion()) { - Alert("Info", "正在升级固件"); + Alert("OTA 升级", "正在升级系统", "happy", std::string(p3_upgrade_start, p3_upgrade_end - p3_upgrade_start)); // Wait for the chat state to be idle do { vTaskDelay(pdMS_TO_TICKS(3000)); @@ -78,7 +65,7 @@ void Application::CheckNewVersion() { SetDeviceState(kDeviceStateUpgrading); display->SetIcon(FONT_AWESOME_DOWNLOAD); - display->SetStatus("新版本 " + ota_.GetFirmwareVersion()); + display->SetChatMessage("system", "新版本 " + ota_.GetFirmwareVersion()); board.SetPowerSaveMode(false); #if CONFIG_USE_AUDIO_PROCESSING @@ -100,20 +87,29 @@ void Application::CheckNewVersion() { ota_.StartUpgrade([display](int progress, size_t speed) { char buffer[64]; snprintf(buffer, sizeof(buffer), "%d%% %zuKB/s", progress, speed / 1024); - display->SetStatus(buffer); + display->SetChatMessage("system", buffer); }); // If upgrade success, the device will reboot and never reach here display->SetStatus("更新失败"); ESP_LOGI(TAG, "Firmware upgrade failed..."); vTaskDelay(pdMS_TO_TICKS(3000)); - esp_restart(); + Reboot(); }); } else { ota_.MarkCurrentVersionValid(); display->ShowNotification("版本 " + ota_.GetCurrentVersion()); + + // Check if the activation code is valid + if (ota_.HasActivationCode()) { + SetDeviceState(kDeviceStateActivating); + ShowActivationCode(); + } else { + SetDeviceState(kDeviceStateIdle); + display->SetChatMessage("system", ""); + return; + } } - return; } // Check again in 60 seconds @@ -121,31 +117,53 @@ void Application::CheckNewVersion() { } } -void Application::DisplayActivationCode() { - ESP_LOGW(TAG, "Activation Message: %s", ota_.GetActivationMessage().c_str()); - ESP_LOGW(TAG, "Activation Code: %s", ota_.GetActivationCode().c_str()); - auto display = Board::GetInstance().GetDisplay(); - display->ShowNotification(ota_.GetActivationMessage(), 30000); +void Application::ShowActivationCode() { + auto& message = ota_.GetActivationMessage(); + auto& code = ota_.GetActivationCode(); + + struct digit_sound { + char digit; + const char* sound_data_start; + const char* sound_data_end; + }; + digit_sound digit_sounds[] = { + {'0', p3_0_start, p3_0_end}, + {'1', p3_1_start, p3_1_end}, + {'2', p3_2_start, p3_2_end}, + {'3', p3_3_start, p3_3_end}, + {'4', p3_4_start, p3_4_end}, + {'5', p3_5_start, p3_5_end}, + {'6', p3_6_start, p3_6_end}, + {'7', p3_7_start, p3_7_end}, + {'8', p3_8_start, p3_8_end}, + {'9', p3_9_start, p3_9_end}, + }; + std::string sound = std::string(p3_activation_start, p3_activation_end - p3_activation_start); + for (const auto& digit : code) { + auto it = std::find_if(digit_sounds, digit_sounds + sizeof(digit_sounds) / sizeof(digit_sound), + [digit](const digit_sound& ds) { return ds.digit == digit; }); + if (it != digit_sounds + sizeof(digit_sounds) / sizeof(digit_sound)) { + sound += std::string(it->sound_data_start, it->sound_data_end - it->sound_data_start); + } + } + Alert("激活设备", message, "happy", sound); } -void Application::Alert(const std::string& title, const std::string& message) { - ESP_LOGW(TAG, "Alert: %s, %s", title.c_str(), message.c_str()); +void Application::Alert(const std::string& status, const std::string& message, const std::string& emotion, const std::string& sound) { + ESP_LOGW(TAG, "Alert %s: %s [%s]", status.c_str(), message.c_str(), emotion.c_str()); auto display = Board::GetInstance().GetDisplay(); - display->ShowNotification(message); - - if (message == "进入配网模式") { - PlayLocalFile(p3_wificonfig_start, p3_wificonfig_end - p3_wificonfig_start); - } else if (message == "正在升级固件") { - PlayLocalFile(p3_upgrade_start, p3_upgrade_end - p3_upgrade_start); - } else if (message == "请插入SIM卡") { - PlayLocalFile(p3_err_pin_start, p3_err_pin_end - p3_err_pin_start); - } else if (message == "无法接入网络,请检查流量卡状态") { - PlayLocalFile(p3_err_reg_start, p3_err_reg_end - p3_err_reg_start); + display->SetStatus(status); + display->SetEmotion(emotion); + display->SetChatMessage("system", message); + if (!sound.empty()) { + PlayLocalFile(sound.data(), sound.size()); } } void Application::PlayLocalFile(const char* data, size_t size) { ESP_LOGI(TAG, "PlayLocalFile: %zu bytes", size); + auto codec = Board::GetInstance().GetAudioCodec(); + codec->EnableOutput(true); SetDecodeSampleRate(16000); for (const char* p = data; p < data + size; ) { auto p3 = (BinaryProtocol3*)p; @@ -164,6 +182,11 @@ void Application::PlayLocalFile(const char* data, size_t size) { void Application::ToggleChatState() { Schedule([this]() { + if (device_state_ == kDeviceStateActivating) { + Reboot(); + return; + } + if (!protocol_) { ESP_LOGE(TAG, "Protocol not initialized"); return; @@ -172,7 +195,7 @@ void Application::ToggleChatState() { if (device_state_ == kDeviceStateIdle) { SetDeviceState(kDeviceStateConnecting); if (!protocol_->OpenAudioChannel()) { - Alert("Error", "Failed to open audio channel"); + Alert("ERROR", "无法建立音频通道"); SetDeviceState(kDeviceStateIdle); return; } @@ -190,6 +213,11 @@ void Application::ToggleChatState() { void Application::StartListening() { Schedule([this]() { + if (device_state_ == kDeviceStateActivating) { + Reboot(); + return; + } + if (!protocol_) { ESP_LOGE(TAG, "Protocol not initialized"); return; @@ -201,7 +229,7 @@ void Application::StartListening() { SetDeviceState(kDeviceStateConnecting); if (!protocol_->OpenAudioChannel()) { SetDeviceState(kDeviceStateIdle); - Alert("Error", "Failed to open audio channel"); + Alert("ERROR", "无法建立音频通道"); return; } } @@ -275,14 +303,14 @@ void Application::Start() { board.StartNetwork(); // Initialize the protocol - display->SetStatus("初始化协议"); + display->SetStatus("加载协议..."); #ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET protocol_ = std::make_unique(); #else protocol_ = std::make_unique(); #endif protocol_->OnNetworkError([this](const std::string& message) { - Alert("Error", std::move(message)); + Alert("ERROR", message); }); protocol_->OnIncomingAudio([this](std::vector&& data) { std::lock_guard lock(mutex_); @@ -371,6 +399,9 @@ void Application::Start() { }); // Check for new firmware version or get the MQTT broker address + ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL); + ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str()); + ota_.SetHeader("Client-Id", board.GetUuid()); xTaskCreate([](void* arg) { Application* app = (Application*)arg; app->CheckNewVersion(); @@ -664,3 +695,8 @@ void Application::UpdateIotStates() { protocol_->SendIotStates(states); } } + +void Application::Reboot() { + ESP_LOGI(TAG, "Rebooting..."); + esp_restart(); +} \ No newline at end of file diff --git a/main/application.h b/main/application.h index 2b8552e7..c7321c16 100644 --- a/main/application.h +++ b/main/application.h @@ -35,6 +35,7 @@ enum DeviceState { kDeviceStateListening, kDeviceStateSpeaking, kDeviceStateUpgrading, + kDeviceStateActivating, kDeviceStateFatalError }; @@ -55,12 +56,13 @@ public: bool IsVoiceDetected() const { return voice_detected_; } void Schedule(std::function callback); void SetDeviceState(DeviceState state); - void Alert(const std::string& title, const std::string& message); + void Alert(const std::string& status, const std::string& message, const std::string& emotion = "", const std::string& sound = ""); void AbortSpeaking(AbortReason reason); void ToggleChatState(); void StartListening(); void StopListening(); void UpdateIotStates(); + void Reboot(); private: Application(); @@ -100,7 +102,7 @@ private: void ResetDecoder(); void SetDecodeSampleRate(int sample_rate); void CheckNewVersion(); - void DisplayActivationCode(); + void ShowActivationCode(); void PlayLocalFile(const char* data, size_t size); }; diff --git a/main/assets/err_pin.p3 b/main/assets/err_pin.p3 deleted file mode 100644 index f00ce48b..00000000 Binary files a/main/assets/err_pin.p3 and /dev/null differ diff --git a/main/assets/err_reg.p3 b/main/assets/err_reg.p3 deleted file mode 100644 index af60b680..00000000 Binary files a/main/assets/err_reg.p3 and /dev/null differ diff --git a/main/assets/upgrade.p3 b/main/assets/upgrade.p3 deleted file mode 100644 index 4447f26a..00000000 Binary files a/main/assets/upgrade.p3 and /dev/null differ diff --git a/main/assets/wificonfig.p3 b/main/assets/wificonfig.p3 deleted file mode 100644 index 54d0db58..00000000 Binary files a/main/assets/wificonfig.p3 and /dev/null differ diff --git a/main/assets/zh/0.p3 b/main/assets/zh/0.p3 new file mode 100644 index 00000000..ec909323 Binary files /dev/null and b/main/assets/zh/0.p3 differ diff --git a/main/assets/zh/1.p3 b/main/assets/zh/1.p3 new file mode 100644 index 00000000..18935e7a Binary files /dev/null and b/main/assets/zh/1.p3 differ diff --git a/main/assets/zh/2.p3 b/main/assets/zh/2.p3 new file mode 100644 index 00000000..f391e4b0 Binary files /dev/null and b/main/assets/zh/2.p3 differ diff --git a/main/assets/zh/3.p3 b/main/assets/zh/3.p3 new file mode 100644 index 00000000..c2564814 Binary files /dev/null and b/main/assets/zh/3.p3 differ diff --git a/main/assets/zh/4.p3 b/main/assets/zh/4.p3 new file mode 100644 index 00000000..108bd24f Binary files /dev/null and b/main/assets/zh/4.p3 differ diff --git a/main/assets/zh/5.p3 b/main/assets/zh/5.p3 new file mode 100644 index 00000000..20146980 Binary files /dev/null and b/main/assets/zh/5.p3 differ diff --git a/main/assets/zh/6.p3 b/main/assets/zh/6.p3 new file mode 100644 index 00000000..ddbec49e Binary files /dev/null and b/main/assets/zh/6.p3 differ diff --git a/main/assets/zh/7.p3 b/main/assets/zh/7.p3 new file mode 100644 index 00000000..2f6f6161 Binary files /dev/null and b/main/assets/zh/7.p3 differ diff --git a/main/assets/zh/8.p3 b/main/assets/zh/8.p3 new file mode 100644 index 00000000..4532d108 Binary files /dev/null and b/main/assets/zh/8.p3 differ diff --git a/main/assets/zh/9.p3 b/main/assets/zh/9.p3 new file mode 100644 index 00000000..e1f147ad Binary files /dev/null and b/main/assets/zh/9.p3 differ diff --git a/main/assets/zh/activation.p3 b/main/assets/zh/activation.p3 new file mode 100644 index 00000000..013d499e Binary files /dev/null and b/main/assets/zh/activation.p3 differ diff --git a/main/assets/zh/binary.h b/main/assets/zh/binary.h new file mode 100644 index 00000000..9407fd9e --- /dev/null +++ b/main/assets/zh/binary.h @@ -0,0 +1,39 @@ +#ifndef BINARY_H +#define BINARY_H + +extern const char p3_err_reg_start[] asm("_binary_err_reg_p3_start"); +extern const char p3_err_reg_end[] asm("_binary_err_reg_p3_end"); +extern const char p3_err_pin_start[] asm("_binary_err_pin_p3_start"); +extern const char p3_err_pin_end[] asm("_binary_err_pin_p3_end"); +extern const char p3_wificonfig_start[] asm("_binary_wificonfig_p3_start"); +extern const char p3_wificonfig_end[] asm("_binary_wificonfig_p3_end"); +extern const char p3_upgrade_start[] asm("_binary_upgrade_p3_start"); +extern const char p3_upgrade_end[] asm("_binary_upgrade_p3_end"); +extern const char p3_activation_start[] asm("_binary_activation_p3_start"); +extern const char p3_activation_end[] asm("_binary_activation_p3_end"); +extern const char p3_welcome_start[] asm("_binary_welcome_p3_start"); +extern const char p3_welcome_end[] asm("_binary_welcome_p3_end"); + +extern const char p3_0_start[] asm("_binary_0_p3_start"); +extern const char p3_0_end[] asm("_binary_0_p3_end"); +extern const char p3_1_start[] asm("_binary_1_p3_start"); +extern const char p3_1_end[] asm("_binary_1_p3_end"); +extern const char p3_2_start[] asm("_binary_2_p3_start"); +extern const char p3_2_end[] asm("_binary_2_p3_end"); +extern const char p3_3_start[] asm("_binary_3_p3_start"); +extern const char p3_3_end[] asm("_binary_3_p3_end"); +extern const char p3_4_start[] asm("_binary_4_p3_start"); +extern const char p3_4_end[] asm("_binary_4_p3_end"); +extern const char p3_5_start[] asm("_binary_5_p3_start"); +extern const char p3_5_end[] asm("_binary_5_p3_end"); +extern const char p3_6_start[] asm("_binary_6_p3_start"); +extern const char p3_6_end[] asm("_binary_6_p3_end"); +extern const char p3_7_start[] asm("_binary_7_p3_start"); +extern const char p3_7_end[] asm("_binary_7_p3_end"); +extern const char p3_8_start[] asm("_binary_8_p3_start"); +extern const char p3_8_end[] asm("_binary_8_p3_end"); +extern const char p3_9_start[] asm("_binary_9_p3_start"); +extern const char p3_9_end[] asm("_binary_9_p3_end"); + + +#endif \ No newline at end of file diff --git a/main/assets/zh/err_pin.p3 b/main/assets/zh/err_pin.p3 new file mode 100644 index 00000000..bf4d8190 Binary files /dev/null and b/main/assets/zh/err_pin.p3 differ diff --git a/main/assets/zh/err_reg.p3 b/main/assets/zh/err_reg.p3 new file mode 100644 index 00000000..cf316fa2 Binary files /dev/null and b/main/assets/zh/err_reg.p3 differ diff --git a/main/assets/zh/sounds.tar.gz b/main/assets/zh/sounds.tar.gz new file mode 100644 index 00000000..16a3bf09 Binary files /dev/null and b/main/assets/zh/sounds.tar.gz differ diff --git a/main/assets/zh/upgrade.p3 b/main/assets/zh/upgrade.p3 new file mode 100644 index 00000000..cb382f83 Binary files /dev/null and b/main/assets/zh/upgrade.p3 differ diff --git a/main/assets/zh/welcome.p3 b/main/assets/zh/welcome.p3 new file mode 100644 index 00000000..c018b54a Binary files /dev/null and b/main/assets/zh/welcome.p3 differ diff --git a/main/assets/zh/wificonfig.p3 b/main/assets/zh/wificonfig.p3 new file mode 100644 index 00000000..330fe99c Binary files /dev/null and b/main/assets/zh/wificonfig.p3 differ diff --git a/main/background_task.cc b/main/background_task.cc index af4a5522..6d526f96 100644 --- a/main/background_task.cc +++ b/main/background_task.cc @@ -20,7 +20,10 @@ BackgroundTask::~BackgroundTask() { void BackgroundTask::Schedule(std::function callback) { std::lock_guard lock(mutex_); if (active_tasks_ >= 30) { - ESP_LOGW(TAG, "active_tasks_ == %u", active_tasks_.load()); + int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); + if (free_sram < 10000) { + ESP_LOGW(TAG, "active_tasks_ == %u, free_sram == %u", active_tasks_.load(), free_sram); + } } active_tasks_++; main_tasks_.emplace_back([this, cb = std::move(callback)]() { diff --git a/main/boards/common/ml307_board.cc b/main/boards/common/ml307_board.cc index 8a1a97b0..11b14386 100644 --- a/main/boards/common/ml307_board.cc +++ b/main/boards/common/ml307_board.cc @@ -3,6 +3,7 @@ #include "application.h" #include "display.h" #include "font_awesome_symbols.h" +#include "assets/zh/binary.h" #include #include @@ -24,7 +25,7 @@ std::string Ml307Board::GetBoardType() { void Ml307Board::StartNetwork() { auto display = Board::GetInstance().GetDisplay(); - display->SetStatus("初始化模块"); + display->SetStatus("检测模组..."); modem_.SetDebug(false); modem_.SetBaudRate(921600); @@ -47,10 +48,10 @@ void Ml307Board::WaitForNetworkReady() { display->SetStatus("等待网络..."); int result = modem_.WaitForNetworkReady(); if (result == -1) { - application.Alert("Error", "请插入SIM卡"); + application.Alert("PIN_ERROR", "请插入SIM卡", "sad", std::string(p3_err_pin_start, p3_err_pin_end - p3_err_pin_start)); return; } else if (result == -2) { - application.Alert("Error", "无法接入网络,请检查流量卡状态"); + application.Alert("REG_ERROR", "无法接入网络,请检查流量卡状态", "sad", std::string(p3_err_reg_start, p3_err_reg_end - p3_err_reg_start)); return; } diff --git a/main/boards/common/wifi_board.cc b/main/boards/common/wifi_board.cc index a0865ffc..d24b86f5 100644 --- a/main/boards/common/wifi_board.cc +++ b/main/boards/common/wifi_board.cc @@ -5,6 +5,7 @@ #include "system_info.h" #include "font_awesome_symbols.h" #include "settings.h" +#include "assets/zh/binary.h" #include #include @@ -37,7 +38,6 @@ std::string WifiBoard::GetBoardType() { void WifiBoard::EnterWifiConfigMode() { auto& application = Application::GetInstance(); - auto display = Board::GetInstance().GetDisplay(); application.SetDeviceState(kDeviceStateWifiConfiguring); auto& wifi_ap = WifiConfigurationAp::GetInstance(); @@ -49,11 +49,9 @@ void WifiBoard::EnterWifiConfigMode() { hint += wifi_ap.GetSsid(); hint += ",然后打开浏览器访问 "; hint += wifi_ap.GetWebServerUrl(); - - display->SetStatus(hint); // 播报配置 WiFi 的提示 - application.Alert("Info", "进入配网模式"); + application.Alert("配网模式", hint, "", std::string(p3_wificonfig_start, p3_wificonfig_end - p3_wificonfig_start)); // Wait forever until reset after configuration while (true) { @@ -83,11 +81,11 @@ void WifiBoard::StartNetwork() { auto& wifi_station = WifiStation::GetInstance(); wifi_station.OnScanBegin([this]() { auto display = Board::GetInstance().GetDisplay(); - display->ShowNotification("正在扫描 WiFi 网络", 30000); + display->ShowNotification("扫描 WiFi...", 30000); }); wifi_station.OnConnect([this](const std::string& ssid) { auto display = Board::GetInstance().GetDisplay(); - display->ShowNotification(std::string("正在连接 ") + ssid, 30000); + display->ShowNotification(std::string("连接 ") + ssid + "...", 30000); }); wifi_station.OnConnected([this](const std::string& ssid) { auto display = Board::GetInstance().GetDisplay(); diff --git a/main/display/display.cc b/main/display/display.cc index 23070d21..e4172b42 100644 --- a/main/display/display.cc +++ b/main/display/display.cc @@ -210,6 +210,11 @@ void Display::SetIcon(const char* icon) { } void Display::SetChatMessage(const std::string &role, const std::string &content) { + DisplayLockGuard lock(this); + if (chat_message_label_ == nullptr) { + return; + } + lv_label_set_text(chat_message_label_, content.c_str()); } void Display::SetBacklight(uint8_t brightness) { diff --git a/main/display/display.h b/main/display/display.h index effe679b..7bab9460 100644 --- a/main/display/display.h +++ b/main/display/display.h @@ -42,6 +42,7 @@ protected: lv_obj_t *notification_label_ = nullptr; lv_obj_t *mute_label_ = nullptr; lv_obj_t *battery_label_ = nullptr; + lv_obj_t* chat_message_label_ = nullptr; const char* battery_icon_ = nullptr; const char* network_icon_ = nullptr; bool muted_ = false; diff --git a/main/display/lcd_display.cc b/main/display/lcd_display.cc index 0279a3ac..68e13fe3 100644 --- a/main/display/lcd_display.cc +++ b/main/display/lcd_display.cc @@ -274,14 +274,6 @@ void LcdDisplay::SetupUI() { lv_obj_set_style_text_font(battery_label_, fonts_.icon_font, 0); } -void LcdDisplay::SetChatMessage(const std::string &role, const std::string &content) { - DisplayLockGuard lock(this); - if (chat_message_label_ == nullptr) { - return; - } - lv_label_set_text(chat_message_label_, content.c_str()); -} - void LcdDisplay::SetEmotion(const std::string &emotion) { struct Emotion { const char* icon; diff --git a/main/display/lcd_display.h b/main/display/lcd_display.h index dc72d1c9..f4fba4de 100644 --- a/main/display/lcd_display.h +++ b/main/display/lcd_display.h @@ -26,7 +26,6 @@ protected: lv_obj_t* content_ = nullptr; lv_obj_t* container_ = nullptr; lv_obj_t* side_bar_ = nullptr; - lv_obj_t* chat_message_label_ = nullptr; DisplayFonts fonts_; @@ -46,7 +45,6 @@ public: DisplayFonts fonts); ~LcdDisplay(); - virtual void SetChatMessage(const std::string &role, const std::string &content) override; virtual void SetEmotion(const std::string &emotion) override; virtual void SetIcon(const char* icon) override; virtual void SetBacklight(uint8_t brightness) override; diff --git a/main/display/ssd1306_display.cc b/main/display/ssd1306_display.cc index 57268f03..4c18c7b4 100644 --- a/main/display/ssd1306_display.cc +++ b/main/display/ssd1306_display.cc @@ -132,6 +132,20 @@ void Ssd1306Display::Unlock() { lvgl_port_unlock(); } +void Ssd1306Display::SetChatMessage(const std::string &role, const std::string &content) { + DisplayLockGuard lock(this); + if (content_right_ == nullptr) { + lv_label_set_text(chat_message_label_, content.c_str()); + } else { + if (content.empty()) { + lv_obj_add_flag(content_right_, LV_OBJ_FLAG_HIDDEN); + } else { + lv_label_set_text(chat_message_label_, content.c_str()); + lv_obj_clear_flag(content_right_, LV_OBJ_FLAG_HIDDEN); + } + } +} + void Ssd1306Display::SetupUI_128x64() { DisplayLockGuard lock(this); @@ -149,20 +163,46 @@ void Ssd1306Display::SetupUI_128x64() { /* Status bar */ status_bar_ = lv_obj_create(container_); - lv_obj_set_size(status_bar_, LV_HOR_RES, 18); + lv_obj_set_size(status_bar_, LV_HOR_RES, 16); + lv_obj_set_style_border_width(status_bar_, 0, 0); + lv_obj_set_style_pad_all(status_bar_, 0, 0); lv_obj_set_style_radius(status_bar_, 0, 0); /* Content */ content_ = lv_obj_create(container_); lv_obj_set_scrollbar_mode(content_, LV_SCROLLBAR_MODE_OFF); - lv_obj_set_style_radius(status_bar_, 0, 0); + lv_obj_set_style_radius(content_, 0, 0); + lv_obj_set_style_pad_all(content_, 0, 0); lv_obj_set_width(content_, LV_HOR_RES); lv_obj_set_flex_grow(content_, 1); + lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_ROW); + lv_obj_set_style_flex_main_place(content_, LV_FLEX_ALIGN_CENTER, 0); - emotion_label_ = lv_label_create(content_); + // 创建左侧固定宽度的容器 + content_left_ = lv_obj_create(content_); + lv_obj_set_size(content_left_, 32, LV_SIZE_CONTENT); // 固定宽度32像素 + lv_obj_set_style_pad_all(content_left_, 0, 0); + lv_obj_set_style_border_width(content_left_, 0, 0); + + emotion_label_ = lv_label_create(content_left_); lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_1, 0); lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP); lv_obj_center(emotion_label_); + lv_obj_set_style_pad_top(emotion_label_, 8, 0); + + // 创建右侧可扩展的容器 + content_right_ = lv_obj_create(content_); + lv_obj_set_size(content_right_, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + lv_obj_set_style_pad_all(content_right_, 0, 0); + lv_obj_set_style_border_width(content_right_, 0, 0); + lv_obj_set_flex_grow(content_right_, 1); + lv_obj_add_flag(content_right_, LV_OBJ_FLAG_HIDDEN); + + chat_message_label_ = lv_label_create(content_right_); + lv_label_set_text(chat_message_label_, ""); + lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_WRAP); + lv_obj_set_width(chat_message_label_, lv_pct(100)); + lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_LEFT, 0); /* Status bar */ lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW); @@ -250,17 +290,19 @@ void Ssd1306Display::SetupUI_128x32() { lv_label_set_text(battery_label_, ""); lv_obj_set_style_text_font(battery_label_, icon_font_, 0); - status_label_ = lv_label_create(side_bar_); - lv_obj_set_flex_grow(status_label_, 1); - lv_obj_set_width(status_label_, width_ - 32); - lv_label_set_long_mode(status_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); + status_label_ = lv_label_create(status_bar_); + lv_obj_set_style_pad_left(status_label_, 2, 0); lv_label_set_text(status_label_, "正在初始化"); - notification_label_ = lv_label_create(side_bar_); - lv_obj_set_flex_grow(notification_label_, 1); - lv_obj_set_width(notification_label_, width_ - 32); - lv_label_set_long_mode(notification_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); + notification_label_ = lv_label_create(status_bar_); lv_label_set_text(notification_label_, "通知"); + lv_obj_set_style_pad_left(notification_label_, 2, 0); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); + + chat_message_label_ = lv_label_create(side_bar_); + lv_obj_set_flex_grow(chat_message_label_, 1); + lv_obj_set_width(chat_message_label_, width_ - 32); + lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); + lv_label_set_text(chat_message_label_, ""); } diff --git a/main/display/ssd1306_display.h b/main/display/ssd1306_display.h index e1fbf194..70f09d31 100644 --- a/main/display/ssd1306_display.h +++ b/main/display/ssd1306_display.h @@ -13,6 +13,8 @@ private: lv_obj_t* status_bar_ = nullptr; lv_obj_t* content_ = nullptr; + lv_obj_t* content_left_ = nullptr; + lv_obj_t* content_right_ = nullptr; lv_obj_t* container_ = nullptr; lv_obj_t* side_bar_ = nullptr; @@ -29,6 +31,8 @@ public: Ssd1306Display(void* i2c_master_handle, int width, int height, bool mirror_x, bool mirror_y, const lv_font_t* text_font, const lv_font_t* icon_font); ~Ssd1306Display(); + + virtual void SetChatMessage(const std::string &role, const std::string &content); }; #endif // SSD1306_DISPLAY_H diff --git a/main/led/circular_strip.cc b/main/led/circular_strip.cc index 6be6a083..1712583c 100644 --- a/main/led/circular_strip.cc +++ b/main/led/circular_strip.cc @@ -211,8 +211,13 @@ void CircularStrip::OnStateChanged() { Blink(color, 100); break; } + case kDeviceStateActivating: { + StripColor color = { LOW_BRIGHTNESS, DEFAULT_BRIGHTNESS, LOW_BRIGHTNESS }; + Blink(color, 500); + break; + } default: - ESP_LOGE(TAG, "Invalid led strip event: %d", device_state); + ESP_LOGW(TAG, "Unknown led strip event: %d", device_state); return; } } diff --git a/main/led/single_led.cc b/main/led/single_led.cc index d4df70be..20a313cb 100644 --- a/main/led/single_led.cc +++ b/main/led/single_led.cc @@ -151,8 +151,12 @@ void SingleLed::OnStateChanged() { SetColor(0, DEFAULT_BRIGHTNESS, 0); StartContinuousBlink(100); break; + case kDeviceStateActivating: + SetColor(0, DEFAULT_BRIGHTNESS, 0); + StartContinuousBlink(500); + break; default: - ESP_LOGE(TAG, "Invalid led strip event: %d", device_state); + ESP_LOGW(TAG, "Unknown led strip event: %d", device_state); return; } } diff --git a/main/ota.cc b/main/ota.cc index 1181eb98..2b05f4de 100644 --- a/main/ota.cc +++ b/main/ota.cc @@ -71,6 +71,7 @@ bool Ota::CheckVersion() { return false; } + has_activation_code_ = false; cJSON *activation = cJSON_GetObjectItem(root, "activation"); if (activation != NULL) { cJSON* message = cJSON_GetObjectItem(activation, "message"); @@ -84,6 +85,7 @@ bool Ota::CheckVersion() { has_activation_code_ = true; } + has_mqtt_config_ = false; cJSON *mqtt = cJSON_GetObjectItem(root, "mqtt"); if (mqtt != NULL) { Settings settings("mqtt", true); diff --git a/main/protocols/websocket_protocol.cc b/main/protocols/websocket_protocol.cc index 9e625803..1c2e6895 100644 --- a/main/protocols/websocket_protocol.cc +++ b/main/protocols/websocket_protocol.cc @@ -10,8 +10,6 @@ #define TAG "WS" -#ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET - WebsocketProtocol::WebsocketProtocol() { event_group_handle_ = xEventGroupCreate(); } @@ -61,7 +59,7 @@ bool WebsocketProtocol::OpenAudioChannel() { websocket_->SetHeader("Authorization", token.c_str()); websocket_->SetHeader("Protocol-Version", "1"); websocket_->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str()); - websocket_->SetHeader("X-Uuid", Board::GetInstance().GetUuid().c_str()); + websocket_->SetHeader("Client-Id", Board::GetInstance().GetUuid().c_str()); websocket_->OnData([this](const char* data, size_t len, bool binary) { if (binary) { @@ -147,5 +145,3 @@ void WebsocketProtocol::ParseServerHello(const cJSON* root) { xEventGroupSetBits(event_group_handle_, WEBSOCKET_PROTOCOL_SERVER_HELLO_EVENT); } - -#endif