From 0808a479697b2ad8ce4d5edc3eba243ada8d39bc Mon Sep 17 00:00:00 2001 From: Forairaaaaa Date: Wed, 4 Nov 2026 10:23:10 +0800 Subject: [PATCH] feat(firmware): update to v1.6.2 - Upgrade to ESP-IDF 6.1 and Xiaozhi v2.5.2. - Use upstream audio and camera drivers and reduce memory pressure during photo uploads. - Update Wi-Fi AP selection and fix ESP-NOW startup and repeated provisioning callbacks. - Fix app visibility storage and keep local settings when account unlinking fails. - Validation: the public-source ESP-IDF 6.1 build and motion host tests passed. Main functions and the 1.6.0-to-1.6.2 OTA upgrade were verified on hardware using the local release firmware; the public authentication placeholder was build-tested only. Thanks to artrobin-cn for the upgrade analysis in m5stack/StackChan#120 and the TCP teardown fix included through 78/esp-ml307#61. Co-authored-by: artrobin <74446074+artrobin-cn@users.noreply.github.com> --- firmware/CMakeLists.txt | 7 +- firmware/README.md | 2 +- firmware/dependencies.lock | 277 ++-- firmware/fetch_repos.py | 45 +- firmware/main/CMakeLists.txt | 58 +- firmware/main/Kconfig.projbuild | 71 +- .../apps/app_espnow_ctrl/app_espnow_ctrl.cpp | 27 +- .../apps/app_espnow_ctrl/app_espnow_ctrl.h | 2 + .../main/apps/app_launcher/app_launcher.cpp | 22 +- firmware/main/apps/app_launcher/view/view.cpp | 40 +- firmware/main/apps/app_launcher/view/view.h | 2 +- firmware/main/apps/app_setup/app_setup.cpp | 4 +- .../main/apps/app_setup/workers/account.cpp | 5 +- .../main/apps/app_setup/workers/ai_agent.cpp | 8 +- .../apps/app_setup/workers/connectivity.cpp | 6 +- .../apps/app_setup/workers/feature_toggle.cpp | 126 +- .../apps/app_setup/workers/feature_toggle.h | 19 +- .../main/apps/app_setup/workers/system.cpp | 28 +- .../apps/app_setup/workers/toggle_row.cpp | 60 +- .../main/apps/app_setup/workers/toggle_row.h | 15 +- .../main/apps/app_setup/workers/workers.h | 4 +- firmware/main/assets/sfx/camera_shutter.ogg | Bin 9734 -> 1631 bytes firmware/main/assets/sfx/new_notification.ogg | Bin 6935 -> 6191 bytes firmware/main/hal/board/cores3_audio_codec.cc | 246 ---- firmware/main/hal/board/cores3_audio_codec.h | 37 - firmware/main/hal/board/stackchan.cc | 2 +- firmware/main/hal/board/stackchan_camera.cc | 1161 +---------------- firmware/main/hal/board/stackchan_camera.h | 75 +- firmware/main/hal/board/stackchan_display.cc | 4 +- firmware/main/hal/drivers/bmi270/bmi270.cpp | 1 + firmware/main/hal/hal.h | 2 +- firmware/main/hal/hal_account.cpp | 6 +- firmware/main/hal/hal_app_center.cpp | 5 +- firmware/main/hal/hal_espnow.cpp | 106 +- firmware/main/hal/hal_ezdata.cpp | 3 +- firmware/main/hal/hal_ota.cpp | 8 +- firmware/main/hal/hal_toggles.cpp | 145 +- firmware/main/hal/hal_toggles.h | 74 +- firmware/main/hal/hal_ws_avatar.cpp | 7 +- .../hal/utils/espnow_radio/espnow_radio.cpp | 60 + .../hal/utils/espnow_radio/espnow_radio.h | 15 + .../hal/utils/local_network/avatar_socket.cpp | 22 +- firmware/main/hal/utils/ota/ota.c | 5 +- .../hal/utils/wifi_connect/wifi_failure.h | 2 +- .../hal/utils/wifi_connect/wifi_station.cc | 4 +- firmware/main/idf_component.yml | 47 +- firmware/main/main.cpp | 19 +- .../main/stackchan/avatar/avatar/avatar.h | 2 + firmware/patches/.gitattributes | 2 + firmware/patches/xiaozhi-esp32.patch | 712 ++++++---- firmware/repos.json | 6 +- firmware/sdkconfig.defaults | 13 +- 52 files changed, 1277 insertions(+), 2342 deletions(-) delete mode 100644 firmware/main/hal/board/cores3_audio_codec.cc delete mode 100644 firmware/main/hal/board/cores3_audio_codec.h create mode 100644 firmware/main/hal/utils/espnow_radio/espnow_radio.cpp create mode 100644 firmware/main/hal/utils/espnow_radio/espnow_radio.h create mode 100644 firmware/patches/.gitattributes diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 72b923d9d..b2fd4393c 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -2,7 +2,12 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) -set(PROJECT_VER "1.6.0") +# Keep compiler and linker arguments below Windows command-line limits. +if(CMAKE_GENERATOR MATCHES "^Ninja") + set(CMAKE_NINJA_FORCE_RESPONSE_FILE TRUE CACHE INTERNAL "Use Ninja response files") +endif() + +set(PROJECT_VER "1.6.2") add_definitions(-DFIRMWARE_VERSION=\"${PROJECT_VER}\") # Add this line to disable the specific warning diff --git a/firmware/README.md b/firmware/README.md index 20a73bf65..a6223f99f 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -9,7 +9,7 @@ python3 ./fetch_repos.py ### Tool Chains -[ESP-IDF v5.5.4](https://docs.espressif.com/projects/esp-idf/en/v5.5.4/esp32s3/index.html) +[ESP-IDF v6.1](https://docs.espressif.com/projects/esp-idf/en/v6.1/esp32s3/index.html) ### Build diff --git a/firmware/dependencies.lock b/firmware/dependencies.lock index 325d3982c..4b7a0b4ba 100644 --- a/firmware/dependencies.lock +++ b/firmware/dependencies.lock @@ -1,32 +1,40 @@ dependencies: 78/esp-ml307: - component_hash: 5231991281a2f48f0e34ec705c2982936264d8b14f6f9373e60b153fd4b62123 + component_hash: 77e7dad27b5d3f1dd69e6ac01f014cd71faa24d04bdf4202205e1fb78d48d44a dependencies: + - name: idf + version: '>=5.5.2' - name: 78/uart-uhci - registry_url: https://components.espressif.com - require: private rules: - if: target not in [esp32] - version: ~0.2.1 - - name: idf - require: private - version: '>=5.5.2' + version: ^0.4.1 + - matches: + - if: idf_version >=6.0 + name: espressif/mqtt + version: ^1.0.0 source: - registry_url: https://components.espressif.com/ - type: service - version: 3.6.5 + git: https://github.com/78/esp-ml307.git + path: . + type: git + version: 39638f2a4e514e12ef5d8afc7241db5b9b394086 78/esp-wifi-connect: - component_hash: e64ce09a795dd3b4883e1f68be10b0dd11260331f021a5f0b1f3bfd9a25d413b + component_hash: 24232dc15ebb239212dab37bdc8f5553e37edf8806268a05c28a7aa42dcd9422 dependencies: + - matches: + - if: idf_version >=6.0 + name: espressif/cjson + registry_url: https://components.espressif.com + require: private + version: ^1.7.19 - name: idf require: private version: '>=5.3' source: registry_url: https://components.espressif.com/ type: service - version: 3.1.2 + version: 3.3.1 78/esp_lcd_nv3023: - component_hash: fa88abfc19a312eb5e6f2ffa187e0a9faf67e01e758bfb979d3f9d92561a494f + component_hash: 2d1c227ae9c8d7303c0be9b2f2b45e595169a2ccb3055aad93b5f1600110b236 dependencies: - name: espressif/cmake_utilities registry_url: https://components.espressif.com @@ -38,27 +46,27 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.0 + version: 1.0.1 78/uart-eth-modem: - component_hash: 7a33284e4ba22f420191be8021cd4fe3e49f5e50eea1db70370b217a18147d8e + component_hash: 71949c4dd1f8beba0c8d69f137f383f457f9f443bdfe7f5a8068e1dd37857a20 dependencies: - name: 78/uart-uhci registry_url: https://components.espressif.com require: private - version: ~0.2.1 + version: ^0.4.1 - name: espressif/iot_eth registry_url: https://components.espressif.com require: private - version: ^0.1.0 + version: ^1.1.0 - name: idf require: private - version: '>=5.5.2' + version: '>=6.0.1' source: registry_url: https://components.espressif.com/ type: service - version: 0.3.4 + version: 0.7.1 78/uart-uhci: - component_hash: 200eb10923cb750dddbee5504a36d7b5eaac611afbf52fb9066dc7130159f696 + component_hash: c9230a65f9c5dc1b87d97612d5dfd4b79a37cf0cb62123f7d659e5d4e2e0f193 dependencies: - name: idf require: private @@ -66,17 +74,21 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 0.2.1 + version: 0.4.1 78/xiaozhi-fonts: - component_hash: 19ee829a7439702e18084a313b7b86017d3b8d17a094684575edc76bb6d11023 + component_hash: 3f4f9fa5dcb703208bf22e46a185bb2b4cc9d002b9474b90f7c77e55e0ac541a dependencies: - name: idf require: private - version: '>=5.3' + version: '>=5.5.2' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: private + version: ~9.5.0 source: registry_url: https://components.espressif.com/ type: service - version: 1.6.0 + version: 2.0.0 espressif/adc_battery_estimation: component_hash: b915167c87ed5a84b13d680bd011c2ed9a15121f1e247c6903141b9f138f3606 dependencies: @@ -110,7 +122,7 @@ dependencies: type: service version: 0.2.2 espressif/bmi270_sensor: - component_hash: fac23a5ecd2effdac0fb0705988d853745b8ffb99a384f87a307b3b25d15d432 + component_hash: 10f18ccc44bc71b09c0680c003c24cfb273905b079314efe1095e8ea9d714147 dependencies: - name: espressif/cmake_utilities registry_url: https://components.espressif.com @@ -126,7 +138,7 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 0.1.1 + version: 0.1.2 espressif/button: component_hash: d0afa32f0e50d60bc0c6fc23f7eea98adc6b02cfe70b590bc52c23c506745287 dependencies: @@ -141,6 +153,16 @@ dependencies: registry_url: https://components.espressif.com/ type: service version: 4.1.6 + espressif/cjson: + component_hash: e788323270d90738662d66fffa910bfe1fba019bba087f01557e70c40485b469 + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.7.19~2 espressif/cmake_utilities: component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f dependencies: @@ -152,7 +174,7 @@ dependencies: type: service version: 0.5.3 espressif/dl_fft: - component_hash: 7dadbd644c0d7ba4733cc3726ec4cff6edf27b043725e1115861dec1609a3d28 + component_hash: 965be7152132b365061abf5fc5fc7b85e525f73b94a204cdda61cfe9572c0d77 dependencies: - name: idf require: private @@ -160,9 +182,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 0.3.1 + version: 0.8.0 espressif/esp-dsp: - component_hash: 42dce32d46ac93dc11f60d368e29a830e9661c7345d794b8a45c343479cae636 + component_hash: 12e44db246517a627bc0fe2b255b8335410c0215c98bfba2df5a8e3edea839ef dependencies: - name: idf require: private @@ -170,27 +192,31 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.7.0 + version: 1.8.0 espressif/esp-sr: - component_hash: 46b1f70f8b561714b958d1c455213cca07162755d1b8df53498081199f15136f + component_hash: 809d0041cdddd98a278f0d5afef7bb60a451290577b98cf718dfffc91bdcbd9b dependencies: + - name: espressif/cjson + registry_url: https://components.espressif.com + require: private + version: ^1.7.19 - name: espressif/dl_fft registry_url: https://components.espressif.com require: private - version: '>=0.2.0' + version: '>=0.6.0' - name: espressif/esp-dsp registry_url: https://components.espressif.com require: private - version: 1.7.0 + version: 1.8.0 - name: idf require: private version: '>=5.0' source: registry_url: https://components.espressif.com/ type: service - version: 2.3.1 + version: 2.4.7 espressif/esp32-camera: - component_hash: ac4228c825b891378bc0806869170d4ebc5d45ba8ebc519b4f8886a56ad2d123 + component_hash: d1770097ec47996a9aba49b8d99672863c9855ea2d47e91fe31e11e58e8c7813 dependencies: - name: espressif/esp_jpeg registry_url: https://components.espressif.com @@ -202,9 +228,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 2.1.5 + version: 2.1.8 espressif/esp_audio_codec: - component_hash: af4663830bc73eca58febcc34a976d003465e78ed57458aa00a14b231e392ca1 + component_hash: 16e2880dbdd5a72264051f750f33a6e5a38fd25621c83e3a32a85a152d2d2643 dependencies: - name: idf require: private @@ -212,16 +238,20 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 2.4.1 + version: 2.5.0 espressif/esp_audio_effects: - component_hash: 8fa2bf7356a3c7fb9a9c7d2185aba474fa033b0506886c70752f67c42cd81b3f - dependencies: [] + component_hash: 6ad72ee106e25b07c9ae5b8ec77b566ef8d697473095f1c982e13247005b9ba6 + dependencies: + - name: espressif/gmf_fft + registry_url: https://components.espressif.com + require: private + version: ~1.0 source: registry_url: https://components.espressif.com/ type: service - version: 1.2.1 + version: 1.3.0~1 espressif/esp_cam_sensor: - component_hash: 35c1648ea87aac81da7a085d35f61b6d5ad88574bd0c5fdc4ab75efdef4ee6c1 + component_hash: 0dd6634a71cc8756f0e46c961476304a23a82db4f4c4deb8aecfa30161d2185c dependencies: - name: espressif/cmake_utilities registry_url: https://components.espressif.com @@ -244,9 +274,10 @@ dependencies: - esp32c5 - esp32c6 - esp32c61 - version: 1.5.2 + - esp32s31 + version: 2.6.0 espressif/esp_codec_dev: - component_hash: 0d9e9bc288156eb55f79338d312e1ebf8c9dfbd5e7d13ef0f20ccb031b4e15cf + component_hash: 4779f31a3ba3f9b38aee1afe6ccb9ba8f6108dc3a6e77d39d7d714e9a215371b dependencies: - name: idf require: private @@ -254,9 +285,9 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.5.4 + version: 1.6.2 espressif/esp_image_effects: - component_hash: a35037db02862d3cef6f72c25f422ae62da6e6c88219ce03c9ea24e0315ceaaa + component_hash: 7f605dcd1ebe56ce4adb76cfb1326d6e25e9455d15d60c547397a85929ba1228 dependencies: - name: idf require: private @@ -264,7 +295,7 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.1 + version: 1.2.0 espressif/esp_io_expander: component_hash: 7b7f91270172e46ca4597e3874f48d8412e280e8c295559f5922ec38656e3e5f dependencies: @@ -276,19 +307,19 @@ dependencies: type: service version: 1.2.1 espressif/esp_io_expander_tca9554: - component_hash: 8a9e3a7dbef589a54f814ff22e9121da86bce19d0e1ccffa53f5df6bb346a140 + component_hash: ee94a439ade1c17129a491efe572c600899306a2ffadbde491852d95333e28b4 dependencies: - name: espressif/esp_io_expander registry_url: https://components.espressif.com require: public - version: ^1.0.1 + version: ^1.2.0 - name: idf require: private version: '>=5.2' source: registry_url: https://components.espressif.com/ type: service - version: 2.0.0 + version: 2.0.3 espressif/esp_io_expander_tca95xx_16bit: component_hash: a9608ba5753ae2a72e79fccd376fc4283343c0306d23505fbf8a46fafdbde2a2 dependencies: @@ -314,7 +345,7 @@ dependencies: type: service version: 1.3.1 espressif/esp_lcd_axs15231b: - component_hash: e614bd75827e95800e39df1ba9474166060ab26c3807d06df9cc312a27021cda + component_hash: 62afe3c46789d7a13e3807c5d7a3a734870cd6375a28f14b12498c3382ad43b4 dependencies: - name: espressif/cmake_utilities registry_url: https://components.espressif.com @@ -326,11 +357,11 @@ dependencies: version: ^1 - name: idf require: private - version: '>5.0.4,!=5.1.1' + version: '>=5.3' source: registry_url: https://components.espressif.com/ type: service - version: 1.0.1~1 + version: 2.1.1 espressif/esp_lcd_co5300: component_hash: 7004162c496b65a5e0ee7c92800c33c12b373ea70d07199318a182cb16c741b7 dependencies: @@ -346,7 +377,7 @@ dependencies: type: service version: 2.0.3 espressif/esp_lcd_gc9a01: - component_hash: 09ddc8f5c9d718a659ffa30a4040e1ae75077e68492c38aec8c2beaaa7b3a9bc + component_hash: e60bc2b5649c7679bd010797a875ae64b2a57cf9cdbd6a2bf7a0ade66913edc0 dependencies: - name: espressif/cmake_utilities registry_url: https://components.espressif.com @@ -358,21 +389,21 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 2.0.1 + version: 2.0.4 espressif/esp_lcd_ili9341: - component_hash: 31f1b793aa2110dd2ae071c21ccbff0a4eb20d9a4ee40b6294c0dc0ad9552c4e + component_hash: 840387ee0c90a473e43fdc75570f77c52f06423aa331d95479bde8b418c43547 dependencies: - - name: idf - require: private - version: '>=4.4' - name: espressif/cmake_utilities registry_url: https://components.espressif.com require: private version: 0.* + - name: idf + require: private + version: '>=5.2' source: registry_url: https://components.espressif.com/ type: service - version: 1.2.0 + version: 2.1.0 espressif/esp_lcd_panel_io_additions: component_hash: 213d5c4a3c322a48c9f993d6bc2372206d34af95db34e87a3e02601bdcad7ce8 dependencies: @@ -540,11 +571,11 @@ dependencies: type: service version: 1.0.1 espressif/esp_lvgl_port: - component_hash: b6360960f47b6776462e7092861b3ea66477ffb762a01baa0aecbb3d74cd50f4 + component_hash: d3c020b45c3dfc0d1706a83b63a1c34a6eb444a6e935f9afff79f92b7b94dcef dependencies: - name: idf require: private - version: '>=5.1' + version: '>=5.2' - name: lvgl/lvgl registry_url: https://components.espressif.com require: public @@ -552,7 +583,7 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 2.7.2 + version: 2.9.0 espressif/esp_mmap_assets: component_hash: 91d785326b03db15e2f7f1314d8c976d38f21aa5759b570dcbbc89bcf247fd27 dependencies: @@ -584,7 +615,7 @@ dependencies: - esp32c6 version: 0.6.1 espressif/esp_sccb_intf: - component_hash: c071b189e49f40940722aea01a5489f873385cc39cd5b14012341135b85d1a9d + component_hash: eb6a7d5c992f09583c163b2e3f5e95a0fdc87a2d9f8e026f3e59153935553889 dependencies: - name: idf require: private @@ -592,9 +623,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 0.0.8 + version: 0.0.9 espressif/esp_video: - component_hash: fbad1178f39cb5a81ed808c460c851b911a9858db833e2e667ada988022c0660 + component_hash: 5694b16c12b8bb26b56ad3f999ad10119f1314d64b2d1c518bc7dfed902df9e1 dependencies: - name: espressif/cmake_utilities registry_url: https://components.espressif.com @@ -603,35 +634,30 @@ dependencies: - name: espressif/esp_cam_sensor registry_url: https://components.espressif.com require: private - version: 1.5.* - - name: espressif/esp_h264 + version: 2.6.* + - matches: + - if: $CONFIG{ESP_VIDEO_USE_CUSTOMIZED_ESP_H264_VERSION} == False + name: espressif/esp_h264 registry_url: https://components.espressif.com require: private rules: - if: target in [esp32p4] - version: 1.0.4 + version: 1.4.* - name: espressif/esp_ipa registry_url: https://components.espressif.com require: private rules: - if: target in [esp32p4] - version: ~1.2.0 + version: 2.4.* - 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.* + - if: target in [esp32p4, esp32s3, esp32s31] + version: 2.5.* source: registry_url: https://components.espressif.com/ type: service @@ -642,9 +668,17 @@ dependencies: - esp32c5 - esp32c6 - esp32c61 - version: 1.3.1 + - esp32s31 + version: 2.5.0 + espressif/gmf_fft: + component_hash: b59451ef2f96d22b80c27a162515b21f1b85954dda434c0b092172164028779d + dependencies: [] + source: + registry_url: https://components.espressif.com + type: service + version: 1.0.0 espressif/i2c_bus: - component_hash: 804ec068d163570fd367b279a310ca383484630d2310c586b50def0bcf21de13 + component_hash: 483112c1619c54c38d0b734fb25f1678733267858a77b7af2f48cc7db6dbbd84 dependencies: - name: espressif/cmake_utilities registry_url: https://components.espressif.com @@ -656,9 +690,9 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 1.5.1 + version: 1.5.2 espressif/iot_eth: - component_hash: ffcea72f079264b6905e428a406cb6d2512dfede10d100c49d3486dd78dc1873 + component_hash: 3be03ff933e27e17887becbed16305a561b35b66a5a4978ac51ae7f9f2ec70ea dependencies: - name: espressif/cmake_utilities registry_url: https://components.espressif.com @@ -670,25 +704,33 @@ dependencies: source: registry_url: https://components.espressif.com type: service - version: 0.1.0 + version: 1.1.0 espressif/iot_usbh_cdc: - component_hash: 6b30e8eeef1a3320a3bc0c27980c62826ed134b681be6f424366f3d78c291814 + component_hash: e924a79e9b7926b38bee52234d9f90101d8926f5e560dd5edf51c395546eac87 dependencies: - - name: idf - require: private - version: '>=5.1' - name: espressif/cmake_utilities registry_url: https://components.espressif.com require: private version: '*' + - name: espressif/usb + registry_url: https://components.espressif.com + require: public + rules: + - if: idf_version >=6.0 + - if: target not in ["linux"] + version: ^1.4.0 + - name: idf + require: private + version: '>=5.1' source: registry_url: https://components.espressif.com type: service targets: - esp32s2 - esp32s3 + - esp32s31 - esp32p4 - version: 3.0.0 + version: 3.1.0 espressif/iot_usbh_rndis: component_hash: 0d91d9c79980fe0d2b59ecae9341d91852b7eb3356cc0e708f76425a3604ff5b dependencies: @@ -739,8 +781,35 @@ dependencies: registry_url: https://components.espressif.com/ type: service version: 3.0.3 + espressif/mqtt: + component_hash: fb18bc3b65aa8c94693a9811ffc322cca8a65d92d5ec84983d3e385080e3969c + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.0 + espressif/usb: + component_hash: 99eee72ea224d9117d4c342442717a1f20bf8112e2ab5d7d7038b827874f4c92 + dependencies: + - name: idf + require: private + version: '>=5.5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32s2 + - esp32s3 + - esp32s31 + - esp32p4 + - esp32h4 + - linux + version: 1.6.0 espressif/usb_host_uvc: - component_hash: b0cb9a3a2eaa09dc6e5cc3e47e04d0a73e47c2d3dcfb3afce959224a1fe5fb5e + component_hash: 3dde9c89980212de1e394a29d3fe782a2988a08fd2a16af34eee6f642aab6ebe dependencies: - name: idf require: private @@ -760,8 +829,9 @@ dependencies: - esp32s3 - esp32p4 - esp32h4 + - esp32s31 - linux - version: 2.3.1 + version: 2.5.1 espressif2022/image_player: component_hash: 0e42ed1c9665debd15f2f3e7e56519100e75e446410962226cb5e5402da3fa43 dependencies: @@ -779,14 +849,14 @@ dependencies: idf: source: type: idf - version: 5.5.4 + version: 6.1.0 lvgl/lvgl: - component_hash: 17e68bfd21f0edf4c3ee838e2273da840bf3930e5dbc3bfa6c1190c3aed41f9f + component_hash: 184e532558c1c45fefed631f3e235423d22582aafb4630f3e8885c35281a49ae dependencies: [] source: registry_url: https://components.espressif.com/ type: service - version: 9.4.0 + version: 9.5.0 tny-robotics/sh1106-esp-idf: component_hash: 7be190d7c58cd635adf8b74b3a841f8245f2f82f36d135a64d8e53cfab39124d dependencies: [] @@ -806,7 +876,7 @@ dependencies: type: service version: 1.1.1 waveshare/custom_io_expander_ch32v003: - component_hash: fefced27c762ed97a3523c9d90d66050deea7eeb7e9eae6328e4ab1a71125d26 + component_hash: 6ed45a7dbe31c1724b13516f9587fdf688b4a89042afd7bfadcae85cb72f5e45 dependencies: - name: espressif/esp_io_expander registry_url: https://components.espressif.com @@ -818,7 +888,7 @@ dependencies: source: registry_url: https://components.espressif.com/ type: service - version: 1.0.1 + version: 2.0.0 waveshare/esp_lcd_sh8601: component_hash: 1a2829e9db4e0515bad6ce83368f199dec60519fbba40251d6c8249f5d070ae1 dependencies: @@ -848,19 +918,25 @@ dependencies: type: service version: 1.0.4 wvirgil123/sscma_client: - component_hash: bf7d4a1f157303f4850d0b9b465c733ce38e8689ff40ac4743a44520524c41d5 + component_hash: 541a36b1102d467c46ea2148fd8cbff0f9ffe9a94ee6e2b18ab8c8f8004901bf dependencies: - name: espressif/esp_io_expander registry_url: https://components.espressif.com require: private version: ^1.0.1 + - name: espressif/cjson + registry_url: https://components.espressif.com + require: private + rules: + - if: idf_version >=6.0 + version: ^1.7.19 - name: idf require: private version: '>=4.4.2' source: registry_url: https://components.espressif.com/ type: service - version: 1.0.2 + version: 1.0.4 direct_dependencies: - 78/esp-ml307 - 78/esp-wifi-connect @@ -871,6 +947,7 @@ direct_dependencies: - espressif/adc_mic - espressif/bmi270_sensor - espressif/button +- espressif/cjson - espressif/cmake_utilities - espressif/esp-sr - espressif/esp32-camera @@ -910,6 +987,6 @@ direct_dependencies: - waveshare/esp_lcd_sh8601 - waveshare/esp_lcd_touch_cst9217 - wvirgil123/sscma_client -manifest_hash: 350e17991f1a3226438480b027525b7cde67c34ed4ec6c5847cdfcb22e18dc4a +manifest_hash: dd78432e7c37103b22012677f813255683b0683303578ff74f378159bd7aaace target: esp32s3 -version: 2.0.0 +version: 3.0.0 diff --git a/firmware/fetch_repos.py b/firmware/fetch_repos.py index 17cc96f77..e99c7b6f6 100644 --- a/firmware/fetch_repos.py +++ b/firmware/fetch_repos.py @@ -3,11 +3,40 @@ import json +def apply_repo_patch(path, patch_path): + # Normalize Windows patch checkouts in memory; Git still handles the + # dependency's own checkout line-ending rules. + with open(patch_path, "rb") as patch_file: + patch_data = patch_file.read().replace(b"\r\n", b"\n") + + command = ["git", "-C", path, "apply"] + check_result = subprocess.run( + command + ["--check", "-"], input=patch_data, capture_output=True + ) + if check_result.returncode == 0: + subprocess.run(command + ["-"], input=patch_data, check=True) + print(f"Applied patch {patch_path} to {path}") + return + + reverse_result = subprocess.run( + command + ["--reverse", "--check", "-"], + input=patch_data, + capture_output=True, + ) + if reverse_result.returncode == 0: + print(f"Patch {patch_path} is already applied to {path}") + return + + detail = check_result.stderr.decode(errors="replace").strip() + raise RuntimeError( + f"Patch {patch_path} cannot be applied to {path} and is not already applied. " + f"Resolve the dependency changes before retrying.\n{detail}" + ) + + 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: @@ -29,15 +58,7 @@ def clone_or_update_repo( 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.") + apply_repo_patch(path, patch_full_path) def fetch_dependencies(): @@ -58,4 +79,4 @@ def fetch_dependencies(): if __name__ == "__main__": - fetch_dependencies() \ No newline at end of file + fetch_dependencies() diff --git a/firmware/main/CMakeLists.txt b/firmware/main/CMakeLists.txt index 3680c6890..d118f7eb6 100644 --- a/firmware/main/CMakeLists.txt +++ b/firmware/main/CMakeLists.txt @@ -1,6 +1,14 @@ # Define xiaozhi source directory set(XIAOZHI_MAIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../xiaozhi-esp32/main") +# HttpClient formats chunk lengths with %zx, which Newlib nano cannot handle. +if(CONFIG_LIBC_NEWLIB_NANO_FORMAT) + message(FATAL_ERROR + "The network SDK requires C99 printf formatting. Disable " + "CONFIG_LIBC_NEWLIB_NANO_FORMAT in the selected SDKCONFIG (menuconfig: " + "Component config -> C standard library).") +endif() + file(GLOB_RECURSE STACK_CHAN_SOURCES "apps/*.c" "apps/*.cc" @@ -31,11 +39,13 @@ set(SOURCES "audio/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" + "audio/audio_debugger.cc" "led/single_led.cc" "led/circular_strip.cc" "led/gpio_led.cc" + "notify/notify_player.cc" "display/display.cc" + "display/text_glyph.cc" "display/lcd_display.cc" "display/oled_display.cc" "display/lvgl_display/lvgl_display.cc" @@ -43,12 +53,14 @@ set(SOURCES "audio/audio_codec.cc" "display/lvgl_display/emoji_collection.cc" "display/lvgl_display/lvgl_theme.cc" "display/lvgl_display/lvgl_font.cc" + "display/lvgl_display/dynamic_glyph_cache.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" "display/lvgl_display/jpg/jpeg_to_image.c" "protocols/protocol.cc" + "protocols/text_glyph_payload.cc" "protocols/mqtt_protocol.cc" "protocols/websocket_protocol.cc" "mcp_server.cc" @@ -76,7 +88,6 @@ list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/boards/common/nt26_board.cc" "${XIAOZHI_MAIN_DIR}/boards/common/dual_network_board.cc" "${XIAOZHI_MAIN_DIR}/boards/common/adc_battery_monitor.cc" - "${XIAOZHI_MAIN_DIR}/boards/common/afsk_demod.cc" "${XIAOZHI_MAIN_DIR}/boards/common/axp2101.cc" "${XIAOZHI_MAIN_DIR}/boards/common/backlight.cc" "${XIAOZHI_MAIN_DIR}/boards/common/button.cc" @@ -106,8 +117,8 @@ function(find_component_by_pattern PATTERN COMPONENT_VAR PATH_VAR) 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) +set(BUILTIN_TEXT_FONT font_noto_sans_basic_14_1) +set(BUILTIN_ICON_FONT font_material_symbols_14_1) set(EMOTE_RESOLUTION "320_240") @@ -115,9 +126,9 @@ set(EMOTE_RESOLUTION "320_240") # 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) + set(BUILTIN_TEXT_FONT font_noto_sans_basic_20_4) + set(BUILTIN_ICON_FONT font_material_symbols_20_4) + set(DEFAULT_EMOJI_COLLECTION noto-color-emoji_64) endif() if(MANUFACTURER) @@ -132,18 +143,17 @@ else() ) endif() list(APPEND SOURCES ${BOARD_SOURCES}) +list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/boards/m5stack/core-s3/cores3_audio_codec.cc") +list(APPEND INCLUDE_DIRS "${XIAOZHI_MAIN_DIR}/boards/m5stack/core-s3") -# Select audio processor according to Kconfig -if(CONFIG_USE_AUDIO_PROCESSOR) - list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/audio/processors/afe_audio_processor.cc") -else() - list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/audio/processors/no_audio_processor.cc") -endif() +# ESP32-S3 shares one AFE engine between wake words and voice processing. if(CONFIG_IDF_TARGET_ESP32S3 OR CONFIG_IDF_TARGET_ESP32P4) - list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/audio/wake_words/afe_wake_word.cc") - list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/audio/wake_words/custom_wake_word.cc") + list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/audio/engines/afe_audio_engine.cc" + "${XIAOZHI_MAIN_DIR}/audio/wake_words/custom_wake_word.cc" + "${XIAOZHI_MAIN_DIR}/audio/wake_words/wake_word_audio_cache.cc") else() - list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/audio/wake_words/esp_wake_word.cc") + list(APPEND SOURCES "${XIAOZHI_MAIN_DIR}/audio/engines/lite_audio_engine.cc" + "${XIAOZHI_MAIN_DIR}/audio/wake_words/esp_wake_word.cc") endif() # Auto Select Additional Sources @@ -299,7 +309,10 @@ idf_component_register(SRCS ${SOURCES} esp_netif esp_http_client mbedtls - json + espressif__cjson + esp_eth + esp_driver_ledc + esp_driver_tsens esp_driver_gpio esp_driver_uart esp_driver_spi @@ -321,6 +334,9 @@ idf_component_register(SRCS ${SOURCES} smooth_ui_toolkit ) +target_compile_features(${COMPONENT_LIB} PUBLIC cxx_std_23) +target_compile_options(${COMPONENT_LIB} PRIVATE $<$:-std=gnu++23>) + # Use target_compile_definitions to define BOARD_TYPE, BOARD_NAME # If BOARD_NAME is empty, use BOARD_TYPE if(NOT BOARD_NAME) @@ -329,6 +345,7 @@ 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} + PRIVATE BOARD_MANUFACTURER=\"m5stack\" TEXT_FONT_SIZE=20 TEXT_FONT_BPP=4 ) # Add generation rules @@ -426,8 +443,13 @@ function(build_default_assets_bin) endif() list(APPEND BUILD_ARGS "--esp_sr_model_path" "${ESP_SR_MODEL_PATH}") - list(APPEND BUILD_ARGS "--xiaozhi_fonts_path" "${XIAOZHI_FONTS_PATH}") + list(APPEND BUILD_ARGS "--noto_fonts_path" "${XIAOZHI_FONTS_PATH}") + partition_table_get_partition_info(assets_partition_size "--partition-name assets" "size") + if(assets_partition_size) + list(APPEND BUILD_ARGS "--max_size" "${assets_partition_size}") + endif() + # Create custom command to build assets add_custom_command( OUTPUT ${GENERATED_ASSETS_BIN} diff --git a/firmware/main/Kconfig.projbuild b/firmware/main/Kconfig.projbuild index c8760d43b..c19aa6635 100644 --- a/firmware/main/Kconfig.projbuild +++ b/firmware/main/Kconfig.projbuild @@ -668,8 +668,8 @@ choice DISPLAY_STYLE config USE_EMOTE_MESSAGE_STYLE bool "Emote animation style" - depends on BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_3 \ - || BOARD_TYPE_ECHOEAR || BOARD_TYPE_LICHUANG_DEV_S3 \ + depends on BOARD_TYPE_ESP32_S3_BOX || BOARD_TYPE_ESP32_S3_BOX_3 \ + || BOARD_TYPE_ESP_VOCAT || BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_RYMCU_BIGSMART \ || BOARD_TYPE_ESP_SENSAIRSHUTTLE endchoice @@ -686,7 +686,7 @@ config USE_MULTILINE_CHAT_MESSAGE choice WAKE_WORD_TYPE prompt "Wake Word Implementation Type" - default USE_AFE_WAKE_WORD if (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM + default USE_AFE_WAKE_WORD if (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 || IDF_TARGET_ESP32S31) && SPIRAM default WAKE_WORD_DISABLED help Choose the type of wake word implementation to use @@ -704,15 +704,15 @@ choice WAKE_WORD_TYPE config USE_AFE_WAKE_WORD bool "Wakenet model with AFE" - depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM + depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 || IDF_TARGET_ESP32S31) && SPIRAM help - Support AEC if available, requires ESP32 S3 and PSRAM + Support AEC if available, requires ESP32-S3, ESP32-P4, or ESP32-S31 and PSRAM config USE_CUSTOM_WAKE_WORD bool "Multinet model (Custom Wake Word)" - depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM + depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 || IDF_TARGET_ESP32S31) && SPIRAM help - Requires ESP32 S3 and PSRAM + Requires ESP32-S3, ESP32-P4, or ESP32-S31 and PSRAM endchoice @@ -756,22 +756,25 @@ config WAKE_WORD_DETECTION_IN_LISTENING When disabled (default), wake word detection is turned off during listening. config USE_AUDIO_PROCESSOR - bool "Enable Audio Noise Reduction" + bool "Enable AFE Audio Processing" default y - depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM + depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 || IDF_TARGET_ESP32S31) && SPIRAM help - Requires ESP32 S3 and PSRAM + Enable the shared AFE uplink path with AEC and VAD. Noise suppression + is not enabled because this project does not provide an NSNet model. + Requires ESP32-S3, ESP32-P4, or ESP32-S31 with PSRAM. config USE_DEVICE_AEC bool "Enable Device-Side AEC" default n - depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE \ - || BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_ESP_KORVO2_V3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83\ - || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B \ - || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 || BOARD_TYPE_YUNLIAO_S3 \ + depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP32_S3_BOX_3 || BOARD_TYPE_ESP32_S3_BOX || BOARD_TYPE_ESP32_S3_BOX_LITE \ + || BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_RYMCU_BIGSMART || BOARD_TYPE_ESP32_S3_KORVO_2_V3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83\ + || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5 \ + || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B \ + || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C || BOARD_TYPE_ESP32_S3_LCD_EV_BOARD_2 || BOARD_TYPE_YUNLIAO_S3 \ || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_8 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_10_1 \ - || BOARD_TYPE_ECHOEAR || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_49 || BOARD_TYPE_WAVESHARE_ESP32_S3_RLCD_4_2 || BOARD_TYPE_ZHENGCHEN_CAM || BOARD_TYPE_ZHENGCHEN_CAM_ML307 \ - || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4_3C) + || BOARD_TYPE_ESP_VOCAT || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_49 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_49_V2 || BOARD_TYPE_WAVESHARE_ESP32_S3_RLCD_4_2 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85B || BOARD_TYPE_ZHENGCHEN_CAM || BOARD_TYPE_ZHENGCHEN_CAM_ML307 \ + || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4_3C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_54 || BOARD_TYPE_WAVESHARE_ESP32_S3_LCD_0_85 || BOARD_TYPE_AI_VOX3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_43C || BOARD_TYPE_ATK_DNESP32S3_BOX3) 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. @@ -797,19 +800,14 @@ menu "WiFi Configuration Method" default y help Use WiFi Hotspot to transmit WiFi configuration data - config USE_ACOUSTIC_WIFI_PROVISIONING - bool "Acoustic" - help - Use audio signal to transmit WiFi configuration data - config USE_ESP_BLUFI_WIFI_PROVISIONING - bool "Esp Blufi" - help - Use esp blufi protocol to transmit WiFi configuration data + bool "ESP-BluFi" select BT_ENABLED select BT_BLE_42_FEATURES_SUPPORTED select BT_BLE_BLUFI_ENABLE - select MBEDTLS_DHM_C + help + Use the PSA Crypto based BluFi security protocol adopted by ESP-IDF 6. + The provisioning client must support ffdhe3072, SHA-256, and AES-CTR. endmenu config AUDIO_DEBUG_UDP_SERVER @@ -830,6 +828,29 @@ menu "Camera Configuration" comment "Warning: Please read the help text before modifying these settings." + config XIAOZHI_CAMERA_MIRROR_CONFIGURED + bool "Override camera mirror settings" + default n + help + When enabled, the common camera implementations apply the + horizontal and vertical mirror settings below after the sensor has + been initialized. Parameterized builds enable this automatically + when either mirror setting is configured. + + config XIAOZHI_CAMERA_HMIRROR + bool "Mirror Camera Horizontally" + default n + depends on XIAOZHI_CAMERA_MIRROR_CONFIGURED + help + Flip the captured camera image across the vertical axis. + + config XIAOZHI_CAMERA_VFLIP + bool "Mirror Camera Vertically" + default n + depends on XIAOZHI_CAMERA_MIRROR_CONFIGURED + help + Flip the captured camera image across the horizontal axis. + config XIAOZHI_CAMERA_ALLOW_JPEG_INPUT bool "Allow JPEG Input" default n diff --git a/firmware/main/apps/app_espnow_ctrl/app_espnow_ctrl.cpp b/firmware/main/apps/app_espnow_ctrl/app_espnow_ctrl.cpp index f85ce6064..1f320d280 100644 --- a/firmware/main/apps/app_espnow_ctrl/app_espnow_ctrl.cpp +++ b/firmware/main/apps/app_espnow_ctrl/app_espnow_ctrl.cpp @@ -61,11 +61,17 @@ void AppEspnowControl::onOpen() _receiver_id, _wifi_channel); // Start espnow - GetHAL().startEspNow(_wifi_channel); - GetHAL().onEspNowData.connect([](const std::vector& data) { + { std::lock_guard lock(_mutex); - _received_data = data; - }); + _received_data.clear(); + } + _espnow_ready = GetHAL().startEspNow(_wifi_channel); + if (_espnow_ready && _receive_signal_id < 0) { + _receive_signal_id = GetHAL().onEspNowData.connect([](const std::vector& data) { + std::lock_guard lock(_mutex); + _received_data = data; + }); + } // Normal avatar LvglLockGuard lock; @@ -87,6 +93,9 @@ void AppEspnowControl::onOpen() view::create_home_indicator([&]() { close(); }, 0xA0D99C, 0x154311); view::create_status_bar(0xA0D99C, 0x154311); + if (!_espnow_ready) { + view::pop_a_toast("ESP-NOW start failed.\nExit and retry.", view::ToastType::Error); + } } bool AppEspnowControl::start_startup_page() @@ -235,9 +244,9 @@ void AppEspnowControl::onRunning() { LvglLockGuard lock; - if (_is_receiver) { + if (_espnow_ready && _is_receiver) { handle_received_data(); - } else { + } else if (_espnow_ready) { handle_send_pose(); } @@ -251,6 +260,12 @@ void AppEspnowControl::onClose() { mclog::tagInfo(getAppInfo().name, "on close"); + _espnow_ready = false; + if (_receive_signal_id >= 0) { + GetHAL().onEspNowData.disconnect(_receive_signal_id); + _receive_signal_id = -1; + } + LvglLockGuard lock; view::destroy_home_indicator(); diff --git a/firmware/main/apps/app_espnow_ctrl/app_espnow_ctrl.h b/firmware/main/apps/app_espnow_ctrl/app_espnow_ctrl.h index 89d365e0e..a19da20e5 100644 --- a/firmware/main/apps/app_espnow_ctrl/app_espnow_ctrl.h +++ b/firmware/main/apps/app_espnow_ctrl/app_espnow_ctrl.h @@ -21,6 +21,8 @@ class AppEspnowControl : public mooncake::AppAbility { void onClose() override; private: + bool _espnow_ready = false; + int _receive_signal_id = -1; bool start_startup_page(); void start_advanced_page(); }; diff --git a/firmware/main/apps/app_launcher/app_launcher.cpp b/firmware/main/apps/app_launcher/app_launcher.cpp index acd8cd389..a8b7861da 100644 --- a/firmware/main/apps/app_launcher/app_launcher.cpp +++ b/firmware/main/apps/app_launcher/app_launcher.cpp @@ -74,16 +74,28 @@ void AppLauncher::onLauncherDestroy() void AppLauncher::create_launcher_view() { _view = std::make_unique(); - - auto app_props = getAppProps(); + hal_toggles::ToggleStates states; + if (!hal_toggles::load_states(states)) { + mclog::tagWarn(getAppInfo().name, "Unable to load menu preferences; showing all apps"); + } + auto app_props = getAppProps(); + int restore_app_id = -1; + const int warm_index = GetHAL().getWarmRebootTarget(); + if (warm_index >= 0) { + // Existing callers store an index in the unfiltered app list. + if (!app_props.empty()) { + const auto index = uitk::clamp(warm_index, 0, static_cast(app_props.size()) - 1); + restore_app_id = app_props[index].appID; + } + GetHAL().clearWarmRebootRequest(); + } std::vector filtered_props; for (const auto& props : app_props) { - if (hal_toggles::is_app_enabled(props.info.name)) { + if (hal_toggles::is_app_enabled(props.appID, states)) { filtered_props.push_back(props); } } - - _view->init(filtered_props); + _view->init(filtered_props, restore_app_id); _view->onAppClicked = [&](int appID) { mclog::tagInfo(getAppInfo().name, "handle open app, app id: {}", appID); openApp(appID); diff --git a/firmware/main/apps/app_launcher/view/view.cpp b/firmware/main/apps/app_launcher/view/view.cpp index e9bc15c6f..9d212854e 100644 --- a/firmware/main/apps/app_launcher/view/view.cpp +++ b/firmware/main/apps/app_launcher/view/view.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -288,7 +289,7 @@ static constexpr int _icon_gap = 320; static constexpr int _loop_copies = 5; static constexpr int _center_copy_index = 2; -static int _last_clicked_icon_pos_x = -1; +static int _last_clicked_app_id = -1; static std::unique_ptr _dynamic_bg_color; static std::unique_ptr _page_indicator; static std::unique_ptr _dynamic_icon_label; @@ -305,7 +306,7 @@ LauncherView::~LauncherView() _dynamic_icon_label.reset(); } -void LauncherView::init(std::vector appPorps) +void LauncherView::init(std::vector appPorps, int restoreAppID) { mclog::tagInfo(_tag, "init"); @@ -344,10 +345,9 @@ void LauncherView::init(std::vector appPorps) // Icon click callback auto app_id = props.appID; - auto pos_x = icon_x; - _icon_panels.back()->onClick().connect([&, app_id, pos_x]() { - _clicked_app_id = app_id; - _last_clicked_icon_pos_x = pos_x; + _icon_panels.back()->onClick().connect([&, app_id]() { + _clicked_app_id = app_id; + _last_clicked_app_id = app_id; }); // Keep track of data for helpers @@ -441,24 +441,22 @@ void LauncherView::init(std::vector appPorps) int base_offset_rounds = _center_copy_index * appPorps.size(); int default_start_x = base_offset_rounds * _icon_gap; - // If warm boot was requested - if (GetHAL().getWarmRebootTarget() >= 0) { - auto app_index = GetHAL().getWarmRebootTarget(); - mclog::tagInfo(_tag, "warm boot was requested, app index: {}", app_index); - app_index = uitk::clamp(app_index, 0, static_cast(appPorps.size()) - 1); + auto restore_position = [&](int app_id) { + const auto found = std::find_if(appPorps.begin(), appPorps.end(), + [app_id](const auto& props) { return props.appID == app_id; }); + const int index = found == appPorps.end() ? 0 : static_cast(found - appPorps.begin()); + return (base_offset_rounds + index) * _icon_gap; + }; - // Restore to center set - restore_icon_pos_x = (base_offset_rounds + app_index) * _icon_gap; + if (restoreAppID >= 0) { + restore_icon_pos_x = restore_position(restoreAppID); need_restore = true; - GetHAL().clearWarmRebootRequest(); } - - if (_last_clicked_icon_pos_x != -1) { - // Just restore where they left off, it should be in a valid range - // mclog::tagInfo(_tag, "navigate to last clicked icon, pos x: {}", _last_clicked_icon_pos_x); - restore_icon_pos_x = _last_clicked_icon_pos_x; - need_restore = true; - _last_clicked_icon_pos_x = -1; + if (_last_clicked_app_id != -1) { + // Menu size and positions can change while Settings is open. + restore_icon_pos_x = restore_position(_last_clicked_app_id); + need_restore = true; + _last_clicked_app_id = -1; } if (need_restore) { diff --git a/firmware/main/apps/app_launcher/view/view.h b/firmware/main/apps/app_launcher/view/view.h index 1c90e2826..8cb00c0a5 100644 --- a/firmware/main/apps/app_launcher/view/view.h +++ b/firmware/main/apps/app_launcher/view/view.h @@ -30,7 +30,7 @@ class LauncherView { std::function onAppClicked; - void init(std::vector appPorps); + void init(std::vector appPorps, int restoreAppID = -1); void update(); private: diff --git a/firmware/main/apps/app_setup/app_setup.cpp b/firmware/main/apps/app_setup/app_setup.cpp index 201f5e0dc..af88c1d56 100644 --- a/firmware/main/apps/app_setup/app_setup.cpp +++ b/firmware/main/apps/app_setup/app_setup.cpp @@ -144,8 +144,8 @@ void AppSetup::onOpen() }, }, { - "Others", - {{"Feature Toggles", + "Apps", + {{"App visibility", [&]() { _destroy_menu = true; _worker = std::make_unique(); diff --git a/firmware/main/apps/app_setup/workers/account.cpp b/firmware/main/apps/app_setup/workers/account.cpp index 1fa6b1703..ac72568ba 100644 --- a/firmware/main/apps/app_setup/workers/account.cpp +++ b/firmware/main/apps/app_setup/workers/account.cpp @@ -131,11 +131,8 @@ void AccountWorker::update() loading_page->setMessage(msg); }); - if (!result) { - GetHAL().delay(5000); - } - GetHAL().lvglLock(); + return result; }); } else if (_page_account->isQuitClicked()) { mclog::tagInfo(_tag, "quit clicked"); diff --git a/firmware/main/apps/app_setup/workers/ai_agent.cpp b/firmware/main/apps/app_setup/workers/ai_agent.cpp index 3f713d95e..fc0e153e2 100644 --- a/firmware/main/apps/app_setup/workers/ai_agent.cpp +++ b/firmware/main/apps/app_setup/workers/ai_agent.cpp @@ -101,7 +101,8 @@ XiaozhiPowerSavingWorker::XiaozhiPowerSavingWorker() _switch_charging->setSize(64, 36); _switch_charging->align(LV_ALIGN_TOP_MID, 0, 66); _switch_charging->setBgColor(lv_color_hex(0xB8D3FD), LV_PART_MAIN); - _switch_charging->setBgColor(lv_color_hex(0x615B9E), LV_PART_INDICATOR | LV_STATE_CHECKED); + _switch_charging->setBgColor(lv_color_hex(0x615B9E), + static_cast(LV_PART_INDICATOR) | LV_STATE_CHECKED); _switch_charging->setBgColor(lv_color_hex(0xFFFFFF), LV_PART_KNOB); if (_config.allowShutdownWhenCharging) { _switch_charging->addState(LV_STATE_CHECKED); @@ -228,7 +229,8 @@ XiaozhiGeneralWorker::XiaozhiGeneralWorker() _switch_start_ai_on_boot->setSize(64, 36); _switch_start_ai_on_boot->align(LV_ALIGN_TOP_MID, 0, 66); _switch_start_ai_on_boot->setBgColor(lv_color_hex(0xB8D3FD), LV_PART_MAIN); - _switch_start_ai_on_boot->setBgColor(lv_color_hex(0x615B9E), LV_PART_INDICATOR | LV_STATE_CHECKED); + _switch_start_ai_on_boot->setBgColor(lv_color_hex(0x615B9E), + static_cast(LV_PART_INDICATOR) | LV_STATE_CHECKED); _switch_start_ai_on_boot->setBgColor(lv_color_hex(0xFFFFFF), LV_PART_KNOB); if (_config.startAiAgentOnBoot) { _switch_start_ai_on_boot->addState(LV_STATE_CHECKED); @@ -253,7 +255,7 @@ void XiaozhiGeneralWorker::update() } if (_confirm_flag) { - _confirm_flag = false; + _confirm_flag = false; _config.startAiAgentOnBoot = _switch_start_ai_on_boot->getValue(); GetHAL().setXiaozhiConfig(_config); mclog::tagInfo(_tag, "xiaozhi config updated: idleRandomMovementLevel={} ({})", _config.idleRandomMovementLevel, diff --git a/firmware/main/apps/app_setup/workers/connectivity.cpp b/firmware/main/apps/app_setup/workers/connectivity.cpp index 48b84c866..7e6f746a8 100644 --- a/firmware/main/apps/app_setup/workers/connectivity.cpp +++ b/firmware/main/apps/app_setup/workers/connectivity.cpp @@ -144,8 +144,10 @@ void WifiSetupWorker::update_state() _is_first_in = false; // Start app config server - _app_config_signal_id = - GetHAL().onAppConfigEvent.connect([this](AppConfigEvent event) { _last_app_config_event = event; }); + if (_app_config_signal_id < 0) { + _app_config_signal_id = GetHAL().onAppConfigEvent.connect( + [this](AppConfigEvent event) { _last_app_config_event = event; }); + } GetHAL().startAppConfigServer(); diff --git a/firmware/main/apps/app_setup/workers/feature_toggle.cpp b/firmware/main/apps/app_setup/workers/feature_toggle.cpp index d063b8a6d..515bc6010 100644 --- a/firmware/main/apps/app_setup/workers/feature_toggle.cpp +++ b/firmware/main/apps/app_setup/workers/feature_toggle.cpp @@ -1,88 +1,114 @@ +// Adapted from ThisIsBenny's StackChan PR #31. #include "feature_toggle.h" -#include "toggle_row.h" -#include -#include #include "common.h" +#include using namespace setup_workers; +using namespace uitk::lvgl_cpp; using namespace hal_toggles; -static const char* TAG = "FeatureToggle"; +namespace { +constexpr const char* TAG = "AppVisibility"; +constexpr const char* HELP = "Only menu icons change.\nAI auto-start is set separately."; +} // namespace FeatureToggleWorker::FeatureToggleWorker() { - if (!hal_toggles::init_nvs()) { - mclog::tagError(TAG, "NVS init failed"); - return; - } - - for (int i = 0; i < TOGGLE_COUNT; i++) { - _states[i] = hal_toggles::get_toggle_state(TOGGLE_LIST[i].key); - } - + // Always build Back / Retry controls, even when preferences cannot load. create_toggle_ui(); + load_preferences(); } void FeatureToggleWorker::create_toggle_ui() { - auto screen = lv_screen_active(); - - _panel = std::make_unique(screen); + _panel = std::make_unique(lv_screen_active()); _panel->setAlign(LV_ALIGN_CENTER); - _panel->setPos(0, 0); _panel->setSize(320, 240); _panel->setBgColor(lv_color_hex(0xEDF4FF)); _panel->setRadius(0); _panel->setBorderWidth(0); _panel->setFlexFlow(LV_FLEX_FLOW_COLUMN); _panel->setFlexAlign(LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - _panel->setPadding(15, 20, 10, 20); - _panel->setPadRow(10); + _panel->setPadding(16, 16, 16, 16); + _panel->setPadRow(12); + _panel->setScrollDir(LV_DIR_VER); + _panel->setScrollbarMode(LV_SCROLLBAR_MODE_ACTIVE); - _title = std::make_unique(_panel->get()); - _title->setText("Feature Toggles"); + _title = std::make_unique