diff --git a/firmware/.clang-format b/firmware/.clang-format new file mode 100644 index 0000000..81d3c79 --- /dev/null +++ b/firmware/.clang-format @@ -0,0 +1,164 @@ +BasedOnStyle: Google +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 4 +UseCRLF: false +UseTab: Never \ No newline at end of file diff --git a/firmware/.gitignore b/firmware/.gitignore new file mode 100644 index 0000000..5c0083c --- /dev/null +++ b/firmware/.gitignore @@ -0,0 +1,67 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# MacOS desktop metadata files +.DS_Store + +# Build files +build/ +cmake-build-*/ +/components/ +managed_components/ + +# PlatformIO +.pio/ + +# VSCode +.vscode/ +.cache/ + +# Python virtual environment +.venv/ + +# Debug / Release files +Debug/ +debug/ +Release/ +release/ +out/ +bin/ +obj/ + +*.code-workspace + +sdkconfig +sdkconfig.old + +xiaozhi-esp32/ diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt new file mode 100644 index 0000000..b9e0ac2 --- /dev/null +++ b/firmware/CMakeLists.txt @@ -0,0 +1,11 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +set(PROJECT_VER "2.0.4") + +# Add this line to disable the specific warning +add_compile_options(-Wno-missing-field-initializers) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(StackChan) diff --git a/firmware/README.md b/firmware/README.md index fc51bde..72069c1 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -1 +1,25 @@ -# StackChan Firmware \ No newline at end of file +# StackChan Firmware + +## Build + +### Fetch Dependencies + +```bash +python3 ./fetch_repos.py +``` + +### Tool Chains + +[ESP-IDF v5.5.1](https://docs.espressif.com/projects/esp-idf/en/v5.5.1/esp32s3/index.html) + +### Build + +```bash +idf.py build +``` + +### Flash + +```bash +idf.py flash +``` diff --git a/firmware/dependencies.lock b/firmware/dependencies.lock new file mode 100644 index 0000000..2b502ab --- /dev/null +++ b/firmware/dependencies.lock @@ -0,0 +1,770 @@ +dependencies: + 78/esp-ml307: + component_hash: 57c8cc3309fb3da9195ec861cba3726b5cf24c362def803e7174c0b1d72e60e3 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 3.3.7 + 78/esp-opus: + component_hash: 8182b733f071d7bfe1e837f4c9f8649a63e4c937177f089e65772880c02f2e17 + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.0.5 + 78/esp-opus-encoder: + component_hash: b80e5b6d6dc4bf6b0bf1a3729f52b80500e9e6b1003b3e827b78ba738283a296 + dependencies: + - name: 78/esp-opus + registry_url: https://components.espressif.com + require: private + version: ^1.0.5 + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.4.1 + 78/esp-wifi-connect: + component_hash: 2613d9970df9f2203c2c549991e3bad74847b0a7facffe6698f4341e0bf73589 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.6.1 + 78/esp_lcd_nv3023: + component_hash: fa88abfc19a312eb5e6f2ffa187e0a9faf67e01e758bfb979d3f9d92561a494f + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.0 + 78/xiaozhi-fonts: + component_hash: 51f67bcf9dec623de90d9deb72de50dfbc3634fb948b7abf27df65b386258b94 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.4 + espressif/adc_battery_estimation: + component_hash: b915167c87ed5a84b13d680bd011c2ed9a15121f1e247c6903141b9f138f3606 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.2.1 + espressif/adc_mic: + component_hash: ca6ef69bba4dc0d3e0ce53937635ed2734d38ac947881da81b8526e6851a71a4 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: private + version: ^1.3.* + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.2.1 + espressif/button: + component_hash: 4ed25fcf354b48aa5e5680b9e7549e38d7738f9b613c94a2e39119c100db9c95 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 4.1.4 + espressif/cmake_utilities: + component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.5.3 + espressif/dl_fft: + component_hash: 7dadbd644c0d7ba4733cc3726ec4cff6edf27b043725e1115861dec1609a3d28 + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.3.1 + espressif/esp-dsp: + component_hash: 42dce32d46ac93dc11f60d368e29a830e9661c7345d794b8a45c343479cae636 + dependencies: + - name: idf + require: private + version: '>=4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.7.0 + espressif/esp_cam_sensor: + component_hash: 35c1648ea87aac81da7a085d35f61b6d5ad88574bd0c5fdc4ab75efdef4ee6c1 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_sccb_intf + registry_url: https://components.espressif.com + require: private + version: '>=0.0.5' + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + - esp32s3 + - esp32c3 + - esp32c5 + - esp32c6 + - esp32c61 + version: 1.5.2 + espressif/esp_codec_dev: + component_hash: 0d9e9bc288156eb55f79338d312e1ebf8c9dfbd5e7d13ef0f20ccb031b4e15cf + dependencies: + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.4 + espressif/esp_image_effects: + component_hash: a35037db02862d3cef6f72c25f422ae62da6e6c88219ce03c9ea24e0315ceaaa + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.1 + espressif/esp_io_expander: + component_hash: abf3ea0d71407d79d15500550aa3ba433848459b0fa94717cf9478822593ee5d + dependencies: + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.0 + espressif/esp_io_expander_tca9554: + component_hash: 8a9e3a7dbef589a54f814ff22e9121da86bce19d0e1ccffa53f5df6bb346a140 + dependencies: + - name: espressif/esp_io_expander + registry_url: https://components.espressif.com + require: public + version: ^1.0.1 + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.0.0 + espressif/esp_io_expander_tca95xx_16bit: + component_hash: c384d55e429775d6a1eef26512fbc33bc41823745a8c2ad6d5927729f33a2c21 + dependencies: + - name: espressif/esp_io_expander + registry_url: https://components.espressif.com + require: public + version: ^1.0.1 + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.0.1 + espressif/esp_lcd_axs15231b: + component_hash: e614bd75827e95800e39df1ba9474166060ab26c3807d06df9cc312a27021cda + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1 + - name: idf + require: private + version: '>5.0.4,!=5.1.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.1~1 + espressif/esp_lcd_gc9a01: + component_hash: 09ddc8f5c9d718a659ffa30a4040e1ae75077e68492c38aec8c2beaaa7b3a9bc + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.0.1 + espressif/esp_lcd_ili9341: + component_hash: 31f1b793aa2110dd2ae071c21ccbff0a4eb20d9a4ee40b6294c0dc0ad9552c4e + dependencies: + - name: idf + require: private + version: '>=4.4' + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.2.0 + espressif/esp_lcd_panel_io_additions: + component_hash: 213d5c4a3c322a48c9f993d6bc2372206d34af95db34e87a3e02601bdcad7ce8 + dependencies: + - name: idf + require: private + version: '>=4.4.2' + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_io_expander + registry_url: https://components.espressif.com + require: public + version: ^1 + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.1 + espressif/esp_lcd_spd2010: + component_hash: 8637c97feeb8d83ba637599f6bf4ef30e83595486930e7df17ffa24f3fef1242 + dependencies: + - name: idf + require: private + version: '>5.0.4,!=5.1.1' + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.2 + espressif/esp_lcd_st7701: + component_hash: 0b11e8733e51e12da83003b9d475777eaf47bfbdce01379879bd2504d14f4f83 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>5.0.4,!=5.1.1' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32s3 + - esp32p4 + version: 1.1.5 + espressif/esp_lcd_st77916: + component_hash: 5fa0f8b1274576d4484e2b8d9358e2a5d09c721511bef0dce6a55b4206b5f0e9 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>5.0.4,!=5.1.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.1 + espressif/esp_lcd_st7796: + component_hash: c37b10363125eb3b4757d509392ffca963c27262b61939fdb49a7b18eeef975c + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32 + - esp32s2 + - esp32s3 + - esp32p4 + version: 1.3.5 + espressif/esp_lcd_touch: + component_hash: 3f85a7d95af876f1a6ecca8eb90a81614890d0f03a038390804e5a77e2caf862 + dependencies: + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.2.1 + espressif/esp_lcd_touch_cst816s: + component_hash: 51dfa9cee38d8a20f4b625d7f89120dff08c8557e613e09dfb31f53d48648235 + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.0.4 + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.0 + espressif/esp_lcd_touch_ft5x06: + component_hash: abaec05f46a793549b60afdda9eff86e3c8e87782c8c169007911232388d2858 + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.0.4 + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.7 + espressif/esp_lcd_touch_gt1151: + component_hash: 69dd86cd5fad2442e3bf15c8c4ddddbe9e8739bdb26cd5f2c5249601b4c3cc38 + dependencies: + - name: idf + require: private + version: '>=4.4.2' + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.0.4 + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.5~2 + espressif/esp_lcd_touch_gt911: + component_hash: acc1c184358aa29ef72506f618c9c76a8cc2bf12af38a2bff3d44d84f3a08857 + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.1.0 + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.3 + espressif/esp_lcd_touch_st7123: + component_hash: 3592f0dac4b3baf34c40679922026c1e22e9b925b9446e981cd1c23306ecfe65 + dependencies: + - name: idf + require: private + version: '>=4.4.2' + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1 + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.0 + espressif/esp_lvgl_port: + component_hash: bfb2778c063b05a6c47c1a8de6d166ee4d911bc86fe0b3ff9e6bab06d2812033 + dependencies: + - name: idf + require: private + version: '>=5.1' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: public + version: '>=8,<10' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.6.2 + espressif/esp_mmap_assets: + component_hash: 91d785326b03db15e2f7f1314d8c976d38f21aa5759b570dcbbc89bcf247fd27 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.4.0 + espressif/esp_new_jpeg: + component_hash: e6af208a875abd0ecfc0213d3751a11b504b463ebde6930f24096047925fa5c1 + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32 + - esp32s2 + - esp32s3 + - esp32p4 + - esp32c2 + - esp32c3 + - esp32c5 + - esp32c6 + version: 0.6.1 + espressif/esp_sccb_intf: + component_hash: 44dfa738680ccbc3705ae6940f59be603165f4f7e4d05c3eb5f433705fdc92be + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + version: 0.0.7 + espressif/esp_video: + component_hash: fbad1178f39cb5a81ed808c460c851b911a9858db833e2e667ada988022c0660 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_cam_sensor + registry_url: https://components.espressif.com + require: private + version: 1.5.* + - name: espressif/esp_h264 + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4] + version: 1.0.4 + - name: espressif/esp_ipa + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4] + version: ~1.2.0 + - name: idf + require: private + version: '>=5.4' + - name: espressif/usb + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4, esp32s3] + - if: idf_version >=6.0 + version: '*' + - name: espressif/usb_host_uvc + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4, esp32s3] + version: 2.3.* + source: + registry_url: https://components.espressif.com/ + type: service + targets: + - esp32p4 + - esp32s3 + - esp32c3 + - esp32c5 + - esp32c6 + - esp32c61 + version: 1.3.1 + espressif/freetype: + component_hash: cd5e2d8458e6e8d73f1120ac474467cabb669d8ea4b25050bf6a348c1e89225e + dependencies: + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com + type: service + version: 2.13.3~1 + espressif/i2c_bus: + component_hash: 4e990dc11734316186b489b362c61d41f23f79d58bc169795cec215e528cba14 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.5.0 + espressif/knob: + component_hash: 23bb6b0f62d4a2bbfe8b65e182397541339b8e48d54cef15909f5d4f5bd43d29 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=4.4.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.1 + espressif/led_strip: + component_hash: 223998f10cae6d81f2ad2dd3c1103c2221be298c708e37917482b0153f3ec64e + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 3.0.1~1 + espressif/usb_host_uvc: + component_hash: b0cb9a3a2eaa09dc6e5cc3e47e04d0a73e47c2d3dcfb3afce959224a1fe5fb5e + dependencies: + - name: idf + require: private + version: '>=5.0' + - name: espressif/usb + registry_url: https://components.espressif.com + require: public + rules: + - if: idf_version >=6.0 + - if: target not in ["linux"] + version: ^1.0.0 + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32s2 + - esp32s3 + - esp32p4 + - esp32h4 + - linux + version: 2.3.1 + espressif2022/esp_emote_gfx: + component_hash: 7478561e4e8bd0efd4307647ec7221da2519de4ff1cd0cb08dccfbd0161cadaa + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_new_jpeg + registry_url: https://components.espressif.com + require: private + version: 0.6.* + - name: espressif/freetype + registry_url: https://components.espressif.com + require: private + version: 2.* + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.2.0~1 + espressif2022/image_player: + component_hash: 0e42ed1c9665debd15f2f3e7e56519100e75e446410962226cb5e5402da3fa43 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.1 + idf: + source: + type: idf + version: 5.5.1 + lvgl/lvgl: + component_hash: b702d642e03e95928046d5c6726558e6444e112420c77efa5fdb6650b0a13c5d + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 9.3.0 + tny-robotics/sh1106-esp-idf: + component_hash: 7be190d7c58cd635adf8b74b3a841f8245f2f82f36d135a64d8e53cfab39124d + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.0 + txp666/otto-emoji-gif-component: + component_hash: 5a722dd0a40014b31d5998ea5b21f0b53a99d1025626d765d08710b47636ee97 + dependencies: + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: private + version: '>=9.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.4 + waveshare/esp_lcd_sh8601: + component_hash: 1a2829e9db4e0515bad6ce83368f199dec60519fbba40251d6c8249f5d070ae1 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.2 + waveshare/esp_lcd_touch_cst9217: + component_hash: c5da844e73ab7b7e02938901e71f38deed94b26976045fb4517e7fc1e8dc4db5 + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.1.0 + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.3 + wvirgil123/sscma_client: + component_hash: bf7d4a1f157303f4850d0b9b465c733ce38e8689ff40ac4743a44520524c41d5 + dependencies: + - name: espressif/esp_io_expander + registry_url: https://components.espressif.com + require: private + version: ^1.0.1 + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.2 +direct_dependencies: +- 78/esp-ml307 +- 78/esp-opus-encoder +- 78/esp-wifi-connect +- 78/esp_lcd_nv3023 +- 78/xiaozhi-fonts +- espressif/adc_battery_estimation +- espressif/adc_mic +- espressif/button +- espressif/cmake_utilities +- espressif/dl_fft +- espressif/esp-dsp +- espressif/esp_codec_dev +- espressif/esp_image_effects +- espressif/esp_io_expander_tca9554 +- espressif/esp_io_expander_tca95xx_16bit +- espressif/esp_lcd_axs15231b +- espressif/esp_lcd_gc9a01 +- espressif/esp_lcd_ili9341 +- espressif/esp_lcd_panel_io_additions +- espressif/esp_lcd_spd2010 +- espressif/esp_lcd_st7701 +- espressif/esp_lcd_st77916 +- espressif/esp_lcd_st7796 +- espressif/esp_lcd_touch_cst816s +- espressif/esp_lcd_touch_ft5x06 +- espressif/esp_lcd_touch_gt1151 +- espressif/esp_lcd_touch_gt911 +- espressif/esp_lcd_touch_st7123 +- espressif/esp_lvgl_port +- espressif/esp_mmap_assets +- espressif/esp_new_jpeg +- espressif/esp_video +- espressif/i2c_bus +- espressif/knob +- espressif/led_strip +- espressif2022/esp_emote_gfx +- espressif2022/image_player +- idf +- lvgl/lvgl +- tny-robotics/sh1106-esp-idf +- txp666/otto-emoji-gif-component +- waveshare/esp_lcd_sh8601 +- waveshare/esp_lcd_touch_cst9217 +- wvirgil123/sscma_client +manifest_hash: 8b8d8d3a405eda302202a654ce1dfdbc17f486994ac285a18867b38f07a065c0 +target: esp32s3 +version: 2.0.0 diff --git a/firmware/fetch_repos.py b/firmware/fetch_repos.py new file mode 100644 index 0000000..17cc96f --- /dev/null +++ b/firmware/fetch_repos.py @@ -0,0 +1,61 @@ +import os +import subprocess +import json + + +def clone_or_update_repo( + repo_url, path, ref=None, with_submodules=False, patch_path=None +): + import os + + if not os.path.exists(path): + subprocess.run(["git", "clone", repo_url, path], check=True) + else: + subprocess.run(["git", "-C", path, "fetch"], check=True) + + if ref: + subprocess.run(["git", "-C", path, "checkout", ref], check=True) + + if with_submodules: + subprocess.run( + ["git", "-C", path, "submodule", "update", "--init", "--recursive"], + check=True, + ) + + # 应用 patch + if patch_path: + patch_full_path = ( + patch_path + if os.path.isabs(patch_path) + else os.path.join(os.getcwd(), patch_path) + ) + # 使用 git apply --check 先检测补丁是否能应用,避免报错 + check_result = subprocess.run( + ["git", "-C", path, "apply", "--check", patch_full_path] + ) + if check_result.returncode == 0: + subprocess.run(["git", "-C", path, "apply", patch_full_path], check=True) + print(f"Applied patch {patch_path} to {path}") + else: + print(f"Patch {patch_path} cannot be applied cleanly to {path}, skipped.") + + +def fetch_dependencies(): + script_dir = os.path.dirname(os.path.abspath(__file__)) + config_path = os.path.join(script_dir, "repos.json") + + with open(config_path) as f: + repos = json.load(f) + + for repo in repos: + repo_path = os.path.join(script_dir, repo["path"]) + branch = repo.get("branch") + with_submodules = repo.get("with_submodules", False) + patch = repo.get("patch") + if patch and not os.path.isabs(patch): + patch = os.path.join(script_dir, patch) + clone_or_update_repo(repo["url"], repo_path, branch, with_submodules, patch) + + +if __name__ == "__main__": + fetch_dependencies() \ No newline at end of file diff --git a/firmware/main/CMakeLists.txt b/firmware/main/CMakeLists.txt new file mode 100644 index 0000000..c56336f --- /dev/null +++ b/firmware/main/CMakeLists.txt @@ -0,0 +1,458 @@ +# Define xiaozhi source directory +set(XIAOZHI_MAIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../xiaozhi-esp32/main") + + +file(GLOB_RECURSE STACK_CHAN_SOURCES + "apps/*.c" + "apps/*.cc" + "apps/*.cpp" + "assets/*.c" + "assets/*.cc" + "assets/*.cpp" + "hal/*.c" + "hal/*.cc" + "hal/*.cpp" + "stackchan/*.c" + "stackchan/*.cc" + "stackchan/*.cpp" +) +set(STACK_CHAN_INCLUDE_DIRS + "." +) +list(APPEND STACK_CHAN_SOURCES main.cpp) + +# Define source files +set(SOURCES "audio/audio_codec.cc" + "audio/audio_service.cc" + "audio/codecs/no_audio_codec.cc" + "audio/codecs/box_audio_codec.cc" + "audio/codecs/es8311_audio_codec.cc" + "audio/codecs/es8374_audio_codec.cc" + "audio/codecs/es8388_audio_codec.cc" + "audio/codecs/es8389_audio_codec.cc" + "audio/codecs/dummy_audio_codec.cc" + "audio/processors/audio_debugger.cc" + "led/single_led.cc" + "led/circular_strip.cc" + "led/gpio_led.cc" + "display/display.cc" + "display/lcd_display.cc" + "display/oled_display.cc" + "display/lvgl_display/lvgl_display.cc" + "display/emote_display.cc" + "display/lvgl_display/emoji_collection.cc" + "display/lvgl_display/lvgl_theme.cc" + "display/lvgl_display/lvgl_font.cc" + "display/lvgl_display/lvgl_image.cc" + "display/lvgl_display/gif/lvgl_gif.cc" + "display/lvgl_display/gif/gifdec.c" + "display/lvgl_display/jpg/image_to_jpeg.cpp" + "protocols/protocol.cc" + "protocols/mqtt_protocol.cc" + "protocols/websocket_protocol.cc" + "mcp_server.cc" + "system_info.cc" + "application.cc" + "ota.cc" + "settings.cc" + "device_state_event.cc" + "assets.cc" + ) + +# Transform relative paths to absolute paths from xiaozhi-esp32/main +list(TRANSFORM SOURCES PREPEND "${XIAOZHI_MAIN_DIR}/") + +set(INCLUDE_DIRS "." "display" "display/lvgl_display" "display/lvgl_display/jpg" "audio" "protocols") +# Transform include dirs to absolute paths from xiaozhi-esp32/main +list(TRANSFORM INCLUDE_DIRS PREPEND "${XIAOZHI_MAIN_DIR}/") +list(APPEND INCLUDE_DIRS) + +# Add board common files +file(GLOB BOARD_COMMON_SOURCES ${XIAOZHI_MAIN_DIR}/boards/common/*.cc) +list(APPEND SOURCES ${BOARD_COMMON_SOURCES} ${STACK_CHAN_SOURCES}) +list(APPEND INCLUDE_DIRS ${XIAOZHI_MAIN_DIR}/boards/common ${STACK_CHAN_INCLUDE_DIRS}) + +idf_build_get_property(build_components BUILD_COMPONENTS) +# Function to find component dynamically by pattern +function(find_component_by_pattern PATTERN COMPONENT_VAR PATH_VAR) + foreach(COMPONENT ${build_components}) + if(COMPONENT MATCHES "${PATTERN}") + set(${COMPONENT_VAR} ${COMPONENT} PARENT_SCOPE) + idf_component_get_property(COMPONENT_PATH ${COMPONENT} COMPONENT_DIR) + set(${PATH_VAR} "${COMPONENT_PATH}" PARENT_SCOPE) + break() + endif() + endforeach() +endfunction() + +# Set default BUILTIN_TEXT_FONT and BUILTIN_ICON_FONT +set(BUILTIN_TEXT_FONT font_puhui_14_1) +set(BUILTIN_ICON_FONT font_awesome_14_1) + +# Add board files according to BOARD_TYPE +# Set default assets if the board uses partition table V2 +if(CONFIG_BOARD_TYPE_M5STACK_STACK_CHAN) + set(BOARD_TYPE "m5stack-stack-chan") + set(BUILTIN_TEXT_FONT font_puhui_basic_20_4) + set(BUILTIN_ICON_FONT font_awesome_20_4) + set(DEFAULT_EMOJI_COLLECTION twemoji_64) +endif() + +file(GLOB BOARD_SOURCES +) +list(APPEND SOURCES ${BOARD_SOURCES}) + +# Select audio processor according to Kconfig +if(CONFIG_USE_AUDIO_PROCESSOR) + set(AUDIO_PROCESSOR_SRC "audio/processors/afe_audio_processor.cc") +else() + set(AUDIO_PROCESSOR_SRC "audio/processors/no_audio_processor.cc") +endif() +list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/${AUDIO_PROCESSOR_SRC}") + +if(CONFIG_IDF_TARGET_ESP32S3 OR CONFIG_IDF_TARGET_ESP32P4) + set(WAKE_WORD_SRCS "audio/wake_words/afe_wake_word.cc" "audio/wake_words/custom_wake_word.cc") +else() + set(WAKE_WORD_SRCS "audio/wake_words/esp_wake_word.cc") +endif() +list(TRANSFORM WAKE_WORD_SRCS PREPEND "${XIAOZHI_MAIN_DIR}/") +list(APPEND SOURCES ${WAKE_WORD_SRCS}) + +# Select language directory according to Kconfig +if(CONFIG_LANGUAGE_ZH_CN) + set(LANG_DIR "zh-CN") +elseif(CONFIG_LANGUAGE_ZH_TW) + set(LANG_DIR "zh-TW") +elseif(CONFIG_LANGUAGE_EN_US) + set(LANG_DIR "en-US") +elseif(CONFIG_LANGUAGE_JA_JP) + set(LANG_DIR "ja-JP") +elseif(CONFIG_LANGUAGE_KO_KR) + set(LANG_DIR "ko-KR") +elseif(CONFIG_LANGUAGE_VI_VN) + set(LANG_DIR "vi-VN") +elseif(CONFIG_LANGUAGE_TH_TH) + set(LANG_DIR "th-TH") +elseif(CONFIG_LANGUAGE_DE_DE) + set(LANG_DIR "de-DE") +elseif(CONFIG_LANGUAGE_FR_FR) + set(LANG_DIR "fr-FR") +elseif(CONFIG_LANGUAGE_ES_ES) + set(LANG_DIR "es-ES") +elseif(CONFIG_LANGUAGE_IT_IT) + set(LANG_DIR "it-IT") +elseif(CONFIG_LANGUAGE_RU_RU) + set(LANG_DIR "ru-RU") +elseif(CONFIG_LANGUAGE_AR_SA) + set(LANG_DIR "ar-SA") +elseif(CONFIG_LANGUAGE_HI_IN) + set(LANG_DIR "hi-IN") +elseif(CONFIG_LANGUAGE_PT_PT) + set(LANG_DIR "pt-PT") +elseif(CONFIG_LANGUAGE_PL_PL) + set(LANG_DIR "pl-PL") +elseif(CONFIG_LANGUAGE_CS_CZ) + set(LANG_DIR "cs-CZ") +elseif(CONFIG_LANGUAGE_FI_FI) + set(LANG_DIR "fi-FI") +elseif(CONFIG_LANGUAGE_TR_TR) + set(LANG_DIR "tr-TR") +elseif(CONFIG_LANGUAGE_ID_ID) + set(LANG_DIR "id-ID") +elseif(CONFIG_LANGUAGE_UK_UA) + set(LANG_DIR "uk-UA") +elseif(CONFIG_LANGUAGE_RO_RO) + set(LANG_DIR "ro-RO") +elseif(CONFIG_LANGUAGE_BG_BG) + set(LANG_DIR "bg-BG") +elseif(CONFIG_LANGUAGE_CA_ES) + set(LANG_DIR "ca-ES") +elseif(CONFIG_LANGUAGE_DA_DK) + set(LANG_DIR "da-DK") +elseif(CONFIG_LANGUAGE_EL_GR) + set(LANG_DIR "el-GR") +elseif(CONFIG_LANGUAGE_FA_IR) + set(LANG_DIR "fa-IR") +elseif(CONFIG_LANGUAGE_FIL_PH) + set(LANG_DIR "fil-PH") +elseif(CONFIG_LANGUAGE_HE_IL) + set(LANG_DIR "he-IL") +elseif(CONFIG_LANGUAGE_HR_HR) + set(LANG_DIR "hr-HR") +elseif(CONFIG_LANGUAGE_HU_HU) + set(LANG_DIR "hu-HU") +elseif(CONFIG_LANGUAGE_MS_MY) + set(LANG_DIR "ms-MY") +elseif(CONFIG_LANGUAGE_NB_NO) + set(LANG_DIR "nb-NO") +elseif(CONFIG_LANGUAGE_NL_NL) + set(LANG_DIR "nl-NL") +elseif(CONFIG_LANGUAGE_SK_SK) + set(LANG_DIR "sk-SK") +elseif(CONFIG_LANGUAGE_SL_SI) + set(LANG_DIR "sl-SI") +elseif(CONFIG_LANGUAGE_SV_SE) + set(LANG_DIR "sv-SE") +elseif(CONFIG_LANGUAGE_SR_RS) + set(LANG_DIR "sr-RS") +endif() + +# Define generation path +set(LANG_JSON "${XIAOZHI_MAIN_DIR}/assets/locales/${LANG_DIR}/language.json") +set(LANG_HEADER "${XIAOZHI_MAIN_DIR}/assets/lang_config.h") + +# Collect current language audio files +file(GLOB LANG_SOUNDS ${XIAOZHI_MAIN_DIR}/assets/locales/${LANG_DIR}/*.ogg) + +# If not en-US, collect en-US audio files as fallback for missing files +if(NOT LANG_DIR STREQUAL "en-US") + file(GLOB EN_US_SOUNDS ${XIAOZHI_MAIN_DIR}/assets/locales/en-US/*.ogg) + + # Extract filenames (without path) from current language + set(EXISTING_NAMES "") + foreach(SOUND_FILE ${LANG_SOUNDS}) + get_filename_component(FILENAME ${SOUND_FILE} NAME) + list(APPEND EXISTING_NAMES ${FILENAME}) + endforeach() + + # Only add en-US audio files that are missing in current language + foreach(EN_SOUND ${EN_US_SOUNDS}) + get_filename_component(FILENAME ${EN_SOUND} NAME) + if(NOT ${FILENAME} IN_LIST EXISTING_NAMES) + list(APPEND LANG_SOUNDS ${EN_SOUND}) + message(STATUS "Using en-US fallback for missing audio: ${FILENAME}") + endif() + endforeach() +endif() + +# file(GLOB COMMON_SOUNDS ${XIAOZHI_MAIN_DIR}/assets/common/*.ogg) +file(GLOB COMMON_SOUNDS + ${XIAOZHI_MAIN_DIR}/assets/common/*.ogg + assets/sfx/*.ogg +) + +# If target chip is ESP32, exclude specific files to avoid build errors +if(CONFIG_IDF_TARGET_ESP32) + set(ESP32_EXCLUDE_FILES + "audio/codecs/box_audio_codec.cc" + "audio/codecs/es8388_audio_codec.cc" + "audio/codecs/es8389_audio_codec.cc" + "led/gpio_led.cc" + "boards/common/esp32_camera.cc" + "display/lvgl_display/jpg/image_to_jpeg.cpp" + ) + list(TRANSFORM ESP32_EXCLUDE_FILES PREPEND "${XIAOZHI_MAIN_DIR}/") + list(REMOVE_ITEM SOURCES ${ESP32_EXCLUDE_FILES}) +endif() + +idf_component_register(SRCS ${SOURCES} + EMBED_FILES ${LANG_SOUNDS} ${COMMON_SOUNDS} + INCLUDE_DIRS ${INCLUDE_DIRS} + WHOLE_ARCHIVE + ) + +# Use target_compile_definitions to define BOARD_TYPE, BOARD_NAME +# If BOARD_NAME is empty, use BOARD_TYPE +if(NOT BOARD_NAME) + set(BOARD_NAME ${BOARD_TYPE}) +endif() +target_compile_definitions(${COMPONENT_LIB} + PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" BOARD_NAME=\"${BOARD_NAME}\" + PRIVATE BUILTIN_TEXT_FONT=${BUILTIN_TEXT_FONT} BUILTIN_ICON_FONT=${BUILTIN_ICON_FONT} + ) + +# Add generation rules +add_custom_command( + OUTPUT ${LANG_HEADER} + COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/../xiaozhi-esp32/scripts/gen_lang.py + --language "${LANG_DIR}" + --output "${LANG_HEADER}" + DEPENDS + ${LANG_JSON} + ${CMAKE_CURRENT_SOURCE_DIR}/../xiaozhi-esp32/scripts/gen_lang.py + COMMENT "Generating ${LANG_DIR} language config" +) + +# Force build generation dependencies +add_custom_target(lang_header ALL + DEPENDS ${LANG_HEADER} +) + +# Find ESP-SR component dynamically +find_component_by_pattern("espressif__esp-sr" ESP_SR_COMPONENT ESP_SR_COMPONENT_PATH) +if(ESP_SR_COMPONENT_PATH) + set(ESP_SR_MODEL_PATH "${ESP_SR_COMPONENT_PATH}/model") +endif() + +# Find xiaozhi-fonts component dynamically +find_component_by_pattern("xiaozhi-fonts" XIAOZHI_FONTS_COMPONENT XIAOZHI_FONTS_COMPONENT_PATH) +if(XIAOZHI_FONTS_COMPONENT_PATH) + set(XIAOZHI_FONTS_PATH "${XIAOZHI_FONTS_COMPONENT_PATH}") +endif() + +if(CONFIG_BOARD_TYPE_ESP_HI) +set(URL "https://github.com/espressif2022/image_player/raw/main/test_apps/test_8bit") +set(EMOJI_DIR "${CMAKE_BINARY_DIR}/emoji") +file(MAKE_DIRECTORY ${EMOJI_DIR}) + +# List all files to download +set(FILES_TO_DOWNLOAD "") +list(APPEND FILES_TO_DOWNLOAD "Anger_enter.aaf" "Anger_loop.aaf" "Anger_return.aaf") +list(APPEND FILES_TO_DOWNLOAD "happy_enter.aaf" "happy_loop.aaf" "happ_return.aaf") +list(APPEND FILES_TO_DOWNLOAD "sad_enter.aaf" "sad_loop.aaf" "sad_return.aaf") +list(APPEND FILES_TO_DOWNLOAD "scorn_enter.aaf" "scorn_loop.aaf" "scorn_return.aaf") +list(APPEND FILES_TO_DOWNLOAD "left_enter.aaf" "left_loop.aaf" "left_return.aaf") +list(APPEND FILES_TO_DOWNLOAD "right_enter.aaf" "right_loop.aaf" "right_return.aaf") +list(APPEND FILES_TO_DOWNLOAD "asking.aaf" "blink_once.aaf" "blink_quick.aaf") +list(APPEND FILES_TO_DOWNLOAD "connecting.aaf" "panic_enter.aaf" "panic_loop.aaf") +list(APPEND FILES_TO_DOWNLOAD "panic_return.aaf" "wake.aaf") + +foreach(FILENAME IN LISTS FILES_TO_DOWNLOAD) + set(REMOTE_FILE "${URL}/${FILENAME}") + set(LOCAL_FILE "${EMOJI_DIR}/${FILENAME}") + + # Check if local file exists + if(EXISTS ${LOCAL_FILE}) + message(STATUS "File ${FILENAME} already exists, skipping download") + else() + message(STATUS "Downloading ${FILENAME}") + file(DOWNLOAD ${REMOTE_FILE} ${LOCAL_FILE} + STATUS DOWNLOAD_STATUS) + list(GET DOWNLOAD_STATUS 0 STATUS_CODE) + if(NOT STATUS_CODE EQUAL 0) + message(FATAL_ERROR "Failed to download ${FILENAME} from ${URL}") + endif() + endif() +endforeach() + +endif() + + +# Function to build default assets based on configuration +function(build_default_assets_bin) + # Set output path for generated assets.bin + set(GENERATED_ASSETS_BIN "${CMAKE_BINARY_DIR}/generated_assets.bin") + + # Prepare arguments for build script + set(BUILD_ARGS + "--sdkconfig" "${SDKCONFIG}" + "--output" "${GENERATED_ASSETS_BIN}" + ) + + # Add builtin text font if defined + if(BUILTIN_TEXT_FONT) + list(APPEND BUILD_ARGS "--builtin_text_font" "${BUILTIN_TEXT_FONT}") + endif() + + # Add default emoji collection if defined + if(DEFAULT_EMOJI_COLLECTION) + list(APPEND BUILD_ARGS "--emoji_collection" "${DEFAULT_EMOJI_COLLECTION}") + endif() + + # Add default assets extra files if defined + if(DEFAULT_ASSETS_EXTRA_FILES) + list(APPEND BUILD_ARGS "--extra_files" "${DEFAULT_ASSETS_EXTRA_FILES}") + endif() + + list(APPEND BUILD_ARGS "--esp_sr_model_path" "${ESP_SR_MODEL_PATH}") + list(APPEND BUILD_ARGS "--xiaozhi_fonts_path" "${XIAOZHI_FONTS_PATH}") + + # Create custom command to build assets + add_custom_command( + OUTPUT ${GENERATED_ASSETS_BIN} + COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/../xiaozhi-esp32/scripts/build_default_assets.py ${BUILD_ARGS} + DEPENDS + ${SDKCONFIG} + ${CMAKE_CURRENT_SOURCE_DIR}/../xiaozhi-esp32/scripts/build_default_assets.py + COMMENT "Building default assets.bin based on configuration" + VERBATIM + ) + + # Create target for generated assets + add_custom_target(generated_default_assets ALL + DEPENDS ${GENERATED_ASSETS_BIN} + ) + + # Set the generated file path in parent scope + set(GENERATED_ASSETS_LOCAL_FILE ${GENERATED_ASSETS_BIN} PARENT_SCOPE) + + message(STATUS "Default assets build configured: ${GENERATED_ASSETS_BIN}") +endfunction() + + +# Function to get local assets file path (handles both URL and local file) +function(get_assets_local_file assets_source assets_local_file_var) + # Check if it's a URL (starts with http:// or https://) + if(assets_source MATCHES "^https?://") + # It's a URL, download it + get_filename_component(ASSETS_FILENAME "${assets_source}" NAME) + set(ASSETS_LOCAL_FILE "${CMAKE_BINARY_DIR}/${ASSETS_FILENAME}") + set(ASSETS_TEMP_FILE "${CMAKE_BINARY_DIR}/${ASSETS_FILENAME}.tmp") + + # Check if local file exists + if(EXISTS ${ASSETS_LOCAL_FILE}) + message(STATUS "Assets file ${ASSETS_FILENAME} already exists, skipping download") + else() + message(STATUS "Downloading ${ASSETS_FILENAME}") + + # Clean up any existing temp file + if(EXISTS ${ASSETS_TEMP_FILE}) + file(REMOVE ${ASSETS_TEMP_FILE}) + endif() + + # Download to temporary file first + file(DOWNLOAD ${assets_source} ${ASSETS_TEMP_FILE} + STATUS DOWNLOAD_STATUS) + list(GET DOWNLOAD_STATUS 0 STATUS_CODE) + if(NOT STATUS_CODE EQUAL 0) + # Clean up temp file on failure + if(EXISTS ${ASSETS_TEMP_FILE}) + file(REMOVE ${ASSETS_TEMP_FILE}) + endif() + message(FATAL_ERROR "Failed to download ${ASSETS_FILENAME} from ${assets_source}") + endif() + + # Move temp file to final location (atomic operation) + file(RENAME ${ASSETS_TEMP_FILE} ${ASSETS_LOCAL_FILE}) + message(STATUS "Successfully downloaded ${ASSETS_FILENAME}") + endif() + else() + # It's a local file path + if(IS_ABSOLUTE "${assets_source}") + set(ASSETS_LOCAL_FILE "${assets_source}") + else() + set(ASSETS_LOCAL_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${assets_source}") + endif() + + # Check if local file exists + if(NOT EXISTS ${ASSETS_LOCAL_FILE}) + message(FATAL_ERROR "Assets file not found: ${ASSETS_LOCAL_FILE}") + endif() + + message(STATUS "Using assets file: ${ASSETS_LOCAL_FILE}") + endif() + + set(${assets_local_file_var} ${ASSETS_LOCAL_FILE} PARENT_SCOPE) +endfunction() + + +partition_table_get_partition_info(size "--partition-name assets" "size") +partition_table_get_partition_info(offset "--partition-name assets" "offset") +if ("${size}" AND "${offset}") + # Flash assets based on configuration + if(CONFIG_FLASH_DEFAULT_ASSETS) + # Build default assets based on configuration + build_default_assets_bin() + esptool_py_flash_to_partition(flash "assets" "${GENERATED_ASSETS_LOCAL_FILE}") + message(STATUS "Generated default assets flash configured: ${GENERATED_ASSETS_LOCAL_FILE} -> assets partition") + elseif(CONFIG_FLASH_CUSTOM_ASSETS) + # Flash custom assets + get_assets_local_file("${CONFIG_CUSTOM_ASSETS_FILE}" ASSETS_LOCAL_FILE) + esptool_py_flash_to_partition(flash "assets" "${ASSETS_LOCAL_FILE}") + message(STATUS "Custom assets flash configured: ${ASSETS_LOCAL_FILE} -> assets partition") + elseif(CONFIG_FLASH_NONE_ASSETS) + message(STATUS "Assets flashing disabled (FLASH_NONE_ASSETS)") + endif() +else() + message(STATUS "Assets partition not found, using v1 partition table") +endif() diff --git a/firmware/main/Kconfig.projbuild b/firmware/main/Kconfig.projbuild new file mode 100644 index 0000000..e1a3742 --- /dev/null +++ b/firmware/main/Kconfig.projbuild @@ -0,0 +1,839 @@ +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 + help + Select the assets to flash. + + config FLASH_NONE_ASSETS + bool "Do not flash assets" + config FLASH_DEFAULT_ASSETS + bool "Flash Default Assets" + config FLASH_CUSTOM_ASSETS + bool "Flash Custom Assets" +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_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_SPARKBOT + bool "Espressif SparkBot" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_ESP_SPOT_S3 + bool "Espressif Spot-S3" + depends on IDF_TARGET_ESP32S3 + 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_ECHOEAR + bool "EchoEar" + 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_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_STACK_CHAN + bool "M5Stack StackChan" + 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_ATOM_MATRIX_ECHO_BASE + bool "M5Stack AtomMatrix + Echo Base" + depends on IDF_TARGET_ESP32 + config BOARD_TYPE_WAVESHARE_S3_AUDIO_BOARD + bool "Waveshare ESP32-S3-Audio-Board" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_8 + bool "Waveshare ESP32-S3-Touch-AMOLED-1.8" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_2_06 + bool "Waveshare ESP32-S3-Touch-AMOLED-2.06" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_75 + bool "Waveshare ESP32-S3-Touch-AMOLED-1.75" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_83 + bool "Waveshare ESP32-S3-Touch-LCD-1.83" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_4B + bool "Waveshare ESP32-S3-Touch-LCD-4B" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_85C + bool "Waveshare ESP32-S3-Touch-LCD-1.85C" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_85 + bool "Waveshare ESP32-S3-Touch-LCD-1.85" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_46 + bool "Waveshare ESP32-S3-Touch-LCD-1.46" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_C6_LCD_1_69 + bool "Waveshare ESP32-C6-LCD-1.69" + depends on IDF_TARGET_ESP32C6 + config BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_1_43 + bool "Waveshare ESP32-C6-Touch-AMOLOED-1.43" + depends on IDF_TARGET_ESP32C6 + config BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_1_32 + bool "Waveshare ESP32-C6-Touch-AMOLOED-1.32" + depends on IDF_TARGET_ESP32C6 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_32 + bool "Waveshare ESP32-S3-Touch-AMOLOED-1.32" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_49 + bool "Waveshare ESP32-S3-Touch-LCD-3.49" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_5 + bool "Waveshare ESP32-S3-Touch-LCD-3.5" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_5B + bool "Waveshare ESP32-S3-Touch-LCD-3.5B" + depends on IDF_TARGET_ESP32S3 + config BOARD_TYPE_WAVESHARE_P4_NANO + bool "Waveshare ESP32-P4-NANO" + depends on IDF_TARGET_ESP32P4 + config BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_4B + bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4B" + depends on IDF_TARGET_ESP32P4 + config BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_7B + bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-7B" + depends on IDF_TARGET_ESP32P4 + config BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_XC + bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C or ESP32-P4-WIFI6-Touch-LCD-4C" + 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_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_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_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 +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_ESP32 + 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_WAVESHARE_P4_NANO || BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_XC || 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_TYPE_800_1280_10_1_INCH + bool "Waveshare 101M-8001280-IPS-CT-K Display" + config LCD_TYPE_800_1280_10_1_INCH_A + bool "Waveshare 10.1-DSI-TOUCH-A Display" + config LCD_TYPE_800_800_3_4_INCH + bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C with 800*800 3.4inch round display" + config LCD_TYPE_720_720_4_INCH + bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4C with 720*720 4inch round display" + config LCD_CUSTOM + bool "Custom LCD (自定义屏幕参数)" +endchoice + +choice DISPLAY_ESP32S3_KORVO2_V3 + depends on BOARD_TYPE_ESP_KORVO2_V3 + 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_AUDIO_BOARD + depends on BOARD_TYPE_WAVESHARE_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_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_3 || BOARD_TYPE_ECHOEAR || BOARD_TYPE_LICHUANG_DEV_S3 +endchoice + +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 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_S3_TOUCH_AMOLED_1_75 || BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_83\ + || BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_7B \ + || BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_XC || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 || BOARD_TYPE_YUNLIAO_S3 \ + || BOARD_TYPE_ECHOEAR || BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_49) + 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 + +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 USE_ACOUSTIC_WIFI_PROVISIONING + bool "Enable Acoustic WiFi Provisioning" + default n + help + Enable acoustic WiFi provisioning, use audio signal to transmit WiFi configuration 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_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_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 (USE WITH CAUTION)" + 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 + 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. + + 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 + +menu "BLE prph Configuration" + + choice EXAMPLE_USE_IO_TYPE + prompt "I/O Capability" + default BLE_SM_IO_CAP_NO_IO + help + I/O capability of device. + + config BLE_SM_IO_CAP_DISP_ONLY + bool "DISPLAY ONLY" + config BLE_SM_IO_CAP_DISP_YES_NO + bool "DISPLAY YESNO" + config BLE_SM_IO_CAP_KEYBOARD_ONLY + bool "KEYBOARD ONLY" + config BLE_SM_IO_CAP_NO_IO + bool "Just works" + config BLE_SM_IO_CAP_KEYBOARD_DISP + bool "Both KEYBOARD & DISPLAY" + endchoice + + config EXAMPLE_IO_TYPE + int + default 0 if BLE_SM_IO_CAP_DISP_ONLY + default 1 if BLE_SM_IO_CAP_DISP_YES_NO + default 2 if BLE_SM_IO_CAP_KEYBOARD_ONLY + default 3 if BLE_SM_IO_CAP_NO_IO + default 4 if BLE_SM_IO_CAP_KEYBOARD_DISP + + config EXAMPLE_BONDING + bool + default n + prompt "Use Bonding" + help + Use this option to enable/disable bonding. + + config EXAMPLE_MITM + bool + default n + prompt "MITM security" + help + Use this option to enable/disable MITM security. + + config EXAMPLE_USE_SC + bool + depends on BT_NIMBLE_SM_SC + default n + prompt "Use Secure Connection feature" + help + Use this option to enable/disable Security Manager Secure Connection 4.2 feature. + + config EXAMPLE_EXTENDED_ADV + bool + depends on SOC_BLE_50_SUPPORTED && BT_NIMBLE_50_FEATURE_SUPPORT + default y if SOC_ESP_NIMBLE_CONTROLLER + select BT_NIMBLE_EXT_ADV + prompt "Enable Extended Adv" + help + Use this option to enable extended advertising in the example. + If this option is disabled, ensure config BT_NIMBLE_EXT_ADV is + also disabled from Nimble stack menuconfig + + config EXAMPLE_RANDOM_ADDR + bool + prompt "Advertise RANDOM Address" + help + Use this option to advertise a random address instead of public address + + config EXAMPLE_ENCRYPTION + bool + prompt "Enable Link Encryption" + help + This adds Encrypted Read and Write permissions in the custom GATT server. + + config EXAMPLE_RESOLVE_PEER_ADDR + bool + prompt "Enable resolving peer address" + help + Use this option to enable resolving peer's address. + +endmenu diff --git a/firmware/main/apps/app_ai_agent/app_ai_agent.cpp b/firmware/main/apps/app_ai_agent/app_ai_agent.cpp new file mode 100644 index 0000000..0caf95e --- /dev/null +++ b/firmware/main/apps/app_ai_agent/app_ai_agent.cpp @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "app_ai_agent.h" +#include +#include +#include +#include +#include + +using namespace mooncake; +using namespace smooth_ui_toolkit::lvgl_cpp; + +AppAiAgent::AppAiAgent() +{ + // Configure App name + setAppInfo().name = "AI.AGENT"; + // Configure App icon + setAppInfo().icon = (void*)&icon_ai_agent; + // Configure App theme color + static uint32_t theme_color = 0x33CC99; + setAppInfo().userData = (void*)&theme_color; +} + +// Called when the App is installed +void AppAiAgent::onCreate() +{ + mclog::tagInfo(getAppInfo().name, "on create"); +} + +// Called when the App is opened +// You can construct UI, initialize operations, etc. here +void AppAiAgent::onOpen() +{ + mclog::tagInfo(getAppInfo().name, "on open"); + + GetHAL().startXiaozhi(); +} + +// Called repeatedly while the App is running +void AppAiAgent::onRunning() +{ +} + +// Called when the App is closed +// You can destroy UI, release resources, etc. here +void AppAiAgent::onClose() +{ + mclog::tagInfo(getAppInfo().name, "on close"); +} diff --git a/firmware/main/apps/app_ai_agent/app_ai_agent.h b/firmware/main/apps/app_ai_agent/app_ai_agent.h new file mode 100644 index 0000000..2b29b2a --- /dev/null +++ b/firmware/main/apps/app_ai_agent/app_ai_agent.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include + +/** + * @brief Derived App + * + */ +class AppAiAgent : public mooncake::AppAbility { +public: + AppAiAgent(); + + // Override lifecycle callbacks + void onCreate() override; + void onOpen() override; + void onRunning() override; + void onClose() override; +}; diff --git a/firmware/main/apps/app_avatar/app_avatar.cpp b/firmware/main/apps/app_avatar/app_avatar.cpp new file mode 100644 index 0000000..fc66c19 --- /dev/null +++ b/firmware/main/apps/app_avatar/app_avatar.cpp @@ -0,0 +1,255 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "app_avatar.h" +#include "view/ws_call.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace mooncake; +using namespace smooth_ui_toolkit::lvgl_cpp; +using namespace stackchan; + +#include +#include +#include + +static bool contains_word(const std::string& text, const std::unordered_set& words) +{ + auto to_lower = [](std::string s) { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; + }; + + std::istringstream iss(text); + std::string token; + while (iss >> token) { + token = to_lower(token); + if (words.find(token) != words.end()) { + return true; + } + } + return false; +} + +AppAvatar::AppAvatar() +{ + // 配置 App 名 + setAppInfo().name = "AVATAR"; + // 配置 App 图标 + setAppInfo().icon = (void*)&icon_sentinel; + // 配置 App 主题颜色 + static uint32_t theme_color = 0xFF6699; + setAppInfo().userData = (void*)&theme_color; +} + +// App 被安装时会被调用 +void AppAvatar::onCreate() +{ + mclog::tagInfo(getAppInfo().name, "on create"); +} + +void AppAvatar::onOpen() +{ + mclog::tagInfo(getAppInfo().name, "on open"); + + // GetHAL().startBleServer(); + GetHAL().startWebSocketAvatar(); + + LvglLockGuard lock; + + // Create default avatar + auto avatar = std::make_unique(); + avatar->init(lv_screen_active()); + GetStackChan().attachAvatar(std::move(avatar)); + + /* ------------------------------- BLE events ------------------------------- */ + _ble_avatar_data.callback_id = GetHAL().onBleAvatarData.connect([&](const char* data) { + std::lock_guard lock(_mutex); + if (_ble_avatar_data.update_flag) { + return; + } + _ble_avatar_data.update_flag = true; + _ble_avatar_data.data_ptr = (char*)data; + }); + + _ble_motion_data.callback_id = GetHAL().onBleMotionData.connect([&](const char* data) { + std::lock_guard lock(_mutex); + if (_ble_motion_data.update_flag) { + return; + } + _ble_motion_data.update_flag = true; + _ble_motion_data.data_ptr = (char*)data; + }); + + /* ---------------------------- Websocket events ---------------------------- */ + // Avatar control + _ws_callback_ids.avatar_id = GetHAL().onWsAvatarData.connect([&](std::string_view data) { + LvglLockGuard lvgl_lock; + GetStackChan().updateAvatarFromJson(data.data()); + }); + + // Motion control + _ws_callback_ids.motion_id = GetHAL().onWsMotionData.connect([&](std::string_view data) { + LvglLockGuard lvgl_lock; + check_auto_angle_sync_mode(); + GetStackChan().updateMotionFromJson(data.data()); + }); + + // Phone call handling + _ws_callback_ids.call_req_id = GetHAL().onWsCallRequest.connect([&](std::string caller) { + if (_ws_call_view_id >= 0) { + mclog::tagWarn(getAppInfo().name, "ws call view already exists"); + return; + } + + LvglLockGuard lvgl_lock; + + auto& avatar = GetStackChan().avatar(); + avatar.setSpeech(""); + avatar.leftEye().setVisible(false); + avatar.rightEye().setVisible(false); + avatar.mouth().setVisible(false); + + auto view = std::make_unique(lv_screen_active(), caller); + view->onAccept = []() { + auto& avatar = GetStackChan().avatar(); + avatar.setSpeech(""); + avatar.leftEye().setVisible(true); + avatar.rightEye().setVisible(true); + avatar.mouth().setVisible(true); + + GetHAL().onWsCallResponse.emit(true); + }; + view->onDecline = []() { + auto& avatar = GetStackChan().avatar(); + avatar.setSpeech(""); + avatar.leftEye().setVisible(true); + avatar.rightEye().setVisible(true); + avatar.mouth().setVisible(true); + + GetHAL().onWsCallResponse.emit(false); + }; + view->onEnd = []() { GetHAL().onWsCallEnd.emit(WsSignalSource::Local); }; + view->onDestory = [&]() { _ws_call_view_id = -1; }; + + _ws_call_view_id = avatar.addDecorator(std::move(view)); + }); + + _ws_callback_ids.call_end_id = GetHAL().onWsCallEnd.connect([&](WsSignalSource source) { + if (source != WsSignalSource::Remote) { + return; + } + + LvglLockGuard lvgl_lock; + + if (_ws_call_view_id < 0) { + mclog::tagWarn(getAppInfo().name, "ws call view does not exist"); + return; + } + + auto& avatar = GetStackChan().avatar(); + avatar.setSpeech(""); + avatar.leftEye().setVisible(true); + avatar.rightEye().setVisible(true); + avatar.mouth().setVisible(true); + + avatar.removeDecorator(_ws_call_view_id); + _ws_call_view_id = -1; + }); + + // Text message handling + _ws_callback_ids.text_msg_id = GetHAL().onWsTextMessage.connect([&](const WsTextMessage_t& message) { + LvglLockGuard lvgl_lock; + + auto& stackchan = GetStackChan(); + auto& avatar = stackchan.avatar(); + + stackchan.addModifier( + std::make_unique(fmt::format("{} says: {}", message.name, message.content), 6000)); + stackchan.addModifier(std::make_unique(2000)); + + // Special handling + if (contains_word(message.content, {"hello", "hi"})) { + stackchan.addModifier(std::make_unique(avatar::Emotion::Happy, 2000)); + } else if (contains_word(message.content, {"love"})) { + stackchan.addModifier(std::make_unique(avatar::Emotion::Happy, 2000)); + } + }); + + _ws_callback_ids.dance_data_id = GetHAL().onWsDanceData.connect([&](std::string_view data) { + LvglLockGuard lvgl_lock; + auto sequence = stackchan::animation::parse_sequence_from_json(data.data()); + if (!sequence.empty()) { + GetStackChan().addModifier(std::make_unique(sequence)); + } + }); + + /* ------------------------------ Video window ------------------------------ */ + _video_window = std::make_unique(lv_screen_active()); +} + +void AppAvatar::onRunning() +{ + std::lock_guard lock(_mutex); + + LvglLockGuard lvgl_lock; + + if (_ble_avatar_data.update_flag) { + GetStackChan().updateAvatarFromJson(_ble_avatar_data.data_ptr); + _ble_avatar_data.update_flag = false; + _ble_avatar_data.data_ptr = nullptr; + } + + if (_ble_motion_data.update_flag) { + check_auto_angle_sync_mode(); + GetStackChan().updateMotionFromJson(_ble_motion_data.data_ptr); + _ble_motion_data.update_flag = false; + _ble_motion_data.data_ptr = nullptr; + } + + GetStackChan().update(); +} + +void AppAvatar::onClose() +{ + mclog::tagInfo(getAppInfo().name, "on close"); + + LvglLockGuard lock; + + GetStackChan().resetAvatar(); + _video_window.reset(); + + GetHAL().onBleAvatarData.disconnect(_ble_avatar_data.callback_id); + GetHAL().onBleMotionData.disconnect(_ble_motion_data.callback_id); + + GetHAL().onWsAvatarData.disconnect(_ws_callback_ids.avatar_id); + GetHAL().onWsMotionData.disconnect(_ws_callback_ids.motion_id); + GetHAL().onWsCallRequest.disconnect(_ws_callback_ids.call_req_id); + GetHAL().onWsCallEnd.disconnect(_ws_callback_ids.call_end_id); + GetHAL().onWsTextMessage.disconnect(_ws_callback_ids.text_msg_id); + GetHAL().onWsDanceData.disconnect(_ws_callback_ids.dance_data_id); +} + +void AppAvatar::check_auto_angle_sync_mode() +{ + auto& motion = GetStackChan().motion(); + + // If far from last command, enable auto angle sync + if (GetHAL().millis() - _last_motion_cmd_tick > 2000) { + motion.setAutoAngleSyncEnabled(true); + } else { + motion.setAutoAngleSyncEnabled(false); + } + + _last_motion_cmd_tick = GetHAL().millis(); +} diff --git a/firmware/main/apps/app_avatar/app_avatar.h b/firmware/main/apps/app_avatar/app_avatar.h new file mode 100644 index 0000000..ffd125d --- /dev/null +++ b/firmware/main/apps/app_avatar/app_avatar.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include "view/video_window.hpp" +#include +#include +#include +#include +#include + +/** + * @brief 派生 App + * + */ +class AppAvatar : public mooncake::AppAbility { +public: + AppAvatar(); + + // 重写生命周期回调 + void onCreate() override; + void onOpen() override; + void onRunning() override; + void onClose() override; + +private: + std::mutex _mutex; + + struct BleHandlerData_t { + bool update_flag = false; + char* data_ptr = nullptr; + int callback_id = -1; + }; + BleHandlerData_t _ble_avatar_data; + BleHandlerData_t _ble_motion_data; + + struct WsCallbackIds_t { + int avatar_id = -1; + int motion_id = -1; + int call_req_id = -1; + int call_end_id = -1; + int text_msg_id = -1; + int dance_data_id = -1; + }; + WsCallbackIds_t _ws_callback_ids; + + int _ws_call_view_id = -1; + + uint32_t _last_motion_cmd_tick = 0; + + std::unique_ptr _video_window; + + void check_auto_angle_sync_mode(); +}; diff --git a/firmware/main/apps/app_avatar/view/assets/icon_phone.c b/firmware/main/apps/app_avatar/view/assets/icon_phone.c new file mode 100644 index 0000000..a3dca92 --- /dev/null +++ b/firmware/main/apps/app_avatar/view/assets/icon_phone.c @@ -0,0 +1,240 @@ +#ifdef __has_include +#if __has_include("lvgl.h") +#ifndef LV_LVGL_H_INCLUDE_SIMPLE +#define LV_LVGL_H_INCLUDE_SIMPLE +#endif +#endif +#endif + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_ICON_PHONE +#define LV_ATTRIBUTE_IMAGE_ICON_PHONE +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_ICON_PHONE uint8_t icon_phone_map[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, + 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, + 0xff, 0xff, 0xbf, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xbf, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +const lv_image_dsc_t icon_phone = { + .header.cf = LV_COLOR_FORMAT_RGB565A8, + .header.magic = LV_IMAGE_HEADER_MAGIC, + .header.w = 36, + .header.h = 36, + .data_size = 1296 * 3, + .data = icon_phone_map, +}; diff --git a/firmware/main/apps/app_avatar/view/video_window.hpp b/firmware/main/apps/app_avatar/view/video_window.hpp new file mode 100644 index 0000000..a3d509f --- /dev/null +++ b/firmware/main/apps/app_avatar/view/video_window.hpp @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include +#include +#include +#include +#include + +namespace view { + +class VideoWindow { +public: + VideoWindow(lv_obj_t* parent) + { + _image = std::make_unique(parent); + _image->align(LV_ALIGN_CENTER, 0, 0); + _image->setSize(320, 240); + _image->setHidden(true); + + _on_ws_video_mode_change_id = GetHAL().onWsVideoModeChange.connect([this](bool enabled) { + LvglLockGuard lock; + if (enabled) { + _image->setHidden(false); + } else { + _image->setHidden(true); + } + }); + + _on_ws_video_frame_id = GetHAL().onWsVideoFrame.connect([this](std::shared_ptr img) { + LvglLockGuard lock; + _image_cached = img; + auto img_dsc = _image_cached->image_dsc(); + _image->setSrc(img_dsc); + }); + } + + ~VideoWindow() + { + GetHAL().onWsVideoModeChange.disconnect(_on_ws_video_mode_change_id); + GetHAL().onWsVideoFrame.disconnect(_on_ws_video_frame_id); + } + +private: + std::unique_ptr _image; + std::shared_ptr _image_cached; + + int _on_ws_video_mode_change_id = -1; + int _on_ws_video_frame_id = -1; +}; + +} // namespace view diff --git a/firmware/main/apps/app_avatar/view/ws_call.cpp b/firmware/main/apps/app_avatar/view/ws_call.cpp new file mode 100644 index 0000000..57a9f66 --- /dev/null +++ b/firmware/main/apps/app_avatar/view/ws_call.cpp @@ -0,0 +1,259 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "ws_call.h" +#include +#include +#include +#include +#include + +using namespace smooth_ui_toolkit::lvgl_cpp; +using namespace smooth_ui_toolkit; +using namespace view; + +LV_IMAGE_DECLARE(icon_phone); + +/** + * @brief + * + */ +class IncomingPanel : public Container { +public: + std::function onAccept; + std::function onDecline; + + IncomingPanel(lv_obj_t* parent, std::string caller) : Container(parent) + { + setSize(320, 240); + align(LV_ALIGN_CENTER, 0, 0); + setBorderWidth(0); + setBgOpa(0); + + _label_caller = std::make_unique