Initial commit

This commit is contained in:
2026-04-26 21:35:04 +08:00
commit da6ca1b09a
1483 changed files with 115719 additions and 0 deletions

1184
main/CMakeLists.txt Normal file

File diff suppressed because it is too large Load Diff

953
main/Kconfig.projbuild Normal file
View File

@@ -0,0 +1,953 @@
menu "Xiaozhi Assistant"
config OTA_URL
string "Default OTA URL"
default "https://api.tenclass.net/xiaozhi/ota/"
help
The application will access this URL to check for new firmwares and server address.
choice
prompt "Flash 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.
config FLASH_NONE_ASSETS
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
depends on FLASH_CUSTOM_ASSETS
string "Custom Assets File"
default "assets.bin"
help
The custom assets file to flash.
It can be a local file relative to the project directory or a remote url.
choice
prompt "Default Language"
default LANGUAGE_ZH_CN
help
Select device display language
config LANGUAGE_ZH_CN
bool "Chinese"
config LANGUAGE_ZH_TW
bool "Chinese Traditional"
config LANGUAGE_EN_US
bool "English"
config LANGUAGE_JA_JP
bool "Japanese"
config LANGUAGE_KO_KR
bool "Korean"
config LANGUAGE_VI_VN
bool "Vietnamese"
config LANGUAGE_TH_TH
bool "Thai"
config LANGUAGE_DE_DE
bool "German"
config LANGUAGE_FR_FR
bool "French"
config LANGUAGE_ES_ES
bool "Spanish"
config LANGUAGE_IT_IT
bool "Italian"
config LANGUAGE_RU_RU
bool "Russian"
config LANGUAGE_AR_SA
bool "Arabic"
config LANGUAGE_HI_IN
bool "Hindi"
config LANGUAGE_PT_PT
bool "Portuguese"
config LANGUAGE_PL_PL
bool "Polish"
config LANGUAGE_CS_CZ
bool "Czech"
config LANGUAGE_FI_FI
bool "Finnish"
config LANGUAGE_TR_TR
bool "Turkish"
config LANGUAGE_ID_ID
bool "Indonesian"
config LANGUAGE_UK_UA
bool "Ukrainian"
config LANGUAGE_RO_RO
bool "Romanian"
config LANGUAGE_BG_BG
bool "Bulgarian"
config LANGUAGE_CA_ES
bool "Catalan"
config LANGUAGE_DA_DK
bool "Danish"
config LANGUAGE_EL_GR
bool "Greek"
config LANGUAGE_FA_IR
bool "Persian"
config LANGUAGE_FIL_PH
bool "Filipino"
config LANGUAGE_HE_IL
bool "Hebrew"
config LANGUAGE_HR_HR
bool "Croatian"
config LANGUAGE_HU_HU
bool "Hungarian"
config LANGUAGE_MS_MY
bool "Malay"
config LANGUAGE_NB_NO
bool "Norwegian"
config LANGUAGE_NL_NL
bool "Dutch"
config LANGUAGE_SK_SK
bool "Slovak"
config LANGUAGE_SL_SI
bool "Slovenian"
config LANGUAGE_SV_SE
bool "Swedish"
config LANGUAGE_SR_RS
bool "Serbian"
endchoice
choice BOARD_TYPE
prompt "Board Type"
default BOARD_TYPE_BREAD_COMPACT_WIFI
help
Board type. 开发板类型
config BOARD_TYPE_BREAD_COMPACT_WIFI
bool "Bread Compact WiFi (面包板)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_WIFI_LCD
bool "Bread Compact WiFi + LCD (面包板)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_WIFI_CAM
bool "Bread Compact WiFi + LCD + Camera (面包板)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_ML307
bool "Bread Compact ML307/EC801E (面包板 4G)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_NT26
bool "Bread Compact NT26 (面包板 4G)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_ESP32
bool "Bread Compact ESP32 DevKit (面包板)"
depends on IDF_TARGET_ESP32
config BOARD_TYPE_BREAD_COMPACT_ESP32_LCD
bool "Bread Compact ESP32 DevKit + LCD (面包板)"
depends on IDF_TARGET_ESP32
config BOARD_TYPE_XMINI_C3_V3
bool "Xmini C3 V3"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_XMINI_C3_4G
bool "Xmini C3 4G"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_XMINI_C3
bool "Xmini C3"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_ESP_KORVO2_V3
bool "Espressif Korvo2 V3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_KORVO2_V3_RNDIS
bool "Espressif Korvo2 V3 RNDIS"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_SPARKBOT
bool "Espressif SparkBot"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_SENSAIRSHUTTLE
bool "Espressif ESP-SensairShuttle"
depends on IDF_TARGET_ESP32C5
config BOARD_TYPE_ESP_SPOT_S3
bool "Espressif Spot-S3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_SPOT_C5
bool "Espressif Spot-C5"
depends on IDF_TARGET_ESP32C5
config BOARD_TYPE_ESP_HI
bool "Espressif ESP-HI"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_ESP_BOX_3
bool "Espressif ESP-BOX-3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_BOX
bool "Espressif ESP-BOX"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_BOX_LITE
bool "Espressif ESP-BOX-Lite"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_P4_FUNCTION_EV_BOARD
bool "Espressif ESP-P4-Function-EV-Board"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_ESP_VOCAT
bool "Espressif ESP-VoCat"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_KEVIN_BOX_2
bool "Kevin Box 2"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_KEVIN_C3
bool "Kevin C3"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_KEVIN_SP_V3_DEV
bool "Kevin SP V3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_KEVIN_SP_V4_DEV
bool "Kevin SP V4"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_KEVIN_YUYING_313LCD
bool "鱼鹰科技 3.13LCD"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_CGC
bool "CGC"
depends on IDF_TARGET_ESP32
config BOARD_TYPE_CGC_144
bool "CGC 144"
depends on IDF_TARGET_ESP32
config BOARD_TYPE_LICHUANG_DEV_S3
bool "立创·实战派 ESP32-S3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LICHUANG_DEV_C3
bool "立创·实战派 ESP32-C3"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_EDA_TV_PRO
bool "EDA课程案例 EDA-TV-Pro"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_EDA_ROBOT_PRO
bool "EDA课程案例 EDA-Robot-Pro"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_EDA_SUPER_BEAR
bool "EDA课程案例 EDA-Super-Bear"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_DF_K10
bool "DFRobot 行空板 k10"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_DF_S3_AI_CAM
bool "DFRobot ESP32-S3 AI智能摄像头模块"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_MAGICLICK_S3_2P4
bool "神奇按钮 Magiclick_2.4"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_MAGICLICK_S3_2P5
bool "神奇按钮 Magiclick_2.5"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_MAGICLICK_C3
bool "神奇按钮 Magiclick_C3"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_MAGICLICK_C3_V2
bool "神奇按钮 Magiclick_C3_v2"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_M5STACK_CORE_S3
bool "M5Stack CoreS3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_M5STACK_CORE_TAB5
bool "M5Stack Tab5"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_M5STACK_ATOM_S3_ECHO_BASE
bool "M5Stack AtomS3 + Echo Base"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_M5STACK_ATOM_S3R_ECHO_BASE
bool "M5Stack AtomS3R + Echo Base"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_M5STACK_ATOM_S3R_CAM_M12_ECHO_BASE
bool "M5Stack AtomS3R CAM/M12 + Echo Base"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_M5STACK_ATOM_ECHOS3R
bool "M5Stack AtomEchoS3R"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_M5STACK_CARDPUTER_ADV
bool "M5Stack Cardputer Adv"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_M5STACK_ATOM_MATRIX_ECHO_BASE
bool "M5Stack AtomMatrix + Echo Base"
depends on IDF_TARGET_ESP32
config BOARD_TYPE_WAVESHARE_ESP32_TOUCH_LCD_3_5
bool "Waveshare ESP32-Touch-LCD-3.5"
depends on IDF_TARGET_ESP32
config BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX
bool "Waveshare ESP32-S3-CAM"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_AUDIO_BOARD
bool "Waveshare ESP32-S3-Audio-Board"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_8
bool "Waveshare ESP32-S3-Touch-AMOLED-1.8"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06
bool "Waveshare ESP32-S3-Touch-AMOLED-2.06"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_2_06
bool "Waveshare ESP32-C6-Touch-AMOLED-2.06"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_2_16
bool "Waveshare ESP32-C6-Touch-AMOLED-2.16"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75
bool "Waveshare ESP32-S3-Touch-AMOLED-1.75"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C
bool "Waveshare ESP32-S3-Touch-AMOLED-1.75C"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83
bool "Waveshare ESP32-S3-Touch-LCD-1.83"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B
bool "Waveshare ESP32-S3-Touch-LCD-4B"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4_3C
bool "Waveshare ESP32-S3-Touch-LCD-4.3C"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85C
bool "Waveshare ESP32-S3-Touch-LCD-1.85C"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85
bool "Waveshare ESP32-S3-Touch-LCD-1.85"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_46
bool "Waveshare ESP32-S3-Touch-LCD-1.46"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_C6_LCD_1_69
bool "Waveshare ESP32-C6-LCD-1.69"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_LCD_1_83
bool "Waveshare ESP32-C6-Touch-LCD-1.83"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_43
bool "Waveshare ESP32-C6-Touch-AMOLOED-1.43"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_32
bool "Waveshare ESP32-C6-Touch-AMOLOED-1.32"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_8
bool "Waveshare ESP32-C6-Touch-AMOLED-1.8"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_32
bool "Waveshare ESP32-S3-Touch-AMOLOED-1.32"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_49
bool "Waveshare ESP32-S3-Touch-LCD-3.49"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_5
bool "Waveshare ESP32-S3-Touch-LCD-3.5"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_1_54_v1
bool "Waveshare ESP32-S3-ePaper-1.54_v1"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_1_54_v2
bool "Waveshare ESP32-S3-ePaper-1.54_v2"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_3_97
bool "Waveshare ESP32-S3-ePaper-3.97"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_RLCD_4_2
bool "Waveshare ESP32-S3-RLCD-4.2"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_5B
bool "Waveshare ESP32-S3-Touch-LCD-3.5B"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_54
bool "Waveshare ESP32-S3-Touch-LCD-1.54"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_LCD_0_85
bool "Waveshare ESP32-S3-LCD-0.85"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_P4_NANO
bool "Waveshare ESP32-P4-NANO"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4B"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-7B"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_5
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4C"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-7"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_8
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-8"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_10_1
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-10.1"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_TUDOUZI
bool "土豆子"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LILYGO_T_CIRCLE_S3
bool "LILYGO T-Circle-S3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_0_V1_1
bool "LILYGO T-CameraPlus-S3_V1_0_V1_1"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_2
bool "LILYGO T-CameraPlus-S3_V1_2"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA
bool "LILYGO T-Display-S3-Pro-MVSRLora"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA_NO_BATTERY
bool "LILYGO T-Display-S3-Pro-MVSRLora_No_Battery"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LILYGO_T_DISPLAY_P4
bool "LILYGO T-Display-P4"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_MOVECALL_MOJI_ESP32S3
bool "Movecall Moji 小智AI衍生版"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_MOVECALL_MOJI2_ESP32C5
bool "Movecall Moji2.0 小智AI衍生版"
depends on IDF_TARGET_ESP32C5
config BOARD_TYPE_MOVECALL_CUICAN_ESP32S3
bool "Movecall CuiCan 璀璨·AI吊坠"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATK_DNESP32S3
bool "正点原子DNESP32S3开发板"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATK_DNESP32S3_BOX
bool "正点原子DNESP32S3-BOX"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATK_DNESP32S3_BOX0
bool "正点原子DNESP32S3-BOX0"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATK_DNESP32S3_BOX2_WIFI
bool "正点原子DNESP32S3-BOX2-WIFI"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATK_DNESP32S3_BOX2_4G
bool "正点原子DNESP32S3-BOX2-4G"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATK_DNESP32S3M_WIFI
bool "正点原子DNESP32S3M-WIFI"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATK_DNESP32S3M_4G
bool "正点原子DNESP32S3M-4G"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_DU_CHATX
bool "嘟嘟开发板CHATX(wifi)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_TAIJI_PI_S3
bool "太极小派esp32s3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_XINGZHI_CUBE_0_85TFT_WIFI
bool "无名科技星智0.85(WIFI)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_XINGZHI_CUBE_0_85TFT_ML307
bool "无名科技星智0.85(ML307)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_XINGZHI_CUBE_0_96OLED_WIFI
bool "无名科技星智0.96(WIFI)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_XINGZHI_CUBE_0_96OLED_ML307
bool "无名科技星智0.96(ML307)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_XINGZHI_CUBE_1_54TFT_WIFI
bool "无名科技星智1.54(WIFI)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_XINGZHI_CUBE_1_54TFT_ML307
bool "无名科技星智1.54(ML307)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_XINGZHI_METAL_1_54_WIFI
bool "无名科技星智1.54 METAL(wifi)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_XINGZHI_ABS_2_0
bool "无名科技星智ABS 2.0"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_SEEED_STUDIO_SENSECAP_WATCHER
bool "Seeed Studio SenseCAP Watcher"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_DOIT_S3_AIBOX
bool "四博智联AI陪伴盒子"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_MIXGO_NOVA
bool "元控·青春"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_GENJUTECH_S3_1_54TFT
bool "亘具科技1.54(s3)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_S3_LCD_EV_Board
bool "乐鑫ESP S3 LCD EV Board开发板"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_S3_LCD_EV_Board_2
bool "乐鑫ESP S3 LCD EV Board 2开发板"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ZHENGCHEN_1_54TFT_WIFI
bool "征辰科技1.54(WIFI)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ZHENGCHEN_1_54TFT_ML307
bool "征辰科技1.54(ML307)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ZHENGCHEN_CAM
bool "征辰科技AI Camera"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ZHENGCHEN_CAM_ML307
bool "征辰科技AI Camera(ML307)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_MINSI_K08_DUAL
bool "敏思科技K08(DUAL)"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_SPOTPEAR_ESP32_S3_1_54_MUMA
bool "Spotpear ESP32-S3-1.54-MUMA"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_SPOTPEAR_ESP32_S3_1_28_BOX
bool "Spotpear ESP32-S3-1.28-BOX"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_OTTO_ROBOT
bool "ottoRobot"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ELECTRON_BOT
bool "electronBot"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_JIUCHUAN
bool "九川智能"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LABPLUS_MPYTHON_V3
bool "labplus mpython_v3 board"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LABPLUS_LEDONG_V2
bool "labplus ledong_v2 board"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_SURFER_C3_1_14TFT
bool "Surfer-C3-1.14TFT"
depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_YUNLIAO_S3
bool "小智云聊-S3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WIRELESS_TAG_WTP4C5MP07S
bool "Wireless-Tag WTP4C5MP07S"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_AIPI_LITE
bool "AIPI-Lite"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_HU_087
bool "HU-087"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_Freenove_ESP32S3_DISPLAY_2_8_LCD
bool "Freenove ESP32S3 Display 2.8 LCD"
depends on IDF_TARGET_ESP32S3
endchoice
choice
depends on BOARD_TYPE_LILYGO_T_DISPLAY_P4
prompt "Select the screen type"
default SCREEN_TYPE_HI8561
config SCREEN_TYPE_HI8561
bool "HI8561"
config SCREEN_TYPE_RM69A10
bool "RM69A10"
endchoice
choice
depends on BOARD_TYPE_LILYGO_T_DISPLAY_P4
prompt "Select the color format of the screen"
default SCREEN_PIXEL_FORMAT_RGB565
config SCREEN_PIXEL_FORMAT_RGB565
bool "RGB565"
config SCREEN_PIXEL_FORMAT_RGB888
bool "RGB888"
endchoice
choice ESP_S3_LCD_EV_Board_Version_TYPE
depends on BOARD_TYPE_ESP_S3_LCD_EV_Board
prompt "EV_BOARD Type"
default ESP_S3_LCD_EV_Board_1p4
config ESP_S3_LCD_EV_Board_1p4
bool "乐鑫ESP32_S3_LCD_EV_Board-MB_V1.4"
config ESP_S3_LCD_EV_Board_1p5
bool "乐鑫ESP32_S3_LCD_EV_Board-MB_V1.5"
endchoice
choice DISPLAY_OLED_TYPE
depends on BOARD_TYPE_BREAD_COMPACT_WIFI || BOARD_TYPE_BREAD_COMPACT_ML307 || BOARD_TYPE_BREAD_COMPACT_NT26 || BOARD_TYPE_BREAD_COMPACT_ESP32 || BOARD_TYPE_HU_087
prompt "OLED Type"
default OLED_SSD1306_128X32
help
OLED Monochrome Display Type
config OLED_SSD1306_128X32
bool "SSD1306 128*32"
config OLED_SSD1306_128X64
bool "SSD1306 128*64"
config OLED_SH1106_128X64
bool "SH1106 128*64"
endchoice
choice DISPLAY_LCD_TYPE
depends on BOARD_TYPE_BREAD_COMPACT_WIFI_LCD || BOARD_TYPE_BREAD_COMPACT_ESP32_LCD || BOARD_TYPE_CGC || BOARD_TYPE_BREAD_COMPACT_WIFI_CAM
prompt "LCD Type"
default LCD_ST7789_240X320
help
LCD Display Type
config LCD_ST7789_240X320
bool "ST7789 240*320, IPS"
config LCD_ST7789_240X320_NO_IPS
bool "ST7789 240*320, Non-IPS"
config LCD_ST7789_170X320
bool "ST7789 170*320"
config LCD_ST7789_172X320
bool "ST7789 172*320"
config LCD_ST7789_240X280
bool "ST7789 240*280"
config LCD_ST7789_240X240
bool "ST7789 240*240"
config LCD_ST7789_240X240_7PIN
bool "ST7789 240*240, 7PIN"
config LCD_ST7789_240X135
bool "ST7789 240*135"
config LCD_ST7735_128X160
bool "ST7735 128*160"
config LCD_ST7735_128X128
bool "ST7735 128*128"
config LCD_ST7796_320X480
bool "ST7796 320*480 IPS"
config LCD_ST7796_320X480_NO_IPS
bool "ST7796 320*480, Non-IPS"
config LCD_ILI9341_240X320
bool "ILI9341 240*320"
config LCD_ILI9341_240X320_NO_IPS
bool "ILI9341 240*320, Non-IPS"
config LCD_GC9A01_240X240
bool "GC9A01 240*240 Circle"
config LCD_CUSTOM
bool "Custom LCD (自定义屏幕参数)"
endchoice
choice DISPLAY_ESP32S3_KORVO2_V3
depends on BOARD_TYPE_ESP_KORVO2_V3 || BOARD_TYPE_ESP_KORVO2_V3_RNDIS
prompt "ESP32S3_KORVO2_V3 LCD Type"
default ESP32S3_KORVO2_V3_LCD_ST7789
help
LCD Display Type
config ESP32S3_KORVO2_V3_LCD_ST7789
bool "ST7789 240*280"
config ESP32S3_KORVO2_V3_LCD_ILI9341
bool "ILI9341 240*320"
endchoice
choice DISPLAY_ESP32S3_CAM_XXXX
depends on BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX
prompt "ESP32S3_CAM LCD Type"
default BSP_LCD_SIZE_2INCH
help
LCD Display Type
config BSP_LCD_SIZE_2INCH
bool "2inch Touch LCD Module"
config BSP_LCD_SIZE_2_8INCH
bool "2.8inch Touch LCD Module"
config BSP_LCD_SIZE_3_5INCH
bool "3.5inch Touch LCD Module"
config BSP_LCD_SIZE_1_83INCH
bool "1.83inch Touch LCD Module"
endchoice
choice DISPLAY_ESP32S3_AUDIO_BOARD
depends on BOARD_TYPE_WAVESHARE_ESP32_S3_AUDIO_BOARD
prompt "ESP32S3_AUDIO_BOARD LCD Type"
default AUDIO_BOARD_LCD_JD9853
help
LCD Display Type
config AUDIO_BOARD_LCD_JD9853
bool "JD9853 320*172"
config AUDIO_BOARD_LCD_ST7789
bool "ST7789 240*320"
endchoice
choice DISPLAY_ESP32S3_TOUCH_LCD_1_85C
depends on BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85C
prompt "ESP32S3_TOUCH_LCD_1_85C version"
default VERSION_2_0
help
hardware version
config VERSION_1_0
bool "version 1.0"
config VERSION_2_0
bool "version 2.0"
endchoice
choice DISPLAY_STYLE
prompt "Select display style"
default USE_DEFAULT_MESSAGE_STYLE
help
Select display style for Xiaozhi device
config USE_DEFAULT_MESSAGE_STYLE
bool "Enable default message style"
config USE_WECHAT_MESSAGE_STYLE
bool "Enable WeChat Message Style"
config USE_EMOTE_MESSAGE_STYLE
bool "Emote animation style"
depends on BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_3 \
|| BOARD_TYPE_ESP_VOCAT || BOARD_TYPE_LICHUANG_DEV_S3 \
|| BOARD_TYPE_ESP_SENSAIRSHUTTLE
endchoice
config USE_MULTILINE_CHAT_MESSAGE
bool "Use multiline chat message display (default mode only)"
depends on USE_DEFAULT_MESSAGE_STYLE
default n
help
When enabled, the chat message area in the default display mode shows
multiple wrapped lines that grow upward from the bottom of the screen,
with auto-adaptive height.
When disabled (default), a single-line horizontally scrolling label
is shown at the bottom of the screen.
choice WAKE_WORD_TYPE
prompt "Wake Word Implementation Type"
default USE_AFE_WAKE_WORD if (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
default WAKE_WORD_DISABLED
help
Choose the type of wake word implementation to use
config WAKE_WORD_DISABLED
bool "Disabled"
help
Disable wake word detection
config USE_ESP_WAKE_WORD
bool "Wakenet model without AFE"
depends on IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C6 || (IDF_TARGET_ESP32 && SPIRAM)
help
Support ESP32 C3、ESP32 C5 与 ESP32 C6, and (ESP32 with PSRAM)
config USE_AFE_WAKE_WORD
bool "Wakenet model with AFE"
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
help
Support AEC if available, requires ESP32 S3 and PSRAM
config USE_CUSTOM_WAKE_WORD
bool "Multinet model (Custom Wake Word)"
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
help
Requires ESP32 S3 and PSRAM
endchoice
config CUSTOM_WAKE_WORD
string "Custom Wake Word"
default "xiao tu dou"
depends on USE_CUSTOM_WAKE_WORD
help
Custom Wake Word, use pinyin for Chinese, separated by spaces
config CUSTOM_WAKE_WORD_DISPLAY
string "Custom Wake Word Display"
default "小土豆"
depends on USE_CUSTOM_WAKE_WORD
help
Greeting sent to the server after wake word detection
config CUSTOM_WAKE_WORD_THRESHOLD
int "Custom Wake Word Threshold (%)"
default 20
range 1 99
depends on USE_CUSTOM_WAKE_WORD
help
Custom Wake Word Threshold, range 1-99, the smaller the more sensitive, default 20
config SEND_WAKE_WORD_DATA
bool "Send Wake Word Data"
default y
depends on USE_AFE_WAKE_WORD || USE_CUSTOM_WAKE_WORD
help
Send wake word data to the server as the first message of the conversation and wait for response
config WAKE_WORD_DETECTION_IN_LISTENING
bool "Enable Wake Word Detection in Listening Mode"
default n
depends on USE_AFE_WAKE_WORD || USE_CUSTOM_WAKE_WORD
help
Enable wake word detection while in listening mode.
When enabled, the device can detect wake word during listening,
which allows interrupting the current conversation.
When disabled (default), wake word detection is turned off during listening.
config USE_AUDIO_PROCESSOR
bool "Enable Audio Noise Reduction"
default y
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
help
Requires ESP32 S3 and PSRAM
config USE_DEVICE_AEC
bool "Enable Device-Side AEC"
default n
depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE \
|| BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_ESP_KORVO2_V3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83\
|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3 \
|| BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B \
|| BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 || BOARD_TYPE_YUNLIAO_S3 \
|| BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_8 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_10_1 \
|| BOARD_TYPE_ESP_VOCAT || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_49 || BOARD_TYPE_WAVESHARE_ESP32_S3_RLCD_4_2 || BOARD_TYPE_ZHENGCHEN_CAM || BOARD_TYPE_ZHENGCHEN_CAM_ML307 \
|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4_3C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_54 || BOARD_TYPE_WAVESHARE_ESP32_S3_LCD_0_85)
help
To work properly, device-side AEC requires a clean output reference path from the speaker signal and physical acoustic isolation between the microphone and speaker.
config USE_SERVER_AEC
bool "Enable Server-Side AEC (Unstable)"
default n
depends on USE_AUDIO_PROCESSOR
help
To work perperly, server-side AEC requires server support
config USE_AUDIO_DEBUGGER
bool "Enable Audio Debugger"
default n
help
Enable audio debugger, send audio data through UDP to the host machine
menu "WiFi Configuration Method"
help
WiFi Configuration Method Selection
config USE_HOTSPOT_WIFI_PROVISIONING
bool "Hotspot"
default y
help
Use WiFi Hotspot to transmit WiFi configuration data
config USE_ACOUSTIC_WIFI_PROVISIONING
bool "Acoustic"
help
Use audio signal to transmit WiFi configuration data
config USE_ESP_BLUFI_WIFI_PROVISIONING
bool "Esp Blufi"
help
Use esp blufi protocol to transmit WiFi configuration data
select BT_ENABLED
select BT_BLE_42_FEATURES_SUPPORTED
select BT_BLE_BLUFI_ENABLE
select MBEDTLS_DHM_C
endmenu
config AUDIO_DEBUG_UDP_SERVER
string "Audio Debug UDP Server Address"
default "192.168.2.100:8000"
depends on USE_AUDIO_DEBUGGER
help
UDP server address, format: IP:PORT, used to receive audio debugging data
config RECEIVE_CUSTOM_MESSAGE
bool "Enable Custom Message Reception"
default n
help
Enable custom message reception, allow the device to receive custom messages from the server (preferably through the MQTT protocol)
menu "Camera Configuration"
depends on !IDF_TARGET_ESP32
comment "Warning: Please read the help text before modifying these settings."
config XIAOZHI_CAMERA_ALLOW_JPEG_INPUT
bool "Allow JPEG Input"
default n
help
Allow JPEG Input format for the camera.
This option may need to be enabled when using a USB camera.
Not currently supported when used simultaneously with XIAOZHI_ENABLE_ROTATE_CAMERA_IMAGE.
config XIAOZHI_ENABLE_HARDWARE_JPEG_ENCODER
bool "Enable Hardware JPEG Encoder"
default y
depends on SOC_JPEG_ENCODE_SUPPORTED
help
Use hardware JPEG encoder on ESP32-P4 to encode image to JPEG.
See https://docs.espressif.com/projects/esp-idf/en/stable/esp32p4/api-reference/peripherals/jpeg.html for more details.
config XIAOZHI_ENABLE_HARDWARE_JPEG_DECODER
bool "Enable Hardware JPEG Decoder"
default n
depends on SOC_JPEG_DECODE_SUPPORTED && XIAOZHI_CAMERA_ALLOW_JPEG_INPUT
help
Use hardware JPEG decoder on ESP32-P4 to decode JPEG to image.
See https://docs.espressif.com/projects/esp-idf/en/stable/esp32p4/api-reference/peripherals/jpeg.html for more details.
config XIAOZHI_ENABLE_CAMERA_DEBUG_MODE
bool "Enable Camera Debug Mode"
default n
help
Enable camera debug mode, print camera debug information to the console.
Only works on boards that support camera.
config XIAOZHI_ENABLE_CAMERA_ENDIANNESS_SWAP
bool "Enable software camera buffer endianness swapping"
default n
depends on !CAMERA_SENSOR_SWAP_PIXEL_BYTE_ORDER
help
This option treats the camera buffer as a uint16_t[] array and performs byte-swapping (endianness conversion) on each element.
Should only be modified by development board integration engineers.
**Incorrect usage may result in incorrect image colors!**
ATTENTION: If the option CAMERA_SENSOR_SWAP_PIXEL_BYTE_ORDER is available for your sensor, please use that instead.
menuconfig XIAOZHI_ENABLE_ROTATE_CAMERA_IMAGE
bool "Enable Camera Image Rotation"
default n
depends on !XIAOZHI_CAMERA_ALLOW_JPEG_INPUT
help
Enable camera image rotation, rotate the camera image to the correct orientation.
- On ESP32-P4, rotation is handled by PPA hardware.
- On other chips, rotation is done in software with performance cost.
- For 180° rotation, use HFlip + VFlip instead of this option.
Not currently supported when used simultaneously with XIAOZHI_CAMERA_ALLOW_JPEG_INPUT.
if XIAOZHI_ENABLE_ROTATE_CAMERA_IMAGE
choice XIAOZHI_CAMERA_IMAGE_ROTATION_ANGLE
prompt "Camera Image Rotation Angle (clockwise)"
default XIAOZHI_CAMERA_IMAGE_ROTATION_ANGLE_90
help
Camera image rotation angle.
config XIAOZHI_CAMERA_IMAGE_ROTATION_ANGLE_90
bool "90°"
config XIAOZHI_CAMERA_IMAGE_ROTATION_ANGLE_270
bool "270°"
comment "For 180° rotation, use HFlip + VFlip instead of this option"
endchoice
endif
endmenu
menu "TAIJIPAI_S3_CONFIG"
depends on BOARD_TYPE_TAIJI_PI_S3
choice I2S_TYPE_TAIJIPI_S3
prompt "taiji-pi-S3 I2S Type"
default TAIJIPAI_I2S_TYPE_STD
help
I2S 类型选择
config TAIJIPAI_I2S_TYPE_STD
bool "I2S Type STD"
config TAIJIPAI_I2S_TYPE_PDM
bool "I2S Type PDM"
endchoice
config I2S_USE_2SLOT
bool "Enable I2S 2 Slot"
default y
help
启动双声道
endmenu
endmenu

