Files
xiaozhi-esp32/scripts/build_default_assets.py

935 lines
35 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
Build default assets based on configuration
This script reads configuration from sdkconfig and builds the appropriate assets.bin
for the current board configuration.
Usage:
./build_default_assets.py --sdkconfig <path> --builtin_text_font <font_name> \
--default_emoji_collection <collection_name> --output <output_path>
"""
import argparse
import io
import os
import shutil
import sys
import json
import struct
from datetime import datetime
# =============================================================================
# Pack model functions (from pack_model.py)
# =============================================================================
def struct_pack_string(string, max_len=None):
"""
pack string to binary data.
if max_len is None, max_len = len(string) + 1
else len(string) < max_len, the left will be padded by struct.pack('x')
"""
if max_len == None :
max_len = len(string)
else:
assert len(string) <= max_len
left_num = max_len - len(string)
out_bytes = None
for char in string:
if out_bytes == None:
out_bytes = struct.pack('b', ord(char))
else:
out_bytes += struct.pack('b', ord(char))
for i in range(left_num):
out_bytes += struct.pack('x')
return out_bytes
def read_data(filename):
"""Read binary data, like index and mndata"""
data = None
with open(filename, "rb") as f:
data = f.read()
return data
def pack_models(model_path, out_file="srmodels.bin"):
"""
Pack all models into one binary file by the following format:
{
model_num: int
model1_info: model_info_t
model2_info: model_info_t
...
model1_index,model1_data,model1_MODEL_INFO
model1_index,model1_data,model1_MODEL_INFO
...
}model_pack_t
{
model_name: char[32]
file_number: int
file1_name: char[32]
file1_start: int
file1_len: int
file2_name: char[32]
file2_start: int // data_len = info_start - data_start
file2_len: int
...
}model_info_t
"""
models = {}
file_num = 0
model_num = 0
for root, dirs, _ in os.walk(model_path):
for model_name in dirs:
models[model_name] = {}
model_dir = os.path.join(root, model_name)
model_num += 1
for _, _, files in os.walk(model_dir):
for file_name in files:
file_num += 1
file_path = os.path.join(model_dir, file_name)
models[model_name][file_name] = read_data(file_path)
model_num = len(models)
header_len = 4 + model_num*(32+4) + file_num*(32+4+4)
out_bin = struct.pack('I', model_num) # model number
data_bin = None
for key in models:
model_bin = struct_pack_string(key, 32) # + model name
model_bin += struct.pack('I', len(models[key])) # + file number in this model
for file_name in models[key]:
model_bin += struct_pack_string(file_name, 32) # + file name
if data_bin == None:
model_bin += struct.pack('I', header_len)
data_bin = models[key][file_name]
model_bin += struct.pack('I', len(models[key][file_name]))
else:
model_bin += struct.pack('I', header_len+len(data_bin))
data_bin += models[key][file_name]
model_bin += struct.pack('I', len(models[key][file_name]))
out_bin += model_bin
assert len(out_bin) == header_len
if data_bin != None:
out_bin += data_bin
out_file = os.path.join(model_path, out_file)
with open(out_file, "wb") as f:
f.write(out_bin)
# =============================================================================
# Build assets functions (from build.py)
# =============================================================================
def ensure_dir(directory):
"""Ensure directory exists, create if not"""
os.makedirs(directory, exist_ok=True)
def copy_file(src, dst):
"""Copy file"""
if os.path.exists(src):
shutil.copy2(src, dst)
print(f"Copied: {src} -> {dst}")
return True
else:
print(f"Warning: Source file does not exist: {src}")
return False
def copy_directory(src, dst):
"""Copy directory"""
if os.path.exists(src):
shutil.copytree(src, dst, dirs_exist_ok=True)
print(f"Copied directory: {src} -> {dst}")
return True
else:
print(f"Warning: Source directory does not exist: {src}")
return False
def process_sr_models(wakenet_model_dirs, multinet_model_dirs, build_dir, assets_dir):
2025-09-16 19:05:28 +08:00
"""Process SR models (wakenet and multinet) and generate srmodels.bin"""
if not wakenet_model_dirs and not multinet_model_dirs:
return None
2025-09-16 19:05:28 +08:00
# Create SR models build directory
sr_models_build_dir = os.path.join(build_dir, "srmodels")
if os.path.exists(sr_models_build_dir):
shutil.rmtree(sr_models_build_dir)
os.makedirs(sr_models_build_dir)
models_processed = 0
# Copy wakenet models if available
if wakenet_model_dirs:
for wakenet_model_dir in wakenet_model_dirs:
wakenet_name = os.path.basename(wakenet_model_dir)
wakenet_dst = os.path.join(sr_models_build_dir, wakenet_name)
if copy_directory(wakenet_model_dir, wakenet_dst):
models_processed += 1
print(f"Added wakenet model: {wakenet_name}")
2025-09-16 19:05:28 +08:00
# Copy multinet models if available
if multinet_model_dirs:
for multinet_model_dir in multinet_model_dirs:
multinet_name = os.path.basename(multinet_model_dir)
multinet_dst = os.path.join(sr_models_build_dir, multinet_name)
if copy_directory(multinet_model_dir, multinet_dst):
models_processed += 1
print(f"Added multinet model: {multinet_name}")
if models_processed == 0:
print("Warning: No SR models were successfully processed")
return None
# Use pack_models function to generate srmodels.bin
2025-09-16 19:05:28 +08:00
srmodels_output = os.path.join(sr_models_build_dir, "srmodels.bin")
try:
2025-09-16 19:05:28 +08:00
pack_models(sr_models_build_dir, "srmodels.bin")
print(f"Generated: {srmodels_output}")
# Copy srmodels.bin to assets directory
copy_file(srmodels_output, os.path.join(assets_dir, "srmodels.bin"))
return "srmodels.bin"
except Exception as e:
print(f"Error: Failed to generate srmodels.bin: {e}")
return None
def process_text_font(text_font_file, assets_dir):
"""Process text_font parameter"""
if not text_font_file:
return None
# Copy input file to build/assets directory
font_filename = os.path.basename(text_font_file)
font_dst = os.path.join(assets_dir, font_filename)
if copy_file(text_font_file, font_dst):
return font_filename
return None
def process_emoji_collection(emoji_collection_dir, assets_dir):
"""Process emoji_collection parameter"""
if not emoji_collection_dir:
return []
emoji_list = []
Refactor emoji initialization for Electron and Otto boards to use Assets system (#1704) * otto v1.4.0 MCP 1.使用MCP协议控制机器人 2.gif继承lcdDisplay,避免修改lcdDisplay * otto v1.4.1 gif as components gif as components * electronBot v1.1.0 mcp 1.增加electronBot支持 2.mcp协议 3.gif 作为组件 4.display子类 * 规范代码 1.规范代码 2.修复切换主题死机bug * fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug * 1.增加robot舵机初始位置校准 2.fix(mcp_sever) 超出范围异常捕获类型 bug * refactor: Update Electron and Otto emoji display implementations - Removed GIF selection from Kconfig for Electron and Otto boards. - Updated Electron and Otto bot versions to 2.0.4 in their respective config files. - Refactored emoji display classes to utilize EmojiCollection for managing emojis. - Enhanced chat label setup and status display functionality in both classes. - Cleaned up unused code and improved initialization logging for emoji displays. * Rename OTTO_ICON_FONT.c to otto_icon_font.c * Rename OTTO_ICON_FONT.c to otto_icon_font.c * refactor: Update Otto emoji display configurations and functionalities - Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays. - Bumped Otto robot version to 2.0.5 in the configuration file. - Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase. - Enhanced servo sequence handling and added support for executing custom servo sequences. - Improved logging and error handling for servo sequence execution. * refactor: Update chat label long mode for Electron and Otto emoji displays - Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays. - Improved consistency in chat label setup across both implementations. * Update Otto robot README with new actions and parameters * Update Otto controller parameters for oscillation settings - Changed default oscillation period from 500ms to 300ms. - Increased default steps from 5.0 to 8.0. - Updated default amplitude from 20 degrees to 0 degrees. - Enhanced documentation with new examples for oscillation modes and sequences. * Fix default amplitude initialization in Otto controller to use a single zero instead of two digits. * chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml * Refactor Otto controller - Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters. - Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system. * Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples. * Add camera support for Otto Robot board - Introduced configuration option to enable the Otto Robot camera in Kconfig. - Updated config.h to define camera-related GPIO pins and settings. - Modified config.json to include camera configuration. - Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled. - Adjusted power_manager.h to manage battery updates during camera operations. - Removed unused SetupChatLabel method from OttoEmojiDisplay class. * Refactor Otto Robot configuration and initialization - Removed the camera configuration option from Kconfig and related code. - Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings. - Updated config.h to define camera and non-camera configurations using the new struct. - Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization. - Enhanced camera detection and initialization logic based on hardware version. - Improved audio codec initialization based on configuration settings. * Refactor emoji initialization for Electron and Otto boards to use Assets system - Removed direct emoji initialization from `InitializeElectronEmojis` and `InitializeOttoEmojis` methods, delegating the responsibility to the Assets system. - Updated `CMakeLists.txt` to set `DEFAULT_EMOJI_COLLECTION` to `otto-gif` for both boards. - Enhanced `build_default_assets.py` to support alias mapping for Otto GIF emojis. - Updated `idf_component.yml` to bump `otto-emoji-gif-component` version to `^1.0.5` for improved functionality.
2026-01-31 18:13:15 +08:00
# Check if this is otto-gif collection
is_otto_gif = 'otto-emoji-gif-component' in emoji_collection_dir or emoji_collection_dir.endswith('otto-gif')
# Otto GIF emoji aliases mapping
otto_gif_aliases = {
"staticstate": ["neutral", "relaxed", "sleepy", "idle"],
"happy": ["laughing", "funny", "loving", "confident", "winking", "cool", "delicious", "kissy", "silly"],
"sad": ["crying"],
"anger": ["angry"],
"scare": ["surprised", "shocked"],
"buxue": ["thinking", "confused", "embarrassed"]
}
# Copy each image from input directory to build/assets directory
for root, dirs, files in os.walk(emoji_collection_dir):
for file in files:
if file.lower().endswith(('.png', '.gif')):
# Copy file
src_file = os.path.join(root, file)
dst_file = os.path.join(assets_dir, file)
if copy_file(src_file, dst_file):
# Get filename without extension
filename_without_ext = os.path.splitext(file)[0]
Refactor emoji initialization for Electron and Otto boards to use Assets system (#1704) * otto v1.4.0 MCP 1.使用MCP协议控制机器人 2.gif继承lcdDisplay,避免修改lcdDisplay * otto v1.4.1 gif as components gif as components * electronBot v1.1.0 mcp 1.增加electronBot支持 2.mcp协议 3.gif 作为组件 4.display子类 * 规范代码 1.规范代码 2.修复切换主题死机bug * fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug * 1.增加robot舵机初始位置校准 2.fix(mcp_sever) 超出范围异常捕获类型 bug * refactor: Update Electron and Otto emoji display implementations - Removed GIF selection from Kconfig for Electron and Otto boards. - Updated Electron and Otto bot versions to 2.0.4 in their respective config files. - Refactored emoji display classes to utilize EmojiCollection for managing emojis. - Enhanced chat label setup and status display functionality in both classes. - Cleaned up unused code and improved initialization logging for emoji displays. * Rename OTTO_ICON_FONT.c to otto_icon_font.c * Rename OTTO_ICON_FONT.c to otto_icon_font.c * refactor: Update Otto emoji display configurations and functionalities - Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays. - Bumped Otto robot version to 2.0.5 in the configuration file. - Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase. - Enhanced servo sequence handling and added support for executing custom servo sequences. - Improved logging and error handling for servo sequence execution. * refactor: Update chat label long mode for Electron and Otto emoji displays - Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays. - Improved consistency in chat label setup across both implementations. * Update Otto robot README with new actions and parameters * Update Otto controller parameters for oscillation settings - Changed default oscillation period from 500ms to 300ms. - Increased default steps from 5.0 to 8.0. - Updated default amplitude from 20 degrees to 0 degrees. - Enhanced documentation with new examples for oscillation modes and sequences. * Fix default amplitude initialization in Otto controller to use a single zero instead of two digits. * chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml * Refactor Otto controller - Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters. - Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system. * Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples. * Add camera support for Otto Robot board - Introduced configuration option to enable the Otto Robot camera in Kconfig. - Updated config.h to define camera-related GPIO pins and settings. - Modified config.json to include camera configuration. - Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled. - Adjusted power_manager.h to manage battery updates during camera operations. - Removed unused SetupChatLabel method from OttoEmojiDisplay class. * Refactor Otto Robot configuration and initialization - Removed the camera configuration option from Kconfig and related code. - Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings. - Updated config.h to define camera and non-camera configurations using the new struct. - Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization. - Enhanced camera detection and initialization logic based on hardware version. - Improved audio codec initialization based on configuration settings. * Refactor emoji initialization for Electron and Otto boards to use Assets system - Removed direct emoji initialization from `InitializeElectronEmojis` and `InitializeOttoEmojis` methods, delegating the responsibility to the Assets system. - Updated `CMakeLists.txt` to set `DEFAULT_EMOJI_COLLECTION` to `otto-gif` for both boards. - Enhanced `build_default_assets.py` to support alias mapping for Otto GIF emojis. - Updated `idf_component.yml` to bump `otto-emoji-gif-component` version to `^1.0.5` for improved functionality.
2026-01-31 18:13:15 +08:00
# Add main emoji entry
emoji_list.append({
"name": filename_without_ext,
"file": file
})
Refactor emoji initialization for Electron and Otto boards to use Assets system (#1704) * otto v1.4.0 MCP 1.使用MCP协议控制机器人 2.gif继承lcdDisplay,避免修改lcdDisplay * otto v1.4.1 gif as components gif as components * electronBot v1.1.0 mcp 1.增加electronBot支持 2.mcp协议 3.gif 作为组件 4.display子类 * 规范代码 1.规范代码 2.修复切换主题死机bug * fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug * 1.增加robot舵机初始位置校准 2.fix(mcp_sever) 超出范围异常捕获类型 bug * refactor: Update Electron and Otto emoji display implementations - Removed GIF selection from Kconfig for Electron and Otto boards. - Updated Electron and Otto bot versions to 2.0.4 in their respective config files. - Refactored emoji display classes to utilize EmojiCollection for managing emojis. - Enhanced chat label setup and status display functionality in both classes. - Cleaned up unused code and improved initialization logging for emoji displays. * Rename OTTO_ICON_FONT.c to otto_icon_font.c * Rename OTTO_ICON_FONT.c to otto_icon_font.c * refactor: Update Otto emoji display configurations and functionalities - Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays. - Bumped Otto robot version to 2.0.5 in the configuration file. - Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase. - Enhanced servo sequence handling and added support for executing custom servo sequences. - Improved logging and error handling for servo sequence execution. * refactor: Update chat label long mode for Electron and Otto emoji displays - Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays. - Improved consistency in chat label setup across both implementations. * Update Otto robot README with new actions and parameters * Update Otto controller parameters for oscillation settings - Changed default oscillation period from 500ms to 300ms. - Increased default steps from 5.0 to 8.0. - Updated default amplitude from 20 degrees to 0 degrees. - Enhanced documentation with new examples for oscillation modes and sequences. * Fix default amplitude initialization in Otto controller to use a single zero instead of two digits. * chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml * Refactor Otto controller - Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters. - Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system. * Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples. * Add camera support for Otto Robot board - Introduced configuration option to enable the Otto Robot camera in Kconfig. - Updated config.h to define camera-related GPIO pins and settings. - Modified config.json to include camera configuration. - Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled. - Adjusted power_manager.h to manage battery updates during camera operations. - Removed unused SetupChatLabel method from OttoEmojiDisplay class. * Refactor Otto Robot configuration and initialization - Removed the camera configuration option from Kconfig and related code. - Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings. - Updated config.h to define camera and non-camera configurations using the new struct. - Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization. - Enhanced camera detection and initialization logic based on hardware version. - Improved audio codec initialization based on configuration settings. * Refactor emoji initialization for Electron and Otto boards to use Assets system - Removed direct emoji initialization from `InitializeElectronEmojis` and `InitializeOttoEmojis` methods, delegating the responsibility to the Assets system. - Updated `CMakeLists.txt` to set `DEFAULT_EMOJI_COLLECTION` to `otto-gif` for both boards. - Enhanced `build_default_assets.py` to support alias mapping for Otto GIF emojis. - Updated `idf_component.yml` to bump `otto-emoji-gif-component` version to `^1.0.5` for improved functionality.
2026-01-31 18:13:15 +08:00
# Add aliases for otto-gif emojis
if is_otto_gif and filename_without_ext in otto_gif_aliases:
for alias in otto_gif_aliases[filename_without_ext]:
emoji_list.append({
"name": alias,
"file": file
})
return emoji_list
def process_extra_files(extra_files_dir, assets_dir):
"""Process default_assets_extra_files parameter"""
if not extra_files_dir:
return []
if not os.path.exists(extra_files_dir):
print(f"Warning: Extra files directory not found: {extra_files_dir}")
return []
extra_files_list = []
# Copy each file from input directory to build/assets directory
for root, dirs, files in os.walk(extra_files_dir):
for file in files:
# Skip hidden files and directories
if file.startswith('.'):
continue
# Copy file
src_file = os.path.join(root, file)
dst_file = os.path.join(assets_dir, file)
if copy_file(src_file, dst_file):
extra_files_list.append(file)
if extra_files_list:
print(f"Processed {len(extra_files_list)} extra files from: {extra_files_dir}")
return extra_files_list
2025-09-16 19:05:28 +08:00
def generate_index_json(assets_dir, srmodels, text_font, emoji_collection, extra_files=None, multinet_model_info=None):
"""Generate index.json file"""
index_data = {
"version": 1
}
if srmodels:
index_data["srmodels"] = srmodels
if text_font:
index_data["text_font"] = text_font
if emoji_collection:
index_data["emoji_collection"] = emoji_collection
if extra_files:
index_data["extra_files"] = extra_files
2025-09-16 19:05:28 +08:00
if multinet_model_info:
index_data["multinet_model"] = multinet_model_info
# Write index.json
index_path = os.path.join(assets_dir, "index.json")
with open(index_path, 'w', encoding='utf-8') as f:
json.dump(index_data, f, indent=4, ensure_ascii=False)
print(f"Generated: {index_path}")
def generate_config_json(build_dir, assets_dir):
"""Generate config.json file"""
config_data = {
"include_path": os.path.join(build_dir, "include"),
"assets_path": assets_dir,
"image_file": os.path.join(build_dir, "output", "assets.bin"),
"lvgl_ver": "9.3.0",
"assets_size": "0x400000",
"support_format": ".png, .gif, .jpg, .bin, .json",
"name_length": "32",
"split_height": "0",
"support_qoi": False,
"support_spng": False,
"support_sjpg": False,
"support_sqoi": False,
"support_raw": False,
"support_raw_dither": False,
"support_raw_bgr": False
}
# Write config.json
config_path = os.path.join(build_dir, "config.json")
with open(config_path, 'w', encoding='utf-8') as f:
json.dump(config_data, f, indent=4, ensure_ascii=False)
print(f"Generated: {config_path}")
return config_path
# =============================================================================
# Simplified SPIFFS assets generation (from spiffs_assets_gen.py)
# =============================================================================
def compute_checksum(data):
checksum = sum(data) & 0xFFFF
return checksum
def sort_key(filename):
basename, extension = os.path.splitext(filename)
return extension, basename
def pack_assets_simple(target_path, include_path, out_file, assets_path, max_name_len=32):
"""
Simplified version of pack_assets that handles basic file packing
"""
merged_data = bytearray()
file_info_list = []
skip_files = ['config.json']
# Ensure output directory exists
os.makedirs(os.path.dirname(out_file), exist_ok=True)
os.makedirs(include_path, exist_ok=True)
file_list = sorted(os.listdir(target_path), key=sort_key)
for filename in file_list:
if filename in skip_files:
continue
file_path = os.path.join(target_path, filename)
if not os.path.isfile(file_path):
continue
file_name = os.path.basename(file_path)
file_size = os.path.getsize(file_path)
file_info_list.append((file_name, len(merged_data), file_size, 0, 0))
# Add 0x5A5A prefix to merged_data
merged_data.extend(b'\x5A' * 2)
with open(file_path, 'rb') as bin_file:
bin_data = bin_file.read()
merged_data.extend(bin_data)
total_files = len(file_info_list)
mmap_table = bytearray()
for file_name, offset, file_size, width, height in file_info_list:
if len(file_name) > max_name_len:
print(f'Warning: "{file_name}" exceeds {max_name_len} bytes and will be truncated.')
fixed_name = file_name.ljust(max_name_len, '\0')[:max_name_len]
mmap_table.extend(fixed_name.encode('utf-8'))
mmap_table.extend(file_size.to_bytes(4, byteorder='little'))
mmap_table.extend(offset.to_bytes(4, byteorder='little'))
mmap_table.extend(width.to_bytes(2, byteorder='little'))
mmap_table.extend(height.to_bytes(2, byteorder='little'))
combined_data = mmap_table + merged_data
combined_checksum = compute_checksum(combined_data)
combined_data_length = len(combined_data).to_bytes(4, byteorder='little')
header_data = total_files.to_bytes(4, byteorder='little') + combined_checksum.to_bytes(4, byteorder='little')
final_data = header_data + combined_data_length + combined_data
with open(out_file, 'wb') as output_bin:
output_bin.write(final_data)
# Generate header file
current_year = datetime.now().year
asset_name = os.path.basename(assets_path)
header_file_path = os.path.join(include_path, f'mmap_generate_{asset_name}.h')
with open(header_file_path, 'w') as output_header:
output_header.write('/*\n')
output_header.write(' * SPDX-FileCopyrightText: 2022-{} Espressif Systems (Shanghai) CO LTD\n'.format(current_year))
output_header.write(' *\n')
output_header.write(' * SPDX-License-Identifier: Apache-2.0\n')
output_header.write(' */\n\n')
output_header.write('/**\n')
output_header.write(' * @file\n')
output_header.write(" * @brief This file was generated by esp_mmap_assets, don't modify it\n")
output_header.write(' */\n\n')
output_header.write('#pragma once\n\n')
output_header.write("#include \"esp_mmap_assets.h\"\n\n")
output_header.write(f'#define MMAP_{asset_name.upper()}_FILES {total_files}\n')
output_header.write(f'#define MMAP_{asset_name.upper()}_CHECKSUM 0x{combined_checksum:04X}\n\n')
output_header.write(f'enum MMAP_{asset_name.upper()}_LISTS {{\n')
for i, (file_name, _, _, _, _) in enumerate(file_info_list):
enum_name = file_name.replace('.', '_')
output_header.write(f' MMAP_{asset_name.upper()}_{enum_name.upper()} = {i}, /*!< {file_name} */\n')
output_header.write('};\n')
print(f'All files have been merged into {os.path.basename(out_file)}')
# =============================================================================
# Configuration and main functions
# =============================================================================
def read_wakenet_from_sdkconfig(sdkconfig_path):
"""
Read wakenet models from sdkconfig (based on movemodel.py logic)
Returns a list of wakenet model names
"""
if not os.path.exists(sdkconfig_path):
print(f"Warning: sdkconfig file not found: {sdkconfig_path}")
return []
models = []
with io.open(sdkconfig_path, "r") as f:
for label in f:
label = label.strip("\n")
if 'CONFIG_SR_WN' in label and '#' not in label[0]:
if '_NONE' in label:
continue
if '=' in label:
label = label.split("=")[0]
if '_MULTI' in label:
label = label[:-6]
model_name = label.split("_SR_WN_")[-1].lower()
models.append(model_name)
return models
2025-09-16 19:05:28 +08:00
def read_multinet_from_sdkconfig(sdkconfig_path):
"""
Read multinet models from sdkconfig (based on movemodel.py logic)
Returns a list of multinet model names
"""
if not os.path.exists(sdkconfig_path):
print(f"Warning: sdkconfig file not found: {sdkconfig_path}")
return []
with io.open(sdkconfig_path, "r") as f:
models_string = ''
for label in f:
label = label.strip("\n")
if 'CONFIG_SR_MN' in label and label[0] != '#':
models_string += label
models = []
if "CONFIG_SR_MN_CN_MULTINET3_SINGLE_RECOGNITION" in models_string:
models.append('mn3_cn')
elif "CONFIG_SR_MN_CN_MULTINET4_5_SINGLE_RECOGNITION_QUANT8" in models_string:
models.append('mn4q8_cn')
elif "CONFIG_SR_MN_CN_MULTINET4_5_SINGLE_RECOGNITION" in models_string:
models.append('mn4_cn')
elif "CONFIG_SR_MN_CN_MULTINET5_RECOGNITION_QUANT8" in models_string:
models.append('mn5q8_cn')
elif "CONFIG_SR_MN_CN_MULTINET6_QUANT" in models_string:
models.append('mn6_cn')
elif "CONFIG_SR_MN_CN_MULTINET6_AC_QUANT" in models_string:
models.append('mn6_cn_ac')
elif "CONFIG_SR_MN_CN_MULTINET7_QUANT" in models_string:
models.append('mn7_cn')
elif "CONFIG_SR_MN_CN_MULTINET7_AC_QUANT" in models_string:
models.append('mn7_cn_ac')
if "CONFIG_SR_MN_EN_MULTINET5_SINGLE_RECOGNITION_QUANT8" in models_string:
models.append('mn5q8_en')
elif "CONFIG_SR_MN_EN_MULTINET5_SINGLE_RECOGNITION" in models_string:
models.append('mn5_en')
elif "CONFIG_SR_MN_EN_MULTINET6_QUANT" in models_string:
models.append('mn6_en')
elif "CONFIG_SR_MN_EN_MULTINET7_QUANT" in models_string:
models.append('mn7_en')
if "MULTINET6" in models_string or "MULTINET7" in models_string:
models.append('fst')
return models
def read_wake_word_type_from_sdkconfig(sdkconfig_path):
"""
Read wake word type configuration from sdkconfig
Returns a dict with wake word type info
"""
if not os.path.exists(sdkconfig_path):
print(f"Warning: sdkconfig file not found: {sdkconfig_path}")
return {
'use_esp_wake_word': False,
'use_afe_wake_word': False,
'use_custom_wake_word': False,
'wake_word_disabled': True
}
config_values = {
'use_esp_wake_word': False,
'use_afe_wake_word': False,
'use_custom_wake_word': False,
'wake_word_disabled': False
}
with io.open(sdkconfig_path, "r") as f:
for line in f:
line = line.strip("\n")
if line.startswith('#'):
continue
# Check for wake word type configuration
if 'CONFIG_USE_ESP_WAKE_WORD=y' in line:
config_values['use_esp_wake_word'] = True
elif 'CONFIG_USE_AFE_WAKE_WORD=y' in line:
config_values['use_afe_wake_word'] = True
elif 'CONFIG_USE_CUSTOM_WAKE_WORD=y' in line:
config_values['use_custom_wake_word'] = True
elif 'CONFIG_WAKE_WORD_DISABLED=y' in line:
config_values['wake_word_disabled'] = True
return config_values
2025-09-16 19:05:28 +08:00
def read_custom_wake_word_from_sdkconfig(sdkconfig_path):
"""
Read custom wake word configuration from sdkconfig
Returns a dict with custom wake word info or None if not configured
"""
if not os.path.exists(sdkconfig_path):
print(f"Warning: sdkconfig file not found: {sdkconfig_path}")
return None
config_values = {}
with io.open(sdkconfig_path, "r") as f:
for line in f:
line = line.strip("\n")
if line.startswith('#') or '=' not in line:
continue
# Check for custom wake word configuration
if 'CONFIG_USE_CUSTOM_WAKE_WORD=y' in line:
config_values['use_custom_wake_word'] = True
elif 'CONFIG_CUSTOM_WAKE_WORD=' in line and not line.startswith('#'):
# Extract string value (remove quotes)
value = line.split('=', 1)[1].strip('"')
config_values['wake_word'] = value
elif 'CONFIG_CUSTOM_WAKE_WORD_DISPLAY=' in line and not line.startswith('#'):
# Extract string value (remove quotes)
value = line.split('=', 1)[1].strip('"')
config_values['display'] = value
elif 'CONFIG_CUSTOM_WAKE_WORD_THRESHOLD=' in line and not line.startswith('#'):
# Extract numeric value
value = line.split('=', 1)[1]
try:
config_values['threshold'] = int(value)
except ValueError:
try:
config_values['threshold'] = float(value)
except ValueError:
print(f"Warning: Invalid threshold value: {value}")
config_values['threshold'] = 20 # default (will be converted to 0.2)
# Return config only if custom wake word is enabled and required fields are present
if (config_values.get('use_custom_wake_word', False) and
'wake_word' in config_values and
'display' in config_values and
'threshold' in config_values):
return {
'wake_word': config_values['wake_word'],
'display': config_values['display'],
'threshold': config_values['threshold'] / 100.0 # Convert to decimal (20 -> 0.2)
}
return None
def get_language_from_multinet_models(multinet_models):
"""
Determine language from multinet model names
Returns 'cn', 'en', or None
"""
if not multinet_models:
return None
# Check for Chinese models
cn_indicators = ['_cn', 'cn_']
en_indicators = ['_en', 'en_']
has_cn = any(any(indicator in model for indicator in cn_indicators) for model in multinet_models)
has_en = any(any(indicator in model for indicator in en_indicators) for model in multinet_models)
# If both or neither, default to cn
if has_cn and not has_en:
return 'cn'
elif has_en and not has_cn:
return 'en'
else:
return 'cn' # Default to Chinese
def get_wakenet_model_paths(model_names, esp_sr_model_path):
"""
Get the full paths to the wakenet model directories
Returns a list of valid model paths
"""
if not model_names:
return []
valid_paths = []
for model_name in model_names:
wakenet_model_path = os.path.join(esp_sr_model_path, 'wakenet_model', model_name)
if os.path.exists(wakenet_model_path):
valid_paths.append(wakenet_model_path)
else:
print(f"Warning: Wakenet model directory not found: {wakenet_model_path}")
return valid_paths
2025-09-16 19:05:28 +08:00
def get_multinet_model_paths(model_names, esp_sr_model_path):
"""
Get the full paths to the multinet model directories
Returns a list of valid model paths
"""
if not model_names:
return []
valid_paths = []
for model_name in model_names:
multinet_model_path = os.path.join(esp_sr_model_path, 'multinet_model', model_name)
if os.path.exists(multinet_model_path):
valid_paths.append(multinet_model_path)
else:
print(f"Warning: Multinet model directory not found: {multinet_model_path}")
return valid_paths
def get_text_font_path(builtin_text_font, xiaozhi_fonts_path):
"""
Get the text font path if needed
Returns the font file path or None if no font is needed
"""
if not builtin_text_font or 'basic' not in builtin_text_font:
return None
# Convert from basic to common font name
# e.g., font_puhui_basic_16_4 -> font_puhui_common_16_4.bin
if builtin_text_font.startswith('font_noto_'):
font_name = builtin_text_font.replace('basic', 'qwen') + '.bin'
else:
font_name = builtin_text_font.replace('basic', 'common') + '.bin'
font_path = os.path.join(xiaozhi_fonts_path, 'cbin', font_name)
if os.path.exists(font_path):
return font_path
else:
print(f"Warning: Font file not found: {font_path}")
return None
Refactor emoji initialization for Electron and Otto boards to use Assets system (#1704) * otto v1.4.0 MCP 1.使用MCP协议控制机器人 2.gif继承lcdDisplay,避免修改lcdDisplay * otto v1.4.1 gif as components gif as components * electronBot v1.1.0 mcp 1.增加electronBot支持 2.mcp协议 3.gif 作为组件 4.display子类 * 规范代码 1.规范代码 2.修复切换主题死机bug * fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug * 1.增加robot舵机初始位置校准 2.fix(mcp_sever) 超出范围异常捕获类型 bug * refactor: Update Electron and Otto emoji display implementations - Removed GIF selection from Kconfig for Electron and Otto boards. - Updated Electron and Otto bot versions to 2.0.4 in their respective config files. - Refactored emoji display classes to utilize EmojiCollection for managing emojis. - Enhanced chat label setup and status display functionality in both classes. - Cleaned up unused code and improved initialization logging for emoji displays. * Rename OTTO_ICON_FONT.c to otto_icon_font.c * Rename OTTO_ICON_FONT.c to otto_icon_font.c * refactor: Update Otto emoji display configurations and functionalities - Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays. - Bumped Otto robot version to 2.0.5 in the configuration file. - Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase. - Enhanced servo sequence handling and added support for executing custom servo sequences. - Improved logging and error handling for servo sequence execution. * refactor: Update chat label long mode for Electron and Otto emoji displays - Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays. - Improved consistency in chat label setup across both implementations. * Update Otto robot README with new actions and parameters * Update Otto controller parameters for oscillation settings - Changed default oscillation period from 500ms to 300ms. - Increased default steps from 5.0 to 8.0. - Updated default amplitude from 20 degrees to 0 degrees. - Enhanced documentation with new examples for oscillation modes and sequences. * Fix default amplitude initialization in Otto controller to use a single zero instead of two digits. * chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml * Refactor Otto controller - Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters. - Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system. * Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples. * Add camera support for Otto Robot board - Introduced configuration option to enable the Otto Robot camera in Kconfig. - Updated config.h to define camera-related GPIO pins and settings. - Modified config.json to include camera configuration. - Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled. - Adjusted power_manager.h to manage battery updates during camera operations. - Removed unused SetupChatLabel method from OttoEmojiDisplay class. * Refactor Otto Robot configuration and initialization - Removed the camera configuration option from Kconfig and related code. - Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings. - Updated config.h to define camera and non-camera configurations using the new struct. - Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization. - Enhanced camera detection and initialization logic based on hardware version. - Improved audio codec initialization based on configuration settings. * Refactor emoji initialization for Electron and Otto boards to use Assets system - Removed direct emoji initialization from `InitializeElectronEmojis` and `InitializeOttoEmojis` methods, delegating the responsibility to the Assets system. - Updated `CMakeLists.txt` to set `DEFAULT_EMOJI_COLLECTION` to `otto-gif` for both boards. - Enhanced `build_default_assets.py` to support alias mapping for Otto GIF emojis. - Updated `idf_component.yml` to bump `otto-emoji-gif-component` version to `^1.0.5` for improved functionality.
2026-01-31 18:13:15 +08:00
def get_emoji_collection_path(default_emoji_collection, xiaozhi_fonts_path, project_root=None):
"""
Get the emoji collection path if needed
Returns the emoji directory path or None if no emoji collection is needed
Refactor emoji initialization for Electron and Otto boards to use Assets system (#1704) * otto v1.4.0 MCP 1.使用MCP协议控制机器人 2.gif继承lcdDisplay,避免修改lcdDisplay * otto v1.4.1 gif as components gif as components * electronBot v1.1.0 mcp 1.增加electronBot支持 2.mcp协议 3.gif 作为组件 4.display子类 * 规范代码 1.规范代码 2.修复切换主题死机bug * fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug * 1.增加robot舵机初始位置校准 2.fix(mcp_sever) 超出范围异常捕获类型 bug * refactor: Update Electron and Otto emoji display implementations - Removed GIF selection from Kconfig for Electron and Otto boards. - Updated Electron and Otto bot versions to 2.0.4 in their respective config files. - Refactored emoji display classes to utilize EmojiCollection for managing emojis. - Enhanced chat label setup and status display functionality in both classes. - Cleaned up unused code and improved initialization logging for emoji displays. * Rename OTTO_ICON_FONT.c to otto_icon_font.c * Rename OTTO_ICON_FONT.c to otto_icon_font.c * refactor: Update Otto emoji display configurations and functionalities - Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays. - Bumped Otto robot version to 2.0.5 in the configuration file. - Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase. - Enhanced servo sequence handling and added support for executing custom servo sequences. - Improved logging and error handling for servo sequence execution. * refactor: Update chat label long mode for Electron and Otto emoji displays - Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays. - Improved consistency in chat label setup across both implementations. * Update Otto robot README with new actions and parameters * Update Otto controller parameters for oscillation settings - Changed default oscillation period from 500ms to 300ms. - Increased default steps from 5.0 to 8.0. - Updated default amplitude from 20 degrees to 0 degrees. - Enhanced documentation with new examples for oscillation modes and sequences. * Fix default amplitude initialization in Otto controller to use a single zero instead of two digits. * chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml * Refactor Otto controller - Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters. - Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system. * Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples. * Add camera support for Otto Robot board - Introduced configuration option to enable the Otto Robot camera in Kconfig. - Updated config.h to define camera-related GPIO pins and settings. - Modified config.json to include camera configuration. - Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled. - Adjusted power_manager.h to manage battery updates during camera operations. - Removed unused SetupChatLabel method from OttoEmojiDisplay class. * Refactor Otto Robot configuration and initialization - Removed the camera configuration option from Kconfig and related code. - Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings. - Updated config.h to define camera and non-camera configurations using the new struct. - Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization. - Enhanced camera detection and initialization logic based on hardware version. - Improved audio codec initialization based on configuration settings. * Refactor emoji initialization for Electron and Otto boards to use Assets system - Removed direct emoji initialization from `InitializeElectronEmojis` and `InitializeOttoEmojis` methods, delegating the responsibility to the Assets system. - Updated `CMakeLists.txt` to set `DEFAULT_EMOJI_COLLECTION` to `otto-gif` for both boards. - Enhanced `build_default_assets.py` to support alias mapping for Otto GIF emojis. - Updated `idf_component.yml` to bump `otto-emoji-gif-component` version to `^1.0.5` for improved functionality.
2026-01-31 18:13:15 +08:00
Supports:
- PNG emoji collections from xiaozhi-fonts (e.g., emojis_32, twemoji_64)
- GIF emoji collections from xiaozhi-fonts (e.g., noto-emoji_128, noto-emoji_64)
Refactor emoji initialization for Electron and Otto boards to use Assets system (#1704) * otto v1.4.0 MCP 1.使用MCP协议控制机器人 2.gif继承lcdDisplay,避免修改lcdDisplay * otto v1.4.1 gif as components gif as components * electronBot v1.1.0 mcp 1.增加electronBot支持 2.mcp协议 3.gif 作为组件 4.display子类 * 规范代码 1.规范代码 2.修复切换主题死机bug * fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug * 1.增加robot舵机初始位置校准 2.fix(mcp_sever) 超出范围异常捕获类型 bug * refactor: Update Electron and Otto emoji display implementations - Removed GIF selection from Kconfig for Electron and Otto boards. - Updated Electron and Otto bot versions to 2.0.4 in their respective config files. - Refactored emoji display classes to utilize EmojiCollection for managing emojis. - Enhanced chat label setup and status display functionality in both classes. - Cleaned up unused code and improved initialization logging for emoji displays. * Rename OTTO_ICON_FONT.c to otto_icon_font.c * Rename OTTO_ICON_FONT.c to otto_icon_font.c * refactor: Update Otto emoji display configurations and functionalities - Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays. - Bumped Otto robot version to 2.0.5 in the configuration file. - Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase. - Enhanced servo sequence handling and added support for executing custom servo sequences. - Improved logging and error handling for servo sequence execution. * refactor: Update chat label long mode for Electron and Otto emoji displays - Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays. - Improved consistency in chat label setup across both implementations. * Update Otto robot README with new actions and parameters * Update Otto controller parameters for oscillation settings - Changed default oscillation period from 500ms to 300ms. - Increased default steps from 5.0 to 8.0. - Updated default amplitude from 20 degrees to 0 degrees. - Enhanced documentation with new examples for oscillation modes and sequences. * Fix default amplitude initialization in Otto controller to use a single zero instead of two digits. * chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml * Refactor Otto controller - Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters. - Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system. * Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples. * Add camera support for Otto Robot board - Introduced configuration option to enable the Otto Robot camera in Kconfig. - Updated config.h to define camera-related GPIO pins and settings. - Modified config.json to include camera configuration. - Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled. - Adjusted power_manager.h to manage battery updates during camera operations. - Removed unused SetupChatLabel method from OttoEmojiDisplay class. * Refactor Otto Robot configuration and initialization - Removed the camera configuration option from Kconfig and related code. - Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings. - Updated config.h to define camera and non-camera configurations using the new struct. - Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization. - Enhanced camera detection and initialization logic based on hardware version. - Improved audio codec initialization based on configuration settings. * Refactor emoji initialization for Electron and Otto boards to use Assets system - Removed direct emoji initialization from `InitializeElectronEmojis` and `InitializeOttoEmojis` methods, delegating the responsibility to the Assets system. - Updated `CMakeLists.txt` to set `DEFAULT_EMOJI_COLLECTION` to `otto-gif` for both boards. - Enhanced `build_default_assets.py` to support alias mapping for Otto GIF emojis. - Updated `idf_component.yml` to bump `otto-emoji-gif-component` version to `^1.0.5` for improved functionality.
2026-01-31 18:13:15 +08:00
- Otto GIF emoji collection (otto-gif)
"""
if not default_emoji_collection:
return None
Refactor emoji initialization for Electron and Otto boards to use Assets system (#1704) * otto v1.4.0 MCP 1.使用MCP协议控制机器人 2.gif继承lcdDisplay,避免修改lcdDisplay * otto v1.4.1 gif as components gif as components * electronBot v1.1.0 mcp 1.增加electronBot支持 2.mcp协议 3.gif 作为组件 4.display子类 * 规范代码 1.规范代码 2.修复切换主题死机bug * fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug * 1.增加robot舵机初始位置校准 2.fix(mcp_sever) 超出范围异常捕获类型 bug * refactor: Update Electron and Otto emoji display implementations - Removed GIF selection from Kconfig for Electron and Otto boards. - Updated Electron and Otto bot versions to 2.0.4 in their respective config files. - Refactored emoji display classes to utilize EmojiCollection for managing emojis. - Enhanced chat label setup and status display functionality in both classes. - Cleaned up unused code and improved initialization logging for emoji displays. * Rename OTTO_ICON_FONT.c to otto_icon_font.c * Rename OTTO_ICON_FONT.c to otto_icon_font.c * refactor: Update Otto emoji display configurations and functionalities - Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays. - Bumped Otto robot version to 2.0.5 in the configuration file. - Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase. - Enhanced servo sequence handling and added support for executing custom servo sequences. - Improved logging and error handling for servo sequence execution. * refactor: Update chat label long mode for Electron and Otto emoji displays - Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays. - Improved consistency in chat label setup across both implementations. * Update Otto robot README with new actions and parameters * Update Otto controller parameters for oscillation settings - Changed default oscillation period from 500ms to 300ms. - Increased default steps from 5.0 to 8.0. - Updated default amplitude from 20 degrees to 0 degrees. - Enhanced documentation with new examples for oscillation modes and sequences. * Fix default amplitude initialization in Otto controller to use a single zero instead of two digits. * chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml * Refactor Otto controller - Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters. - Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system. * Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples. * Add camera support for Otto Robot board - Introduced configuration option to enable the Otto Robot camera in Kconfig. - Updated config.h to define camera-related GPIO pins and settings. - Modified config.json to include camera configuration. - Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled. - Adjusted power_manager.h to manage battery updates during camera operations. - Removed unused SetupChatLabel method from OttoEmojiDisplay class. * Refactor Otto Robot configuration and initialization - Removed the camera configuration option from Kconfig and related code. - Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings. - Updated config.h to define camera and non-camera configurations using the new struct. - Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization. - Enhanced camera detection and initialization logic based on hardware version. - Improved audio codec initialization based on configuration settings. * Refactor emoji initialization for Electron and Otto boards to use Assets system - Removed direct emoji initialization from `InitializeElectronEmojis` and `InitializeOttoEmojis` methods, delegating the responsibility to the Assets system. - Updated `CMakeLists.txt` to set `DEFAULT_EMOJI_COLLECTION` to `otto-gif` for both boards. - Enhanced `build_default_assets.py` to support alias mapping for Otto GIF emojis. - Updated `idf_component.yml` to bump `otto-emoji-gif-component` version to `^1.0.5` for improved functionality.
2026-01-31 18:13:15 +08:00
# Special handling for otto-gif collection
if default_emoji_collection == 'otto-gif':
if project_root:
otto_gif_path = os.path.join(project_root, 'managed_components',
'txp666__otto-emoji-gif-component', 'gifs')
if os.path.exists(otto_gif_path):
return otto_gif_path
else:
print(f"Warning: Otto GIF emoji collection directory not found: {otto_gif_path}")
return None
else:
print("Warning: project_root not provided, cannot locate otto-gif collection")
return None
# Try PNG emoji collections first (e.g., emojis_32, twemoji_64)
emoji_path = os.path.join(xiaozhi_fonts_path, 'png', default_emoji_collection)
if os.path.exists(emoji_path):
return emoji_path
# Try GIF emoji collections (e.g., noto-emoji_128, noto-emoji_64, noto-emoji_32)
emoji_path = os.path.join(xiaozhi_fonts_path, 'gif', default_emoji_collection)
if os.path.exists(emoji_path):
return emoji_path
print(f"Warning: Emoji collection directory not found in png/ or gif/: {default_emoji_collection}")
return None
def build_assets_integrated(wakenet_model_paths, multinet_model_paths, text_font_path, emoji_collection_path, extra_files_path, output_path, multinet_model_info=None):
"""
Build assets using integrated functions (no external dependencies)
"""
# Create temporary build directory
temp_build_dir = os.path.join(os.path.dirname(output_path), "temp_build")
assets_dir = os.path.join(temp_build_dir, "assets")
try:
# Clean and create directories
if os.path.exists(temp_build_dir):
shutil.rmtree(temp_build_dir)
ensure_dir(temp_build_dir)
ensure_dir(assets_dir)
print("Starting to build assets...")
# Process each component
srmodels = process_sr_models(wakenet_model_paths, multinet_model_paths, temp_build_dir, assets_dir) if (wakenet_model_paths or multinet_model_paths) else None
text_font = process_text_font(text_font_path, assets_dir) if text_font_path else None
emoji_collection = process_emoji_collection(emoji_collection_path, assets_dir) if emoji_collection_path else None
extra_files = process_extra_files(extra_files_path, assets_dir) if extra_files_path else None
# Generate index.json
2025-09-16 19:05:28 +08:00
generate_index_json(assets_dir, srmodels, text_font, emoji_collection, extra_files, multinet_model_info)
# Generate config.json for packing
config_path = generate_config_json(temp_build_dir, assets_dir)
# Load config and pack assets
with open(config_path, 'r') as f:
config_data = json.load(f)
# Use simplified packing function
include_path = config_data['include_path']
image_file = config_data['image_file']
pack_assets_simple(assets_dir, include_path, image_file, "assets", int(config_data['name_length']))
# Copy final assets.bin to output location
if os.path.exists(image_file):
shutil.copy2(image_file, output_path)
print(f"Successfully generated assets.bin: {output_path}")
# Show size information
total_size = os.path.getsize(output_path)
print(f"Assets file size: {total_size / 1024:.2f}K ({total_size} bytes)")
return True
else:
print(f"Error: Generated assets.bin not found: {image_file}")
return False
except Exception as e:
print(f"Error: Failed to build assets: {e}")
return False
finally:
# Clean up temporary directory
if os.path.exists(temp_build_dir):
shutil.rmtree(temp_build_dir)
def main():
parser = argparse.ArgumentParser(description='Build default assets based on configuration')
parser.add_argument('--sdkconfig', required=True, help='Path to sdkconfig file')
parser.add_argument('--builtin_text_font', help='Builtin text font name (e.g., font_puhui_basic_16_4)')
parser.add_argument('--emoji_collection', help='Default emoji collection name (e.g., emojis_32)')
parser.add_argument('--output', required=True, help='Output path for assets.bin')
parser.add_argument('--esp_sr_model_path', help='Path to ESP-SR model directory')
parser.add_argument('--xiaozhi_fonts_path', help='Path to xiaozhi-fonts component directory')
parser.add_argument('--extra_files', help='Path to extra files directory to be included in assets')
args = parser.parse_args()
# Set default paths if not provided
if not args.esp_sr_model_path or not args.xiaozhi_fonts_path:
# Calculate project root from script location
script_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(script_dir)
if not args.esp_sr_model_path:
args.esp_sr_model_path = os.path.join(project_root, "managed_components", "espressif__esp-sr", "model")
if not args.xiaozhi_fonts_path:
args.xiaozhi_fonts_path = os.path.join(project_root, "components", "xiaozhi-fonts")
print("Building default assets...")
print(f" sdkconfig: {args.sdkconfig}")
print(f" builtin_text_font: {args.builtin_text_font}")
print(f" emoji_collection: {args.emoji_collection}")
print(f" output: {args.output}")
# Read wake word type configuration from sdkconfig
wake_word_config = read_wake_word_type_from_sdkconfig(args.sdkconfig)
2025-09-16 19:05:28 +08:00
# Read SR models from sdkconfig
wakenet_model_names = read_wakenet_from_sdkconfig(args.sdkconfig)
2025-09-16 19:05:28 +08:00
multinet_model_names = read_multinet_from_sdkconfig(args.sdkconfig)
# Apply wake word logic to decide which models to package
wakenet_model_paths = []
multinet_model_paths = []
# 1. Only package wakenet models if USE_ESP_WAKE_WORD=y or USE_AFE_WAKE_WORD=y
if wake_word_config['use_esp_wake_word'] or wake_word_config['use_afe_wake_word']:
wakenet_model_paths = get_wakenet_model_paths(wakenet_model_names, args.esp_sr_model_path)
elif wakenet_model_names:
print(f" Note: Found wakenet models {wakenet_model_names} but wake word type is not ESP/AFE, skipping")
# 2. Error check: if USE_CUSTOM_WAKE_WORD=y but no multinet models selected, report error
if wake_word_config['use_custom_wake_word'] and not multinet_model_names:
print("Error: USE_CUSTOM_WAKE_WORD is enabled but no multinet models are selected in sdkconfig")
print("Please select appropriate CONFIG_SR_MN_* options in menuconfig, or disable USE_CUSTOM_WAKE_WORD")
sys.exit(1)
# 3. Only package multinet models if USE_CUSTOM_WAKE_WORD=y
if wake_word_config['use_custom_wake_word']:
multinet_model_paths = get_multinet_model_paths(multinet_model_names, args.esp_sr_model_path)
elif multinet_model_names:
print(f" Note: Found multinet models {multinet_model_names} but USE_CUSTOM_WAKE_WORD is disabled, skipping")
2025-09-16 19:05:28 +08:00
# Print model information (only for models that will actually be packaged)
if wakenet_model_paths:
print(f" wakenet models: {', '.join(wakenet_model_names)} (will be packaged)")
if multinet_model_paths:
print(f" multinet models: {', '.join(multinet_model_names)} (will be packaged)")
# Get text font path if needed
text_font_path = get_text_font_path(args.builtin_text_font, args.xiaozhi_fonts_path)
# Get emoji collection path if needed
Refactor emoji initialization for Electron and Otto boards to use Assets system (#1704) * otto v1.4.0 MCP 1.使用MCP协议控制机器人 2.gif继承lcdDisplay,避免修改lcdDisplay * otto v1.4.1 gif as components gif as components * electronBot v1.1.0 mcp 1.增加electronBot支持 2.mcp协议 3.gif 作为组件 4.display子类 * 规范代码 1.规范代码 2.修复切换主题死机bug * fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug * 1.增加robot舵机初始位置校准 2.fix(mcp_sever) 超出范围异常捕获类型 bug * refactor: Update Electron and Otto emoji display implementations - Removed GIF selection from Kconfig for Electron and Otto boards. - Updated Electron and Otto bot versions to 2.0.4 in their respective config files. - Refactored emoji display classes to utilize EmojiCollection for managing emojis. - Enhanced chat label setup and status display functionality in both classes. - Cleaned up unused code and improved initialization logging for emoji displays. * Rename OTTO_ICON_FONT.c to otto_icon_font.c * Rename OTTO_ICON_FONT.c to otto_icon_font.c * refactor: Update Otto emoji display configurations and functionalities - Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays. - Bumped Otto robot version to 2.0.5 in the configuration file. - Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase. - Enhanced servo sequence handling and added support for executing custom servo sequences. - Improved logging and error handling for servo sequence execution. * refactor: Update chat label long mode for Electron and Otto emoji displays - Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays. - Improved consistency in chat label setup across both implementations. * Update Otto robot README with new actions and parameters * Update Otto controller parameters for oscillation settings - Changed default oscillation period from 500ms to 300ms. - Increased default steps from 5.0 to 8.0. - Updated default amplitude from 20 degrees to 0 degrees. - Enhanced documentation with new examples for oscillation modes and sequences. * Fix default amplitude initialization in Otto controller to use a single zero instead of two digits. * chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml * Refactor Otto controller - Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters. - Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system. * Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples. * Add camera support for Otto Robot board - Introduced configuration option to enable the Otto Robot camera in Kconfig. - Updated config.h to define camera-related GPIO pins and settings. - Modified config.json to include camera configuration. - Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled. - Adjusted power_manager.h to manage battery updates during camera operations. - Removed unused SetupChatLabel method from OttoEmojiDisplay class. * Refactor Otto Robot configuration and initialization - Removed the camera configuration option from Kconfig and related code. - Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings. - Updated config.h to define camera and non-camera configurations using the new struct. - Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization. - Enhanced camera detection and initialization logic based on hardware version. - Improved audio codec initialization based on configuration settings. * Refactor emoji initialization for Electron and Otto boards to use Assets system - Removed direct emoji initialization from `InitializeElectronEmojis` and `InitializeOttoEmojis` methods, delegating the responsibility to the Assets system. - Updated `CMakeLists.txt` to set `DEFAULT_EMOJI_COLLECTION` to `otto-gif` for both boards. - Enhanced `build_default_assets.py` to support alias mapping for Otto GIF emojis. - Updated `idf_component.yml` to bump `otto-emoji-gif-component` version to `^1.0.5` for improved functionality.
2026-01-31 18:13:15 +08:00
# Calculate project root from script location for otto-gif support
script_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(script_dir)
emoji_collection_path = get_emoji_collection_path(args.emoji_collection, args.xiaozhi_fonts_path, project_root)
# Get extra files path if provided
extra_files_path = args.extra_files
2025-09-16 19:05:28 +08:00
# Read custom wake word configuration
custom_wake_word_config = read_custom_wake_word_from_sdkconfig(args.sdkconfig)
multinet_model_info = None
if custom_wake_word_config and multinet_model_paths:
2025-09-16 19:05:28 +08:00
# Determine language from multinet models
language = get_language_from_multinet_models(multinet_model_names)
# Build multinet_model info structure
multinet_model_info = {
"language": language,
"duration": 3000, # Default duration in ms
"threshold": custom_wake_word_config['threshold'],
"commands": [
{
"command": custom_wake_word_config['wake_word'],
"text": custom_wake_word_config['display'],
"action": "wake"
}
]
}
print(f" custom wake word: {custom_wake_word_config['wake_word']} ({custom_wake_word_config['display']})")
print(f" wake word language: {language}")
print(f" wake word threshold: {custom_wake_word_config['threshold']}")
# Check if we have anything to build
if not wakenet_model_paths and not multinet_model_paths and not text_font_path and not emoji_collection_path and not extra_files_path and not multinet_model_info:
2025-09-16 19:05:28 +08:00
print("Warning: No assets to build (no SR models, text font, emoji collection, extra files, or custom wake word)")
# Create an empty assets.bin file
os.makedirs(os.path.dirname(args.output), exist_ok=True)
with open(args.output, 'wb') as f:
pass # Create empty file
print(f"Created empty assets.bin: {args.output}")
return
# Build the assets
success = build_assets_integrated(wakenet_model_paths, multinet_model_paths, text_font_path, emoji_collection_path,
2025-09-16 19:05:28 +08:00
extra_files_path, args.output, multinet_model_info)
if not success:
sys.exit(1)
print("Build completed successfully!")
if __name__ == "__main__":
main()