From 1e8fefbede07ea77d53028c9413df16852c5c4d9 Mon Sep 17 00:00:00 2001 From: espressif2022 <111102666+espressif2022@users.noreply.github.com> Date: Wed, 7 Jan 2026 20:39:17 +0800 Subject: [PATCH] feat: update emote display (#1629) --- main/CMakeLists.txt | 14 + main/Kconfig.projbuild | 11 +- main/assets.cc | 390 +++++++++-------- main/assets.h | 51 ++- main/boards/echoear/EchoEar.cc | 2 +- main/boards/echoear/config.json | 4 +- main/boards/echoear/emote.json | 22 - main/boards/echoear/layout.json | 37 -- main/boards/esp-box-3/emote.json | 22 - main/boards/esp-box-3/layout.json | 37 -- main/boards/esp-box/esp_box_board.cc | 8 +- main/boards/lichuang-dev/emote.json | 22 - main/boards/lichuang-dev/layout.json | 37 -- main/display/emote_display.cc | 630 +++++---------------------- main/display/emote_display.h | 74 +--- main/idf_component.yml | 2 +- scripts/spiffs_assets/build_all.py | 63 +-- 17 files changed, 412 insertions(+), 1014 deletions(-) delete mode 100644 main/boards/echoear/emote.json delete mode 100644 main/boards/echoear/layout.json delete mode 100644 main/boards/esp-box-3/emote.json delete mode 100644 main/boards/esp-box-3/layout.json delete mode 100644 main/boards/lichuang-dev/emote.json delete mode 100644 main/boards/lichuang-dev/layout.json diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index e5b1eb81..d57d408e 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -62,6 +62,8 @@ endfunction() set(BUILTIN_TEXT_FONT font_puhui_14_1) set(BUILTIN_ICON_FONT font_awesome_14_1) +set(EMOTE_RESOLUTION "320_240") + # Add board files according to BOARD_TYPE # Set default assets if the board uses partition table V2 if(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI) @@ -90,11 +92,13 @@ elseif(CONFIG_BOARD_TYPE_ESP_BOX_3) set(BUILTIN_TEXT_FONT font_puhui_basic_20_4) set(BUILTIN_ICON_FONT font_awesome_20_4) set(DEFAULT_EMOJI_COLLECTION twemoji_64) + set(EMOTE_RESOLUTION "320_240") elseif(CONFIG_BOARD_TYPE_ESP_BOX) set(BOARD_TYPE "esp-box") set(BUILTIN_TEXT_FONT font_puhui_basic_20_4) set(BUILTIN_ICON_FONT font_awesome_20_4) set(DEFAULT_EMOJI_COLLECTION twemoji_64) + set(EMOTE_RESOLUTION "320_240") elseif(CONFIG_BOARD_TYPE_ESP_BOX_LITE) set(BOARD_TYPE "esp-box-lite") set(BUILTIN_TEXT_FONT font_puhui_basic_20_4) @@ -212,6 +216,8 @@ elseif(CONFIG_BOARD_TYPE_ECHOEAR) set(BUILTIN_TEXT_FONT font_puhui_20_4) set(BUILTIN_ICON_FONT font_awesome_20_4) set(DEFAULT_EMOJI_COLLECTION twemoji_64) + set(EMOTE_RESOLUTION "360_360") + # set(EMOTE_EXTERNAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/boards/echoear/assets") elseif(CONFIG_BOARD_TYPE_ESP_SENSAIRSHUTTLE) set(BOARD_TYPE "esp-sensairshuttle") set(BUILTIN_TEXT_FONT font_puhui_basic_16_4) @@ -928,6 +934,14 @@ if ("${size}" AND "${offset}") get_assets_local_file("${CONFIG_CUSTOM_ASSETS_FILE}" ASSETS_LOCAL_FILE) esptool_py_flash_to_partition(flash "assets" "${ASSETS_LOCAL_FILE}") message(STATUS "Custom assets flash configured: ${ASSETS_LOCAL_FILE} -> assets partition") + elseif(CONFIG_FLASH_EXPRESSION_ASSETS) + set(ASSETS_NAME "expression_assets") + set(ASSETS_PARTITION "assets") + set(ASSETS_FILE "${CMAKE_BINARY_DIR}/${ASSETS_NAME}.bin") + + build_speaker_assets_bin("${ASSETS_PARTITION}" ${EMOTE_RESOLUTION} ${ASSETS_FILE} ${CONFIG_MMAP_FILE_NAME_LENGTH}) + message(STATUS "Generated emote assets: ${ASSETS_FILE} -> ${ASSETS_PARTITION} partition") + esptool_py_flash_to_partition(flash "${ASSETS_PARTITION}" "${ASSETS_FILE}") elseif(CONFIG_FLASH_NONE_ASSETS) message(STATUS "Assets flashing disabled (FLASH_NONE_ASSETS)") endif() diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 5e7a7278..94176542 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -8,7 +8,8 @@ config OTA_URL choice prompt "Flash Assets" - default FLASH_DEFAULT_ASSETS + default FLASH_DEFAULT_ASSETS if !USE_EMOTE_MESSAGE_STYLE + default FLASH_EXPRESSION_ASSETS if USE_EMOTE_MESSAGE_STYLE help Select the assets to flash. @@ -16,8 +17,12 @@ choice bool "Do not flash assets" config FLASH_DEFAULT_ASSETS bool "Flash Default Assets" + depends on !USE_EMOTE_MESSAGE_STYLE config FLASH_CUSTOM_ASSETS bool "Flash Custom Assets" + config FLASH_EXPRESSION_ASSETS + bool "Flash Emote Assets" + depends on USE_EMOTE_MESSAGE_STYLE endchoice config CUSTOM_ASSETS_FILE @@ -585,7 +590,9 @@ choice DISPLAY_STYLE config USE_EMOTE_MESSAGE_STYLE bool "Emote animation style" - depends on BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ECHOEAR || BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_ESP_SENSAIRSHUTTLE + depends on BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_3 \ + || BOARD_TYPE_ECHOEAR || BOARD_TYPE_LICHUANG_DEV_S3 \ + || BOARD_TYPE_ESP_SENSAIRSHUTTLE endchoice choice WAKE_WORD_TYPE diff --git a/main/assets.cc b/main/assets.cc index 2d45bd36..f363cae5 100644 --- a/main/assets.cc +++ b/main/assets.cc @@ -4,17 +4,19 @@ #include "application.h" #include "lvgl_theme.h" #include "emote_display.h" -#ifdef HAVE_LVGL +#include "expression_emote.h" +#if HAVE_LVGL #include "display/lcd_display.h" +#include #endif #include -#include #include #include #define TAG "Assets" +#define PARTITION_LABEL "assets" struct mmap_assets_table { char asset_name[32]; /*!< Name of the asset */ @@ -24,19 +26,99 @@ struct mmap_assets_table { uint16_t asset_height; /*!< Height of the asset */ }; - Assets::Assets() { +#if HAVE_LVGL + strategy_ = std::make_unique(); +#else + strategy_ = std::make_unique(); +#endif // Initialize the partition InitializePartition(); } Assets::~Assets() { - if (mmap_handle_ != 0) { - esp_partition_munmap(mmap_handle_); + UnApplyPartition(); +} + +bool Assets::FindPartition(Assets* assets) { + assets->partition_ = esp_partition_find_first(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, PARTITION_LABEL); + if (assets->partition_ == nullptr) { + ESP_LOGI(TAG, "No assets partition found"); + return false; + } + return true; +} + +bool Assets::Apply() { + return strategy_ ? strategy_->Apply(this) : false; +} + +bool Assets::InitializePartition() { + return strategy_ ? strategy_->InitializePartition(this) : false; +} + +void Assets::UnApplyPartition() { + if (strategy_) { + strategy_->UnApplyPartition(this); } } -uint32_t Assets::CalculateChecksum(const char* data, uint32_t length) { +bool Assets::GetAssetData(const std::string& name, void*& ptr, size_t& size) { + return strategy_ ? strategy_->GetAssetData(this, name, ptr, size) : false; +} + +bool Assets::LoadSrmodelsFromIndex(Assets* assets, cJSON* root) { + void* ptr = nullptr; + size_t size = 0; + bool need_delete_root = false; + + // If root is not provided, parse index.json + if (root == nullptr) { + if (!assets->GetAssetData("index.json", ptr, size)) { + ESP_LOGE(TAG, "The index.json file is not found"); + return false; + } + + root = cJSON_ParseWithLength(static_cast(ptr), size); + if (root == nullptr) { + ESP_LOGE(TAG, "The index.json file is not valid"); + return false; + } + need_delete_root = true; + } + + cJSON* srmodels = cJSON_GetObjectItem(root, "srmodels"); + if (cJSON_IsString(srmodels)) { + std::string srmodels_file = srmodels->valuestring; + if (assets->GetAssetData(srmodels_file, ptr, size)) { + if (assets->models_list_ != nullptr) { + esp_srmodel_deinit(assets->models_list_); + assets->models_list_ = nullptr; + } + assets->models_list_ = srmodel_load(static_cast(ptr)); + if (assets->models_list_ != nullptr) { + auto& app = Application::GetInstance(); + app.GetAudioService().SetModelsList(assets->models_list_); + if (need_delete_root) { + cJSON_Delete(root); + } + return true; + } else { + ESP_LOGE(TAG, "Failed to load srmodels.bin"); + } + } else { + ESP_LOGE(TAG, "The srmodels file %s is not found", srmodels_file.c_str()); + } + } + + if (need_delete_root) { + cJSON_Delete(root); + } + return false; +} + +#if HAVE_LVGL +uint32_t Assets::LvglStrategy::CalculateChecksum(const char* data, uint32_t length) { uint32_t checksum = 0; for (uint32_t i = 0; i < length; i++) { checksum += data[i]; @@ -44,40 +126,37 @@ uint32_t Assets::CalculateChecksum(const char* data, uint32_t length) { return checksum & 0xFFFF; } -bool Assets::InitializePartition() { - partition_valid_ = false; - checksum_valid_ = false; +bool Assets::LvglStrategy::InitializePartition(Assets* assets) { + assets->partition_valid_ = false; assets_.clear(); - partition_ = esp_partition_find_first(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, "assets"); - if (partition_ == nullptr) { - ESP_LOGI(TAG, "No assets partition found"); + if (!Assets::FindPartition(assets)) { return false; } int free_pages = spi_flash_mmap_get_free_pages(SPI_FLASH_MMAP_DATA); uint32_t storage_size = free_pages * 64 * 1024; ESP_LOGI(TAG, "The storage free size is %ld KB", storage_size / 1024); - ESP_LOGI(TAG, "The partition size is %ld KB", partition_->size / 1024); - if (storage_size < partition_->size) { - ESP_LOGE(TAG, "The free size %ld KB is less than assets partition required %ld KB", storage_size / 1024, partition_->size / 1024); + ESP_LOGI(TAG, "The partition size is %ld KB", assets->partition_->size / 1024); + if (storage_size < assets->partition_->size) { + ESP_LOGE(TAG, "The free size %ld KB is less than assets partition required %ld KB", storage_size / 1024, assets->partition_->size / 1024); return false; } - esp_err_t err = esp_partition_mmap(partition_, 0, partition_->size, ESP_PARTITION_MMAP_DATA, (const void**)&mmap_root_, &mmap_handle_); + esp_err_t err = esp_partition_mmap(assets->partition_, 0, assets->partition_->size, ESP_PARTITION_MMAP_DATA, (const void**)&mmap_root_, &mmap_handle_); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to mmap assets partition: %s", esp_err_to_name(err)); return false; } - partition_valid_ = true; + assets->partition_valid_ = true; uint32_t stored_files = *(uint32_t*)(mmap_root_ + 0); uint32_t stored_chksum = *(uint32_t*)(mmap_root_ + 4); uint32_t stored_len = *(uint32_t*)(mmap_root_ + 8); - if (stored_len > partition_->size - 12) { - ESP_LOGD(TAG, "The stored_len (0x%lx) is greater than the partition size (0x%lx) - 12", stored_len, partition_->size); + if (stored_len > assets->partition_->size - 12) { + ESP_LOGD(TAG, "The stored_len (0x%lx) is greater than the partition size (0x%lx) - 12", stored_len, assets->partition_->size); return false; } @@ -104,10 +183,37 @@ bool Assets::InitializePartition() { return checksum_valid_; } -bool Assets::Apply() { +void Assets::LvglStrategy::UnApplyPartition(Assets* assets) { + if (mmap_handle_ != 0) { + esp_partition_munmap(mmap_handle_); + mmap_handle_ = 0; + mmap_root_ = nullptr; + } + checksum_valid_ = false; + assets_.clear(); + (void)assets; // Unused parameter +} + +bool Assets::LvglStrategy::GetAssetData(Assets* assets, const std::string& name, void*& ptr, size_t& size) { + auto asset = assets_.find(name); + if (asset == assets_.end()) { + return false; + } + auto data = (const char*)(mmap_root_ + asset->second.offset); + if (data[0] != 'Z' || data[1] != 'Z') { + ESP_LOGE(TAG, "The asset %s is not valid with magic %02x%02x", name.c_str(), data[0], data[1]); + return false; + } + + ptr = static_cast(const_cast(data + 2)); + size = asset->second.size; + return true; +} + +bool Assets::LvglStrategy::Apply(Assets* assets) { void* ptr = nullptr; size_t size = 0; - if (!GetAssetData("index.json", ptr, size)) { + if (!assets->GetAssetData("index.json", ptr, size)) { ESP_LOGE(TAG, "The index.json file is not found"); return false; } @@ -125,28 +231,9 @@ bool Assets::Apply() { return false; } } - - cJSON* srmodels = cJSON_GetObjectItem(root, "srmodels"); - if (cJSON_IsString(srmodels)) { - std::string srmodels_file = srmodels->valuestring; - if (GetAssetData(srmodels_file, ptr, size)) { - if (models_list_ != nullptr) { - esp_srmodel_deinit(models_list_); - models_list_ = nullptr; - } - models_list_ = srmodel_load(static_cast(ptr)); - if (models_list_ != nullptr) { - auto& app = Application::GetInstance(); - app.GetAudioService().SetModelsList(models_list_); - } else { - ESP_LOGE(TAG, "Failed to load srmodels.bin"); - } - } else { - ESP_LOGE(TAG, "The srmodels file %s is not found", srmodels_file.c_str()); - } - } -#ifdef HAVE_LVGL + Assets::LoadSrmodelsFromIndex(assets, root); + auto& theme_manager = LvglThemeManager::GetInstance(); auto light_theme = theme_manager.GetTheme("light"); auto dark_theme = theme_manager.GetTheme("dark"); @@ -154,7 +241,7 @@ bool Assets::Apply() { cJSON* font = cJSON_GetObjectItem(root, "text_font"); if (cJSON_IsString(font)) { std::string fonts_text_file = font->valuestring; - if (GetAssetData(fonts_text_file, ptr, size)) { + if (assets->GetAssetData(fonts_text_file, ptr, size)) { auto text_font = std::make_shared(ptr); if (text_font->font() == nullptr) { ESP_LOGE(TAG, "Failed to load fonts.bin"); @@ -182,7 +269,7 @@ bool Assets::Apply() { cJSON* file = cJSON_GetObjectItem(emoji, "file"); cJSON* eaf = cJSON_GetObjectItem(emoji, "eaf"); if (cJSON_IsString(name) && cJSON_IsString(file) && (NULL== eaf)) { - if (!GetAssetData(file->valuestring, ptr, size)) { + if (!assets->GetAssetData(file->valuestring, ptr, size)) { ESP_LOGE(TAG, "Emoji %s image file %s is not found", name->valuestring, file->valuestring); continue; } @@ -213,7 +300,7 @@ bool Assets::Apply() { light_theme->set_chat_background_color(LvglTheme::ParseColor(background_color->valuestring)); } if (cJSON_IsString(background_image)) { - if (!GetAssetData(background_image->valuestring, ptr, size)) { + if (!assets->GetAssetData(background_image->valuestring, ptr, size)) { ESP_LOGE(TAG, "The background image file %s is not found", background_image->valuestring); return false; } @@ -234,7 +321,7 @@ bool Assets::Apply() { dark_theme->set_chat_background_color(LvglTheme::ParseColor(background_color->valuestring)); } if (cJSON_IsString(background_image)) { - if (!GetAssetData(background_image->valuestring, ptr, size)) { + if (!assets->GetAssetData(background_image->valuestring, ptr, size)) { ESP_LOGE(TAG, "The background image file %s is not found", background_image->valuestring); return false; } @@ -262,137 +349,84 @@ bool Assets::Apply() { ESP_LOGI(TAG, "Set hide_subtitle to %s", hide ? "true" : "false"); } } - -#elif defined(CONFIG_USE_EMOTE_MESSAGE_STYLE) - auto &board = Board::GetInstance(); - auto display = board.GetDisplay(); - auto emote_display = dynamic_cast(display); - - cJSON* font = cJSON_GetObjectItem(root, "text_font"); - if (cJSON_IsString(font)) { - std::string fonts_text_file = font->valuestring; - if (GetAssetData(fonts_text_file, ptr, size)) { - auto text_font = std::make_shared(ptr); - if (text_font->font() == nullptr) { - ESP_LOGE(TAG, "Failed to load fonts.bin"); - return false; - } - - if (emote_display) { - emote_display->AddTextFont(text_font); - } - } else { - ESP_LOGE(TAG, "The font file %s is not found", fonts_text_file.c_str()); - } - } - - cJSON* emoji_collection = cJSON_GetObjectItem(root, "emoji_collection"); - if (cJSON_IsArray(emoji_collection)) { - int emoji_count = cJSON_GetArraySize(emoji_collection); - if (emote_display) { - for (int i = 0; i < emoji_count; i++) { - cJSON* icon = cJSON_GetArrayItem(emoji_collection, i); - if (cJSON_IsObject(icon)) { - cJSON* name = cJSON_GetObjectItem(icon, "name"); - cJSON* file = cJSON_GetObjectItem(icon, "file"); - - if (cJSON_IsString(name) && cJSON_IsString(file)) { - if (GetAssetData(file->valuestring, ptr, size)) { - cJSON* eaf = cJSON_GetObjectItem(icon, "eaf"); - bool lack_value = false; - bool loop_value = false; - int fps_value = 0; - - if (cJSON_IsObject(eaf)) { - cJSON* lack = cJSON_GetObjectItem(eaf, "lack"); - cJSON* loop = cJSON_GetObjectItem(eaf, "loop"); - cJSON* fps = cJSON_GetObjectItem(eaf, "fps"); - - lack_value = lack ? cJSON_IsTrue(lack) : false; - loop_value = loop ? cJSON_IsTrue(loop) : false; - fps_value = fps ? fps->valueint : 0; - - emote_display->AddEmojiData(name->valuestring, ptr, size, - static_cast(fps_value), - loop_value, lack_value); - } - - } else { - ESP_LOGE(TAG, "Emoji \"%10s\" image file %s is not found", name->valuestring, file->valuestring); - } - } - } - } - } - } - - cJSON* icon_collection = cJSON_GetObjectItem(root, "icon_collection"); - if (cJSON_IsArray(icon_collection)) { - if (emote_display) { - int icon_count = cJSON_GetArraySize(icon_collection); - for (int i = 0; i < icon_count; i++) { - cJSON* icon = cJSON_GetArrayItem(icon_collection, i); - if (cJSON_IsObject(icon)) { - cJSON* name = cJSON_GetObjectItem(icon, "name"); - cJSON* file = cJSON_GetObjectItem(icon, "file"); - - if (cJSON_IsString(name) && cJSON_IsString(file)) { - if (GetAssetData(file->valuestring, ptr, size)) { - emote_display->AddIconData(name->valuestring, ptr, size); - } else { - ESP_LOGE(TAG, "Icon \"%10s\" image file %s is not found", name->valuestring, file->valuestring); - } - } - } - } - } - } - - cJSON* layout_json = cJSON_GetObjectItem(root, "layout"); - if (cJSON_IsArray(layout_json)) { - int layout_count = cJSON_GetArraySize(layout_json); - - for (int i = 0; i < layout_count; i++) { - cJSON* layout_item = cJSON_GetArrayItem(layout_json, i); - if (cJSON_IsObject(layout_item)) { - cJSON* name = cJSON_GetObjectItem(layout_item, "name"); - cJSON* align = cJSON_GetObjectItem(layout_item, "align"); - cJSON* x = cJSON_GetObjectItem(layout_item, "x"); - cJSON* y = cJSON_GetObjectItem(layout_item, "y"); - cJSON* width = cJSON_GetObjectItem(layout_item, "width"); - cJSON* height = cJSON_GetObjectItem(layout_item, "height"); - - if (cJSON_IsString(name) && cJSON_IsString(align) && cJSON_IsNumber(x) && cJSON_IsNumber(y)) { - int width_val = cJSON_IsNumber(width) ? width->valueint : 0; - int height_val = cJSON_IsNumber(height) ? height->valueint : 0; - - if (emote_display) { - emote_display->AddLayoutData(name->valuestring, align->valuestring, - x->valueint, y->valueint, width_val, height_val); - } - } else { - ESP_LOGW(TAG, "Invalid layout item %d: missing required fields", i); - } - } - } - } -#endif - + cJSON_Delete(root); return true; } +#endif // HAVE_LVGL + +bool Assets::EmoteStrategy::InitializePartition(Assets* assets) { + assets->partition_valid_ = false; + + if (!Assets::FindPartition(assets)) { + return false; + } + + esp_err_t ret = ESP_ERR_INVALID_STATE; + auto display = Board::GetInstance().GetDisplay(); + auto* emote_display = dynamic_cast(display); + if (emote_display && emote_display->GetEmoteHandle() != nullptr) { + const emote_data_t data = { + .type = EMOTE_SOURCE_PARTITION, + .source = { + .partition_label = PARTITION_LABEL, + }, + .flags = { + .mmap_enable = true, //must be true here!!! + }, + }; + ret = emote_mount_assets(emote_display->GetEmoteHandle(), &data); + } else { + ESP_LOGE(TAG, "Emote display is not initialized"); + } + assets->partition_valid_ = ((ret == ESP_OK) ? true : false); + return assets->partition_valid_; +} + +void Assets::EmoteStrategy::UnApplyPartition(Assets* assets) { + auto display = Board::GetInstance().GetDisplay(); + auto* emote_display = dynamic_cast(display); + if (emote_display && emote_display->GetEmoteHandle() != nullptr) { + emote_unmount_assets(emote_display->GetEmoteHandle()); + } + (void)assets; // Unused parameter +} + +bool Assets::EmoteStrategy::GetAssetData(Assets* assets, const std::string& name, void*& ptr, size_t& size) { + auto display = Board::GetInstance().GetDisplay(); + auto* emote_display = dynamic_cast(display); + if (emote_display && emote_display->GetEmoteHandle() != nullptr) { + const uint8_t* data = nullptr; + size_t data_size = 0; + if (ESP_OK == emote_get_asset_data_by_name(emote_display->GetEmoteHandle(), name.c_str(), &data, &data_size)) { + ptr = const_cast(static_cast(data)); + size = data_size; + return true; + } + ESP_LOGE(TAG, "Failed to get asset data by name: %s", name.c_str()); + return false; + } + (void)assets; // Unused parameter + return false; +} + +bool Assets::EmoteStrategy::Apply(Assets* assets) { + Assets::LoadSrmodelsFromIndex(assets); + + auto display = Board::GetInstance().GetDisplay(); + auto* emote_display = dynamic_cast(display); + + if (emote_display && emote_display->GetEmoteHandle() != nullptr) { + emote_load_assets(emote_display->GetEmoteHandle()); + } + return true; +} bool Assets::Download(std::string url, std::function progress_callback) { ESP_LOGI(TAG, "Downloading new version of assets from %s", url.c_str()); - + // 取消当前资源分区的内存映射 - if (mmap_handle_ != 0) { - esp_partition_munmap(mmap_handle_); - mmap_handle_ = 0; - mmap_root_ = nullptr; - } - checksum_valid_ = false; - assets_.clear(); + UnApplyPartition(); // 下载新的资源文件 auto network = Board::GetInstance().GetNetwork(); @@ -514,19 +548,3 @@ bool Assets::Download(std::string url, std::functionsecond.offset); - if (data[0] != 'Z' || data[1] != 'Z') { - ESP_LOGE(TAG, "The asset %s is not valid with magic %02x%02x", name.c_str(), data[0], data[1]); - return false; - } - - ptr = static_cast(const_cast(data + 2)); - size = asset->second.size; - return true; -} diff --git a/main/assets.h b/main/assets.h index 016692ee..3692e0c7 100644 --- a/main/assets.h +++ b/main/assets.h @@ -1,14 +1,19 @@ #ifndef ASSETS_H #define ASSETS_H -#include #include #include +#include #include #include #include +#include +#include +#if HAVE_LVGL +#include +#endif struct Asset { size_t size; @@ -28,7 +33,6 @@ public: bool GetAssetData(const std::string& name, void*& ptr, size_t& size); inline bool partition_valid() const { return partition_valid_; } - inline bool checksum_valid() const { return checksum_valid_; } inline std::string default_assets_url() const { return default_assets_url_; } private: @@ -37,16 +41,49 @@ private: Assets& operator=(const Assets&) = delete; bool InitializePartition(); - uint32_t CalculateChecksum(const char* data, uint32_t length); + void UnApplyPartition(); + static bool FindPartition(Assets* assets); + static bool LoadSrmodelsFromIndex(Assets* assets, cJSON* root = nullptr); + + class AssetStrategy { + public: + virtual ~AssetStrategy() = default; + virtual bool Apply(Assets* assets) = 0; + virtual bool InitializePartition(Assets* assets) = 0; + virtual void UnApplyPartition(Assets* assets) = 0; + virtual bool GetAssetData(Assets* assets, const std::string& name, void*& ptr, size_t& size) = 0; + }; + + class LvglStrategy : public AssetStrategy { + public: + bool Apply(Assets* assets) override; + bool InitializePartition(Assets* assets) override; + void UnApplyPartition(Assets* assets) override; + bool GetAssetData(Assets* assets, const std::string& name, void*& ptr, size_t& size) override; + private: + static uint32_t CalculateChecksum(const char* data, uint32_t length); + std::map assets_; + esp_partition_mmap_handle_t mmap_handle_ = 0; + const char* mmap_root_ = nullptr; + bool checksum_valid_ = false; + }; + + class EmoteStrategy : public AssetStrategy { + public: + bool Apply(Assets* assets) override; + bool InitializePartition(Assets* assets) override; + void UnApplyPartition(Assets* assets) override; + bool GetAssetData(Assets* assets, const std::string& name, void*& ptr, size_t& size) override; + }; + + // Strategy instance + std::unique_ptr strategy_; +protected: const esp_partition_t* partition_ = nullptr; - esp_partition_mmap_handle_t mmap_handle_ = 0; - const char* mmap_root_ = nullptr; bool partition_valid_ = false; - bool checksum_valid_ = false; std::string default_assets_url_; srmodel_list_t* models_list_ = nullptr; - std::map assets_; }; #endif diff --git a/main/boards/echoear/EchoEar.cc b/main/boards/echoear/EchoEar.cc index c155c163..d3882b72 100644 --- a/main/boards/echoear/EchoEar.cc +++ b/main/boards/echoear/EchoEar.cc @@ -471,7 +471,7 @@ private: auto &app = Application::GetInstance(); auto &board = (EchoEar &)Board::GetInstance(); - ESP_LOGI(TAG, "Touch event, TP_PIN_NUM_INT: %d", gpio_get_level(TP_PIN_NUM_INT)); + ESP_LOGD(TAG, "Touch event, TP_PIN_NUM_INT: %d", gpio_get_level(TP_PIN_NUM_INT)); touchpad->UpdateTouchPoint(); auto touch_event = touchpad->CheckTouchEvent(); diff --git a/main/boards/echoear/config.json b/main/boards/echoear/config.json index cbabd9ff..e725e40b 100644 --- a/main/boards/echoear/config.json +++ b/main/boards/echoear/config.json @@ -5,8 +5,8 @@ "name": "echoear", "sdkconfig_append": [ "CONFIG_USE_EMOTE_MESSAGE_STYLE=y", - "CONFIG_FLASH_CUSTOM_ASSETS=y", - "CONFIG_CUSTOM_ASSETS_FILE=\"https://dl.espressif.com/AE/wn9_nihaoxiaozhi_tts-font_puhui_common_20_4-echoear.bin\"" + "CONFIG_MMAP_FILE_NAME_LENGTH=32", + "CONFIG_FLASH_EXPRESSION_ASSETS=y" ] } ] diff --git a/main/boards/echoear/emote.json b/main/boards/echoear/emote.json deleted file mode 100644 index abc11c8f..00000000 --- a/main/boards/echoear/emote.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - {"emote": "happy", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "laughing", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "funny", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "loving", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "embarrassed", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "confident", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "delicious", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "sad", "src": "Sad.eaf", "loop": true, "fps": 20}, - {"emote": "crying", "src": "cry.eaf", "loop": true, "fps": 20}, - {"emote": "sleepy", "src": "sleep.eaf", "loop": true, "fps": 20}, - {"emote": "silly", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "angry", "src": "angry.eaf", "loop": true, "fps": 20}, - {"emote": "surprised", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "shocked", "src": "shocked.eaf", "loop": true, "fps": 20}, - {"emote": "thinking", "src": "confused.eaf", "loop": true, "fps": 20}, - {"emote": "winking", "src": "neutral.eaf", "loop": true, "fps": 20}, - {"emote": "relaxed", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "confused", "src": "confused.eaf", "loop": true, "fps": 20}, - {"emote": "neutral", "src": "winking.eaf", "loop": false, "fps": 20}, - {"emote": "idle", "src": "neutral.eaf", "loop": false, "fps": 20} -] diff --git a/main/boards/echoear/layout.json b/main/boards/echoear/layout.json deleted file mode 100644 index f9169f6e..00000000 --- a/main/boards/echoear/layout.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "eye_anim", - "align": "GFX_ALIGN_LEFT_MID", - "x": 10, - "y": 10 - }, - { - "name": "status_icon", - "align": "GFX_ALIGN_TOP_MID", - "x": -100, - "y": 38 - }, - { - "name": "toast_label", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 40, - "width": 160, - "height": 40 - }, - { - "name": "clock_label", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 40, - "width": 60, - "height": 50 - }, - { - "name": "listen_anim", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 25 - } -] - \ No newline at end of file diff --git a/main/boards/esp-box-3/emote.json b/main/boards/esp-box-3/emote.json deleted file mode 100644 index abc11c8f..00000000 --- a/main/boards/esp-box-3/emote.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - {"emote": "happy", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "laughing", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "funny", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "loving", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "embarrassed", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "confident", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "delicious", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "sad", "src": "Sad.eaf", "loop": true, "fps": 20}, - {"emote": "crying", "src": "cry.eaf", "loop": true, "fps": 20}, - {"emote": "sleepy", "src": "sleep.eaf", "loop": true, "fps": 20}, - {"emote": "silly", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "angry", "src": "angry.eaf", "loop": true, "fps": 20}, - {"emote": "surprised", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "shocked", "src": "shocked.eaf", "loop": true, "fps": 20}, - {"emote": "thinking", "src": "confused.eaf", "loop": true, "fps": 20}, - {"emote": "winking", "src": "neutral.eaf", "loop": true, "fps": 20}, - {"emote": "relaxed", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "confused", "src": "confused.eaf", "loop": true, "fps": 20}, - {"emote": "neutral", "src": "winking.eaf", "loop": false, "fps": 20}, - {"emote": "idle", "src": "neutral.eaf", "loop": false, "fps": 20} -] diff --git a/main/boards/esp-box-3/layout.json b/main/boards/esp-box-3/layout.json deleted file mode 100644 index 8a1dfbcc..00000000 --- a/main/boards/esp-box-3/layout.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "eye_anim", - "align": "GFX_ALIGN_LEFT_MID", - "x": 10, - "y": 30 - }, - { - "name": "status_icon", - "align": "GFX_ALIGN_TOP_MID", - "x": -120, - "y": 18 - }, - { - "name": "toast_label", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 20, - "width": 200, - "height": 40 - }, - { - "name": "clock_label", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 20, - "width": 200, - "height": 50 - }, - { - "name": "listen_anim", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 5 - } -] - \ No newline at end of file diff --git a/main/boards/esp-box/esp_box_board.cc b/main/boards/esp-box/esp_box_board.cc index 4cbf71e4..15fdc9c2 100644 --- a/main/boards/esp-box/esp_box_board.cc +++ b/main/boards/esp-box/esp_box_board.cc @@ -1,6 +1,7 @@ #include "wifi_board.h" #include "codecs/box_audio_codec.h" #include "display/lcd_display.h" +#include "display/emote_display.h" #include "esp_lcd_ili9341.h" #include "application.h" #include "button.h" @@ -38,7 +39,7 @@ class EspBox3Board : public WifiBoard { private: i2c_master_bus_handle_t i2c_bus_; Button boot_button_; - LcdDisplay* display_; + Display* display_; void InitializeI2c() { // Initialize I2C peripheral @@ -125,8 +126,13 @@ private: esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); esp_lcd_panel_disp_on_off(panel, true); + +#if CONFIG_USE_EMOTE_MESSAGE_STYLE + display_ = new emote::EmoteDisplay(panel, panel_io, DISPLAY_WIDTH, DISPLAY_HEIGHT); +#else display_ = new SpiLcdDisplay(panel_io, panel, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY); +#endif } public: diff --git a/main/boards/lichuang-dev/emote.json b/main/boards/lichuang-dev/emote.json deleted file mode 100644 index abc11c8f..00000000 --- a/main/boards/lichuang-dev/emote.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - {"emote": "happy", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "laughing", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "funny", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "loving", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "embarrassed", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "confident", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "delicious", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "sad", "src": "Sad.eaf", "loop": true, "fps": 20}, - {"emote": "crying", "src": "cry.eaf", "loop": true, "fps": 20}, - {"emote": "sleepy", "src": "sleep.eaf", "loop": true, "fps": 20}, - {"emote": "silly", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "angry", "src": "angry.eaf", "loop": true, "fps": 20}, - {"emote": "surprised", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "shocked", "src": "shocked.eaf", "loop": true, "fps": 20}, - {"emote": "thinking", "src": "confused.eaf", "loop": true, "fps": 20}, - {"emote": "winking", "src": "neutral.eaf", "loop": true, "fps": 20}, - {"emote": "relaxed", "src": "Happy.eaf", "loop": true, "fps": 20}, - {"emote": "confused", "src": "confused.eaf", "loop": true, "fps": 20}, - {"emote": "neutral", "src": "winking.eaf", "loop": false, "fps": 20}, - {"emote": "idle", "src": "neutral.eaf", "loop": false, "fps": 20} -] diff --git a/main/boards/lichuang-dev/layout.json b/main/boards/lichuang-dev/layout.json deleted file mode 100644 index 8a1dfbcc..00000000 --- a/main/boards/lichuang-dev/layout.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "name": "eye_anim", - "align": "GFX_ALIGN_LEFT_MID", - "x": 10, - "y": 30 - }, - { - "name": "status_icon", - "align": "GFX_ALIGN_TOP_MID", - "x": -120, - "y": 18 - }, - { - "name": "toast_label", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 20, - "width": 200, - "height": 40 - }, - { - "name": "clock_label", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 20, - "width": 200, - "height": 50 - }, - { - "name": "listen_anim", - "align": "GFX_ALIGN_TOP_MID", - "x": 0, - "y": 5 - } -] - \ No newline at end of file diff --git a/main/display/emote_display.cc b/main/display/emote_display.cc index e4a3f76a..54cf1613 100644 --- a/main/display/emote_display.cc +++ b/main/display/emote_display.cc @@ -5,6 +5,7 @@ #include #include #include +#include // Standard C headers #include @@ -14,16 +15,18 @@ #include #include #include +#include // FreeRTOS headers #include #include // Project headers -#include "assets.h" #include "assets/lang_config.h" +#include "assets.h" #include "board.h" #include "gfx.h" +#include "expression_emote.h" LV_FONT_DECLARE(BUILTIN_TEXT_FONT); @@ -35,153 +38,32 @@ namespace emote { static const char* TAG = "EmoteDisplay"; -// UI Element Names - Centralized Management -#define UI_ELEMENT_EYE_ANIM "eye_anim" -#define UI_ELEMENT_TOAST_LABEL "toast_label" -#define UI_ELEMENT_CLOCK_LABEL "clock_label" -#define UI_ELEMENT_LISTEN_ANIM "listen_anim" -#define UI_ELEMENT_STATUS_ICON "status_icon" - -// Icon Names - Centralized Management -#define ICON_MIC "icon_mic" -#define ICON_BATTERY "icon_Battery" -#define ICON_SPEAKER_ZZZ "icon_speaker_zzz" -#define ICON_WIFI_FAILED "icon_WiFi_failed" -#define ICON_WIFI_OK "icon_wifi" -#define ICON_LISTEN "listen" - -using FlushIoReadyCallback = std::function; -using FlushCallback = std::function; - -// ============================================================================ -// Global Variables -// ============================================================================ - -// UI element management -static gfx_obj_t* g_obj_label_toast = nullptr; -static gfx_obj_t* g_obj_label_clock = nullptr; -static gfx_obj_t* g_obj_anim_eye = nullptr; -static gfx_obj_t* g_obj_anim_listen = nullptr; -static gfx_obj_t* g_obj_img_status = nullptr; - -// Track current icon to determine when to show time -static std::string g_current_icon_type = ICON_WIFI_FAILED; -static gfx_image_dsc_t g_icon_img_dsc; - - // ============================================================================ // Forward Declarations // ============================================================================ class EmoteDisplay; -class EmoteEngine; - -enum class UIDisplayMode : uint8_t { - SHOW_LISTENING = 1, // Show g_obj_anim_listen - SHOW_TIME = 2, // Show g_obj_label_clock - SHOW_TIPS = 3 // Show g_obj_label_toast -}; // ============================================================================ // Helper Functions // ============================================================================ -// Function to convert align string to GFX_ALIGN enum value -char StringToGfxAlign(const std::string &align_str) +static bool OnFlushIoReady(const esp_lcd_panel_io_handle_t panel_io, + esp_lcd_panel_io_event_data_t* const edata, void* user_ctx) { - static const std::unordered_map align_map = { - {"GFX_ALIGN_DEFAULT", GFX_ALIGN_DEFAULT}, - {"GFX_ALIGN_TOP_LEFT", GFX_ALIGN_TOP_LEFT}, - {"GFX_ALIGN_TOP_MID", GFX_ALIGN_TOP_MID}, - {"GFX_ALIGN_TOP_RIGHT", GFX_ALIGN_TOP_RIGHT}, - {"GFX_ALIGN_LEFT_MID", GFX_ALIGN_LEFT_MID}, - {"GFX_ALIGN_CENTER", GFX_ALIGN_CENTER}, - {"GFX_ALIGN_RIGHT_MID", GFX_ALIGN_RIGHT_MID}, - {"GFX_ALIGN_BOTTOM_LEFT", GFX_ALIGN_BOTTOM_LEFT}, - {"GFX_ALIGN_BOTTOM_MID", GFX_ALIGN_BOTTOM_MID}, - {"GFX_ALIGN_BOTTOM_RIGHT", GFX_ALIGN_BOTTOM_RIGHT}, - {"GFX_ALIGN_OUT_TOP_LEFT", GFX_ALIGN_OUT_TOP_LEFT}, - {"GFX_ALIGN_OUT_TOP_MID", GFX_ALIGN_OUT_TOP_MID}, - {"GFX_ALIGN_OUT_TOP_RIGHT", GFX_ALIGN_OUT_TOP_RIGHT}, - {"GFX_ALIGN_OUT_LEFT_TOP", GFX_ALIGN_OUT_LEFT_TOP}, - {"GFX_ALIGN_OUT_LEFT_MID", GFX_ALIGN_OUT_LEFT_MID}, - {"GFX_ALIGN_OUT_LEFT_BOTTOM", GFX_ALIGN_OUT_LEFT_BOTTOM}, - {"GFX_ALIGN_OUT_RIGHT_TOP", GFX_ALIGN_OUT_RIGHT_TOP}, - {"GFX_ALIGN_OUT_RIGHT_MID", GFX_ALIGN_OUT_RIGHT_MID}, - {"GFX_ALIGN_OUT_RIGHT_BOTTOM", GFX_ALIGN_OUT_RIGHT_BOTTOM}, - {"GFX_ALIGN_OUT_BOTTOM_LEFT", GFX_ALIGN_OUT_BOTTOM_LEFT}, - {"GFX_ALIGN_OUT_BOTTOM_MID", GFX_ALIGN_OUT_BOTTOM_MID}, - {"GFX_ALIGN_OUT_BOTTOM_RIGHT", GFX_ALIGN_OUT_BOTTOM_RIGHT} - }; - - const auto it = align_map.find(align_str); - if (it != align_map.cend()) { - return it->second; + emote_handle_t handle = static_cast(user_ctx); + if (handle) { + emote_notify_flush_finished(handle); } - - ESP_LOGW(TAG, "Unknown align string: %s, using GFX_ALIGN_DEFAULT", align_str.c_str()); - return GFX_ALIGN_DEFAULT; + return true; } -// ============================================================================ -// EmoteEngine Class Declaration -// ============================================================================ - -class EmoteEngine { -public: - EmoteEngine(const esp_lcd_panel_handle_t panel, const esp_lcd_panel_io_handle_t panel_io, - const int width, const int height, EmoteDisplay* const display); - ~EmoteEngine(); - - void SetEyes(const std::string &emoji_name, const bool repeat, const int fps, EmoteDisplay* const display); - void SetIcon(const std::string &icon_name, EmoteDisplay* const display); - - void* GetEngineHandle() const - { - return engine_handle_; - } - - // Callback functions (public to be accessible from static helper functions) - static bool OnFlushIoReady(const esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t* const edata, void* const user_ctx); - static void OnFlush(const gfx_handle_t handle, const int x_start, const int y_start, const int x_end, const int y_end, const void* const color_data); - -private: - gfx_handle_t engine_handle_; -}; - -// ============================================================================ -// UI Management Functions -// ============================================================================ - -static void SetUIDisplayMode(const UIDisplayMode mode, EmoteDisplay* const display) +// Flush callback for emote +static void OnFlushCallback(int x_start, int y_start, int x_end, int y_end, const void* data, emote_handle_t handle) { - if (!display) { - ESP_LOGE(TAG, "SetUIDisplayMode: display is nullptr"); - return; - } - - gfx_obj_set_visible(g_obj_anim_listen, false); - gfx_obj_set_visible(g_obj_label_clock, false); - gfx_obj_set_visible(g_obj_label_toast, false); - - // Show the selected control - switch (mode) { - case UIDisplayMode::SHOW_LISTENING: { - gfx_obj_set_visible(g_obj_anim_listen, true); - const AssetData emoji_data = display->GetIconData(ICON_LISTEN); - if (emoji_data.data) { - gfx_anim_set_src(g_obj_anim_listen, emoji_data.data, emoji_data.size); - gfx_anim_set_segment(g_obj_anim_listen, 0, 0xFFFF, 20, true); - gfx_anim_start(g_obj_anim_listen); - } - break; - } - case UIDisplayMode::SHOW_TIME: - gfx_obj_set_visible(g_obj_label_clock, true); - break; - case UIDisplayMode::SHOW_TIPS: - gfx_obj_set_visible(g_obj_label_toast, true); - break; + esp_lcd_panel_handle_t panel = (esp_lcd_panel_handle_t)emote_get_user_data(handle); + if (panel != nullptr) { + esp_lcd_panel_draw_bitmap(panel, x_start, y_start, x_end, y_end, data); } } @@ -189,193 +71,44 @@ static void SetUIDisplayMode(const UIDisplayMode mode, EmoteDisplay* const displ // Graphics Initialization Functions // ============================================================================ -static void InitializeGraphics(const esp_lcd_panel_handle_t panel, gfx_handle_t* const engine_handle, - const int width, const int height) +static emote_handle_t InitializeEmote(const esp_lcd_panel_handle_t panel, const int width, const int height) { - if (!panel || !engine_handle) { - ESP_LOGE(TAG, "InitializeGraphics: Invalid parameters"); - return; + if (!panel) { + ESP_LOGE(TAG, "Invalid panel"); + return nullptr; } - gfx_core_config_t gfx_cfg = { - .flush_cb = EmoteEngine::OnFlush, - .user_data = panel, + emote_config_t emote_cfg = { .flags = { .swap = true, .double_buffer = true, - .buff_dma = true, + .buff_dma = false, + }, + .gfx_emote = { + .h_res = width, + .v_res = height, + .fps = 30, }, - .h_res = static_cast(width), - .v_res = static_cast(height), - .fps = 30, .buffers = { - .buf1 = nullptr, - .buf2 = nullptr, .buf_pixels = static_cast(width * 16), }, - .task = GFX_EMOTE_INIT_CONFIG() + .task = { + .task_priority = 5, + .task_stack = 6 * 1024, + .task_affinity = 0, + .task_stack_in_ext = false, + }, + .flush_cb = OnFlushCallback, + .user_data = (void*)panel, }; - gfx_cfg.task.task_stack_caps = MALLOC_CAP_DEFAULT; - gfx_cfg.task.task_affinity = 0; - gfx_cfg.task.task_priority = 5; - gfx_cfg.task.task_stack = 8 * 1024; - - *engine_handle = gfx_emote_init(&gfx_cfg); -} - -static void SetupUI(const gfx_handle_t engine_handle, EmoteDisplay* const display) -{ - if (!display) { - ESP_LOGE(TAG, "SetupUI: display is nullptr"); - return; + emote_handle_t emote_handle = emote_init(&emote_cfg); + if (!emote_handle) { + ESP_LOGE(TAG, "Failed to initialize emote"); + return nullptr; } - gfx_emote_set_bg_color(engine_handle, GFX_COLOR_HEX(0x000000)); - - g_obj_anim_eye = gfx_anim_create(engine_handle); - gfx_obj_align(g_obj_anim_eye, GFX_ALIGN_LEFT_MID, 10, 30); - gfx_anim_set_auto_mirror(g_obj_anim_eye, true); - gfx_obj_set_visible(g_obj_anim_eye, false); - - g_obj_label_toast = gfx_label_create(engine_handle); - gfx_obj_align(g_obj_label_toast, GFX_ALIGN_TOP_MID, 0, 20); - gfx_obj_set_size(g_obj_label_toast, 200, 40); - gfx_label_set_text(g_obj_label_toast, Lang::Strings::INITIALIZING); - gfx_label_set_color(g_obj_label_toast, GFX_COLOR_HEX(0xFFFFFF)); - gfx_label_set_text_align(g_obj_label_toast, GFX_TEXT_ALIGN_CENTER); - gfx_label_set_long_mode(g_obj_label_toast, GFX_LABEL_LONG_SCROLL); - gfx_label_set_scroll_speed(g_obj_label_toast, 20); - gfx_label_set_scroll_loop(g_obj_label_toast, true); - gfx_label_set_font(g_obj_label_toast, (gfx_font_t)&BUILTIN_TEXT_FONT); - - g_obj_label_clock = gfx_label_create(engine_handle); - gfx_obj_align(g_obj_label_clock, GFX_ALIGN_TOP_MID, 0, 15); - gfx_obj_set_size(g_obj_label_clock, 200, 50); - gfx_label_set_text(g_obj_label_clock, "--:--"); - gfx_label_set_color(g_obj_label_clock, GFX_COLOR_HEX(0xFFFFFF)); - gfx_label_set_text_align(g_obj_label_clock, GFX_TEXT_ALIGN_CENTER); - gfx_label_set_font(g_obj_label_clock, (gfx_font_t)&BUILTIN_TEXT_FONT); - - g_obj_anim_listen = gfx_anim_create(engine_handle); - gfx_obj_align(g_obj_anim_listen, GFX_ALIGN_TOP_MID, 0, 5); - gfx_anim_start(g_obj_anim_listen); - gfx_obj_set_visible(g_obj_anim_listen, false); - - g_obj_img_status = gfx_img_create(engine_handle); - gfx_obj_align(g_obj_img_status, GFX_ALIGN_TOP_MID, -120, 18); - - SetUIDisplayMode(UIDisplayMode::SHOW_TIPS, display); -} - -static void RegisterCallbacks(const esp_lcd_panel_io_handle_t panel_io, const gfx_handle_t engine_handle) -{ - if (!panel_io) { - ESP_LOGE(TAG, "RegisterCallbacks: panel_io is nullptr"); - return; - } - - const esp_lcd_panel_io_callbacks_t cbs = { - .on_color_trans_done = EmoteEngine::OnFlushIoReady, - }; - esp_lcd_panel_io_register_event_callbacks(panel_io, &cbs, engine_handle); -} - -// ============================================================================ -// EmoteEngine Class Implementation -// ============================================================================ - -EmoteEngine::EmoteEngine(const esp_lcd_panel_handle_t panel, const esp_lcd_panel_io_handle_t panel_io, - const int width, const int height, EmoteDisplay* const display) -{ - InitializeGraphics(panel, &engine_handle_, width, height); - - if (display) { - gfx_emote_lock(engine_handle_); - SetupUI(engine_handle_, display); - gfx_emote_unlock(engine_handle_); - } - - RegisterCallbacks(panel_io, engine_handle_); -} - -EmoteEngine::~EmoteEngine() -{ - if (engine_handle_) { - gfx_emote_deinit(engine_handle_); - engine_handle_ = nullptr; - } -} - -void EmoteEngine::SetEyes(const std::string &emoji_name, const bool repeat, const int fps, EmoteDisplay* const display) -{ - if (!engine_handle_) { - ESP_LOGE(TAG, "SetEyes: engine_handle_ is nullptr"); - return; - } - - if (!display) { - ESP_LOGE(TAG, "SetEyes: display is nullptr"); - return; - } - - const AssetData emoji_data = display->GetEmojiData(emoji_name); - if (emoji_data.data) { - DisplayLockGuard lock(display); - gfx_anim_set_src(g_obj_anim_eye, emoji_data.data, emoji_data.size); - gfx_anim_set_segment(g_obj_anim_eye, 0, 0xFFFF, fps, repeat); - gfx_obj_set_visible(g_obj_anim_eye, true); - gfx_anim_start(g_obj_anim_eye); - } else { - ESP_LOGW(TAG, "SetEyes: No emoji data found for %s", emoji_name.c_str()); - } -} - -void EmoteEngine::SetIcon(const std::string &icon_name, EmoteDisplay* const display) -{ - if (!engine_handle_) { - ESP_LOGE(TAG, "SetIcon: engine_handle_ is nullptr"); - return; - } - - if (!display) { - ESP_LOGE(TAG, "SetIcon: display is nullptr"); - return; - } - - const AssetData icon_data = display->GetIconData(icon_name); - if (icon_data.data) { - DisplayLockGuard lock(display); - - std::memcpy(&g_icon_img_dsc.header, icon_data.data, sizeof(gfx_image_header_t)); - g_icon_img_dsc.data = static_cast(icon_data.data) + sizeof(gfx_image_header_t); - g_icon_img_dsc.data_size = icon_data.size - sizeof(gfx_image_header_t); - - gfx_img_set_src(g_obj_img_status, &g_icon_img_dsc); - } else { - ESP_LOGW(TAG, "SetIcon: No icon data found for %s", icon_name.c_str()); - } - g_current_icon_type = icon_name; -} - -bool EmoteEngine::OnFlushIoReady(const esp_lcd_panel_io_handle_t panel_io, - esp_lcd_panel_io_event_data_t* const edata, - void* const user_ctx) -{ - gfx_handle_t handle = static_cast(user_ctx); - if (handle) { - gfx_emote_flush_ready(handle, true); - } - return false; -} - -void EmoteEngine::OnFlush(const gfx_handle_t handle, const int x_start, const int y_start, - const int x_end, const int y_end, const void* const color_data) -{ - auto* const panel = static_cast(gfx_emote_get_user_data(handle)); - if (panel) { - esp_lcd_panel_draw_bitmap(panel, x_start, y_start, x_end, y_end, color_data); - } + return emote_handle; } // ============================================================================ @@ -385,131 +118,84 @@ void EmoteEngine::OnFlush(const gfx_handle_t handle, const int x_start, const in EmoteDisplay::EmoteDisplay(const esp_lcd_panel_handle_t panel, const esp_lcd_panel_io_handle_t panel_io, const int width, const int height) { - InitializeEngine(panel, panel_io, width, height); + emote_handle_ = InitializeEmote(panel, width, height); + + const esp_lcd_panel_io_callbacks_t cbs = { + .on_color_trans_done = OnFlushIoReady, + }; + esp_lcd_panel_io_register_event_callbacks(panel_io, &cbs, emote_handle_); } -EmoteDisplay::~EmoteDisplay() = default; +EmoteDisplay::~EmoteDisplay() +{ + if (emote_handle_) { + emote_deinit(emote_handle_); + emote_handle_ = nullptr; + } +} void EmoteDisplay::SetEmotion(const char* const emotion) { - if (!emotion) { - ESP_LOGE(TAG, "SetEmotion: emotion is nullptr"); - return; - } - ESP_LOGI(TAG, "SetEmotion: %s", emotion); - if (!engine_) { - return; + if (emote_handle_ && emotion && strlen(emotion) > 0) { + emote_set_anim_emoji(emote_handle_, emotion); } - - const AssetData emoji_data = GetEmojiData(emotion); - bool repeat = emoji_data.loop; - int fps = emoji_data.fps > 0 ? emoji_data.fps : 20; - - if (std::strcmp(emotion, "idle") == 0 || std::strcmp(emotion, "neutral") == 0) { - repeat = false; - } - - DisplayLockGuard lock(this); - engine_->SetEyes(emotion, repeat, fps, this); } void EmoteDisplay::SetChatMessage(const char* const role, const char* const content) { - if (!engine_) { - return; - } - - DisplayLockGuard lock(this); - if (content && strlen(content) > 0) { - gfx_label_set_text(g_obj_label_toast, content); - SetUIDisplayMode(UIDisplayMode::SHOW_TIPS, this); + ESP_LOGI(TAG, "SetChatMessage: %s, %s", role, content); + if (emote_handle_ && content && strlen(content) > 0) { + if ((std::strcmp(role, "system") == 0) && std::strstr(content, "xiaozhi.me")) { + size_t len = strlen(content); + char* new_content = new char[len + 1]; + strcpy(new_content, content); + std::replace(new_content, new_content + len, static_cast(0x0A), static_cast(0x20)); + emote_set_event_msg(emote_handle_, EMOTE_MGR_EVT_SYS, new_content); + delete[] new_content; + } else { + emote_set_event_msg(emote_handle_, EMOTE_MGR_EVT_SPEAK, content); + } } } void EmoteDisplay::SetStatus(const char* const status) { - if (!status) { - ESP_LOGE(TAG, "SetStatus: status is nullptr"); - return; - } - - if (!engine_) { - return; - } - - DisplayLockGuard lock(this); - - if (std::strcmp(status, Lang::Strings::LISTENING) == 0) { - SetUIDisplayMode(UIDisplayMode::SHOW_LISTENING, this); - engine_->SetEyes("happy", true, 20, this); - engine_->SetIcon(ICON_MIC, this); - } else if (std::strcmp(status, Lang::Strings::STANDBY) == 0) { - SetUIDisplayMode(UIDisplayMode::SHOW_TIME, this); - engine_->SetIcon(ICON_BATTERY, this); - } else if (std::strcmp(status, Lang::Strings::SPEAKING) == 0) { - SetUIDisplayMode(UIDisplayMode::SHOW_TIPS, this); - engine_->SetIcon(ICON_SPEAKER_ZZZ, this); - } else if (std::strcmp(status, Lang::Strings::ERROR) == 0) { - SetUIDisplayMode(UIDisplayMode::SHOW_TIPS, this); - engine_->SetIcon(ICON_WIFI_FAILED, this); - } - - if (std::strcmp(status, Lang::Strings::CONNECTING) != 0) { - gfx_label_set_text(g_obj_label_toast, status); + ESP_LOGI(TAG, "SetStatus: %s", status); + if (emote_handle_ && status && strlen(status) > 0) { + if (std::strcmp(status, Lang::Strings::LISTENING) == 0) { + emote_set_event_msg(emote_handle_, EMOTE_MGR_EVT_LISTEN, NULL); + } else if (std::strcmp(status, Lang::Strings::STANDBY) == 0) { + emote_set_event_msg(emote_handle_, EMOTE_MGR_EVT_IDLE, NULL); + } else if (std::strcmp(status, Lang::Strings::SPEAKING) == 0) { + emote_set_event_msg(emote_handle_, EMOTE_MGR_EVT_SPEAK, NULL); + } else if (std::strcmp(status, Lang::Strings::ERROR) == 0) { + emote_set_event_msg(emote_handle_, EMOTE_MGR_EVT_SET, NULL); + } } } void EmoteDisplay::ShowNotification(const char* notification, int duration_ms) { - if (!notification || !engine_) { - return; - } ESP_LOGI(TAG, "ShowNotification: %s", notification); - - DisplayLockGuard lock(this); - gfx_label_set_text(g_obj_label_toast, notification); - SetUIDisplayMode(UIDisplayMode::SHOW_TIPS, this); + if (emote_handle_ && notification && strlen(notification) > 0) { + emote_set_event_msg(emote_handle_, EMOTE_MGR_EVT_SYS, notification); + } } void EmoteDisplay::UpdateStatusBar(bool update_all) { - if (!engine_) { + ESP_LOGD(TAG, "UpdateStatusBar: %s", update_all ? "true" : "false"); + if (!emote_handle_) { return; } - - // Only display time when battery icon is shown - DisplayLockGuard lock(this); - if (g_current_icon_type == ICON_BATTERY) { - time_t now; - struct tm timeinfo; - time(&now); - - setenv("TZ", "GMT+0", 1); - tzset(); - localtime_r(&now, &timeinfo); - - char time_str[6]; - snprintf(time_str, sizeof(time_str), "%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min); - - DisplayLockGuard lock(this); - gfx_label_set_text(g_obj_label_clock, time_str); - SetUIDisplayMode(UIDisplayMode::SHOW_TIME, this); - } } void EmoteDisplay::SetPowerSaveMode(bool on) { - if (!engine_) { - return; - } - - DisplayLockGuard lock(this); ESP_LOGI(TAG, "SetPowerSaveMode: %s", on ? "ON" : "OFF"); - if (on) { - gfx_anim_stop(g_obj_anim_eye); - } else { - gfx_anim_start(g_obj_anim_eye); + if (!emote_handle_) { + return; } } @@ -517,141 +203,47 @@ void EmoteDisplay::SetPreviewImage(const void* image) { if (image) { ESP_LOGI(TAG, "SetPreviewImage: Preview image not supported, using default icon"); - if (engine_) { - } } } void EmoteDisplay::SetTheme(Theme* const theme) { ESP_LOGI(TAG, "SetTheme: %p", theme); - -} -void EmoteDisplay::AddEmojiData(const std::string &name, const void* const data, const size_t size, - uint8_t fps, bool loop, bool lack) -{ - emoji_data_map_[name] = AssetData(data, size, fps, loop, lack); - ESP_LOGD(TAG, "Added emoji data: %s, size: %d, fps: %d, loop: %s, lack: %s", - name.c_str(), size, fps, loop ? "true" : "false", lack ? "true" : "false"); - - DisplayLockGuard lock(this); - if (name == "happy") { - engine_->SetEyes("happy", loop, fps > 0 ? fps : 20, this); - } -} - -void EmoteDisplay::AddIconData(const std::string &name, const void* const data, const size_t size) -{ - icon_data_map_[name] = AssetData(data, size); - ESP_LOGD(TAG, "Added icon data: %s, size: %d", name.c_str(), size); - - DisplayLockGuard lock(this); - if (name == ICON_WIFI_FAILED) { - SetUIDisplayMode(UIDisplayMode::SHOW_TIPS, this); - engine_->SetIcon(ICON_WIFI_FAILED, this); - } -} - -void EmoteDisplay::AddLayoutData(const std::string &name, const std::string &align_str, - const int x, const int y, const int width, const int height) -{ - const char align_enum = StringToGfxAlign(align_str); - ESP_LOGI(TAG, "layout: %-12s | %-20s(%d) | %4d, %4d | %4dx%-4d", - name.c_str(), align_str.c_str(), align_enum, x, y, width, height); - - struct UIElement { - gfx_obj_t* obj; - const char* name; - }; - - const UIElement elements[] = { - {g_obj_anim_eye, UI_ELEMENT_EYE_ANIM}, - {g_obj_label_toast, UI_ELEMENT_TOAST_LABEL}, - {g_obj_label_clock, UI_ELEMENT_CLOCK_LABEL}, - {g_obj_anim_listen, UI_ELEMENT_LISTEN_ANIM}, - {g_obj_img_status, UI_ELEMENT_STATUS_ICON} - }; - - DisplayLockGuard lock(this); - for (const auto &element : elements) { - if (name == element.name && element.obj) { - gfx_obj_align(element.obj, align_enum, x, y); - if (width > 0 && height > 0) { - gfx_obj_set_size(element.obj, width, height); - } - return; - } - } - - ESP_LOGW(TAG, "AddLayoutData: UI element '%s' not found", name.c_str()); -} - -void EmoteDisplay::AddTextFont(std::shared_ptr text_font) -{ - if (!text_font) { - ESP_LOGW(TAG, "AddTextFont: text_font is nullptr"); - return; - } - - text_font_ = text_font; - ESP_LOGD(TAG, "AddTextFont: Text font added successfully"); - - DisplayLockGuard lock(this); - if (g_obj_label_toast && text_font_) { - gfx_label_set_font(g_obj_label_toast, const_cast(static_cast(text_font_->font()))); - } - if (g_obj_label_clock && text_font_) { - gfx_label_set_font(g_obj_label_clock, const_cast(static_cast(text_font_->font()))); - } -} - -AssetData EmoteDisplay::GetEmojiData(const std::string &name) const -{ - const auto it = emoji_data_map_.find(name); - if (it != emoji_data_map_.cend()) { - return it->second; - } - return AssetData(); -} - -AssetData EmoteDisplay::GetIconData(const std::string &name) const -{ - const auto it = icon_data_map_.find(name); - if (it != icon_data_map_.cend()) { - return it->second; - } - return AssetData(); -} - -EmoteEngine* EmoteDisplay::GetEngine() const -{ - return engine_.get(); -} - -void* EmoteDisplay::GetEngineHandle() const -{ - return engine_ ? engine_->GetEngineHandle() : nullptr; -} - -void EmoteDisplay::InitializeEngine(const esp_lcd_panel_handle_t panel, const esp_lcd_panel_io_handle_t panel_io, - const int width, const int height) -{ - engine_ = std::make_unique(panel, panel_io, width, height, this); } bool EmoteDisplay::Lock(const int timeout_ms) { - if (engine_ && engine_->GetEngineHandle()) { - gfx_emote_lock(engine_->GetEngineHandle()); - return true; - } - return false; + (void)timeout_ms; + return true; } void EmoteDisplay::Unlock() { - if (engine_ && engine_->GetEngineHandle()) { - gfx_emote_unlock(engine_->GetEngineHandle()); +} + +bool EmoteDisplay::StopAnimDialog() +{ + ESP_LOGI(TAG, "StopAnimDialog"); + if (emote_handle_) { + return emote_stop_anim_dialog(emote_handle_); + } + return false; +} + +bool EmoteDisplay::InsertAnimDialog(const char* emoji_name, uint32_t duration_ms) +{ + ESP_LOGI(TAG, "InsertAnimDialog: %s, %d", emoji_name, duration_ms); + if (emote_handle_ && emoji_name) { + return emote_insert_anim_dialog(emote_handle_, emoji_name, duration_ms); + } + return false; +} + +void EmoteDisplay::RefreshAll() +{ + if (emote_handle_) { + emote_notify_all_refresh(emote_handle_); + return; } } diff --git a/main/display/emote_display.h b/main/display/emote_display.h index ffa02a4c..20f1870b 100644 --- a/main/display/emote_display.h +++ b/main/display/emote_display.h @@ -1,59 +1,14 @@ #pragma once #include "display.h" -#include "lvgl_font.h" #include -#include -#include #include #include #include +#include "expression_emote.h" namespace emote { -// Simple data structure for storing asset data without LVGL dependency -struct AssetData { - const void* data; - size_t size; - union { - uint8_t flags; // 1 byte for all animation flags - struct { - uint8_t fps : 6; // FPS (0-63) - 6 bits - uint8_t loop : 1; // Loop animation - 1 bit - uint8_t lack : 1; // Lack animation - 1 bit - }; - }; - - AssetData() : data(nullptr), size(0), flags(0) {} - AssetData(const void* d, size_t s) : data(d), size(s), flags(0) {} - AssetData(const void* d, size_t s, uint8_t f, bool l, bool k) - : data(d), size(s) - { - fps = f > 63 ? 63 : f; // 限制 FPS 到 6 位范围 - loop = l; - lack = k; - } -}; - -// Layout element data structure -struct LayoutData { - char align; // Store as char instead of string - int x; - int y; - int width; - int height; - bool has_size; - - LayoutData() : align(0), x(0), y(0), width(0), height(0), has_size(false) {} - LayoutData(char a, int x_pos, int y_pos, int w = 0, int h = 0) - : align(a), x(x_pos), y(y_pos), width(w), height(h), has_size(w > 0 && h > 0) {} -}; - -// Function to convert align string to GFX_ALIGN enum value -char StringToGfxAlign(const std::string &align_str); - -class EmoteEngine; - class EmoteDisplay : public Display { public: EmoteDisplay(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t panel_io, int width, int height); @@ -68,34 +23,19 @@ public: virtual void SetPowerSaveMode(bool on) override; virtual void SetPreviewImage(const void* image); - void AddEmojiData(const std::string &name, const void* data, size_t size, uint8_t fps = 0, bool loop = false, bool lack = false); - void AddIconData(const std::string &name, const void* data, size_t size); - void AddLayoutData(const std::string &name, const std::string &align_str, int x, int y, int width = 0, int height = 0); - void AddTextFont(std::shared_ptr text_font); - AssetData GetEmojiData(const std::string &name) const; - AssetData GetIconData(const std::string &name) const; + bool StopAnimDialog(); + bool InsertAnimDialog(const char* emoji_name, uint32_t duration_ms); - EmoteEngine* GetEngine() const; - void* GetEngineHandle() const; + void RefreshAll(); - inline std::shared_ptr text_font() const - { - return text_font_; - } + // Get emote handle for internal use + emote_handle_t GetEmoteHandle() const { return emote_handle_; } private: - void InitializeEngine(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t panel_io, int width, int height); virtual bool Lock(int timeout_ms = 0) override; virtual void Unlock() override; - std::unique_ptr engine_; - - // Font management - std::shared_ptr text_font_ = nullptr; - - // Non-LVGL asset data storage - std::map emoji_data_map_; - std::map icon_data_map_; + emote_handle_t emote_handle_ = nullptr; }; diff --git a/main/idf_component.yml b/main/idf_component.yml index 217e034d..df225162 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -45,7 +45,7 @@ dependencies: esp_lvgl_port: ~2.6.0 espressif/esp_io_expander_tca95xx_16bit: ^2.0.0 espressif2022/image_player: ^1.1.1 - espressif2022/esp_emote_gfx: ==2.0.0 + espressif2022/esp_emote_expression: ^0.1.0 espressif/adc_mic: ^0.2.1 espressif/esp_mmap_assets: '>=1.2' txp666/otto-emoji-gif-component: diff --git a/scripts/spiffs_assets/build_all.py b/scripts/spiffs_assets/build_all.py index e812f8d0..33efbbcd 100644 --- a/scripts/spiffs_assets/build_all.py +++ b/scripts/spiffs_assets/build_all.py @@ -15,8 +15,6 @@ import os import sys import shutil import subprocess -import argparse -from pathlib import Path def ensure_dir(directory): @@ -31,7 +29,7 @@ def get_file_path(base_dir, filename): return os.path.join(base_dir, f"{filename}.bin" if not filename.startswith("emojis_") else filename) -def build_assets(wakenet_model, text_font, emoji_collection, target_board, build_dir, final_dir): +def build_assets(wakenet_model, text_font, emoji_collection, build_dir, final_dir): """Build assets.bin using build.py with given parameters""" # Prepare arguments for build.py @@ -48,15 +46,8 @@ def build_assets(wakenet_model, text_font, emoji_collection, target_board, build if emoji_collection != "none": emoji_path = os.path.join("../../components/xiaozhi-fonts/build", emoji_collection) cmd.extend(["--emoji_collection", emoji_path]) - - if target_board != "none": - res_path = os.path.join("../../managed_components/espressif2022__esp_emote_gfx/emoji_large", "") - cmd.extend(["--res_path", res_path]) - - target_board_path = os.path.join("../../main/boards/", f"{target_board}") - cmd.extend(["--target_board", target_board_path]) - print(f"\n正在构建: {wakenet_model}-{text_font}-{emoji_collection}-{target_board}") + print(f"\n正在构建: {wakenet_model}-{text_font}-{emoji_collection}") print(f"执行命令: {' '.join(cmd)}") try: @@ -64,10 +55,7 @@ def build_assets(wakenet_model, text_font, emoji_collection, target_board, build result = subprocess.run(cmd, check=True, cwd=os.path.dirname(__file__)) # Generate output filename - if(target_board != "none"): - output_name = f"{wakenet_model}-{text_font}-{target_board}.bin" - else: - output_name = f"{wakenet_model}-{text_font}-{emoji_collection}.bin" + output_name = f"{wakenet_model}-{text_font}-{emoji_collection}.bin" # Copy generated assets.bin to final directory with new name src_path = os.path.join(build_dir, "assets.bin") @@ -90,15 +78,6 @@ def build_assets(wakenet_model, text_font, emoji_collection, target_board, build def main(): - # Parse command line arguments - parser = argparse.ArgumentParser(description='构建多个 SPIFFS assets 分区') - parser.add_argument('--mode', - choices=['emoji_collections', 'emoji_target_boards'], - default='emoji_collections', - help='选择运行模式: emoji_collections 或 emoji_target_boards (默认: emoji_collections)') - - args = parser.parse_args() - # Configuration wakenet_models = [ "none", @@ -119,11 +98,6 @@ def main(): "emojis_32", "emojis_64", ] - - emoji_target_boards = [ - "esp-box-3", - "echoear", - ] # Get script directory script_dir = os.path.dirname(os.path.abspath(__file__)) @@ -137,33 +111,20 @@ def main(): ensure_dir(final_dir) print("开始构建多个 SPIFFS assets 分区...") - print(f"运行模式: {args.mode}") print(f"输出目录: {final_dir}") + # Calculate total combinations + total_combinations = len(wakenet_models) * len(text_fonts) * len(emoji_collections) + # Track successful builds successful_builds = 0 - if args.mode == 'emoji_collections': - # Calculate total combinations for emoji_collections mode - total_combinations = len(wakenet_models) * len(text_fonts) * len(emoji_collections) - - # Build all combinations with emoji_collections - for wakenet_model in wakenet_models: - for text_font in text_fonts: - for emoji_collection in emoji_collections: - if build_assets(wakenet_model, text_font, emoji_collection, "none", build_dir, final_dir): - successful_builds += 1 - - elif args.mode == 'emoji_target_boards': - # Calculate total combinations for emoji_target_boards mode - total_combinations = len(wakenet_models) * len(text_fonts) * len(emoji_target_boards) - - # Build all combinations with emoji_target_boards - for wakenet_model in wakenet_models: - for text_font in text_fonts: - for emoji_target_board in emoji_target_boards: - if build_assets(wakenet_model, text_font, "none", emoji_target_board, build_dir, final_dir): - successful_builds += 1 + # Build all combinations with emoji_collections + for wakenet_model in wakenet_models: + for text_font in text_fonts: + for emoji_collection in emoji_collections: + if build_assets(wakenet_model, text_font, emoji_collection, build_dir, final_dir): + successful_builds += 1 print(f"\n构建完成!") print(f"成功构建: {successful_builds}/{total_combinations}")