1116
main/application.cc Normal file

File diff suppressed because it is too large Load Diff

189
main/application.h Normal file
View File

@@ -0,0 +1,189 @@
#ifndef _APPLICATION_H_
#define _APPLICATION_H_
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <freertos/task.h>
#include <esp_timer.h>
#include <string>
#include <mutex>
#include <deque>
#include <memory>
#include "protocol.h"
#include "ota.h"
#include "audio_service.h"
#include "device_state.h"
#include "device_state_machine.h"
// Main event bits
#define MAIN_EVENT_SCHEDULE (1 << 0)
#define MAIN_EVENT_SEND_AUDIO (1 << 1)
#define MAIN_EVENT_WAKE_WORD_DETECTED (1 << 2)
#define MAIN_EVENT_VAD_CHANGE (1 << 3)
#define MAIN_EVENT_ERROR (1 << 4)
#define MAIN_EVENT_ACTIVATION_DONE (1 << 5)
#define MAIN_EVENT_CLOCK_TICK (1 << 6)
#define MAIN_EVENT_NETWORK_CONNECTED (1 << 7)
#define MAIN_EVENT_NETWORK_DISCONNECTED (1 << 8)
#define MAIN_EVENT_TOGGLE_CHAT (1 << 9)
#define MAIN_EVENT_START_LISTENING (1 << 10)
#define MAIN_EVENT_STOP_LISTENING (1 << 11)
#define MAIN_EVENT_STATE_CHANGED (1 << 12)
enum AecMode {
kAecOff,
kAecOnDeviceSide,
kAecOnServerSide,
};
class Application {
public:
static Application& GetInstance() {
static Application instance;
return instance;
}
// Delete copy constructor and assignment operator
Application(const Application&) = delete;
Application& operator=(const Application&) = delete;
/**
* Initialize the application
* This sets up display, audio, network callbacks, etc.
* Network connection starts asynchronously.
*/
void Initialize();
/**
* Run the main event loop
* This function runs in the main task and never returns.
* It handles all events including network, state changes, and user interactions.
*/
void Run();
DeviceState GetDeviceState() const { return state_machine_.GetState(); }
bool IsVoiceDetected() const { return audio_service_.IsVoiceDetected(); }
/**
* Request state transition
* Returns true if transition was successful
*/
bool SetDeviceState(DeviceState state);
/**
* Schedule a callback to be executed in the main task
*/
void Schedule(std::function<void()>&& callback);
/**
* Alert with status, message, emotion and optional sound
*/
void Alert(const char* status, const char* message, const char* emotion = "", const std::string_view& sound = "");
void DismissAlert();
void AbortSpeaking(AbortReason reason);
/**
* Toggle chat state (event-based, thread-safe)
* Sends MAIN_EVENT_TOGGLE_CHAT to be handled in Run()
*/
void ToggleChatState();
/**
* Start listening (event-based, thread-safe)
* Sends MAIN_EVENT_START_LISTENING to be handled in Run()
*/
void StartListening();
/**
* Stop listening (event-based, thread-safe)
* Sends MAIN_EVENT_STOP_LISTENING to be handled in Run()
*/
void StopListening();
void Reboot();
void WakeWordInvoke(const std::string& wake_word);
bool UpgradeFirmware(const std::string& url, const std::string& version = "");
bool CanEnterSleepMode();
void SendMcpMessage(const std::string& payload);
void SetAecMode(AecMode mode);
AecMode GetAecMode() const { return aec_mode_; }
void PlaySound(const std::string_view& sound);
AudioService& GetAudioService() { return audio_service_; }
/**
* Reset protocol resources (thread-safe)
* Can be called from any task to release resources allocated after network connected
* This includes closing audio channel, resetting protocol and ota objects
*/
void ResetProtocol();
private:
Application();
~Application();
std::mutex mutex_;
std::deque<std::function<void()>> main_tasks_;
std::unique_ptr<Protocol> protocol_;
EventGroupHandle_t event_group_ = nullptr;
esp_timer_handle_t clock_timer_handle_ = nullptr;
DeviceStateMachine state_machine_;
ListeningMode listening_mode_ = kListeningModeAutoStop;
AecMode aec_mode_ = kAecOff;
std::string last_error_message_;
AudioService audio_service_;
std::unique_ptr<Ota> ota_;
bool has_server_time_ = false;
bool aborted_ = false;
bool assets_version_checked_ = false;
bool play_popup_on_listening_ = false; // Flag to play popup sound after state changes to listening
int clock_ticks_ = 0;
TaskHandle_t activation_task_handle_ = nullptr;
// Event handlers
void HandleStateChangedEvent();
void HandleToggleChatEvent();
void HandleStartListeningEvent();
void HandleStopListeningEvent();
void HandleNetworkConnectedEvent();
void HandleNetworkDisconnectedEvent();
void HandleActivationDoneEvent();
void HandleWakeWordDetectedEvent();
void ContinueOpenAudioChannel(ListeningMode mode);
void ContinueWakeWordInvoke(const std::string& wake_word);
// Activation task (runs in background)
void ActivationTask();
// Helper methods
void CheckAssetsVersion();
void CheckNewVersion();
void InitializeProtocol();
void ShowActivationCode(const std::string& code, const std::string& message);
void SetListeningMode(ListeningMode mode);
ListeningMode GetDefaultListeningMode() const;
// State change handler called by state machine
void OnStateChanged(DeviceState old_state, DeviceState new_state);
};
class TaskPriorityReset {
public:
TaskPriorityReset(BaseType_t priority) {
original_priority_ = uxTaskPriorityGet(NULL);
vTaskPrioritySet(NULL, priority);
}
~TaskPriorityReset() {
vTaskPrioritySet(NULL, original_priority_);
}
private:
BaseType_t original_priority_;
};
#endif // _APPLICATION_H_

562
main/assets.cc Normal file
View File

@@ -0,0 +1,562 @@
#include "assets.h"
#include "board.h"
#include "display.h"
#include "application.h"
#include "lvgl_theme.h"
#include "emote_display.h"
#include "expression_emote.h"
#if HAVE_LVGL
#include "display/lcd_display.h"
#include <spi_flash_mmap.h>
#endif
#include <esp_log.h>
#include <esp_timer.h>
#include <esp_heap_caps.h>
#include <cbin_font.h>
#define TAG "Assets"
#define PARTITION_LABEL "assets"
struct mmap_assets_table {
char asset_name[32]; /*!< Name of the asset */
uint32_t asset_size; /*!< Size of the asset */
uint32_t asset_offset; /*!< Offset of the asset */
uint16_t asset_width; /*!< Width of the asset */
uint16_t asset_height; /*!< Height of the asset */
};
Assets::Assets() {
#if HAVE_LVGL
strategy_ = std::make_unique<Assets::LvglStrategy>();
#else
strategy_ = std::make_unique<Assets::EmoteStrategy>();
#endif
// Initialize the partition
InitializePartition();
}
Assets::~Assets() {
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(bool refresh_display_theme) {
return strategy_ ? strategy_->Apply(this, refresh_display_theme) : false;
}
bool Assets::InitializePartition() {
return strategy_ ? strategy_->InitializePartition(this) : false;
}
void Assets::UnApplyPartition() {
if (strategy_) {
strategy_->UnApplyPartition(this);
}
}
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<char*>(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<uint8_t*>(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];
}
return checksum & 0xFFFF;
}
bool Assets::LvglStrategy::InitializePartition(Assets* assets) {
assets->partition_valid_ = false;
assets_.clear();
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", 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(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;
}
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 > 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;
}
auto start_time = esp_timer_get_time();
uint32_t calculated_checksum = CalculateChecksum(mmap_root_ + 12, stored_len);
auto end_time = esp_timer_get_time();
ESP_LOGI(TAG, "The checksum calculation time is %d ms", int((end_time - start_time) / 1000));
if (calculated_checksum != stored_chksum) {
ESP_LOGE(TAG, "The calculated checksum (0x%lx) does not match the stored checksum (0x%lx)", calculated_checksum, stored_chksum);
return false;
}
checksum_valid_ = true;
for (uint32_t i = 0; i < stored_files; i++) {
auto item = (const mmap_assets_table*)(mmap_root_ + 12 + i * sizeof(mmap_assets_table));
auto asset = Asset{
.size = static_cast<size_t>(item->asset_size),
.offset = static_cast<size_t>(12 + sizeof(mmap_assets_table) * stored_files + item->asset_offset)
};
assets_[item->asset_name] = asset;
}
return checksum_valid_;
}
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<void*>(const_cast<char*>(data + 2));
size = asset->second.size;
return true;
}
bool Assets::LvglStrategy::Apply(Assets* assets, bool refresh_display_theme) {
void* ptr = nullptr;
size_t size = 0;
if (!assets->GetAssetData("index.json", ptr, size)) {
ESP_LOGE(TAG, "The index.json file is not found");
return false;
}
cJSON* root = cJSON_ParseWithLength(static_cast<char*>(ptr), size);
if (root == nullptr) {
ESP_LOGE(TAG, "The index.json file is not valid");
return false;
}
cJSON* version = cJSON_GetObjectItem(root, "version");
if (cJSON_IsNumber(version)) {
if (version->valuedouble > 1) {
ESP_LOGE(TAG, "The assets version %d is not supported, please upgrade the firmware", version->valueint);
return false;
}
}
Assets::LoadSrmodelsFromIndex(assets, root);
auto& theme_manager = LvglThemeManager::GetInstance();
auto light_theme = theme_manager.GetTheme("light");
auto dark_theme = theme_manager.GetTheme("dark");
cJSON* font = cJSON_GetObjectItem(root, "text_font");
if (cJSON_IsString(font)) {
std::string fonts_text_file = font->valuestring;
if (assets->GetAssetData(fonts_text_file, ptr, size)) {
auto text_font = std::make_shared<LvglCBinFont>(ptr);
if (text_font->font() == nullptr) {
ESP_LOGE(TAG, "Failed to load fonts.bin");
return false;
}
if (light_theme != nullptr) {
light_theme->set_text_font(text_font);
}
if (dark_theme != nullptr) {
dark_theme->set_text_font(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)) {
auto custom_emoji_collection = std::make_shared<EmojiCollection>();
int emoji_count = cJSON_GetArraySize(emoji_collection);
for (int i = 0; i < emoji_count; i++) {
cJSON* emoji = cJSON_GetArrayItem(emoji_collection, i);
if (cJSON_IsObject(emoji)) {
cJSON* name = cJSON_GetObjectItem(emoji, "name");
cJSON* file = cJSON_GetObjectItem(emoji, "file");
cJSON* eaf = cJSON_GetObjectItem(emoji, "eaf");
if (cJSON_IsString(name) && cJSON_IsString(file) && (NULL== eaf)) {
if (!assets->GetAssetData(file->valuestring, ptr, size)) {
ESP_LOGE(TAG, "Emoji %s image file %s is not found", name->valuestring, file->valuestring);
continue;
}
custom_emoji_collection->AddEmoji(name->valuestring, new LvglRawImage(ptr, size));
}
}
}
if (light_theme != nullptr) {
light_theme->set_emoji_collection(custom_emoji_collection);
}
if (dark_theme != nullptr) {
dark_theme->set_emoji_collection(custom_emoji_collection);
}
}
cJSON* skin = cJSON_GetObjectItem(root, "skin");
if (cJSON_IsObject(skin)) {
cJSON* light_skin = cJSON_GetObjectItem(skin, "light");
if (cJSON_IsObject(light_skin) && light_theme != nullptr) {
cJSON* text_color = cJSON_GetObjectItem(light_skin, "text_color");
cJSON* background_color = cJSON_GetObjectItem(light_skin, "background_color");
cJSON* background_image = cJSON_GetObjectItem(light_skin, "background_image");
if (cJSON_IsString(text_color)) {
light_theme->set_text_color(LvglTheme::ParseColor(text_color->valuestring));
}
if (cJSON_IsString(background_color)) {
light_theme->set_background_color(LvglTheme::ParseColor(background_color->valuestring));
light_theme->set_chat_background_color(LvglTheme::ParseColor(background_color->valuestring));
}
if (cJSON_IsString(background_image)) {
if (!assets->GetAssetData(background_image->valuestring, ptr, size)) {
ESP_LOGE(TAG, "The background image file %s is not found", background_image->valuestring);
return false;
}
auto background_image = std::make_shared<LvglCBinImage>(ptr);
light_theme->set_background_image(background_image);
}
}
cJSON* dark_skin = cJSON_GetObjectItem(skin, "dark");
if (cJSON_IsObject(dark_skin) && dark_theme != nullptr) {
cJSON* text_color = cJSON_GetObjectItem(dark_skin, "text_color");
cJSON* background_color = cJSON_GetObjectItem(dark_skin, "background_color");
cJSON* background_image = cJSON_GetObjectItem(dark_skin, "background_image");
if (cJSON_IsString(text_color)) {
dark_theme->set_text_color(LvglTheme::ParseColor(text_color->valuestring));
}
if (cJSON_IsString(background_color)) {
dark_theme->set_background_color(LvglTheme::ParseColor(background_color->valuestring));
dark_theme->set_chat_background_color(LvglTheme::ParseColor(background_color->valuestring));
}
if (cJSON_IsString(background_image)) {
if (!assets->GetAssetData(background_image->valuestring, ptr, size)) {
ESP_LOGE(TAG, "The background image file %s is not found", background_image->valuestring);
return false;
}
auto background_image = std::make_shared<LvglCBinImage>(ptr);
dark_theme->set_background_image(background_image);
}
}
}
if (refresh_display_theme) {
auto display = Board::GetInstance().GetDisplay();
ESP_LOGI(TAG, "Refreshing display theme...");
auto current_theme = display->GetTheme();
if (current_theme != nullptr) {
display->SetTheme(current_theme);
}
// Parse hide_subtitle configuration
cJSON* hide_subtitle = cJSON_GetObjectItem(root, "hide_subtitle");
if (cJSON_IsBool(hide_subtitle)) {
bool hide = cJSON_IsTrue(hide_subtitle);
auto lcd_display = dynamic_cast<LcdDisplay*>(display);
if (lcd_display != nullptr) {
lcd_display->SetHideSubtitle(hide);
ESP_LOGI(TAG, "Set hide_subtitle to %s", hide ? "true" : "false");
}
}
}
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<emote::EmoteDisplay*>(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<emote::EmoteDisplay*>(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<emote::EmoteDisplay*>(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<void*>(static_cast<const void*>(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, bool refresh_display_theme) {
Assets::LoadSrmodelsFromIndex(assets);
auto display = Board::GetInstance().GetDisplay();
auto* emote_display = dynamic_cast<emote::EmoteDisplay*>(display);
if (emote_display && emote_display->GetEmoteHandle() != nullptr) {
emote_load_assets(emote_display->GetEmoteHandle());
}
return true;
}
bool Assets::Download(std::string url, std::function<void(int progress, size_t speed)> progress_callback) {
ESP_LOGI(TAG, "Downloading new version of assets from %s", url.c_str());
// 取消当前资源分区的内存映射
UnApplyPartition();
// 下载新的资源文件
auto network = Board::GetInstance().GetNetwork();
auto http = network->CreateHttp(0);
if (!http->Open("GET", url)) {
ESP_LOGE(TAG, "Failed to open HTTP connection");
return false;
}
if (http->GetStatusCode() != 200) {
ESP_LOGE(TAG, "Failed to get assets, status code: %d", http->GetStatusCode());
return false;
}
size_t content_length = http->GetBodyLength();
if (content_length == 0) {
ESP_LOGE(TAG, "Failed to get content length");
return false;
}
if (content_length > partition_->size) {
ESP_LOGE(TAG, "Assets file size (%u) is larger than partition size (%lu)", content_length, partition_->size);
return false;
}
// 定义扇区大小为4KBESP32的标准扇区大小
const size_t SECTOR_SIZE = esp_partition_get_main_flash_sector_size();
// 计算需要擦除的扇区数量
size_t sectors_to_erase = (content_length + SECTOR_SIZE - 1) / SECTOR_SIZE; // 向上取整
size_t total_erase_size = sectors_to_erase * SECTOR_SIZE;
ESP_LOGI(TAG, "Sector size: %u, content length: %u, sectors to erase: %u, total erase size: %u",
SECTOR_SIZE, content_length, sectors_to_erase, total_erase_size);
// 写入新的资源文件到分区一边erase一边写入
char* buffer = (char*)heap_caps_malloc(SECTOR_SIZE, MALLOC_CAP_INTERNAL);
if (buffer == nullptr) {
ESP_LOGE(TAG, "Failed to allocate buffer");
return false;
}
size_t total_written = 0;
size_t recent_written = 0;
size_t current_sector = 0;
auto last_calc_time = esp_timer_get_time();
while (true) {
int ret = http->Read(buffer, SECTOR_SIZE);
if (ret < 0) {
ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret));
heap_caps_free(buffer);
return false;
}
if (ret == 0) {
break;
}
// 检查是否需要擦除新的扇区
size_t write_end_offset = total_written + ret;
size_t needed_sectors = (write_end_offset + SECTOR_SIZE - 1) / SECTOR_SIZE;
// 擦除需要的新扇区
while (current_sector < needed_sectors) {
size_t sector_start = current_sector * SECTOR_SIZE;
size_t sector_end = (current_sector + 1) * SECTOR_SIZE;
// 确保擦除范围不超过分区大小
if (sector_end > partition_->size) {
ESP_LOGE(TAG, "Sector end (%u) exceeds partition size (%lu)", sector_end, partition_->size);
heap_caps_free(buffer);
return false;
}
ESP_LOGD(TAG, "Erasing sector %u (offset: %u, size: %u)", current_sector, sector_start, SECTOR_SIZE);
esp_err_t err = esp_partition_erase_range(partition_, sector_start, SECTOR_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase sector %u at offset %u: %s", current_sector, sector_start, esp_err_to_name(err));
heap_caps_free(buffer);
return false;
}
current_sector++;
}
// 写入数据到分区
esp_err_t err = esp_partition_write(partition_, total_written, buffer, ret);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write to assets partition at offset %u: %s", total_written, esp_err_to_name(err));
heap_caps_free(buffer);
return false;
}
total_written += ret;
recent_written += ret;
// 计算进度和速度
if (esp_timer_get_time() - last_calc_time >= 1000000 || total_written == content_length || ret == 0) {
size_t progress = total_written * 100 / content_length;
size_t speed = recent_written; // 每秒的字节数
ESP_LOGI(TAG, "Progress: %u%% (%u/%u), Speed: %u B/s, Sectors erased: %u",
progress, total_written, content_length, speed, current_sector);
if (progress_callback) {
progress_callback(progress, speed);
}
last_calc_time = esp_timer_get_time();
recent_written = 0; // 重置最近写入的字节数
}
}
http->Close();
heap_caps_free(buffer);
if (total_written != content_length) {
ESP_LOGE(TAG, "Downloaded size (%u) does not match expected size (%u)", total_written, content_length);
return false;
}
ESP_LOGI(TAG, "Assets download completed, total written: %u bytes, total sectors erased: %u",
total_written, current_sector);
// 重新初始化资源分区
if (!InitializePartition()) {
ESP_LOGE(TAG, "Failed to re-initialize assets partition");
return false;
}
return true;
}

89
main/assets.h Normal file
View File

@@ -0,0 +1,89 @@
#ifndef ASSETS_H
#define ASSETS_H
#include <string>
#include <functional>
#include <memory>
#include <cJSON.h>
#include <esp_partition.h>
#include <model_path.h>
#include <map>
#include <string>
#if HAVE_LVGL
#include <spi_flash_mmap.h>
#endif
struct Asset {
size_t size;
size_t offset;
};
class Assets {
public:
static Assets& GetInstance() {
static Assets instance;
return instance;
}
~Assets();
bool Download(std::string url, std::function<void(int progress, size_t speed)> progress_callback);
bool Apply(bool refresh_display_theme = true);
bool GetAssetData(const std::string& name, void*& ptr, size_t& size);
inline bool partition_valid() const { return partition_valid_; }
inline std::string default_assets_url() const { return default_assets_url_; }
private:
Assets();
Assets(const Assets&) = delete;
Assets& operator=(const Assets&) = delete;
bool InitializePartition();
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, bool refresh_display_theme = true) = 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, bool refresh_display_theme = true) 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<std::string, Asset> 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, bool refresh_display_theme = true) 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<AssetStrategy> strategy_;
protected:
const esp_partition_t* partition_ = nullptr;
bool partition_valid_ = false;
std::string default_assets_url_;
srmodel_list_t* models_list_ = nullptr;
};
#endif

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,59 @@
{
"language": {
"type": "ar-SA"
},
"strings": {
"WARNING": "تحذير",
"INFO": "معلومات",
"ERROR": "خطأ",
"VERSION": "الإصدار ",
"LOADING_PROTOCOL": "الاتصال بالخادم...",
"INITIALIZING": "التهيئة...",
"PIN_ERROR": "يرجى إدخال بطاقة SIM",
"REG_ERROR": "لا يمكن الوصول إلى الشبكة، يرجى التحقق من حالة بطاقة البيانات",
"DETECTING_MODULE": "اكتشاف الوحدة...",
"REGISTERING_NETWORK": "انتظار الشبكة...",
"CHECKING_NEW_VERSION": "فحص الإصدار الجديد...",
"CHECK_NEW_VERSION_FAILED": "فشل فحص الإصدار الجديد، سيتم المحاولة خلال %d ثانية: %s",
"SWITCH_TO_WIFI_NETWORK": "التبديل إلى Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "التبديل إلى 4G...",
"STANDBY": "في الانتظار",
"CONNECT_TO": "الاتصال بـ ",
"CONNECTING": "جاري الاتصال...",
"CONNECTED_TO": "متصل بـ ",
"LISTENING": "الاستماع...",
"SPEAKING": "التحدث...",
"SERVER_NOT_FOUND": "البحث عن خدمة متاحة",
"SERVER_NOT_CONNECTED": "لا يمكن الاتصال بالخدمة، يرجى المحاولة لاحقاً",
"SERVER_TIMEOUT": "انتهت مهلة الاستجابة",
"SERVER_ERROR": "فشل الإرسال، يرجى التحقق من الشبكة",
"CONNECT_TO_HOTSPOT": "اتصل الهاتف بنقطة الاتصال ",
"ACCESS_VIA_BROWSER": "،الوصول عبر المتصفح ",
"WIFI_CONFIG_MODE": "وضع تكوين الشبكة",
"ENTERING_WIFI_CONFIG_MODE": "الدخول في وضع تكوين الشبكة...",
"SCANNING_WIFI": "فحص Wi-Fi...",
"NEW_VERSION": "إصدار جديد ",
"OTA_UPGRADE": "تحديث OTA",
"UPGRADING": "تحديث النظام...",
"UPGRADE_FAILED": "فشل التحديث",
"ACTIVATION": "تفعيل الجهاز",
"BATTERY_LOW": "البطارية منخفضة",
"BATTERY_CHARGING": "جاري الشحن",
"BATTERY_FULL": "البطارية ممتلئة",
"BATTERY_NEED_CHARGE": "البطارية منخفضة، يرجى الشحن",
"VOLUME": "الصوت ",
"MUTED": "صامت",
"MAX_VOLUME": "أقصى صوت",
"RTC_MODE_OFF": "AEC مُوقف",
"RTC_MODE_ON": "AEC مُشغل",
"DOWNLOAD_ASSETS_FAILED": "فشل في تنزيل الموارد",
"LOADING_ASSETS": "جاري تحميل الموارد...",
"PLEASE_WAIT": "يرجى الانتظار...",
"FOUND_NEW_ASSETS": "تم العثور على موارد جديدة: %s",
"HELLO_MY_FRIEND": "مرحباً، صديقي!",
"CONNECTION_SUCCESSFUL": "تم الاتصال بنجاح",
"FLIGHT_MODE_OFF": "وضع الطيران معطل",
"FLIGHT_MODE_ON": "وضع الطيران قيد التشغيل",
"MODEM_INIT_ERROR": "فشل تهيئة المودم"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,59 @@
{
"language": {
"type": "bg-BG"
},
"strings": {
"WARNING": "Предупреждение",
"INFO": "Информация",
"ERROR": "Грешка",
"VERSION": "Версия ",
"LOADING_PROTOCOL": "Влизане в системата...",
"INITIALIZING": "Инициализация...",
"PIN_ERROR": "Моля, поставете SIM карта",
"REG_ERROR": "Не може да се осъществи достъп до мрежата, моля проверете статуса на SIM картата",
"DETECTING_MODULE": "Откриване на модул...",
"REGISTERING_NETWORK": "Изчакване на мрежата...",
"CHECKING_NEW_VERSION": "Проверка за нова версия...",
"CHECK_NEW_VERSION_FAILED": "Проверката за нова версия е неуспешна, ще се опита отново след %d секунди: %s",
"SWITCH_TO_WIFI_NETWORK": "Превключване към Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "Превключване към 4G...",
"STANDBY": "Режим на готовност",
"CONNECT_TO": "Свързване към ",
"CONNECTING": "Свързване...",
"CONNECTION_SUCCESSFUL": "Успешно свързване",
"CONNECTED_TO": "Свързан към ",
"LISTENING": "Слушане...",
"SPEAKING": "Говорене...",
"SERVER_NOT_FOUND": "Търсене на налична услуга",
"SERVER_NOT_CONNECTED": "Не може да се свърже с услугата, моля опитайте по-късно",
"SERVER_TIMEOUT": "Времето за изчакване на отговор изтече",
"SERVER_ERROR": "Неуспешно изпращане, моля проверете мрежата",
"CONNECT_TO_HOTSPOT": орещa точка: ",
"ACCESS_VIA_BROWSER": " Конфигурационен URL: ",
"WIFI_CONFIG_MODE": "Режим на конфигуриране на Wi-Fi",
"ENTERING_WIFI_CONFIG_MODE": "Влизане в режим на конфигуриране на Wi-Fi...",
"SCANNING_WIFI": "Сканиране на Wi-Fi...",
"NEW_VERSION": "Нова версия ",
"OTA_UPGRADE": "OTA надстройка",
"UPGRADING": "Системата се надстройва...",
"UPGRADE_FAILED": "Надстройката е неуспешна",
"ACTIVATION": "Активация",
"BATTERY_LOW": "Слаба батерия",
"BATTERY_CHARGING": "Зарядна",
"BATTERY_FULL": "Батерията е пълна",
"BATTERY_NEED_CHARGE": "Слаба батерия, моля заредете",
"VOLUME": "Сила на звука ",
"MUTED": "Заглушено",
"MAX_VOLUME": "Максимална сила на звука",
"RTC_MODE_OFF": "AEC изключен",
"RTC_MODE_ON": "AEC включен",
"PLEASE_WAIT": "Моля, изчакайте...",
"FOUND_NEW_ASSETS": "Намерени нови ресурси: %s",
"DOWNLOAD_ASSETS_FAILED": "Неуспешно изтегляне на ресурси",
"LOADING_ASSETS": "Зареждане на ресурси...",
"HELLO_MY_FRIEND": "Здравей, мой приятел!",
"FLIGHT_MODE_OFF": "Режим на самолет е изключен",
"FLIGHT_MODE_ON": "Режим на самолет е включен",
"MODEM_INIT_ERROR": "Неуспешна инициализация на модема"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,59 @@
{
"language": {
"type": "ca-ES"
},
"strings": {
"WARNING": "Advertència",
"INFO": "Informació",
"ERROR": "Error",
"VERSION": "Versió ",
"LOADING_PROTOCOL": "Iniciant sessió...",
"INITIALIZING": "Inicialitzant...",
"PIN_ERROR": "Si us plau, inseriu la targeta SIM",
"REG_ERROR": "No es pot accedir a la xarxa, si us plau comproveu l'estat de la targeta SIM",
"DETECTING_MODULE": "Detectant mòdul...",
"REGISTERING_NETWORK": "Esperant la xarxa...",
"CHECKING_NEW_VERSION": "Comprovant nova versió...",
"CHECK_NEW_VERSION_FAILED": "La comprovació de nova versió ha fallat, es tornarà a intentar en %d segons: %s",
"SWITCH_TO_WIFI_NETWORK": "Canviant a Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "Canviant a 4G...",
"STANDBY": "En espera",
"CONNECT_TO": "Connectar a ",
"CONNECTING": "Connectant...",
"CONNECTION_SUCCESSFUL": "Connexió exitosa",
"CONNECTED_TO": "Connectat a ",
"LISTENING": "Escoltant...",
"SPEAKING": "Parlant...",
"SERVER_NOT_FOUND": "Buscant servei disponible",
"SERVER_NOT_CONNECTED": "No es pot connectar al servei, si us plau intenteu-ho més tard",
"SERVER_TIMEOUT": "Temps d'espera de resposta exhaurit",
"SERVER_ERROR": "L'enviament ha fallat, si us plau comproveu la xarxa",
"CONNECT_TO_HOTSPOT": "Punt d'accés: ",
"ACCESS_VIA_BROWSER": " URL de configuració: ",
"WIFI_CONFIG_MODE": "Mode de configuració Wi-Fi",
"ENTERING_WIFI_CONFIG_MODE": "Entrant en mode de configuració Wi-Fi...",
"SCANNING_WIFI": "Escanejant Wi-Fi...",
"NEW_VERSION": "Nova versió ",
"OTA_UPGRADE": "Actualització OTA",
"UPGRADING": "El sistema s'està actualitzant...",
"UPGRADE_FAILED": "L'actualització ha fallat",
"ACTIVATION": "Activació",
"BATTERY_LOW": "Bateria baixa",
"BATTERY_CHARGING": "Carregant",
"BATTERY_FULL": "Bateria plena",
"BATTERY_NEED_CHARGE": "Bateria baixa, si us plau carregueu",
"VOLUME": "Volum ",
"MUTED": "Silenciat",
"MAX_VOLUME": "Volum màxim",
"RTC_MODE_OFF": "AEC desactivat",
"RTC_MODE_ON": "AEC activat",
"PLEASE_WAIT": "Si us plau, espereu...",
"FOUND_NEW_ASSETS": "S'han trobat nous recursos: %s",
"DOWNLOAD_ASSETS_FAILED": "No s'han pogut descarregar els recursos",
"LOADING_ASSETS": "Carregant recursos...",
"HELLO_MY_FRIEND": "Hola, amic meu!",
"FLIGHT_MODE_OFF": "El mode avió està desactivat",
"FLIGHT_MODE_ON": "El mode avió està activat",
"MODEM_INIT_ERROR": "Error d'inicialització del mòdem"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,59 @@
{
"language": {
"type": "cs-CZ"
},
"strings": {
"WARNING": "Varování",
"INFO": "Informace",
"ERROR": "Chyba",
"VERSION": "Verze ",
"LOADING_PROTOCOL": "Připojování k serveru...",
"INITIALIZING": "Inicializace...",
"PIN_ERROR": "Prosím vložte SIM kartu",
"REG_ERROR": "Nelze se připojit k síti, zkontrolujte stav datové karty",
"DETECTING_MODULE": "Detekce modulu...",
"REGISTERING_NETWORK": "Čekání na síť...",
"CHECKING_NEW_VERSION": "Kontrola nové verze...",
"CHECK_NEW_VERSION_FAILED": "Kontrola nové verze selhala, opakování za %d sekund: %s",
"SWITCH_TO_WIFI_NETWORK": "Přepínání na Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "Přepínání na 4G...",
"STANDBY": "Pohotovost",
"CONNECT_TO": "Připojit k ",
"CONNECTING": "Připojování...",
"CONNECTED_TO": "Připojeno k ",
"LISTENING": "Naslouchání...",
"SPEAKING": "Mluvení...",
"SERVER_NOT_FOUND": "Hledání dostupné služby",
"SERVER_NOT_CONNECTED": "Nelze se připojit ke službě, zkuste to později",
"SERVER_TIMEOUT": "Čas odpovědi vypršel",
"SERVER_ERROR": "Odeslání selhalo, zkontrolujte síť",
"CONNECT_TO_HOTSPOT": "Připojte telefon k hotspotu ",
"ACCESS_VIA_BROWSER": "přístup přes prohlížeč ",
"WIFI_CONFIG_MODE": "Režim konfigurace sítě",
"ENTERING_WIFI_CONFIG_MODE": "Vstup do režimu konfigurace sítě...",
"SCANNING_WIFI": "Skenování Wi-Fi...",
"NEW_VERSION": "Nová verze ",
"OTA_UPGRADE": "OTA upgrade",
"UPGRADING": "Aktualizace systému...",
"UPGRADE_FAILED": "Upgrade selhal",
"ACTIVATION": "Aktivace zařízení",
"BATTERY_LOW": "Slabá baterie",
"BATTERY_CHARGING": "Nabíjení",
"BATTERY_FULL": "Baterie plná",
"BATTERY_NEED_CHARGE": "Slabá baterie, prosím nabijte",
"VOLUME": "Hlasitost ",
"MUTED": "Ztlumeno",
"MAX_VOLUME": "Maximální hlasitost",
"RTC_MODE_OFF": "AEC vypnuto",
"RTC_MODE_ON": "AEC zapnuto",
"DOWNLOAD_ASSETS_FAILED": "Nepodařilo se stáhnout prostředky",
"LOADING_ASSETS": "Načítání prostředků...",
"PLEASE_WAIT": "Prosím čekejte...",
"FOUND_NEW_ASSETS": "Nalezeny nové prostředky: %s",
"HELLO_MY_FRIEND": "Ahoj, můj příteli!",
"CONNECTION_SUCCESSFUL": "Připojení úspěšné",
"FLIGHT_MODE_OFF": "Letecký režim je vypnutý",
"FLIGHT_MODE_ON": "Letecký režim je zapnutý",
"MODEM_INIT_ERROR": "Chyba inicializace modemu"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,59 @@
{
"language": {
"type": "da-DK"
},
"strings": {
"WARNING": "Advarsel",
"INFO": "Information",
"ERROR": "Fejl",
"VERSION": "Version ",
"LOADING_PROTOCOL": "Logger ind...",
"INITIALIZING": "Initialiserer...",
"PIN_ERROR": "Indsæt venligst SIM-kort",
"REG_ERROR": "Kan ikke få adgang til netværket, tjek venligst SIM-kortets status",
"DETECTING_MODULE": "Detekterer modul...",
"REGISTERING_NETWORK": "Venter på netværk...",
"CHECKING_NEW_VERSION": "Tjekker for ny version...",
"CHECK_NEW_VERSION_FAILED": "Tjek for ny version mislykkedes, prøver igen om %d sekunder: %s",
"SWITCH_TO_WIFI_NETWORK": "Skifter til Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "Skifter til 4G...",
"STANDBY": "Standby",
"CONNECT_TO": "Forbind til ",
"CONNECTING": "Forbinder...",
"CONNECTION_SUCCESSFUL": "Forbindelse lykkedes",
"CONNECTED_TO": "Forbundet til ",
"LISTENING": "Lytter...",
"SPEAKING": "Taler...",
"SERVER_NOT_FOUND": "Søger efter tilgængelig tjeneste",
"SERVER_NOT_CONNECTED": "Kan ikke forbinde til tjeneste, prøv venligst igen senere",
"SERVER_TIMEOUT": "Timeout ved venten på svar",
"SERVER_ERROR": "Afsendelse mislykkedes, tjek venligst netværket",
"CONNECT_TO_HOTSPOT": "Hotspot: ",
"ACCESS_VIA_BROWSER": " Konfigurations-URL: ",
"WIFI_CONFIG_MODE": "Wi-Fi-konfigurationstilstand",
"ENTERING_WIFI_CONFIG_MODE": "Går ind i Wi-Fi-konfigurationstilstand...",
"SCANNING_WIFI": "Scanner Wi-Fi...",
"NEW_VERSION": "Ny version ",
"OTA_UPGRADE": "OTA-opgradering",
"UPGRADING": "Systemet opgraderes...",
"UPGRADE_FAILED": "Opgradering mislykkedes",
"ACTIVATION": "Aktivering",
"BATTERY_LOW": "Lavt batteri",
"BATTERY_CHARGING": "Oplader",
"BATTERY_FULL": "Batteriet er fuldt",
"BATTERY_NEED_CHARGE": "Lavt batteri, oplad venligst",
"VOLUME": "Lydstyrke ",
"MUTED": "Lydløs",
"MAX_VOLUME": "Maksimal lydstyrke",
"RTC_MODE_OFF": "AEC slukket",
"RTC_MODE_ON": "AEC tændt",
"PLEASE_WAIT": "Vent venligst...",
"FOUND_NEW_ASSETS": "Fandt nye ressourcer: %s",
"DOWNLOAD_ASSETS_FAILED": "Download af ressourcer mislykkedes",
"LOADING_ASSETS": "Indlæser ressourcer...",
"HELLO_MY_FRIEND": "Hej, min ven!",
"FLIGHT_MODE_OFF": "Flytilstand er slukket",
"FLIGHT_MODE_ON": "Flytilstand er tændt",
"MODEM_INIT_ERROR": "Modeminitialisering mislykkedes"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More