Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 969dde0d10 | |||
| 20d91a3507 | |||
| 7a7c74a747 | |||
| cc7cbe78ba | |||
| 314edc5c51 | |||
| 7e7890183e | |||
| 2c5d7757e5 | |||
| 43b4d35b2e | |||
| 33ed917172 | |||
| 89d51fdc23 | |||
| 022d9848de | |||
| 8afebe560a | |||
| a63f8bc08b | |||
| 4dc1a8c75f | |||
| 121a2d45e7 | |||
| 06b3b7613c | |||
| addf5fcc64 | |||
| a877d95f74 | |||
| 76a5c19000 | |||
| 7d63797dfa | |||
| 0f3199a812 | |||
| 6d51b9dbde | |||
| d340efe58d | |||
| d9f0ef13aa | |||
| 358819bf2e | |||
| cef581d723 | |||
| dbb8e1d409 | |||
| 78008ab9d3 | |||
| cab8a7cecf | |||
| 74fa5413fc | |||
| cc4e12fa74 | |||
| 1c195fea4f | |||
| a4bf13b30d | |||
| 619dd2801c | |||
| 280b2ff856 | |||
| 527cd1fd58 | |||
| 55698b2d74 | |||
| e503a32d84 | |||
| 564e929aea | |||
| bbd5f70c3c | |||
| d35f03134f | |||
| d71841d248 | |||
| e77dedb130 | |||
| 1f37f7806e | |||
| 322466d6c1 | |||
| 1d2b71f27e | |||
| 443a3ba120 | |||
| e37a24bea8 | |||
| 05f1a034ce | |||
| feb61d4e45 | |||
| 332c65238d | |||
| dbecf229af | |||
| 2542074545 | |||
| 2576801a89 | |||
| 025059aadf | |||
| b778d2f0cd | |||
| 71c86ab62b | |||
| 4666ecef82 | |||
| b34a9b19ba | |||
| cf59430991 | |||
| 8e34995944 | |||
| fe66f39ecc | |||
| 87cff80098 | |||
| d545f746bc | |||
| 6be351b5a0 | |||
| d9447ad060 | |||
| 9215a04a7e | |||
| 7b7d22c495 | |||
| b4eada876a | |||
| 49cd6625f4 | |||
| 6f71868bad | |||
| 173eaa7463 | |||
| 2b025c4ea6 | |||
| 37110a9d05 | |||
| 796312db4c | |||
| 9e1724e892 | |||
| 0b3b98eca7 | |||
| abd62648cb | |||
| 0883a36537 | |||
| b6c61fe390 | |||
| f7284a57df | |||
| 96f34ec70f | |||
| aad2f60b87 | |||
| 5b874bc3ad |
@@ -20,7 +20,7 @@ jobs:
|
||||
variants: ${{ steps.select.outputs.variants }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
fi
|
||||
|
||||
build:
|
||||
name: Build ${{ matrix.name }}
|
||||
name: Build ${{ matrix.full_name }}
|
||||
needs: prepare
|
||||
if: ${{ needs.prepare.outputs.variants != '[]' }}
|
||||
strategy:
|
||||
@@ -106,6 +106,6 @@ jobs:
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xiaozhi_${{ matrix.name }}_${{ github.sha }}.bin
|
||||
name: xiaozhi_${{ matrix.full_name }}_${{ github.sha }}
|
||||
path: build/merged-binary.bin
|
||||
if-no-files-found: error
|
||||
|
||||
@@ -2,6 +2,7 @@ tmp/
|
||||
components/
|
||||
managed_components/
|
||||
build/
|
||||
dist/
|
||||
.vscode/
|
||||
.devcontainer/
|
||||
sdkconfig.old
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
|
||||
set(PROJECT_VER "2.2.1")
|
||||
set(PROJECT_VER "2.2.5")
|
||||
project(xiaozhi)
|
||||
|
||||
@@ -160,7 +160,7 @@ This is an open-source ESP32 project, released under the MIT license, allowing a
|
||||
|
||||
We hope this project helps everyone understand AI hardware development and apply rapidly evolving large language models to real hardware devices.
|
||||
|
||||
If you have any ideas or suggestions, please feel free to raise Issues or join our [Discord](https://discord.gg/bXqgAfRm) or QQ group: 994694848
|
||||
If you have any ideas or suggestions, please feel free to raise Issues or join our [Discord](https://discord.gg/C759fGMBcZ) or QQ group: 994694848
|
||||
|
||||
## Star History
|
||||
|
||||
@@ -170,4 +170,4 @@ If you have any ideas or suggestions, please feel free to raise Issues or join o
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
</a>
|
||||
+1
-1
@@ -156,7 +156,7 @@ Feishuドキュメントチュートリアルをご覧ください:
|
||||
|
||||
このプロジェクトを通じて、AIハードウェア開発を理解し、急速に進化する大規模言語モデルを実際のハードウェアデバイスに応用できるようになることを目指しています。
|
||||
|
||||
ご意見やご提案があれば、いつでもIssueを提出するか、[Discord](https://discord.gg/bXqgAfRm) または QQグループ:1011329060 にご参加ください。
|
||||
ご意見やご提案があれば、いつでもIssueを提出するか、[Discord](https://discord.gg/C759fGMBcZ) または QQグループ:1011329060 にご参加ください。
|
||||
|
||||
## スター履歴
|
||||
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ v1 的稳定版本为 1.9.2,可以通过 `git checkout v1` 来切换到 v1 版
|
||||
|
||||
我们希望通过这个项目,能够帮助大家了解 AI 硬件开发,将当下飞速发展的大语言模型应用到实际的硬件设备中。
|
||||
|
||||
如果你有任何想法或建议,请随时提出 Issues 或加入 [Discord](https://discord.gg/bXqgAfRm) 或 QQ 群:1011329060
|
||||
如果你有任何想法或建议,请随时提出 Issues 或加入 [Discord](https://discord.gg/C759fGMBcZ) 或 QQ 群:1011329060
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
+236
-91
@@ -1,6 +1,7 @@
|
||||
# Define source files
|
||||
set(SOURCES "audio/audio_codec.cc"
|
||||
"audio/audio_service.cc"
|
||||
"audio/demuxer/ogg_demuxer.cc"
|
||||
"audio/codecs/no_audio_codec.cc"
|
||||
"audio/codecs/box_audio_codec.cc"
|
||||
"audio/codecs/es8311_audio_codec.cc"
|
||||
@@ -38,7 +39,7 @@ set(SOURCES "audio/audio_codec.cc"
|
||||
"main.cc"
|
||||
)
|
||||
|
||||
set(INCLUDE_DIRS "." "display" "display/lvgl_display" "display/lvgl_display/jpg" "audio" "protocols")
|
||||
set(INCLUDE_DIRS "." "display" "display/lvgl_display" "display/lvgl_display/jpg" "audio" "audio/demuxer" "protocols")
|
||||
|
||||
# Add board common files
|
||||
list(APPEND SOURCES
|
||||
@@ -104,28 +105,28 @@ elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ESP32_LCD)
|
||||
set(BUILTIN_ICON_FONT font_awesome_14_1)
|
||||
elseif(CONFIG_BOARD_TYPE_DF_K10)
|
||||
set(BOARD_TYPE "df-k10")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_DF_S3_AI_CAM)
|
||||
set(BOARD_TYPE "df-s3-ai-cam")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_BOX_3)
|
||||
set(BOARD_TYPE "esp-box-3")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
set(EMOTE_RESOLUTION "320_240")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_BOX)
|
||||
set(BOARD_TYPE "esp-box")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
set(EMOTE_RESOLUTION "320_240")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_BOX_LITE)
|
||||
set(BOARD_TYPE "esp-box-lite")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_2)
|
||||
set(BOARD_TYPE "kevin-box-2")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
|
||||
@@ -134,14 +135,14 @@ elseif(CONFIG_BOARD_TYPE_KEVIN_C3)
|
||||
set(BOARD_TYPE "kevin-c3")
|
||||
elseif(CONFIG_BOARD_TYPE_KEVIN_SP_V3_DEV)
|
||||
set(BOARD_TYPE "kevin-sp-v3-dev")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_KEVIN_SP_V4_DEV)
|
||||
set(BOARD_TYPE "kevin-sp-v4-dev")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_KEVIN_YUYING_313LCD)
|
||||
set(BOARD_TYPE "kevin-yuying-313lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
@@ -149,24 +150,35 @@ elseif(CONFIG_BOARD_TYPE_KEVIN_YUYING_313LCD)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV_S3)
|
||||
set(BOARD_TYPE "lichuang-dev")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV_C3)
|
||||
set(BOARD_TYPE "lichuang-c3-dev")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_EDA_TV_PRO)
|
||||
set(MANUFACTURER "lceda-course-examples")
|
||||
set(BOARD_TYPE "eda-tv-pro")
|
||||
elseif(CONFIG_BOARD_TYPE_EDA_ROBOT_PRO)
|
||||
set(MANUFACTURER "lceda-course-examples")
|
||||
set(BOARD_TYPE "eda-robot-pro")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
elseif(CONFIG_BOARD_TYPE_EDA_SUPER_BEAR)
|
||||
set(MANUFACTURER "lceda-course-examples")
|
||||
set(BOARD_TYPE "eda-super-bear")
|
||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_2P4)
|
||||
set(BOARD_TYPE "magiclick-2p4")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_2P5)
|
||||
set(BOARD_TYPE "magiclick-2p5")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3)
|
||||
set(BOARD_TYPE "magiclick-c3")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
@@ -201,6 +213,11 @@ elseif(CONFIG_BOARD_TYPE_M5STACK_ATOM_S3R_CAM_M12_ECHO_BASE)
|
||||
set(BOARD_TYPE "atoms3r-cam-m12-echo-base")
|
||||
elseif(CONFIG_BOARD_TYPE_M5STACK_ATOM_ECHOS3R)
|
||||
set(BOARD_TYPE "atom-echos3r")
|
||||
elseif(CONFIG_BOARD_TYPE_M5STACK_CARDPUTER_ADV)
|
||||
set(BOARD_TYPE "m5stack-cardputer-adv")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_M5STACK_ATOM_MATRIX_ECHO_BASE)
|
||||
set(BOARD_TYPE "atommatrix-echo-base")
|
||||
elseif(CONFIG_BOARD_TYPE_XMINI_C3_V3)
|
||||
@@ -238,143 +255,249 @@ elseif(CONFIG_BOARD_TYPE_ESP_HI)
|
||||
set(BOARD_TYPE "esp-hi")
|
||||
# Set ESP_HI emoji directory for DEFAULT_ASSETS_EXTRA_FILES
|
||||
set(DEFAULT_ASSETS_EXTRA_FILES "${CMAKE_BINARY_DIR}/emoji")
|
||||
elseif(CONFIG_BOARD_TYPE_ECHOEAR)
|
||||
set(BOARD_TYPE "echoear")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_VOCAT)
|
||||
set(BOARD_TYPE "esp-vocat")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(EMOTE_RESOLUTION "360_360")
|
||||
# set(EMOTE_EXTERNAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/boards/echoear/assets")
|
||||
# set(EMOTE_EXTERNAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/boards/esp-vocat/assets")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP_SENSAIRSHUTTLE)
|
||||
set(BOARD_TYPE "esp-sensairshuttle")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_AUDIO_BOARD)
|
||||
set(BOARD_TYPE "waveshare-s3-audio-board")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_TOUCH_LCD_3_5)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-touch-lcd-3.5")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-cam")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_AUDIO_BOARD)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-audio-board")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_8)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_8)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-amoled-1.8")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_1_8)
|
||||
set(BOARD_TYPE "waveshare-c6-touch-amoled-1.8")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_8)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-c6-touch-amoled-1.8")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_2_06)
|
||||
set(BOARD_TYPE "waveshare-s3-touch-amoled-2.06")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-amoled-2.06")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_2_06)
|
||||
set(BOARD_TYPE "waveshare-c6-touch-amoled-2.06")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_2_06)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-c6-touch-amoled-2.06")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_4B)
|
||||
set(BOARD_TYPE "waveshare-s3-touch-lcd-4b")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_2_16)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-c6-touch-amoled-2.16")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_4_3C)
|
||||
set(BOARD_TYPE "waveshare-s3-touch-lcd-4.3c")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-4b")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_75)
|
||||
set(BOARD_TYPE "waveshare-s3-touch-amoled-1.75")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4_3C)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-4.3c")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_83)
|
||||
set(BOARD_TYPE "waveshare-s3-touch-lcd-1.83")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-amoled-1.75")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-amoled-1.75")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-1.83")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_85C)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85C)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-1.85c")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_85)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-1.85")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_46)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_46)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-1.46")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_5)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_5)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-3.5")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_5B)
|
||||
set(BOARD_TYPE "waveshare-s3-touch-lcd-3.5b")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_5B)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-3.5b")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_ePaper_1_54)
|
||||
set(BOARD_TYPE "waveshare-s3-epaper-1.54")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_1_54_v1)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-epaper-1.54")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_1_54_v2)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-epaper-1.54")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_RLCD_4_2)
|
||||
set(BOARD_TYPE "waveshare-s3-rlcd-4.2")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_3_97)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-epaper-3.97")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_49)
|
||||
set(BOARD_TYPE "waveshare-s3-touch-lcd-3.49")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_RLCD_4_2)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-rlcd-4.2")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_49)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-3.49")
|
||||
set(LVGL_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(LVGL_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_C6_LCD_1_69)
|
||||
set(BOARD_TYPE "waveshare-c6-lcd-1.69")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_54)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-lcd-1.54")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_C6_TOUCH_LCD_1_83)
|
||||
set(BOARD_TYPE "waveshare-c6-touch-lcd-1.83")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_LCD_0_85)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-lcd-0.85")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_C6_LCD_1_69)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-c6-lcd-1.69")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_LCD_1_83)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-c6-touch-lcd-1.83")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_1_43)
|
||||
set(BOARD_TYPE "waveshare-c6-touch-amoled-1.43")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_43)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-c6-touch-amoled-1.43")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_1_32)
|
||||
set(BOARD_TYPE "waveshare-c6-touch-amoled-1.32")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_32)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-c6-touch-amoled-1.32")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_32)
|
||||
set(BOARD_TYPE "waveshare-s3-touch-amoled-1.32")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_32)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-s3-touch-amoled-1.32")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_P4_NANO)
|
||||
set(BOARD_TYPE "waveshare-p4-nano")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_NANO)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-nano")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_4B)
|
||||
set(BOARD_TYPE "waveshare-p4-wifi6-touch-lcd-4b")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_7B)
|
||||
set(BOARD_TYPE "waveshare-p4-wifi6-touch-lcd-7b")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_XC)
|
||||
set(BOARD_TYPE "waveshare-p4-wifi6-touch-lcd-xc")
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_5)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd-3.5")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_8)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_10_1)
|
||||
set(MANUFACTURER "waveshare")
|
||||
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_30_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
@@ -436,29 +559,29 @@ elseif(CONFIG_BOARD_TYPE_MOVECALL_CUICAN_ESP32S3)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3)
|
||||
set(BOARD_TYPE "atk-dnesp32s3")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX)
|
||||
set(BOARD_TYPE "atk-dnesp32s3-box")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX0)
|
||||
set(BOARD_TYPE "atk-dnesp32s3-box0")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX2_WIFI)
|
||||
set(BOARD_TYPE "atk-dnesp32s3-box2-wifi")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX2_4G)
|
||||
set(BOARD_TYPE "atk-dnesp32s3-box2-4g")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3M_WIFI)
|
||||
set(BOARD_TYPE "atk-dnesp32s3m-wifi")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
@@ -499,24 +622,29 @@ elseif(CONFIG_BOARD_TYPE_XINGZHI_CUBE_0_96OLED_ML307)
|
||||
set(BUILTIN_ICON_FONT font_awesome_14_1)
|
||||
elseif(CONFIG_BOARD_TYPE_XINGZHI_CUBE_1_54TFT_WIFI)
|
||||
set(BOARD_TYPE "xingzhi-cube-1.54tft-wifi")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_XINGZHI_CUBE_1_54TFT_ML307)
|
||||
set(BOARD_TYPE "xingzhi-cube-1.54tft-ml307")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_XINGZHI_METAL_1_54_WIFI)
|
||||
set(BOARD_TYPE "xingzhi-metal-1.54-wifi")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_XINGZHI_ABS_2_0)
|
||||
set(BOARD_TYPE "xingzhi-abs-2.0")
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_SEEED_STUDIO_SENSECAP_WATCHER)
|
||||
set(BOARD_TYPE "sensecap-watcher")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
|
||||
set(BUILTIN_TEXT_FONT font_noto_basic_30_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_128)
|
||||
elseif(CONFIG_BOARD_TYPE_DOIT_S3_AIBOX)
|
||||
set(BOARD_TYPE "doit-s3-aibox")
|
||||
elseif(CONFIG_BOARD_TYPE_MIXGO_NOVA)
|
||||
@@ -586,10 +714,12 @@ elseif(CONFIG_BOARD_TYPE_OTTO_ROBOT)
|
||||
set(BOARD_TYPE "otto-robot")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_16_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION otto-gif)
|
||||
elseif(CONFIG_BOARD_TYPE_ELECTRON_BOT)
|
||||
set(BOARD_TYPE "electron-bot")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_20_4)
|
||||
set(BUILTIN_ICON_FONT font_awesome_20_4)
|
||||
set(DEFAULT_EMOJI_COLLECTION otto-gif)
|
||||
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_CAM)
|
||||
set(BOARD_TYPE "bread-compact-wifi-s3cam")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
@@ -634,12 +764,24 @@ elseif(CONFIG_BOARD_TYPE_HU_087)
|
||||
set(BOARD_TYPE "hu-087")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
|
||||
set(BUILTIN_ICON_FONT font_awesome_14_1)
|
||||
elseif(CONFIG_BOARD_TYPE_Freenove_ESP32S3_DISPLAY_2_8_LCD)
|
||||
set(BOARD_TYPE "freenove-esp32s3-display-2.8-lcd")
|
||||
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
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c
|
||||
)
|
||||
if(MANUFACTURER)
|
||||
file(GLOB BOARD_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${MANUFACTURER}/${BOARD_TYPE}/*.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${MANUFACTURER}/${BOARD_TYPE}/*.c
|
||||
)
|
||||
else()
|
||||
file(GLOB BOARD_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c
|
||||
)
|
||||
endif()
|
||||
list(APPEND SOURCES ${BOARD_SOURCES})
|
||||
|
||||
# Select audio processor according to Kconfig
|
||||
@@ -777,6 +919,8 @@ if(CONFIG_IDF_TARGET_ESP32)
|
||||
"display/lvgl_display/jpg/image_to_jpeg.cpp"
|
||||
"display/lvgl_display/jpg/jpeg_to_image.c"
|
||||
"boards/common/nt26_board.cc"
|
||||
"boards/common/ml307_board.cc"
|
||||
"boards/common/dual_network_board.cc"
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -813,6 +957,7 @@ idf_component_register(SRCS ${SOURCES}
|
||||
console
|
||||
efuse
|
||||
bt
|
||||
fatfs
|
||||
)
|
||||
|
||||
# Use target_compile_definitions to define BOARD_TYPE, BOARD_NAME
|
||||
|
||||
+153
-46
@@ -185,8 +185,8 @@ choice BOARD_TYPE
|
||||
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"
|
||||
config BOARD_TYPE_ESP_VOCAT
|
||||
bool "Espressif ESP-VoCat"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_KEVIN_BOX_2
|
||||
bool "Kevin Box 2"
|
||||
@@ -215,6 +215,15 @@ choice BOARD_TYPE
|
||||
config BOARD_TYPE_LICHUANG_DEV_C3
|
||||
bool "立创·实战派 ESP32-C3"
|
||||
depends on IDF_TARGET_ESP32C3
|
||||
config BOARD_TYPE_EDA_TV_PRO
|
||||
bool "EDA课程案例 EDA-TV-Pro"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_EDA_ROBOT_PRO
|
||||
bool "EDA课程案例 EDA-Robot-Pro"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_EDA_SUPER_BEAR
|
||||
bool "EDA课程案例 EDA-Super-Bear"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_DF_K10
|
||||
bool "DFRobot 行空板 k10"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
@@ -251,86 +260,131 @@ choice BOARD_TYPE
|
||||
config BOARD_TYPE_M5STACK_ATOM_ECHOS3R
|
||||
bool "M5Stack AtomEchoS3R"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_M5STACK_CARDPUTER_ADV
|
||||
bool "M5Stack Cardputer Adv"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_M5STACK_ATOM_MATRIX_ECHO_BASE
|
||||
bool "M5Stack AtomMatrix + Echo Base"
|
||||
depends on IDF_TARGET_ESP32
|
||||
config BOARD_TYPE_WAVESHARE_S3_AUDIO_BOARD
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_TOUCH_LCD_3_5
|
||||
bool "Waveshare ESP32-Touch-LCD-3.5"
|
||||
depends on IDF_TARGET_ESP32
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX
|
||||
bool "Waveshare ESP32-S3-CAM"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_AUDIO_BOARD
|
||||
bool "Waveshare ESP32-S3-Audio-Board"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_8
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_8
|
||||
bool "Waveshare ESP32-S3-Touch-AMOLED-1.8"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_2_06
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06
|
||||
bool "Waveshare ESP32-S3-Touch-AMOLED-2.06"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_2_06
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_2_06
|
||||
bool "Waveshare ESP32-C6-Touch-AMOLED-2.06"
|
||||
depends on IDF_TARGET_ESP32C6
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_75
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_2_16
|
||||
bool "Waveshare ESP32-C6-Touch-AMOLED-2.16"
|
||||
depends on IDF_TARGET_ESP32C6
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75
|
||||
bool "Waveshare ESP32-S3-Touch-AMOLED-1.75"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_83
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C
|
||||
bool "Waveshare ESP32-S3-Touch-AMOLED-1.75C"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-1.83"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_4B
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-4B"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_4_3C
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4_3C
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-4.3C"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_85C
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85C
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-1.85C"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_85
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-1.85"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_1_46
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_46
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-1.46"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_C6_LCD_1_69
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_C6_LCD_1_69
|
||||
bool "Waveshare ESP32-C6-LCD-1.69"
|
||||
depends on IDF_TARGET_ESP32C6
|
||||
config BOARD_TYPE_WAVESHARE_C6_TOUCH_LCD_1_83
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_LCD_1_83
|
||||
bool "Waveshare ESP32-C6-Touch-LCD-1.83"
|
||||
depends on IDF_TARGET_ESP32C6
|
||||
config BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_1_43
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_43
|
||||
bool "Waveshare ESP32-C6-Touch-AMOLOED-1.43"
|
||||
depends on IDF_TARGET_ESP32C6
|
||||
config BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_1_32
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_32
|
||||
bool "Waveshare ESP32-C6-Touch-AMOLOED-1.32"
|
||||
depends on IDF_TARGET_ESP32C6
|
||||
config BOARD_TYPE_WAVESHARE_C6_TOUCH_AMOLED_1_8
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_1_8
|
||||
bool "Waveshare ESP32-C6-Touch-AMOLED-1.8"
|
||||
depends on IDF_TARGET_ESP32C6
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_AMOLED_1_32
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_32
|
||||
bool "Waveshare ESP32-S3-Touch-AMOLOED-1.32"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_49
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_49
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-3.49"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_5
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_5
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-3.5"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_ePaper_1_54
|
||||
bool "Waveshare ESP32-S3-ePaper-1.54"
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_1_54_v1
|
||||
bool "Waveshare ESP32-S3-ePaper-1.54_v1"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_RLCD_4_2
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_1_54_v2
|
||||
bool "Waveshare ESP32-S3-ePaper-1.54_v2"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_ePaper_3_97
|
||||
bool "Waveshare ESP32-S3-ePaper-3.97"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_RLCD_4_2
|
||||
bool "Waveshare ESP32-S3-RLCD-4.2"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_3_5B
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_5B
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-3.5B"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_P4_NANO
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_54
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-1.54"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_S3_LCD_0_85
|
||||
bool "Waveshare ESP32-S3-LCD-0.85"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_NANO
|
||||
bool "Waveshare ESP32-P4-NANO"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_4B
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4B"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_7B
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-7B"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_P4_WIFI6_TOUCH_LCD_XC
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C or ESP32-P4-WIFI6-Touch-LCD-4C"
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_5
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4C"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-7"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_8
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-8"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_10_1
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-10.1"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config BOARD_TYPE_TUDOUZI
|
||||
bool "土豆子"
|
||||
@@ -410,6 +464,9 @@ choice BOARD_TYPE
|
||||
config BOARD_TYPE_XINGZHI_METAL_1_54_WIFI
|
||||
bool "无名科技星智1.54 METAL(wifi)"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_XINGZHI_ABS_2_0
|
||||
bool "无名科技星智ABS 2.0"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_SEEED_STUDIO_SENSECAP_WATCHER
|
||||
bool "Seeed Studio SenseCAP Watcher"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
@@ -479,6 +536,9 @@ choice BOARD_TYPE
|
||||
config BOARD_TYPE_HU_087
|
||||
bool "HU-087"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_Freenove_ESP32S3_DISPLAY_2_8_LCD
|
||||
bool "Freenove ESP32S3 Display 2.8 LCD"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
endchoice
|
||||
|
||||
choice
|
||||
@@ -526,7 +586,7 @@ choice DISPLAY_OLED_TYPE
|
||||
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
|
||||
depends on BOARD_TYPE_BREAD_COMPACT_WIFI_LCD || BOARD_TYPE_BREAD_COMPACT_ESP32_LCD || BOARD_TYPE_CGC || BOARD_TYPE_BREAD_COMPACT_WIFI_CAM
|
||||
prompt "LCD Type"
|
||||
default LCD_ST7789_240X320
|
||||
help
|
||||
@@ -561,14 +621,6 @@ choice DISPLAY_LCD_TYPE
|
||||
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
|
||||
@@ -585,8 +637,27 @@ choice DISPLAY_ESP32S3_KORVO2_V3
|
||||
bool "ILI9341 240*320"
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_ESP32S3_CAM_XXXX
|
||||
depends on BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX
|
||||
prompt "ESP32S3_CAM LCD Type"
|
||||
default BSP_LCD_SIZE_2INCH
|
||||
help
|
||||
LCD Display Type
|
||||
config BSP_LCD_SIZE_2INCH
|
||||
bool "2inch Touch LCD Module"
|
||||
|
||||
config BSP_LCD_SIZE_2_8INCH
|
||||
bool "2.8inch Touch LCD Module"
|
||||
|
||||
config BSP_LCD_SIZE_3_5INCH
|
||||
bool "3.5inch Touch LCD Module"
|
||||
|
||||
config BSP_LCD_SIZE_1_83INCH
|
||||
bool "1.83inch Touch LCD Module"
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_ESP32S3_AUDIO_BOARD
|
||||
depends on BOARD_TYPE_WAVESHARE_S3_AUDIO_BOARD
|
||||
depends on BOARD_TYPE_WAVESHARE_ESP32_S3_AUDIO_BOARD
|
||||
prompt "ESP32S3_AUDIO_BOARD LCD Type"
|
||||
default AUDIO_BOARD_LCD_JD9853
|
||||
help
|
||||
@@ -597,6 +668,19 @@ choice DISPLAY_ESP32S3_AUDIO_BOARD
|
||||
bool "ST7789 240*320"
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_ESP32S3_TOUCH_LCD_1_85C
|
||||
depends on BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_85C
|
||||
prompt "ESP32S3_TOUCH_LCD_1_85C version"
|
||||
default VERSION_2_0
|
||||
help
|
||||
hardware version
|
||||
config VERSION_1_0
|
||||
bool "version 1.0"
|
||||
config VERSION_2_0
|
||||
bool "version 2.0"
|
||||
endchoice
|
||||
|
||||
|
||||
choice DISPLAY_STYLE
|
||||
prompt "Select display style"
|
||||
default USE_DEFAULT_MESSAGE_STYLE
|
||||
@@ -612,10 +696,21 @@ 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 \
|
||||
|| BOARD_TYPE_ESP_VOCAT || BOARD_TYPE_LICHUANG_DEV_S3 \
|
||||
|| BOARD_TYPE_ESP_SENSAIRSHUTTLE
|
||||
endchoice
|
||||
|
||||
config USE_MULTILINE_CHAT_MESSAGE
|
||||
bool "Use multiline chat message display (default mode only)"
|
||||
depends on USE_DEFAULT_MESSAGE_STYLE
|
||||
default n
|
||||
help
|
||||
When enabled, the chat message area in the default display mode shows
|
||||
multiple wrapped lines that grow upward from the bottom of the screen,
|
||||
with auto-adaptive height.
|
||||
When disabled (default), a single-line horizontally scrolling label
|
||||
is shown at the bottom of the screen.
|
||||
|
||||
choice WAKE_WORD_TYPE
|
||||
prompt "Wake Word Implementation Type"
|
||||
default USE_AFE_WAKE_WORD if (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
|
||||
@@ -677,6 +772,16 @@ config SEND_WAKE_WORD_DATA
|
||||
help
|
||||
Send wake word data to the server as the first message of the conversation and wait for response
|
||||
|
||||
config WAKE_WORD_DETECTION_IN_LISTENING
|
||||
bool "Enable Wake Word Detection in Listening Mode"
|
||||
default n
|
||||
depends on USE_AFE_WAKE_WORD || USE_CUSTOM_WAKE_WORD
|
||||
help
|
||||
Enable wake word detection while in listening mode.
|
||||
When enabled, the device can detect wake word during listening,
|
||||
which allows interrupting the current conversation.
|
||||
When disabled (default), wake word detection is turned off during listening.
|
||||
|
||||
config USE_AUDIO_PROCESSOR
|
||||
bool "Enable Audio Noise Reduction"
|
||||
default y
|
||||
@@ -688,11 +793,13 @@ 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 || BOARD_TYPE_WAVESHARE_S3_RLCD_4_2 || BOARD_TYPE_ZHENGCHEN_CAM || BOARD_TYPE_ZHENGCHEN_CAM_ML307 \
|
||||
|| BOARD_TYPE_WAVESHARE_S3_TOUCH_LCD_4_3C)
|
||||
|| BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_ESP_KORVO2_V3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83\
|
||||
|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3 \
|
||||
|| BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B \
|
||||
|| BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 || BOARD_TYPE_YUNLIAO_S3 \
|
||||
|| BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_8 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_10_1 \
|
||||
|| BOARD_TYPE_ESP_VOCAT || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_49 || BOARD_TYPE_WAVESHARE_ESP32_S3_RLCD_4_2 || BOARD_TYPE_ZHENGCHEN_CAM || BOARD_TYPE_ZHENGCHEN_CAM_ML307 \
|
||||
|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4_3C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_54 || BOARD_TYPE_WAVESHARE_ESP32_S3_LCD_0_85)
|
||||
help
|
||||
To work properly, device-side AEC requires a clean output reference path from the speaker signal and physical acoustic isolation between the microphone and speaker.
|
||||
|
||||
|
||||
+124
-71
@@ -64,7 +64,7 @@ void Application::Initialize() {
|
||||
|
||||
// Setup the display
|
||||
auto display = board.GetDisplay();
|
||||
|
||||
display->SetupUI();
|
||||
// Print board name/version info
|
||||
display->SetChatMessage("system", SystemInfo::GetUserAgent().c_str());
|
||||
|
||||
@@ -309,13 +309,15 @@ void Application::HandleActivationDoneEvent() {
|
||||
display->ShowNotification(message.c_str());
|
||||
display->SetChatMessage("system", "");
|
||||
|
||||
// Play the success sound to indicate the device is ready
|
||||
audio_service_.PlaySound(Lang::Sounds::OGG_SUCCESS);
|
||||
|
||||
// Release OTA object after activation is complete
|
||||
ota_.reset();
|
||||
auto& board = Board::GetInstance();
|
||||
board.SetPowerSaveLevel(PowerSaveLevel::LOW_POWER);
|
||||
|
||||
Schedule([this]() {
|
||||
// Play the success sound to indicate the device is ready
|
||||
audio_service_.PlaySound(Lang::Sounds::OGG_SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
void Application::ActivationTask() {
|
||||
@@ -368,12 +370,12 @@ void Application::CheckAssetsVersion() {
|
||||
board.SetPowerSaveLevel(PowerSaveLevel::PERFORMANCE);
|
||||
display->SetChatMessage("system", Lang::Strings::PLEASE_WAIT);
|
||||
|
||||
bool success = assets.Download(download_url, [display](int progress, size_t speed) -> void {
|
||||
std::thread([display, progress, speed]() {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%d%% %uKB/s", progress, speed / 1024);
|
||||
display->SetChatMessage("system", buffer);
|
||||
}).detach();
|
||||
bool success = assets.Download(download_url, [this, display](int progress, size_t speed) -> void {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%d%% %uKB/s", progress, speed / 1024);
|
||||
Schedule([display, message = std::string(buffer)]() {
|
||||
display->SetChatMessage("system", message.c_str());
|
||||
});
|
||||
});
|
||||
|
||||
board.SetPowerSaveLevel(PowerSaveLevel::LOW_POWER);
|
||||
@@ -691,14 +693,16 @@ void Application::HandleToggleChatEvent() {
|
||||
}
|
||||
|
||||
if (state == kDeviceStateIdle) {
|
||||
ListeningMode mode = GetDefaultListeningMode();
|
||||
if (!protocol_->IsAudioChannelOpened()) {
|
||||
SetDeviceState(kDeviceStateConnecting);
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
return;
|
||||
}
|
||||
// Schedule to let the state change be processed first (UI update)
|
||||
Schedule([this, mode]() {
|
||||
ContinueOpenAudioChannel(mode);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
|
||||
SetListeningMode(mode);
|
||||
} else if (state == kDeviceStateSpeaking) {
|
||||
AbortSpeaking(kAbortReasonNone);
|
||||
} else if (state == kDeviceStateListening) {
|
||||
@@ -706,6 +710,21 @@ void Application::HandleToggleChatEvent() {
|
||||
}
|
||||
}
|
||||
|
||||
void Application::ContinueOpenAudioChannel(ListeningMode mode) {
|
||||
// Check state again in case it was changed during scheduling
|
||||
if (GetDeviceState() != kDeviceStateConnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!protocol_->IsAudioChannelOpened()) {
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SetListeningMode(mode);
|
||||
}
|
||||
|
||||
void Application::HandleStartListeningEvent() {
|
||||
auto state = GetDeviceState();
|
||||
|
||||
@@ -726,11 +745,12 @@ void Application::HandleStartListeningEvent() {
|
||||
if (state == kDeviceStateIdle) {
|
||||
if (!protocol_->IsAudioChannelOpened()) {
|
||||
SetDeviceState(kDeviceStateConnecting);
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
return;
|
||||
}
|
||||
// Schedule to let the state change be processed first (UI update)
|
||||
Schedule([this]() {
|
||||
ContinueOpenAudioChannel(kListeningModeManualStop);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
SetListeningMode(kListeningModeManualStop);
|
||||
} else if (state == kDeviceStateSpeaking) {
|
||||
AbortSpeaking(kAbortReasonNone);
|
||||
@@ -759,42 +779,76 @@ void Application::HandleWakeWordDetectedEvent() {
|
||||
}
|
||||
|
||||
auto state = GetDeviceState();
|
||||
|
||||
auto wake_word = audio_service_.GetLastWakeWord();
|
||||
ESP_LOGI(TAG, "Wake word detected: %s (state: %d)", wake_word.c_str(), (int)state);
|
||||
|
||||
if (state == kDeviceStateIdle) {
|
||||
audio_service_.EncodeWakeWord();
|
||||
auto wake_word = audio_service_.GetLastWakeWord();
|
||||
|
||||
if (!protocol_->IsAudioChannelOpened()) {
|
||||
SetDeviceState(kDeviceStateConnecting);
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
audio_service_.EnableWakeWordDetection(true);
|
||||
return;
|
||||
}
|
||||
// Schedule to let the state change be processed first (UI update),
|
||||
// then continue with OpenAudioChannel which may block for ~1 second
|
||||
Schedule([this, wake_word]() {
|
||||
ContinueWakeWordInvoke(wake_word);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
auto wake_word = audio_service_.GetLastWakeWord();
|
||||
ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
|
||||
#if CONFIG_SEND_WAKE_WORD_DATA
|
||||
// Encode and send the wake word data to the server
|
||||
while (auto packet = audio_service_.PopWakeWordPacket()) {
|
||||
protocol_->SendAudio(std::move(packet));
|
||||
}
|
||||
// Set the chat state to wake word detected
|
||||
protocol_->SendWakeWordDetected(wake_word);
|
||||
SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
|
||||
#else
|
||||
// Set flag to play popup sound after state changes to listening
|
||||
// (PlaySound here would be cleared by ResetDecoder in EnableVoiceProcessing)
|
||||
play_popup_on_listening_ = true;
|
||||
SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
|
||||
#endif
|
||||
} else if (state == kDeviceStateSpeaking) {
|
||||
// Channel already opened, continue directly
|
||||
ContinueWakeWordInvoke(wake_word);
|
||||
} else if (state == kDeviceStateSpeaking || state == kDeviceStateListening) {
|
||||
AbortSpeaking(kAbortReasonWakeWordDetected);
|
||||
// Clear send queue to avoid sending residues to server
|
||||
while (audio_service_.PopPacketFromSendQueue());
|
||||
|
||||
if (state == kDeviceStateListening) {
|
||||
protocol_->SendStartListening(GetDefaultListeningMode());
|
||||
audio_service_.ResetDecoder();
|
||||
audio_service_.PlaySound(Lang::Sounds::OGG_POPUP);
|
||||
// Re-enable wake word detection as it was stopped by the detection itself
|
||||
audio_service_.EnableWakeWordDetection(true);
|
||||
} else {
|
||||
// Play popup sound and start listening again
|
||||
play_popup_on_listening_ = true;
|
||||
SetListeningMode(GetDefaultListeningMode());
|
||||
}
|
||||
} else if (state == kDeviceStateActivating) {
|
||||
// Restart the activation check if the wake word is detected during activation
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::ContinueWakeWordInvoke(const std::string& wake_word) {
|
||||
// Check state again in case it was changed during scheduling
|
||||
if (GetDeviceState() != kDeviceStateConnecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!protocol_->IsAudioChannelOpened()) {
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
audio_service_.EnableWakeWordDetection(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
|
||||
#if CONFIG_SEND_WAKE_WORD_DATA
|
||||
// Encode and send the wake word data to the server
|
||||
while (auto packet = audio_service_.PopWakeWordPacket()) {
|
||||
protocol_->SendAudio(std::move(packet));
|
||||
}
|
||||
// Set the chat state to wake word detected
|
||||
protocol_->SendWakeWordDetected(wake_word);
|
||||
SetListeningMode(GetDefaultListeningMode());
|
||||
#else
|
||||
// Set flag to play popup sound after state changes to listening
|
||||
// (PlaySound here would be cleared by ResetDecoder in EnableVoiceProcessing)
|
||||
play_popup_on_listening_ = true;
|
||||
SetListeningMode(GetDefaultListeningMode());
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::HandleStateChangedEvent() {
|
||||
DeviceState new_state = state_machine_.GetState();
|
||||
clock_ticks_ = 0;
|
||||
@@ -808,7 +862,8 @@ void Application::HandleStateChangedEvent() {
|
||||
case kDeviceStateUnknown:
|
||||
case kDeviceStateIdle:
|
||||
display->SetStatus(Lang::Strings::STANDBY);
|
||||
display->SetEmotion("neutral");
|
||||
display->ClearChatMessages(); // Clear messages first
|
||||
display->SetEmotion("neutral"); // Then set emotion (wechat mode checks child count)
|
||||
audio_service_.EnableVoiceProcessing(false);
|
||||
audio_service_.EnableWakeWordDetection(true);
|
||||
break;
|
||||
@@ -822,7 +877,7 @@ void Application::HandleStateChangedEvent() {
|
||||
display->SetEmotion("neutral");
|
||||
|
||||
// Make sure the audio processor is running
|
||||
if (!audio_service_.IsAudioProcessorRunning()) {
|
||||
if (play_popup_on_listening_ || !audio_service_.IsAudioProcessorRunning()) {
|
||||
// For auto mode, wait for playback queue to be empty before enabling voice processing
|
||||
// This prevents audio truncation when STOP arrives late due to network jitter
|
||||
if (listening_mode_ == kListeningModeAutoStop) {
|
||||
@@ -832,9 +887,16 @@ void Application::HandleStateChangedEvent() {
|
||||
// Send the start listening command
|
||||
protocol_->SendStartListening(listening_mode_);
|
||||
audio_service_.EnableVoiceProcessing(true);
|
||||
audio_service_.EnableWakeWordDetection(false);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WAKE_WORD_DETECTION_IN_LISTENING
|
||||
// Enable wake word detection in listening mode (configured via Kconfig)
|
||||
audio_service_.EnableWakeWordDetection(audio_service_.IsAfeWakeWord());
|
||||
#else
|
||||
// Disable wake word detection in listening mode
|
||||
audio_service_.EnableWakeWordDetection(false);
|
||||
#endif
|
||||
|
||||
// Play popup sound after ResetDecoder (in EnableVoiceProcessing) has been called
|
||||
if (play_popup_on_listening_) {
|
||||
play_popup_on_listening_ = false;
|
||||
@@ -882,6 +944,10 @@ void Application::SetListeningMode(ListeningMode mode) {
|
||||
SetDeviceState(kDeviceStateListening);
|
||||
}
|
||||
|
||||
ListeningMode Application::GetDefaultListeningMode() const {
|
||||
return aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime;
|
||||
}
|
||||
|
||||
void Application::Reboot() {
|
||||
ESP_LOGI(TAG, "Rebooting...");
|
||||
// Disconnect the audio channel
|
||||
@@ -921,12 +987,12 @@ bool Application::UpgradeFirmware(const std::string& url, const std::string& ver
|
||||
audio_service_.Stop();
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
bool upgrade_success = Ota::Upgrade(upgrade_url, [display](int progress, size_t speed) {
|
||||
std::thread([display, progress, speed]() {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%d%% %uKB/s", progress, speed / 1024);
|
||||
display->SetChatMessage("system", buffer);
|
||||
}).detach();
|
||||
bool upgrade_success = Ota::Upgrade(upgrade_url, [this, display](int progress, size_t speed) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "%d%% %uKB/s", progress, speed / 1024);
|
||||
Schedule([display, message = std::string(buffer)]() {
|
||||
display->SetChatMessage("system", message.c_str());
|
||||
});
|
||||
});
|
||||
|
||||
if (!upgrade_success) {
|
||||
@@ -959,27 +1025,14 @@ void Application::WakeWordInvoke(const std::string& wake_word) {
|
||||
|
||||
if (!protocol_->IsAudioChannelOpened()) {
|
||||
SetDeviceState(kDeviceStateConnecting);
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
audio_service_.EnableWakeWordDetection(true);
|
||||
return;
|
||||
}
|
||||
// Schedule to let the state change be processed first (UI update)
|
||||
Schedule([this, wake_word]() {
|
||||
ContinueWakeWordInvoke(wake_word);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
|
||||
#if CONFIG_USE_AFE_WAKE_WORD || CONFIG_USE_CUSTOM_WAKE_WORD
|
||||
// Encode and send the wake word data to the server
|
||||
while (auto packet = audio_service_.PopWakeWordPacket()) {
|
||||
protocol_->SendAudio(std::move(packet));
|
||||
}
|
||||
// Set the chat state to wake word detected
|
||||
protocol_->SendWakeWordDetected(wake_word);
|
||||
SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
|
||||
#else
|
||||
// Set flag to play popup sound after state changes to listening
|
||||
// (PlaySound here would be cleared by ResetDecoder in EnableVoiceProcessing)
|
||||
play_popup_on_listening_ = true;
|
||||
SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
|
||||
#endif
|
||||
// Channel already opened, continue directly
|
||||
ContinueWakeWordInvoke(wake_word);
|
||||
} else if (state == kDeviceStateSpeaking) {
|
||||
Schedule([this]() {
|
||||
AbortSpeaking(kAbortReasonNone);
|
||||
|
||||
@@ -153,6 +153,8 @@ private:
|
||||
void HandleNetworkDisconnectedEvent();
|
||||
void HandleActivationDoneEvent();
|
||||
void HandleWakeWordDetectedEvent();
|
||||
void ContinueOpenAudioChannel(ListeningMode mode);
|
||||
void ContinueWakeWordInvoke(const std::string& wake_word);
|
||||
|
||||
// Activation task (runs in background)
|
||||
void ActivationTask();
|
||||
@@ -163,6 +165,7 @@ private:
|
||||
void InitializeProtocol();
|
||||
void ShowActivationCode(const std::string& code, const std::string& message);
|
||||
void SetListeningMode(ListeningMode mode);
|
||||
ListeningMode GetDefaultListeningMode() const;
|
||||
|
||||
// State change handler called by state machine
|
||||
void OnStateChanged(DeviceState old_state, DeviceState new_state);
|
||||
|
||||
+32
-20
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <cbin_font.h>
|
||||
|
||||
|
||||
@@ -49,8 +50,8 @@ bool Assets::FindPartition(Assets* assets) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Assets::Apply() {
|
||||
return strategy_ ? strategy_->Apply(this) : false;
|
||||
bool Assets::Apply(bool refresh_display_theme) {
|
||||
return strategy_ ? strategy_->Apply(this, refresh_display_theme) : false;
|
||||
}
|
||||
|
||||
bool Assets::InitializePartition() {
|
||||
@@ -210,7 +211,7 @@ bool Assets::LvglStrategy::GetAssetData(Assets* assets, const std::string& name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Assets::LvglStrategy::Apply(Assets* assets) {
|
||||
bool Assets::LvglStrategy::Apply(Assets* assets, bool refresh_display_theme) {
|
||||
void* ptr = nullptr;
|
||||
size_t size = 0;
|
||||
if (!assets->GetAssetData("index.json", ptr, size)) {
|
||||
@@ -331,22 +332,24 @@ bool Assets::LvglStrategy::Apply(Assets* assets) {
|
||||
}
|
||||
}
|
||||
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
ESP_LOGI(TAG, "Refreshing display theme...");
|
||||
if (refresh_display_theme) {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
ESP_LOGI(TAG, "Refreshing display theme...");
|
||||
|
||||
auto current_theme = display->GetTheme();
|
||||
if (current_theme != nullptr) {
|
||||
display->SetTheme(current_theme);
|
||||
}
|
||||
auto current_theme = display->GetTheme();
|
||||
if (current_theme != nullptr) {
|
||||
display->SetTheme(current_theme);
|
||||
}
|
||||
|
||||
// Parse hide_subtitle configuration
|
||||
cJSON* hide_subtitle = cJSON_GetObjectItem(root, "hide_subtitle");
|
||||
if (cJSON_IsBool(hide_subtitle)) {
|
||||
bool hide = cJSON_IsTrue(hide_subtitle);
|
||||
auto lcd_display = dynamic_cast<LcdDisplay*>(display);
|
||||
if (lcd_display != nullptr) {
|
||||
lcd_display->SetHideSubtitle(hide);
|
||||
ESP_LOGI(TAG, "Set hide_subtitle to %s", hide ? "true" : "false");
|
||||
// Parse hide_subtitle configuration
|
||||
cJSON* hide_subtitle = cJSON_GetObjectItem(root, "hide_subtitle");
|
||||
if (cJSON_IsBool(hide_subtitle)) {
|
||||
bool hide = cJSON_IsTrue(hide_subtitle);
|
||||
auto lcd_display = dynamic_cast<LcdDisplay*>(display);
|
||||
if (lcd_display != nullptr) {
|
||||
lcd_display->SetHideSubtitle(hide);
|
||||
ESP_LOGI(TAG, "Set hide_subtitle to %s", hide ? "true" : "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,7 +413,7 @@ bool Assets::EmoteStrategy::GetAssetData(Assets* assets, const std::string& name
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Assets::EmoteStrategy::Apply(Assets* assets) {
|
||||
bool Assets::EmoteStrategy::Apply(Assets* assets, bool refresh_display_theme) {
|
||||
Assets::LoadSrmodelsFromIndex(assets);
|
||||
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
@@ -464,16 +467,21 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
|
||||
SECTOR_SIZE, content_length, sectors_to_erase, total_erase_size);
|
||||
|
||||
// 写入新的资源文件到分区,一边erase一边写入
|
||||
char buffer[512];
|
||||
char* buffer = (char*)heap_caps_malloc(SECTOR_SIZE, MALLOC_CAP_INTERNAL);
|
||||
if (buffer == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to allocate buffer");
|
||||
return false;
|
||||
}
|
||||
size_t total_written = 0;
|
||||
size_t recent_written = 0;
|
||||
size_t current_sector = 0;
|
||||
auto last_calc_time = esp_timer_get_time();
|
||||
|
||||
while (true) {
|
||||
int ret = http->Read(buffer, sizeof(buffer));
|
||||
int ret = http->Read(buffer, SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret));
|
||||
heap_caps_free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -493,6 +501,7 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
|
||||
// 确保擦除范围不超过分区大小
|
||||
if (sector_end > partition_->size) {
|
||||
ESP_LOGE(TAG, "Sector end (%u) exceeds partition size (%lu)", sector_end, partition_->size);
|
||||
heap_caps_free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -500,6 +509,7 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
|
||||
esp_err_t err = esp_partition_erase_range(partition_, sector_start, SECTOR_SIZE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to erase sector %u at offset %u: %s", current_sector, sector_start, esp_err_to_name(err));
|
||||
heap_caps_free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -510,6 +520,7 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
|
||||
esp_err_t err = esp_partition_write(partition_, total_written, buffer, ret);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to write to assets partition at offset %u: %s", total_written, esp_err_to_name(err));
|
||||
heap_caps_free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -531,6 +542,7 @@ bool Assets::Download(std::string url, std::function<void(int progress, size_t s
|
||||
}
|
||||
|
||||
http->Close();
|
||||
heap_caps_free(buffer);
|
||||
|
||||
if (total_written != content_length) {
|
||||
ESP_LOGE(TAG, "Downloaded size (%u) does not match expected size (%u)", total_written, content_length);
|
||||
|
||||
+4
-4
@@ -29,7 +29,7 @@ public:
|
||||
~Assets();
|
||||
|
||||
bool Download(std::string url, std::function<void(int progress, size_t speed)> progress_callback);
|
||||
bool Apply();
|
||||
bool Apply(bool refresh_display_theme = true);
|
||||
bool GetAssetData(const std::string& name, void*& ptr, size_t& size);
|
||||
|
||||
inline bool partition_valid() const { return partition_valid_; }
|
||||
@@ -48,7 +48,7 @@ private:
|
||||
class AssetStrategy {
|
||||
public:
|
||||
virtual ~AssetStrategy() = default;
|
||||
virtual bool Apply(Assets* assets) = 0;
|
||||
virtual bool Apply(Assets* assets, bool refresh_display_theme = true) = 0;
|
||||
virtual bool InitializePartition(Assets* assets) = 0;
|
||||
virtual void UnApplyPartition(Assets* assets) = 0;
|
||||
virtual bool GetAssetData(Assets* assets, const std::string& name, void*& ptr, size_t& size) = 0;
|
||||
@@ -56,7 +56,7 @@ private:
|
||||
|
||||
class LvglStrategy : public AssetStrategy {
|
||||
public:
|
||||
bool Apply(Assets* assets) override;
|
||||
bool Apply(Assets* assets, bool refresh_display_theme = true) override;
|
||||
bool InitializePartition(Assets* assets) override;
|
||||
void UnApplyPartition(Assets* assets) override;
|
||||
bool GetAssetData(Assets* assets, const std::string& name, void*& ptr, size_t& size) override;
|
||||
@@ -70,7 +70,7 @@ private:
|
||||
|
||||
class EmoteStrategy : public AssetStrategy {
|
||||
public:
|
||||
bool Apply(Assets* assets) override;
|
||||
bool Apply(Assets* assets, bool refresh_display_theme = true) override;
|
||||
bool InitializePartition(Assets* assets) override;
|
||||
void UnApplyPartition(Assets* assets) override;
|
||||
bool GetAssetData(Assets* assets, const std::string& name, void*& ptr, size_t& size) override;
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "جاري تحميل الموارد...",
|
||||
"PLEASE_WAIT": "يرجى الانتظار...",
|
||||
"FOUND_NEW_ASSETS": "تم العثور على موارد جديدة: %s",
|
||||
"HELLO_MY_FRIEND": "مرحباً، صديقي!"
|
||||
"HELLO_MY_FRIEND": "مرحباً، صديقي!",
|
||||
"CONNECTION_SUCCESSFUL": "تم الاتصال بنجاح",
|
||||
"FLIGHT_MODE_OFF": "وضع الطيران معطل",
|
||||
"FLIGHT_MODE_ON": "وضع الطيران قيد التشغيل",
|
||||
"MODEM_INIT_ERROR": "فشل تهيئة المودم"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Намерени нови ресурси: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Неуспешно изтегляне на ресурси",
|
||||
"LOADING_ASSETS": "Зареждане на ресурси...",
|
||||
"HELLO_MY_FRIEND": "Здравей, мой приятел!"
|
||||
"HELLO_MY_FRIEND": "Здравей, мой приятел!",
|
||||
"FLIGHT_MODE_OFF": "Режим на самолет е изключен",
|
||||
"FLIGHT_MODE_ON": "Режим на самолет е включен",
|
||||
"MODEM_INIT_ERROR": "Неуспешна инициализация на модема"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "S'han trobat nous recursos: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "No s'han pogut descarregar els recursos",
|
||||
"LOADING_ASSETS": "Carregant recursos...",
|
||||
"HELLO_MY_FRIEND": "Hola, amic meu!"
|
||||
"HELLO_MY_FRIEND": "Hola, amic meu!",
|
||||
"FLIGHT_MODE_OFF": "El mode avió està desactivat",
|
||||
"FLIGHT_MODE_ON": "El mode avió està activat",
|
||||
"MODEM_INIT_ERROR": "Error d'inicialització del mòdem"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Načítání prostředků...",
|
||||
"PLEASE_WAIT": "Prosím čekejte...",
|
||||
"FOUND_NEW_ASSETS": "Nalezeny nové prostředky: %s",
|
||||
"HELLO_MY_FRIEND": "Ahoj, můj příteli!"
|
||||
"HELLO_MY_FRIEND": "Ahoj, můj příteli!",
|
||||
"CONNECTION_SUCCESSFUL": "Připojení úspěšné",
|
||||
"FLIGHT_MODE_OFF": "Letecký režim je vypnutý",
|
||||
"FLIGHT_MODE_ON": "Letecký režim je zapnutý",
|
||||
"MODEM_INIT_ERROR": "Chyba inicializace modemu"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Fandt nye ressourcer: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Download af ressourcer mislykkedes",
|
||||
"LOADING_ASSETS": "Indlæser ressourcer...",
|
||||
"HELLO_MY_FRIEND": "Hej, min ven!"
|
||||
"HELLO_MY_FRIEND": "Hej, min ven!",
|
||||
"FLIGHT_MODE_OFF": "Flytilstand er slukket",
|
||||
"FLIGHT_MODE_ON": "Flytilstand er tændt",
|
||||
"MODEM_INIT_ERROR": "Modeminitialisering mislykkedes"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Ressourcen werden geladen...",
|
||||
"PLEASE_WAIT": "Bitte warten...",
|
||||
"FOUND_NEW_ASSETS": "Neue Ressourcen gefunden: %s",
|
||||
"HELLO_MY_FRIEND": "Hallo, mein Freund!"
|
||||
"HELLO_MY_FRIEND": "Hallo, mein Freund!",
|
||||
"CONNECTION_SUCCESSFUL": "Verbindung erfolgreich",
|
||||
"FLIGHT_MODE_OFF": "Flugmodus ist deaktiviert",
|
||||
"FLIGHT_MODE_ON": "Flugmodus ist aktiviert",
|
||||
"MODEM_INIT_ERROR": "Modem-Initialisierung fehlgeschlagen"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Βρέθηκαν νέοι πόροι: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Αποτυχία λήψης πόρων",
|
||||
"LOADING_ASSETS": "Φόρτωση πόρων...",
|
||||
"HELLO_MY_FRIEND": "Γεια σου, φίλε μου!"
|
||||
"HELLO_MY_FRIEND": "Γεια σου, φίλε μου!",
|
||||
"FLIGHT_MODE_OFF": "Η λειτουργία πτήσης είναι απενεργοποιημένη",
|
||||
"FLIGHT_MODE_ON": "Η λειτουργία πτήσης είναι ενεργή",
|
||||
"MODEM_INIT_ERROR": "Αποτυχία αρχικοποίησης modem"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,8 @@
|
||||
"REG_ERROR": "Unable to access network, please check SIM card status",
|
||||
"MODEM_INIT_ERROR": "Modem initialization failed",
|
||||
"DETECTING_MODULE": "Detecting module...",
|
||||
"FLIGHT_MODE_ON": "Flight mode is on",
|
||||
"FLIGHT_MODE_OFF": "Flight mode is off",
|
||||
"REGISTERING_NETWORK": "Waiting for network...",
|
||||
"CHECKING_NEW_VERSION": "Checking for new version...",
|
||||
"CHECK_NEW_VERSION_FAILED": "Check for new version failed, will retry in %d seconds: %s",
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Cargando recursos...",
|
||||
"PLEASE_WAIT": "Por favor espere...",
|
||||
"FOUND_NEW_ASSETS": "Encontrados nuevos recursos: %s",
|
||||
"HELLO_MY_FRIEND": "¡Hola, mi amigo!"
|
||||
"HELLO_MY_FRIEND": "¡Hola, mi amigo!",
|
||||
"CONNECTION_SUCCESSFUL": "Conexión exitosa",
|
||||
"FLIGHT_MODE_OFF": "El modo avión está desactivado",
|
||||
"FLIGHT_MODE_ON": "El modo avión está activado",
|
||||
"MODEM_INIT_ERROR": "Error de inicialización del módem"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "منابع جدید یافت شد: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "دانلود منابع ناموفق بود",
|
||||
"LOADING_ASSETS": "بارگذاری منابع...",
|
||||
"HELLO_MY_FRIEND": "سلام، دوست من!"
|
||||
"HELLO_MY_FRIEND": "سلام، دوست من!",
|
||||
"FLIGHT_MODE_OFF": "حالت پرواز خاموش است",
|
||||
"FLIGHT_MODE_ON": "حالت پرواز روشن است",
|
||||
"MODEM_INIT_ERROR": "خطا در راهاندازی مودم"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Ladataan resursseja...",
|
||||
"PLEASE_WAIT": "Odota hetki...",
|
||||
"FOUND_NEW_ASSETS": "Löydetty uusia resursseja: %s",
|
||||
"HELLO_MY_FRIEND": "Hei, ystäväni!"
|
||||
"HELLO_MY_FRIEND": "Hei, ystäväni!",
|
||||
"CONNECTION_SUCCESSFUL": "Yhteys onnistui",
|
||||
"FLIGHT_MODE_OFF": "Lentotila on pois päältä",
|
||||
"FLIGHT_MODE_ON": "Lentotila on päällä",
|
||||
"MODEM_INIT_ERROR": "Modeemin alustus epäonnistui"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Nakahanap ng mga bagong assets: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Nabigo ang pag-download ng mga assets",
|
||||
"LOADING_ASSETS": "Nilo-load ang mga assets...",
|
||||
"HELLO_MY_FRIEND": "Kumusta, kaibigan ko!"
|
||||
"HELLO_MY_FRIEND": "Kumusta, kaibigan ko!",
|
||||
"FLIGHT_MODE_OFF": "Naka-off ang flight mode",
|
||||
"FLIGHT_MODE_ON": "Naka-on ang flight mode",
|
||||
"MODEM_INIT_ERROR": "Nabigo ang pag-initialize ng modem"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Chargement des ressources...",
|
||||
"PLEASE_WAIT": "Veuillez patienter...",
|
||||
"FOUND_NEW_ASSETS": "Nouvelles ressources trouvées: %s",
|
||||
"HELLO_MY_FRIEND": "Bonjour, mon ami !"
|
||||
"HELLO_MY_FRIEND": "Bonjour, mon ami !",
|
||||
"CONNECTION_SUCCESSFUL": "Connexion réussie",
|
||||
"FLIGHT_MODE_OFF": "Le mode avion est désactivé",
|
||||
"FLIGHT_MODE_ON": "Le mode avion est activé",
|
||||
"MODEM_INIT_ERROR": "Échec de l'initialisation du modem"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "נמצאו משאבים חדשים: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "הורדת משאבים נכשלה",
|
||||
"LOADING_ASSETS": "טוען משאבים...",
|
||||
"HELLO_MY_FRIEND": "שלום, ידידי!"
|
||||
"HELLO_MY_FRIEND": "שלום, ידידי!",
|
||||
"FLIGHT_MODE_OFF": "מצב טיסה כבוי",
|
||||
"FLIGHT_MODE_ON": "מצב טיסה מופעל",
|
||||
"MODEM_INIT_ERROR": "אתחול המודם נכשל"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "संसाधन लोड हो रहे हैं...",
|
||||
"PLEASE_WAIT": "कृपया प्रतीक्षा करें...",
|
||||
"FOUND_NEW_ASSETS": "नए संसाधन मिले: %s",
|
||||
"HELLO_MY_FRIEND": "नमस्ते, मेरे दोस्त!"
|
||||
"HELLO_MY_FRIEND": "नमस्ते, मेरे दोस्त!",
|
||||
"CONNECTION_SUCCESSFUL": "कनेक्शन सफल",
|
||||
"FLIGHT_MODE_OFF": "फ़्लाइट मोड बंद है",
|
||||
"FLIGHT_MODE_ON": "फ़्लाइट मोड चालू है",
|
||||
"MODEM_INIT_ERROR": "मॉडेम आरंभीकरण विफल"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Pronađeni novi resursi: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Preuzimanje resursa nije uspjelo",
|
||||
"LOADING_ASSETS": "Učitavanje resursa...",
|
||||
"HELLO_MY_FRIEND": "Bok, moj prijatelju!"
|
||||
"HELLO_MY_FRIEND": "Bok, moj prijatelju!",
|
||||
"FLIGHT_MODE_OFF": "Način rada u zrakoplovu je isključen",
|
||||
"FLIGHT_MODE_ON": "Način rada u zrakoplovu je uključen",
|
||||
"MODEM_INIT_ERROR": "Neuspjela inicijalizacija modema"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Új erőforrások találva: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Az erőforrások letöltése sikertelen",
|
||||
"LOADING_ASSETS": "Erőforrások betöltése...",
|
||||
"HELLO_MY_FRIEND": "Helló, barátom!"
|
||||
"HELLO_MY_FRIEND": "Helló, barátom!",
|
||||
"FLIGHT_MODE_OFF": "A repülési mód ki van kapcsolva",
|
||||
"FLIGHT_MODE_ON": "A repülési mód be van kapcsolva",
|
||||
"MODEM_INIT_ERROR": "A modem inicializálása sikertelen"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Memuat aset...",
|
||||
"PLEASE_WAIT": "Mohon tunggu...",
|
||||
"FOUND_NEW_ASSETS": "Ditemukan aset baru: %s",
|
||||
"HELLO_MY_FRIEND": "Halo, teman saya!"
|
||||
"HELLO_MY_FRIEND": "Halo, teman saya!",
|
||||
"CONNECTION_SUCCESSFUL": "Koneksi berhasil",
|
||||
"FLIGHT_MODE_OFF": "Mode pesawat nonaktif",
|
||||
"FLIGHT_MODE_ON": "Mode pesawat aktif",
|
||||
"MODEM_INIT_ERROR": "Gagal menginisialisasi modem"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Caricamento risorse...",
|
||||
"PLEASE_WAIT": "Attendere prego...",
|
||||
"FOUND_NEW_ASSETS": "Trovate nuove risorse: %s",
|
||||
"HELLO_MY_FRIEND": "Ciao, amico mio!"
|
||||
"HELLO_MY_FRIEND": "Ciao, amico mio!",
|
||||
"CONNECTION_SUCCESSFUL": "Connessione riuscita",
|
||||
"FLIGHT_MODE_OFF": "La modalità aereo è disattivata",
|
||||
"FLIGHT_MODE_ON": "La modalità aereo è attiva",
|
||||
"MODEM_INIT_ERROR": "Inizializzazione modem non riuscita"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "アセットを読み込み中...",
|
||||
"PLEASE_WAIT": "お待ちください...",
|
||||
"FOUND_NEW_ASSETS": "新しいアセットが見つかりました: %s",
|
||||
"HELLO_MY_FRIEND": "こんにちは、友達!"
|
||||
"HELLO_MY_FRIEND": "こんにちは、友達!",
|
||||
"CONNECTION_SUCCESSFUL": "接続成功",
|
||||
"FLIGHT_MODE_OFF": "機内モードがオフです",
|
||||
"FLIGHT_MODE_ON": "機内モードがオンです",
|
||||
"MODEM_INIT_ERROR": "モデムの初期化に失敗しました"
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,9 @@
|
||||
"LOADING_ASSETS": "에셋 로딩 중...",
|
||||
"PLEASE_WAIT": "잠시 기다려 주세요...",
|
||||
"FOUND_NEW_ASSETS": "새로운 에셋을 발견했습니다: %s",
|
||||
"HELLO_MY_FRIEND": "안녕하세요, 친구!"
|
||||
"HELLO_MY_FRIEND": "안녕하세요, 친구!",
|
||||
"FLIGHT_MODE_OFF": "비행기 모드가 꺼져 있습니다",
|
||||
"FLIGHT_MODE_ON": "비행기 모드가 켜져 있습니다",
|
||||
"MODEM_INIT_ERROR": "모뎀 초기화 실패"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Menemui aset baharu: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Gagal memuat turun aset",
|
||||
"LOADING_ASSETS": "Memuatkan aset...",
|
||||
"HELLO_MY_FRIEND": "Hai, kawan saya!"
|
||||
"HELLO_MY_FRIEND": "Hai, kawan saya!",
|
||||
"FLIGHT_MODE_OFF": "Mod penerbangan dimatikan",
|
||||
"FLIGHT_MODE_ON": "Mod penerbangan dihidupkan",
|
||||
"MODEM_INIT_ERROR": "Modem gagal dimulakan"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Fant nye ressurser: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Nedlasting av ressurser mislyktes",
|
||||
"LOADING_ASSETS": "Laster ressurser...",
|
||||
"HELLO_MY_FRIEND": "Hei, min venn!"
|
||||
"HELLO_MY_FRIEND": "Hei, min venn!",
|
||||
"FLIGHT_MODE_OFF": "Flymodus er av",
|
||||
"FLIGHT_MODE_ON": "Flymodus er på",
|
||||
"MODEM_INIT_ERROR": "Modeminitialisering mislyktes"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Nieuwe bronnen gevonden: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Downloaden van bronnen mislukt",
|
||||
"LOADING_ASSETS": "Bronnen laden...",
|
||||
"HELLO_MY_FRIEND": "Hallo, mijn vriend!"
|
||||
"HELLO_MY_FRIEND": "Hallo, mijn vriend!",
|
||||
"FLIGHT_MODE_OFF": "Vliegtuigmodus is uitgeschakeld",
|
||||
"FLIGHT_MODE_ON": "Vliegtuigmodus is ingeschakeld",
|
||||
"MODEM_INIT_ERROR": "Modeminitialisatie mislukt"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Ładowanie zasobów...",
|
||||
"PLEASE_WAIT": "Proszę czekać...",
|
||||
"FOUND_NEW_ASSETS": "Znaleziono nowe zasoby: %s",
|
||||
"HELLO_MY_FRIEND": "Cześć, mój przyjacielu!"
|
||||
"HELLO_MY_FRIEND": "Cześć, mój przyjacielu!",
|
||||
"CONNECTION_SUCCESSFUL": "Połączenie udane",
|
||||
"FLIGHT_MODE_OFF": "Tryb samolotowy jest wyłączony",
|
||||
"FLIGHT_MODE_ON": "Tryb samolotowy jest włączony",
|
||||
"MODEM_INIT_ERROR": "Inicjalizacja modemu nie powiodła się"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "A carregar recursos...",
|
||||
"PLEASE_WAIT": "Por favor aguarde...",
|
||||
"FOUND_NEW_ASSETS": "Encontrados novos recursos: %s",
|
||||
"HELLO_MY_FRIEND": "Olá, meu amigo!"
|
||||
"HELLO_MY_FRIEND": "Olá, meu amigo!",
|
||||
"CONNECTION_SUCCESSFUL": "Ligação bem-sucedida",
|
||||
"FLIGHT_MODE_OFF": "O modo avião está desativado",
|
||||
"FLIGHT_MODE_ON": "O modo avião está ativado",
|
||||
"MODEM_INIT_ERROR": "Falha na inicialização do modem"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Se încarcă resursele...",
|
||||
"PLEASE_WAIT": "Vă rugăm să așteptați...",
|
||||
"FOUND_NEW_ASSETS": "S-au găsit resurse noi: %s",
|
||||
"HELLO_MY_FRIEND": "Salut, prietenul meu!"
|
||||
"HELLO_MY_FRIEND": "Salut, prietenul meu!",
|
||||
"CONNECTION_SUCCESSFUL": "Conexiune reușită",
|
||||
"FLIGHT_MODE_OFF": "Modul avion este dezactivat",
|
||||
"FLIGHT_MODE_ON": "Modul avion este activat",
|
||||
"MODEM_INIT_ERROR": "Inițializarea modemului a eșuat"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Загрузка ресурсов...",
|
||||
"PLEASE_WAIT": "Пожалуйста, подождите...",
|
||||
"FOUND_NEW_ASSETS": "Найдены новые ресурсы: %s",
|
||||
"HELLO_MY_FRIEND": "Привет, мой друг!"
|
||||
"HELLO_MY_FRIEND": "Привет, мой друг!",
|
||||
"CONNECTION_SUCCESSFUL": "Подключение успешно",
|
||||
"FLIGHT_MODE_OFF": "Режим полета выключен",
|
||||
"FLIGHT_MODE_ON": "Режим полета включен",
|
||||
"MODEM_INIT_ERROR": "Ошибка инициализации модема"
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Nájdené nové zdroje: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Sťahovanie zdrojov zlyhalo",
|
||||
"LOADING_ASSETS": "Načítavanie zdrojov...",
|
||||
"HELLO_MY_FRIEND": "Ahoj, môj priateľ!"
|
||||
"HELLO_MY_FRIEND": "Ahoj, môj priateľ!",
|
||||
"FLIGHT_MODE_OFF": "Letecký režim je vypnutý",
|
||||
"FLIGHT_MODE_ON": "Letecký režim je zapnutý",
|
||||
"MODEM_INIT_ERROR": "Chyba inicializácie modemu"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Najdeni novi viri: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Prenos virov ni uspel",
|
||||
"LOADING_ASSETS": "Nalaganje virov...",
|
||||
"HELLO_MY_FRIEND": "Pozdravljeni, moj prijatelj!"
|
||||
"HELLO_MY_FRIEND": "Pozdravljeni, moj prijatelj!",
|
||||
"FLIGHT_MODE_OFF": "Način leta je izklopljen",
|
||||
"FLIGHT_MODE_ON": "Način leta je vklopljen",
|
||||
"MODEM_INIT_ERROR": "Inicializacija modema ni uspela"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Пронађени нови ресурси: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Преузимање ресурса није успело",
|
||||
"LOADING_ASSETS": "Учитавање ресурса...",
|
||||
"HELLO_MY_FRIEND": "Здраво, пријатељу!"
|
||||
"HELLO_MY_FRIEND": "Здраво, пријатељу!",
|
||||
"FLIGHT_MODE_OFF": "Режим лета је искључен",
|
||||
"FLIGHT_MODE_ON": "Режим лета је укључен",
|
||||
"MODEM_INIT_ERROR": "Иницијализација модема није успела"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,7 +51,9 @@
|
||||
"FOUND_NEW_ASSETS": "Hittade nya resurser: %s",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Nedladdning av resurser misslyckades",
|
||||
"LOADING_ASSETS": "Laddar resurser...",
|
||||
"HELLO_MY_FRIEND": "Hej, min vän!"
|
||||
"HELLO_MY_FRIEND": "Hej, min vän!",
|
||||
"FLIGHT_MODE_OFF": "Flygläge är av",
|
||||
"FLIGHT_MODE_ON": "Flygläge är på",
|
||||
"MODEM_INIT_ERROR": "Modeminitiering misslyckades"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,6 +51,9 @@
|
||||
"LOADING_ASSETS": "กำลังโหลดทรัพยากร...",
|
||||
"PLEASE_WAIT": "กรุณารอสักครู่...",
|
||||
"FOUND_NEW_ASSETS": "พบทรัพยากรใหม่: %s",
|
||||
"HELLO_MY_FRIEND": "สวัสดี เพื่อนของฉัน!"
|
||||
"HELLO_MY_FRIEND": "สวัสดี เพื่อนของฉัน!",
|
||||
"FLIGHT_MODE_OFF": "โหมดเครื่องบินปิดอยู่",
|
||||
"FLIGHT_MODE_ON": "โหมดเครื่องบินเปิดอยู่",
|
||||
"MODEM_INIT_ERROR": "การเริ่มต้นโมเด็มล้มเหลว"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Varlıklar yükleniyor...",
|
||||
"PLEASE_WAIT": "Lütfen bekleyin...",
|
||||
"FOUND_NEW_ASSETS": "Yeni varlıklar bulundu: %s",
|
||||
"HELLO_MY_FRIEND": "Merhaba, arkadaşım!"
|
||||
"HELLO_MY_FRIEND": "Merhaba, arkadaşım!",
|
||||
"CONNECTION_SUCCESSFUL": "Bağlantı başarılı",
|
||||
"FLIGHT_MODE_OFF": "Uçak modu kapalı",
|
||||
"FLIGHT_MODE_ON": "Uçak modu açık",
|
||||
"MODEM_INIT_ERROR": "Modem başlatma hatası"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "Завантаження ресурсів...",
|
||||
"PLEASE_WAIT": "Будь ласка, зачекайте...",
|
||||
"FOUND_NEW_ASSETS": "Знайдено нові ресурси: %s",
|
||||
"HELLO_MY_FRIEND": "Привіт, мій друже!"
|
||||
"HELLO_MY_FRIEND": "Привіт, мій друже!",
|
||||
"CONNECTION_SUCCESSFUL": "Підключення успішне",
|
||||
"FLIGHT_MODE_OFF": "Режим польоту вимкнено",
|
||||
"FLIGHT_MODE_ON": "Режим польоту увімкнено",
|
||||
"MODEM_INIT_ERROR": "Помилка ініціалізації модему"
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,9 @@
|
||||
"LOADING_ASSETS": "Đang tải tài nguyên...",
|
||||
"PLEASE_WAIT": "Vui lòng đợi...",
|
||||
"FOUND_NEW_ASSETS": "Tìm thấy tài nguyên mới: %s",
|
||||
"HELLO_MY_FRIEND": "Xin chào, bạn của tôi!"
|
||||
"HELLO_MY_FRIEND": "Xin chào, bạn của tôi!",
|
||||
"FLIGHT_MODE_OFF": "Chế độ máy bay đang tắt",
|
||||
"FLIGHT_MODE_ON": "Chế độ máy bay đang bật",
|
||||
"MODEM_INIT_ERROR": "Khởi tạo modem thất bại"
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,9 @@
|
||||
"LOADING_ASSETS": "加载资源...",
|
||||
"PLEASE_WAIT": "请稍候...",
|
||||
"FOUND_NEW_ASSETS": "发现新资源: %s",
|
||||
"HELLO_MY_FRIEND": "你好,我的朋友!"
|
||||
"HELLO_MY_FRIEND": "你好,我的朋友!",
|
||||
"CONNECTION_SUCCESSFUL": "连接成功",
|
||||
"FLIGHT_MODE_OFF": "飞行模式已关闭",
|
||||
"FLIGHT_MODE_ON": "飞行模式已开启"
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@
|
||||
"LOADING_ASSETS": "載入資源...",
|
||||
"PLEASE_WAIT": "請稍候...",
|
||||
"FOUND_NEW_ASSETS": "發現新資源: %s",
|
||||
"HELLO_MY_FRIEND": "你好,我的朋友!"
|
||||
"HELLO_MY_FRIEND": "你好,我的朋友!",
|
||||
"CONNECTION_SUCCESSFUL": "連線成功",
|
||||
"FLIGHT_MODE_OFF": "飛航模式已關閉",
|
||||
"FLIGHT_MODE_ON": "飛航模式已開啟",
|
||||
"MODEM_INIT_ERROR": "模組初始化失敗"
|
||||
}
|
||||
}
|
||||
@@ -34,16 +34,6 @@ void AudioCodec::Start() {
|
||||
output_volume_ = 10;
|
||||
}
|
||||
|
||||
if (tx_handle_ != nullptr) {
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
}
|
||||
|
||||
if (rx_handle_ != nullptr) {
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
}
|
||||
|
||||
EnableInput(true);
|
||||
EnableOutput(true);
|
||||
ESP_LOGI(TAG, "Audio codec started");
|
||||
}
|
||||
|
||||
|
||||
+26
-105
@@ -265,32 +265,23 @@ void AudioService::AudioInputTask() {
|
||||
}
|
||||
}
|
||||
|
||||
/* Feed the wake word */
|
||||
if (bits & AS_EVENT_WAKE_WORD_RUNNING) {
|
||||
/* Feed the wake word and/or audio processor */
|
||||
if (bits & (AS_EVENT_WAKE_WORD_RUNNING | AS_EVENT_AUDIO_PROCESSOR_RUNNING)) {
|
||||
int samples = 160; // 10ms
|
||||
std::vector<int16_t> data;
|
||||
int samples = wake_word_->GetFeedSize();
|
||||
if (samples > 0) {
|
||||
if (ReadAudioData(data, 16000, samples)) {
|
||||
if (ReadAudioData(data, 16000, samples)) {
|
||||
if (bits & AS_EVENT_WAKE_WORD_RUNNING) {
|
||||
wake_word_->Feed(data);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Feed the audio processor */
|
||||
if (bits & AS_EVENT_AUDIO_PROCESSOR_RUNNING) {
|
||||
std::vector<int16_t> data;
|
||||
int samples = audio_processor_->GetFeedSize();
|
||||
if (samples > 0) {
|
||||
if (ReadAudioData(data, 16000, samples)) {
|
||||
if (bits & AS_EVENT_AUDIO_PROCESSOR_RUNNING) {
|
||||
audio_processor_->Feed(std::move(data));
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "Should not be here, bits: %lx", bits);
|
||||
break;
|
||||
// Read timeout/error should not terminate the input task.
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "Audio input task stopped");
|
||||
@@ -314,6 +305,7 @@ void AudioService::AudioOutputTask() {
|
||||
esp_timer_start_periodic(audio_power_timer_, AUDIO_POWER_CHECK_INTERVAL_MS * 1000);
|
||||
codec_->EnableOutput(true);
|
||||
}
|
||||
|
||||
codec_->OutputData(task->pcm);
|
||||
|
||||
/* Update the last output time */
|
||||
@@ -645,94 +637,20 @@ void AudioService::PlaySound(const std::string_view& ogg) {
|
||||
codec_->EnableOutput(true);
|
||||
}
|
||||
|
||||
const uint8_t* buf = reinterpret_cast<const uint8_t*>(ogg.data());
|
||||
const auto* buf = reinterpret_cast<const uint8_t*>(ogg.data());
|
||||
size_t size = ogg.size();
|
||||
size_t offset = 0;
|
||||
|
||||
auto find_page = [&](size_t start)->size_t {
|
||||
for (size_t i = start; i + 4 <= size; ++i) {
|
||||
if (buf[i] == 'O' && buf[i+1] == 'g' && buf[i+2] == 'g' && buf[i+3] == 'S') return i;
|
||||
}
|
||||
return static_cast<size_t>(-1);
|
||||
};
|
||||
|
||||
bool seen_head = false;
|
||||
bool seen_tags = false;
|
||||
int sample_rate = 16000; // 默认值
|
||||
|
||||
while (true) {
|
||||
size_t pos = find_page(offset);
|
||||
if (pos == static_cast<size_t>(-1)) break;
|
||||
offset = pos;
|
||||
if (offset + 27 > size) break;
|
||||
|
||||
const uint8_t* page = buf + offset;
|
||||
uint8_t page_segments = page[26];
|
||||
size_t seg_table_off = offset + 27;
|
||||
if (seg_table_off + page_segments > size) break;
|
||||
|
||||
size_t body_size = 0;
|
||||
for (size_t i = 0; i < page_segments; ++i) body_size += page[27 + i];
|
||||
|
||||
size_t body_off = seg_table_off + page_segments;
|
||||
if (body_off + body_size > size) break;
|
||||
|
||||
// Parse packets using lacing
|
||||
size_t cur = body_off;
|
||||
size_t seg_idx = 0;
|
||||
while (seg_idx < page_segments) {
|
||||
size_t pkt_len = 0;
|
||||
size_t pkt_start = cur;
|
||||
bool continued = false;
|
||||
do {
|
||||
uint8_t l = page[27 + seg_idx++];
|
||||
pkt_len += l;
|
||||
cur += l;
|
||||
continued = (l == 255);
|
||||
} while (continued && seg_idx < page_segments);
|
||||
|
||||
if (pkt_len == 0) continue;
|
||||
const uint8_t* pkt_ptr = buf + pkt_start;
|
||||
|
||||
if (!seen_head) {
|
||||
// 解析OpusHead包
|
||||
if (pkt_len >= 19 && std::memcmp(pkt_ptr, "OpusHead", 8) == 0) {
|
||||
seen_head = true;
|
||||
// OpusHead结构:[0-7] "OpusHead", [8] version, [9] channel_count, [10-11] pre_skip
|
||||
// [12-15] input_sample_rate, [16-17] output_gain, [18] mapping_family
|
||||
if (pkt_len >= 12) {
|
||||
uint8_t version = pkt_ptr[8];
|
||||
uint8_t channel_count = pkt_ptr[9];
|
||||
if (pkt_len >= 16) {
|
||||
// 读取输入采样率 (little-endian)
|
||||
sample_rate = pkt_ptr[12] | (pkt_ptr[13] << 8) |
|
||||
(pkt_ptr[14] << 16) | (pkt_ptr[15] << 24);
|
||||
ESP_LOGI(TAG, "OpusHead: version=%d, channels=%d, sample_rate=%d",
|
||||
version, channel_count, sample_rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!seen_tags) {
|
||||
// Expect OpusTags in second packet
|
||||
if (pkt_len >= 8 && std::memcmp(pkt_ptr, "OpusTags", 8) == 0) {
|
||||
seen_tags = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Audio packet (Opus)
|
||||
auto packet = std::make_unique<AudioStreamPacket>();
|
||||
packet->sample_rate = sample_rate;
|
||||
packet->frame_duration = 60;
|
||||
packet->payload.resize(pkt_len);
|
||||
std::memcpy(packet->payload.data(), pkt_ptr, pkt_len);
|
||||
PushPacketToDecodeQueue(std::move(packet), true);
|
||||
}
|
||||
|
||||
offset = body_off + body_size;
|
||||
}
|
||||
auto demuxer = std::make_unique<OggDemuxer>();
|
||||
demuxer->OnDemuxerFinished([this](const uint8_t* data, int sample_rate, size_t size){
|
||||
auto packet = std::make_unique<AudioStreamPacket>();
|
||||
packet->sample_rate = sample_rate;
|
||||
packet->frame_duration = 60;
|
||||
packet->payload.resize(size);
|
||||
std::memcpy(packet->payload.data(), data, size);
|
||||
PushPacketToDecodeQueue(std::move(packet), true);
|
||||
});
|
||||
demuxer->Reset();
|
||||
demuxer->Process(buf, size);
|
||||
}
|
||||
|
||||
bool AudioService::IsIdle() {
|
||||
@@ -769,7 +687,10 @@ void AudioService::CheckAndUpdateAudioPowerState() {
|
||||
codec_->EnableInput(false);
|
||||
}
|
||||
if (output_elapsed > AUDIO_POWER_TIMEOUT_MS && codec_->output_enabled()) {
|
||||
codec_->EnableOutput(false);
|
||||
// Keep TX clock when duplex RX is active; otherwise RX may stall on some boards.
|
||||
if (!(codec_->duplex() && codec_->input_enabled())) {
|
||||
codec_->EnableOutput(false);
|
||||
}
|
||||
}
|
||||
if (!codec_->input_enabled() && !codec_->output_enabled()) {
|
||||
esp_timer_stop(audio_power_timer_);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "processors/audio_debugger.h"
|
||||
#include "wake_word.h"
|
||||
#include "protocol.h"
|
||||
|
||||
#include "ogg_demuxer.h"
|
||||
|
||||
/*
|
||||
* There are two types of audio data flow:
|
||||
|
||||
@@ -176,6 +176,8 @@ void BoxAudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gpio_
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_handle_, &tdm_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
|
||||
@@ -150,11 +150,16 @@ void Es8311AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gp
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
void Es8311AudioCodec::SetOutputVolume(int volume) {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(dev_, volume));
|
||||
std::lock_guard<std::mutex> lock(data_if_mutex_);
|
||||
if (dev_ != nullptr) {
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(dev_, volume));
|
||||
}
|
||||
AudioCodec::SetOutputVolume(volume);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@ void Es8374AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gp
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,8 @@ void Es8388AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gp
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
@@ -186,9 +188,6 @@ void Es8388AudioCodec::EnableOutput(bool enable) {
|
||||
|
||||
// Set analog output volume to 0dB, default is -45dB
|
||||
uint8_t reg_val = 30; // 0dB
|
||||
if(input_reference_){
|
||||
reg_val = 27;
|
||||
}
|
||||
uint8_t regs[] = { 46, 47, 48, 49 }; // HP_LVOL, HP_RVOL, SPK_LVOL, SPK_RVOL
|
||||
for (uint8_t reg : regs) {
|
||||
ctrl_if_->write_reg(ctrl_if_, reg, 1, ®_val, 1);
|
||||
|
||||
@@ -132,6 +132,8 @@ void Es8389AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gp
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
|
||||
@@ -239,10 +239,10 @@ int NoAudioCodec::Write(const int16_t* data, int samples) {
|
||||
|
||||
int NoAudioCodec::Read(int16_t* dest, int samples) {
|
||||
size_t bytes_read;
|
||||
constexpr TickType_t kReadTimeoutTicks = pdMS_TO_TICKS(200);
|
||||
|
||||
std::vector<int32_t> bit32_buffer(samples);
|
||||
if (i2s_channel_read(rx_handle_, bit32_buffer.data(), samples * sizeof(int32_t), &bytes_read, portMAX_DELAY) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Read Failed!");
|
||||
if (i2s_channel_read(rx_handle_, bit32_buffer.data(), samples * sizeof(int32_t), &bytes_read, kReadTimeoutTicks) != ESP_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -254,6 +254,32 @@ int NoAudioCodec::Read(int16_t* dest, int samples) {
|
||||
return samples;
|
||||
}
|
||||
|
||||
void NoAudioCodec::EnableInput(bool enable) {
|
||||
std::lock_guard<std::mutex> lock(data_if_mutex_);
|
||||
if (enable == input_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (enable) {
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(i2s_channel_disable(rx_handle_));
|
||||
}
|
||||
AudioCodec::EnableInput(enable);
|
||||
}
|
||||
|
||||
void NoAudioCodec::EnableOutput(bool enable) {
|
||||
std::lock_guard<std::mutex> lock(data_if_mutex_);
|
||||
if (enable == output_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (enable) {
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(i2s_channel_disable(tx_handle_));
|
||||
}
|
||||
AudioCodec::EnableOutput(enable);
|
||||
}
|
||||
|
||||
// Delegating constructor: calls the main constructor with default slot mask
|
||||
NoAudioCodecSimplexPdm::NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_din)
|
||||
: NoAudioCodecSimplexPdm(input_sample_rate, output_sample_rate, spk_bclk, spk_ws, spk_dout, I2S_STD_SLOT_LEFT, mic_sck, mic_din) {
|
||||
|
||||
@@ -13,6 +13,8 @@ protected:
|
||||
|
||||
virtual int Write(const int16_t* data, int samples) override;
|
||||
virtual int Read(int16_t* dest, int samples) override;
|
||||
virtual void EnableInput(bool enable) override;
|
||||
virtual void EnableOutput(bool enable) override;
|
||||
|
||||
public:
|
||||
virtual ~NoAudioCodec();
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
#include "ogg_demuxer.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TAG "OggDemuxer"
|
||||
|
||||
/// @brief 重置解封器
|
||||
void OggDemuxer::Reset()
|
||||
{
|
||||
opus_info_ = {
|
||||
.head_seen = false,
|
||||
.tags_seen = false,
|
||||
.sample_rate = 48000
|
||||
};
|
||||
|
||||
state_ = ParseState::FIND_PAGE;
|
||||
ctx_.packet_len = 0;
|
||||
ctx_.seg_count = 0;
|
||||
ctx_.seg_index = 0;
|
||||
ctx_.data_offset = 0;
|
||||
ctx_.bytes_needed = 4; // 需要4字节"OggS"
|
||||
ctx_.seg_remaining = 0;
|
||||
ctx_.body_size = 0;
|
||||
ctx_.body_offset = 0;
|
||||
ctx_.packet_continued = false;
|
||||
|
||||
// 清空缓冲区数据
|
||||
memset(ctx_.header, 0, sizeof(ctx_.header));
|
||||
memset(ctx_.seg_table, 0, sizeof(ctx_.seg_table));
|
||||
memset(ctx_.packet_buf, 0, sizeof(ctx_.packet_buf));
|
||||
}
|
||||
|
||||
/// @brief 处理数据块
|
||||
/// @param data 输入数据
|
||||
/// @param size 输入数据大小
|
||||
/// @return 已处理的字节数
|
||||
size_t OggDemuxer::Process(const uint8_t* data, size_t size)
|
||||
{
|
||||
size_t processed = 0; // 已处理的字节数
|
||||
|
||||
while (processed < size) {
|
||||
switch (state_) {
|
||||
case ParseState::FIND_PAGE: {
|
||||
// 寻找页头"OggS"
|
||||
if (ctx_.bytes_needed < 4) {
|
||||
// 处理不完整的"OggS"匹配(跨数据块)
|
||||
size_t to_copy = std::min(size - processed, ctx_.bytes_needed);
|
||||
memcpy(ctx_.header + (4 - ctx_.bytes_needed), data + processed, to_copy);
|
||||
|
||||
processed += to_copy;
|
||||
ctx_.bytes_needed -= to_copy;
|
||||
|
||||
if (ctx_.bytes_needed == 0) {
|
||||
// 检查是否匹配"OggS"
|
||||
if (memcmp(ctx_.header, "OggS", 4) == 0) {
|
||||
state_ = ParseState::PARSE_HEADER;
|
||||
ctx_.data_offset = 4;
|
||||
ctx_.bytes_needed = 27 - 4; // 还需要23字节完成页头
|
||||
} else {
|
||||
// 匹配失败,滑动1字节继续匹配
|
||||
memmove(ctx_.header, ctx_.header + 1, 3);
|
||||
ctx_.bytes_needed = 1;
|
||||
}
|
||||
} else {
|
||||
// 数据不足,等待更多数据
|
||||
return processed;
|
||||
}
|
||||
} else if (ctx_.bytes_needed == 4) {
|
||||
// 在数据块中查找完整的"OggS"
|
||||
bool found = false;
|
||||
size_t i = 0;
|
||||
size_t remaining = size - processed;
|
||||
|
||||
// 搜索"OggS"
|
||||
for (; i + 4 <= remaining; i++) {
|
||||
if (memcmp(data + processed + i, "OggS", 4) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
// 找到"OggS",跳过已搜索的字节
|
||||
processed += i;
|
||||
|
||||
// 不记录找到的"OggS",无必要
|
||||
// memcpy(ctx_.header, data + processed, 4);
|
||||
processed += 4;
|
||||
|
||||
state_ = ParseState::PARSE_HEADER;
|
||||
ctx_.data_offset = 4;
|
||||
ctx_.bytes_needed = 27 - 4; // 还需要23字节
|
||||
} else {
|
||||
// 没有找到完整"OggS",保存可能的部分匹配
|
||||
size_t partial_len = remaining - i;
|
||||
if (partial_len > 0) {
|
||||
memcpy(ctx_.header, data + processed + i, partial_len);
|
||||
ctx_.bytes_needed = 4 - partial_len;
|
||||
processed += i + partial_len;
|
||||
} else {
|
||||
processed += i; // 已搜索所有字节
|
||||
}
|
||||
return processed; // 返回已处理的字节数
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "OggDemuxer run in error state: bytes_needed=%zu", ctx_.bytes_needed);
|
||||
Reset();
|
||||
return processed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ParseState::PARSE_HEADER: {
|
||||
size_t available = size - processed;
|
||||
|
||||
if (available < ctx_.bytes_needed) {
|
||||
// 数据不足,复制可用的部分
|
||||
memcpy(ctx_.header + ctx_.data_offset,
|
||||
data + processed, available);
|
||||
|
||||
ctx_.data_offset += available;
|
||||
ctx_.bytes_needed -= available;
|
||||
processed += available;
|
||||
return processed; // 等待更多数据
|
||||
} else {
|
||||
// 有足够的数据完成页头
|
||||
size_t to_copy = ctx_.bytes_needed;
|
||||
memcpy(ctx_.header + ctx_.data_offset,
|
||||
data + processed, to_copy);
|
||||
|
||||
processed += to_copy;
|
||||
ctx_.data_offset += to_copy;
|
||||
ctx_.bytes_needed = 0;
|
||||
|
||||
// 验证页头
|
||||
if (ctx_.header[4] != 0) {
|
||||
ESP_LOGE(TAG, "无效的Ogg版本: %d", ctx_.header[4]);
|
||||
state_ = ParseState::FIND_PAGE;
|
||||
ctx_.bytes_needed = 4;
|
||||
ctx_.data_offset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ctx_.seg_count = ctx_.header[26];
|
||||
if (ctx_.seg_count > 0 && ctx_.seg_count <= 255) {
|
||||
state_ = ParseState::PARSE_SEGMENTS;
|
||||
ctx_.bytes_needed = ctx_.seg_count;
|
||||
ctx_.data_offset = 0;
|
||||
} else if (ctx_.seg_count == 0) {
|
||||
// 没有段,直接跳到下一个页面
|
||||
state_ = ParseState::FIND_PAGE;
|
||||
ctx_.bytes_needed = 4;
|
||||
ctx_.data_offset = 0;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "无效的段数: %u", ctx_.seg_count);
|
||||
state_ = ParseState::FIND_PAGE;
|
||||
ctx_.bytes_needed = 4;
|
||||
ctx_.data_offset = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ParseState::PARSE_SEGMENTS: {
|
||||
size_t available = size - processed;
|
||||
|
||||
if (available < ctx_.bytes_needed) {
|
||||
memcpy(ctx_.seg_table + ctx_.data_offset,
|
||||
data + processed, available);
|
||||
|
||||
ctx_.data_offset += available;
|
||||
ctx_.bytes_needed -= available;
|
||||
processed += available;
|
||||
return processed; // 等待更多数据
|
||||
} else {
|
||||
size_t to_copy = ctx_.bytes_needed;
|
||||
memcpy(ctx_.seg_table + ctx_.data_offset,
|
||||
data + processed, to_copy);
|
||||
|
||||
processed += to_copy;
|
||||
ctx_.data_offset += to_copy;
|
||||
ctx_.bytes_needed = 0;
|
||||
|
||||
state_ = ParseState::PARSE_DATA;
|
||||
ctx_.seg_index = 0;
|
||||
ctx_.data_offset = 0;
|
||||
|
||||
// 计算数据体总大小
|
||||
ctx_.body_size = 0;
|
||||
for (size_t i = 0; i < ctx_.seg_count; ++i) {
|
||||
ctx_.body_size += ctx_.seg_table[i];
|
||||
}
|
||||
ctx_.body_offset = 0;
|
||||
ctx_.seg_remaining = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ParseState::PARSE_DATA: {
|
||||
while (ctx_.seg_index < ctx_.seg_count && processed < size) {
|
||||
uint8_t seg_len = ctx_.seg_table[ctx_.seg_index];
|
||||
|
||||
// 检查段数据是否已经部分读取
|
||||
if (ctx_.seg_remaining > 0) {
|
||||
seg_len = ctx_.seg_remaining;
|
||||
} else {
|
||||
ctx_.seg_remaining = seg_len;
|
||||
}
|
||||
|
||||
// 检查缓冲区是否足够
|
||||
if (ctx_.packet_len + seg_len > sizeof(ctx_.packet_buf)) {
|
||||
ESP_LOGE(TAG, "包缓冲区溢出: %zu + %u > %zu", ctx_.packet_len, seg_len, sizeof(ctx_.packet_buf));
|
||||
state_ = ParseState::FIND_PAGE;
|
||||
ctx_.packet_len = 0;
|
||||
ctx_.packet_continued = false;
|
||||
ctx_.seg_remaining = 0;
|
||||
ctx_.bytes_needed = 4;
|
||||
return processed;
|
||||
}
|
||||
|
||||
// 复制数据
|
||||
size_t to_copy = std::min(size - processed, (size_t)seg_len);
|
||||
memcpy(ctx_.packet_buf + ctx_.packet_len, data + processed, to_copy);
|
||||
|
||||
processed += to_copy;
|
||||
ctx_.packet_len += to_copy;
|
||||
ctx_.body_offset += to_copy;
|
||||
ctx_.seg_remaining -= to_copy;
|
||||
|
||||
// 检查段是否完整
|
||||
if (ctx_.seg_remaining > 0) {
|
||||
// 段不完整,等待更多数据
|
||||
return processed;
|
||||
}
|
||||
|
||||
// 段完整
|
||||
bool seg_continued = (ctx_.seg_table[ctx_.seg_index] == 255);
|
||||
|
||||
if (!seg_continued) {
|
||||
// 包结束
|
||||
if (ctx_.packet_len) {
|
||||
if (!opus_info_.head_seen) {
|
||||
if (ctx_.packet_len >=8 && memcmp(ctx_.packet_buf, "OpusHead", 8) == 0) {
|
||||
opus_info_.head_seen = true;
|
||||
if (ctx_.packet_len >= 19) {
|
||||
opus_info_.sample_rate = ctx_.packet_buf[12] |
|
||||
(ctx_.packet_buf[13] << 8) |
|
||||
(ctx_.packet_buf[14] << 16) |
|
||||
(ctx_.packet_buf[15] << 24);
|
||||
ESP_LOGD(TAG, "OpusHead found, sample_rate=%d", opus_info_.sample_rate);
|
||||
}
|
||||
ctx_.packet_len = 0;
|
||||
ctx_.packet_continued = false;
|
||||
ctx_.seg_index++;
|
||||
ctx_.seg_remaining = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!opus_info_.tags_seen) {
|
||||
if (ctx_.packet_len >= 8 && memcmp(ctx_.packet_buf, "OpusTags", 8) == 0) {
|
||||
opus_info_.tags_seen = true;
|
||||
ESP_LOGD(TAG, "OpusTags found.");
|
||||
ctx_.packet_len = 0;
|
||||
ctx_.packet_continued = false;
|
||||
ctx_.seg_index++;
|
||||
ctx_.seg_remaining = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (opus_info_.head_seen && opus_info_.tags_seen) {
|
||||
if (on_demuxer_finished_) {
|
||||
on_demuxer_finished_(ctx_.packet_buf, opus_info_.sample_rate, ctx_.packet_len);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "当前Ogg容器未解析到OpusHead/OpusTags,丢弃");
|
||||
}
|
||||
}
|
||||
ctx_.packet_len = 0;
|
||||
ctx_.packet_continued = false;
|
||||
} else {
|
||||
ctx_.packet_continued = true;
|
||||
}
|
||||
|
||||
ctx_.seg_index++;
|
||||
ctx_.seg_remaining = 0;
|
||||
}
|
||||
|
||||
if (ctx_.seg_index == ctx_.seg_count) {
|
||||
// 检查是否所有数据体都已读取
|
||||
if (ctx_.body_offset < ctx_.body_size) {
|
||||
ESP_LOGW(TAG, "数据体不完整: %zu/%zu",
|
||||
ctx_.body_offset, ctx_.body_size);
|
||||
}
|
||||
|
||||
// 如果包跨页,保持packet_len和packet_continued
|
||||
if (!ctx_.packet_continued) {
|
||||
ctx_.packet_len = 0;
|
||||
}
|
||||
|
||||
// 进入下一页面
|
||||
state_ = ParseState::FIND_PAGE;
|
||||
ctx_.bytes_needed = 4;
|
||||
ctx_.data_offset = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef OGG_DEMUXER_H_
|
||||
#define OGG_DEMUXER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
class OggDemuxer {
|
||||
private:
|
||||
enum ParseState : int8_t {
|
||||
FIND_PAGE,
|
||||
PARSE_HEADER,
|
||||
PARSE_SEGMENTS,
|
||||
PARSE_DATA
|
||||
};
|
||||
|
||||
struct Opus_t {
|
||||
bool head_seen{false};
|
||||
bool tags_seen{false};
|
||||
int sample_rate{48000};
|
||||
};
|
||||
|
||||
|
||||
// 使用固定大小的缓冲区避免动态分配
|
||||
struct context_t {
|
||||
bool packet_continued{false}; // 当前包是否跨多个段
|
||||
uint8_t header[27]; // Ogg页头
|
||||
uint8_t seg_table[255]; // 当前存储的段表
|
||||
uint8_t packet_buf[8192]; // 8KB包缓冲区
|
||||
size_t packet_len = 0; // 缓冲区中累计的数据长度
|
||||
size_t seg_count = 0; // 当前页段数
|
||||
size_t seg_index = 0; // 当前处理的段索引
|
||||
size_t data_offset = 0; // 解析当前阶段已读取的字节数
|
||||
size_t bytes_needed = 0; // 解析当前字段还需要读取的字节数
|
||||
size_t seg_remaining = 0; // 当前段剩余需要读取的字节数
|
||||
size_t body_size = 0; // 数据体总大小
|
||||
size_t body_offset = 0; // 数据体已读取的字节数
|
||||
};
|
||||
|
||||
public:
|
||||
OggDemuxer() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Reset();
|
||||
|
||||
size_t Process(const uint8_t* data, size_t size);
|
||||
|
||||
/// @brief 设置解封装完毕后回调处理函数
|
||||
/// @param on_demuxer_finished
|
||||
void OnDemuxerFinished(std::function<void(const uint8_t* data, int sample_rate, size_t len)> on_demuxer_finished) {
|
||||
on_demuxer_finished_ = on_demuxer_finished;
|
||||
}
|
||||
private:
|
||||
|
||||
ParseState state_ = ParseState::FIND_PAGE;
|
||||
context_t ctx_;
|
||||
Opus_t opus_info_;
|
||||
std::function<void(const uint8_t*, int, size_t)> on_demuxer_finished_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -92,7 +92,18 @@ void AfeAudioProcessor::Feed(std::vector<int16_t>&& data) {
|
||||
if (afe_data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
afe_iface_->feed(afe_data_, data.data());
|
||||
|
||||
std::lock_guard<std::mutex> lock(input_buffer_mutex_);
|
||||
// Check running state inside lock to avoid TOCTOU race with Stop()
|
||||
if (!IsRunning()) {
|
||||
return;
|
||||
}
|
||||
input_buffer_.insert(input_buffer_.end(), data.begin(), data.end());
|
||||
size_t chunk_size = afe_iface_->get_feed_chunksize(afe_data_) * codec_->input_channels();
|
||||
while (input_buffer_.size() >= chunk_size) {
|
||||
afe_iface_->feed(afe_data_, input_buffer_.data());
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + chunk_size);
|
||||
}
|
||||
}
|
||||
|
||||
void AfeAudioProcessor::Start() {
|
||||
@@ -101,9 +112,12 @@ void AfeAudioProcessor::Start() {
|
||||
|
||||
void AfeAudioProcessor::Stop() {
|
||||
xEventGroupClearBits(event_group_, PROCESSOR_RUNNING);
|
||||
|
||||
std::lock_guard<std::mutex> lock(input_buffer_mutex_);
|
||||
if (afe_data_ != nullptr) {
|
||||
afe_iface_->reset_buffer(afe_data_);
|
||||
}
|
||||
input_buffer_.clear();
|
||||
}
|
||||
|
||||
bool AfeAudioProcessor::IsRunning() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
#include "audio_processor.h"
|
||||
#include "audio_codec.h"
|
||||
@@ -37,6 +38,8 @@ private:
|
||||
AudioCodec* codec_ = nullptr;
|
||||
int frame_samples_ = 0;
|
||||
bool is_speaking_ = false;
|
||||
std::vector<int16_t> input_buffer_;
|
||||
std::mutex input_buffer_mutex_;
|
||||
std::vector<int16_t> output_buffer_;
|
||||
|
||||
void AudioProcessorTask();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
void NoAudioProcessor::Initialize(AudioCodec* codec, int frame_duration_ms, srmodel_list_t* models_list) {
|
||||
codec_ = codec;
|
||||
frame_samples_ = frame_duration_ms * 16000 / 1000;
|
||||
output_buffer_.reserve(frame_samples_);
|
||||
}
|
||||
|
||||
void NoAudioProcessor::Feed(std::vector<int16_t>&& data) {
|
||||
@@ -13,15 +14,25 @@ void NoAudioProcessor::Feed(std::vector<int16_t>&& data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert stereo to mono if needed
|
||||
if (codec_->input_channels() == 2) {
|
||||
// If input channels is 2, we need to fetch the left channel data
|
||||
auto mono_data = std::vector<int16_t>(data.size() / 2);
|
||||
for (size_t i = 0, j = 0; i < mono_data.size(); ++i, j += 2) {
|
||||
mono_data[i] = data[j];
|
||||
for (size_t i = 0, j = 0; i < data.size() / 2; ++i, j += 2) {
|
||||
output_buffer_.push_back(data[j]);
|
||||
}
|
||||
output_callback_(std::move(mono_data));
|
||||
} else {
|
||||
output_callback_(std::move(data));
|
||||
output_buffer_.insert(output_buffer_.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
// Output complete frames when buffer has enough data
|
||||
while (output_buffer_.size() >= (size_t)frame_samples_) {
|
||||
if (output_buffer_.size() == (size_t)frame_samples_) {
|
||||
output_callback_(std::move(output_buffer_));
|
||||
output_buffer_.clear();
|
||||
output_buffer_.reserve(frame_samples_);
|
||||
} else {
|
||||
output_callback_(std::vector<int16_t>(output_buffer_.begin(), output_buffer_.begin() + frame_samples_));
|
||||
output_buffer_.erase(output_buffer_.begin(), output_buffer_.begin() + frame_samples_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +42,7 @@ void NoAudioProcessor::Start() {
|
||||
|
||||
void NoAudioProcessor::Stop() {
|
||||
is_running_ = false;
|
||||
output_buffer_.clear();
|
||||
}
|
||||
|
||||
bool NoAudioProcessor::IsRunning() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
|
||||
#include "audio_processor.h"
|
||||
#include "audio_codec.h"
|
||||
@@ -25,9 +26,10 @@ public:
|
||||
private:
|
||||
AudioCodec* codec_ = nullptr;
|
||||
int frame_samples_ = 0;
|
||||
std::vector<int16_t> output_buffer_;
|
||||
std::function<void(std::vector<int16_t>&& data)> output_callback_;
|
||||
std::function<void(bool speaking)> vad_state_change_callback_;
|
||||
bool is_running_ = false;
|
||||
std::atomic<bool> is_running_ = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -99,16 +99,30 @@ void AfeWakeWord::Start() {
|
||||
|
||||
void AfeWakeWord::Stop() {
|
||||
xEventGroupClearBits(event_group_, DETECTION_RUNNING_EVENT);
|
||||
|
||||
std::lock_guard<std::mutex> lock(input_buffer_mutex_);
|
||||
if (afe_data_ != nullptr) {
|
||||
afe_iface_->reset_buffer(afe_data_);
|
||||
}
|
||||
input_buffer_.clear();
|
||||
}
|
||||
|
||||
void AfeWakeWord::Feed(const std::vector<int16_t>& data) {
|
||||
if (afe_data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
afe_iface_->feed(afe_data_, data.data());
|
||||
|
||||
std::lock_guard<std::mutex> lock(input_buffer_mutex_);
|
||||
// Check running state inside lock to avoid TOCTOU race with Stop()
|
||||
if (!(xEventGroupGetBits(event_group_) & DETECTION_RUNNING_EVENT)) {
|
||||
return;
|
||||
}
|
||||
input_buffer_.insert(input_buffer_.end(), data.begin(), data.end());
|
||||
size_t chunk_size = afe_iface_->get_feed_chunksize(afe_data_) * codec_->input_channels();
|
||||
while (input_buffer_.size() >= chunk_size) {
|
||||
afe_iface_->feed(afe_data_, input_buffer_.data());
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + chunk_size);
|
||||
}
|
||||
}
|
||||
|
||||
size_t AfeWakeWord::GetFeedSize() {
|
||||
|
||||
@@ -44,6 +44,8 @@ private:
|
||||
std::function<void(const std::string& wake_word)> wake_word_detected_callback_;
|
||||
AudioCodec* codec_ = nullptr;
|
||||
std::string last_detected_wake_word_;
|
||||
std::vector<int16_t> input_buffer_;
|
||||
std::mutex input_buffer_mutex_;
|
||||
|
||||
TaskHandle_t wake_word_encode_task_ = nullptr;
|
||||
StaticTask_t* wake_word_encode_task_buffer_ = nullptr;
|
||||
|
||||
@@ -138,49 +138,64 @@ void CustomWakeWord::Start() {
|
||||
|
||||
void CustomWakeWord::Stop() {
|
||||
running_ = false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(input_buffer_mutex_);
|
||||
input_buffer_.clear();
|
||||
}
|
||||
|
||||
void CustomWakeWord::Feed(const std::vector<int16_t>& data) {
|
||||
if (multinet_model_data_ == nullptr || !running_) {
|
||||
if (multinet_model_data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(input_buffer_mutex_);
|
||||
// Check running state inside lock to avoid TOCTOU race with Stop()
|
||||
if (!running_) {
|
||||
return;
|
||||
}
|
||||
|
||||
esp_mn_state_t mn_state;
|
||||
// If input channels is 2, we need to fetch the left channel data
|
||||
if (codec_->input_channels() == 2) {
|
||||
auto mono_data = std::vector<int16_t>(data.size() / 2);
|
||||
for (size_t i = 0, j = 0; i < mono_data.size(); ++i, j += 2) {
|
||||
mono_data[i] = data[j];
|
||||
for (size_t i = 0; i < data.size(); i += 2) {
|
||||
input_buffer_.push_back(data[i]);
|
||||
}
|
||||
|
||||
StoreWakeWordData(mono_data);
|
||||
mn_state = multinet_->detect(multinet_model_data_, const_cast<int16_t*>(mono_data.data()));
|
||||
} else {
|
||||
StoreWakeWordData(data);
|
||||
mn_state = multinet_->detect(multinet_model_data_, const_cast<int16_t*>(data.data()));
|
||||
input_buffer_.insert(input_buffer_.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
if (mn_state == ESP_MN_STATE_DETECTING) {
|
||||
return;
|
||||
} else if (mn_state == ESP_MN_STATE_DETECTED) {
|
||||
esp_mn_results_t *mn_result = multinet_->get_results(multinet_model_data_);
|
||||
for (int i = 0; i < mn_result->num && running_; i++) {
|
||||
ESP_LOGI(TAG, "Custom wake word detected: command_id=%d, string=%s, prob=%f",
|
||||
mn_result->command_id[i], mn_result->string, mn_result->prob[i]);
|
||||
auto& command = commands_[mn_result->command_id[i] - 1];
|
||||
if (command.action == "wake") {
|
||||
last_detected_wake_word_ = command.text;
|
||||
running_ = false;
|
||||
|
||||
if (wake_word_detected_callback_) {
|
||||
wake_word_detected_callback_(last_detected_wake_word_);
|
||||
int chunksize = multinet_->get_samp_chunksize(multinet_model_data_);
|
||||
while (input_buffer_.size() >= chunksize) {
|
||||
std::vector<int16_t> chunk(input_buffer_.begin(), input_buffer_.begin() + chunksize);
|
||||
StoreWakeWordData(chunk);
|
||||
|
||||
esp_mn_state_t mn_state = multinet_->detect(multinet_model_data_, chunk.data());
|
||||
|
||||
if (mn_state == ESP_MN_STATE_DETECTED) {
|
||||
esp_mn_results_t *mn_result = multinet_->get_results(multinet_model_data_);
|
||||
for (int i = 0; i < mn_result->num && running_; i++) {
|
||||
ESP_LOGI(TAG, "Custom wake word detected: command_id=%d, string=%s, prob=%f",
|
||||
mn_result->command_id[i], mn_result->string, mn_result->prob[i]);
|
||||
auto& command = commands_[mn_result->command_id[i] - 1];
|
||||
if (command.action == "wake") {
|
||||
last_detected_wake_word_ = command.text;
|
||||
running_ = false;
|
||||
input_buffer_.clear();
|
||||
|
||||
if (wake_word_detected_callback_) {
|
||||
wake_word_detected_callback_(last_detected_wake_word_);
|
||||
}
|
||||
}
|
||||
}
|
||||
multinet_->clean(multinet_model_data_);
|
||||
} else if (mn_state == ESP_MN_STATE_TIMEOUT) {
|
||||
ESP_LOGD(TAG, "Command word detection timeout, cleaning state");
|
||||
multinet_->clean(multinet_model_data_);
|
||||
}
|
||||
multinet_->clean(multinet_model_data_);
|
||||
} else if (mn_state == ESP_MN_STATE_TIMEOUT) {
|
||||
ESP_LOGD(TAG, "Command word detection timeout, cleaning state");
|
||||
multinet_->clean(multinet_model_data_);
|
||||
|
||||
if (!running_) {
|
||||
break;
|
||||
}
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + chunksize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ private:
|
||||
AudioCodec* codec_ = nullptr;
|
||||
std::string last_detected_wake_word_;
|
||||
std::atomic<bool> running_ = false;
|
||||
std::vector<int16_t> input_buffer_;
|
||||
std::mutex input_buffer_mutex_;
|
||||
|
||||
TaskHandle_t wake_word_encode_task_ = nullptr;
|
||||
StaticTask_t* wake_word_encode_task_buffer_ = nullptr;
|
||||
|
||||
@@ -54,21 +54,44 @@ void EspWakeWord::Start() {
|
||||
|
||||
void EspWakeWord::Stop() {
|
||||
running_ = false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(input_buffer_mutex_);
|
||||
input_buffer_.clear();
|
||||
}
|
||||
|
||||
void EspWakeWord::Feed(const std::vector<int16_t>& data) {
|
||||
if (wakenet_data_ == nullptr || !running_) {
|
||||
if (wakenet_data_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
int res = wakenet_iface_->detect(wakenet_data_, (int16_t *)data.data());
|
||||
if (res > 0) {
|
||||
last_detected_wake_word_ = wakenet_iface_->get_word_name(wakenet_data_, res);
|
||||
running_ = false;
|
||||
std::lock_guard<std::mutex> lock(input_buffer_mutex_);
|
||||
// Check running state inside lock to avoid TOCTOU race with Stop()
|
||||
if (!running_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wake_word_detected_callback_) {
|
||||
wake_word_detected_callback_(last_detected_wake_word_);
|
||||
if (codec_->input_channels() == 2) {
|
||||
for (size_t i = 0; i < data.size(); i += 2) {
|
||||
input_buffer_.push_back(data[i]);
|
||||
}
|
||||
} else {
|
||||
input_buffer_.insert(input_buffer_.end(), data.begin(), data.end());
|
||||
}
|
||||
|
||||
int chunksize = wakenet_iface_->get_samp_chunksize(wakenet_data_);
|
||||
while (input_buffer_.size() >= chunksize) {
|
||||
int res = wakenet_iface_->detect(wakenet_data_, input_buffer_.data());
|
||||
if (res > 0) {
|
||||
last_detected_wake_word_ = wakenet_iface_->get_word_name(wakenet_data_, res);
|
||||
running_ = false;
|
||||
input_buffer_.clear();
|
||||
|
||||
if (wake_word_detected_callback_) {
|
||||
wake_word_detected_callback_(last_detected_wake_word_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + chunksize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include "audio_codec.h"
|
||||
#include "wake_word.h"
|
||||
@@ -37,6 +38,8 @@ private:
|
||||
|
||||
std::function<void(const std::string& wake_word)> wake_word_detected_callback_;
|
||||
std::string last_detected_wake_word_;
|
||||
std::vector<int16_t> input_buffer_;
|
||||
std::mutex input_buffer_mutex_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -106,6 +106,10 @@ private:
|
||||
InitializeGc9107Display();
|
||||
InitializeButtons();
|
||||
GetBacklight()->SetBrightness(100);
|
||||
|
||||
// Ensure UI is set up before displaying error
|
||||
display_->SetupUI();
|
||||
|
||||
display_->SetStatus(Lang::Strings::ERROR);
|
||||
display_->SetEmotion("triangle_exclamation");
|
||||
display_->SetChatMessage("system", "Echo Base\nnot connected");
|
||||
|
||||
@@ -173,6 +173,10 @@ private:
|
||||
InitializeGc9107Display();
|
||||
InitializeButtons();
|
||||
GetBacklight()->SetBrightness(100);
|
||||
|
||||
// Ensure UI is set up before displaying error
|
||||
display_->SetupUI();
|
||||
|
||||
display_->SetStatus(Lang::Strings::ERROR);
|
||||
display_->SetEmotion("triangle_exclamation");
|
||||
display_->SetChatMessage("system", "Echo Base\nnot connected");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "dual_network_board.h"
|
||||
#include "wifi_board.h"
|
||||
#include "codecs/no_audio_codec.h"
|
||||
#include "display/lcd_display.h"
|
||||
#include "system_reset.h"
|
||||
@@ -57,7 +57,7 @@ static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
|
||||
|
||||
#define TAG "ESP32-LCD-MarsbearSupport"
|
||||
|
||||
class CompactWifiBoardLCD : public DualNetworkBoard {
|
||||
class CompactWifiBoardLCD : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Button touch_button_;
|
||||
@@ -136,26 +136,14 @@ private:
|
||||
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (GetNetworkType() == NetworkType::WIFI) {
|
||||
if (app.GetDeviceState() == kDeviceStateStarting) {
|
||||
// cast to WifiBoard
|
||||
auto& wifi_board = static_cast<WifiBoard&>(GetCurrentBoard());
|
||||
wifi_board.EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
if (app.GetDeviceState() == kDeviceStateStarting) {
|
||||
EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
gpio_set_level(BUILTIN_LED_GPIO, 1);
|
||||
app.ToggleChatState();
|
||||
});
|
||||
|
||||
|
||||
boot_button_.OnDoubleClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) {
|
||||
SwitchNetworkType();
|
||||
}
|
||||
});
|
||||
|
||||
asr_button_.OnClick([this]() {
|
||||
std::string wake_word="你好小智";
|
||||
Application::GetInstance().WakeWordInvoke(wake_word);
|
||||
@@ -174,8 +162,7 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
CompactWifiBoardLCD() :
|
||||
DualNetworkBoard(ML307_TX_PIN, ML307_RX_PIN),
|
||||
CompactWifiBoardLCD() : WifiBoard(),
|
||||
boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO) {
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "dual_network_board.h"
|
||||
#include "wifi_board.h"
|
||||
#include "codecs/no_audio_codec.h"
|
||||
#include "system_reset.h"
|
||||
#include "application.h"
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#define TAG "ESP32-MarsbearSupport"
|
||||
|
||||
class CompactWifiBoard : public DualNetworkBoard {
|
||||
class CompactWifiBoard : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Button touch_button_;
|
||||
@@ -104,26 +104,14 @@ private:
|
||||
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (GetNetworkType() == NetworkType::WIFI) {
|
||||
if (app.GetDeviceState() == kDeviceStateStarting) {
|
||||
// cast to WifiBoard
|
||||
auto& wifi_board = static_cast<WifiBoard&>(GetCurrentBoard());
|
||||
wifi_board.EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
if (app.GetDeviceState() == kDeviceStateStarting) {
|
||||
EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
gpio_set_level(BUILTIN_LED_GPIO, 1);
|
||||
app.ToggleChatState();
|
||||
});
|
||||
|
||||
|
||||
boot_button_.OnDoubleClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) {
|
||||
SwitchNetworkType();
|
||||
}
|
||||
});
|
||||
|
||||
asr_button_.OnClick([this]() {
|
||||
std::string wake_word="你好小智";
|
||||
Application::GetInstance().WakeWordInvoke(wake_word);
|
||||
@@ -145,7 +133,7 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
CompactWifiBoard() : DualNetworkBoard(ML307_TX_PIN, ML307_RX_PIN), boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO)
|
||||
CompactWifiBoard() : WifiBoard(), boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO)
|
||||
{
|
||||
InitializeDisplayI2c();
|
||||
InitializeSsd1306Display();
|
||||
|
||||
@@ -24,26 +24,6 @@ idf.py menuconfig
|
||||
Xiaozhi Assistant -> Board Type ->面包板新版接线(WiFi)+ LCD + Camera
|
||||
```
|
||||
|
||||
**配置摄像头传感器:**
|
||||
|
||||
> **注意:** 确认摄像头传感器型号,确定型号在 esp_cam_sensor 支持的范围内。当前板子用的是 OV2640,是符合支持范围。
|
||||
|
||||
在 menuconfig 中按以下步骤启用对应型号的支持:
|
||||
|
||||
1. **导航到传感器配置:**
|
||||
```
|
||||
(Top) → Component config → Espressif Camera Sensors Configurations → Camera Sensor Configuration → Select and Set Camera Sensor
|
||||
```
|
||||
|
||||
2. **选择传感器型号:**
|
||||
- 选中所需的传感器型号(OV2640)
|
||||
|
||||
3. **配置传感器参数:**
|
||||
- 按 → 进入传感器详细设置
|
||||
- 启用 **Auto detect**
|
||||
- 推荐将 **default output format** 调整为 **YUV422** 及合适的分辨率大小
|
||||
- (目前支持 YUV422、RGB565,YUV422 更节省内存空间)
|
||||
|
||||
**编译烧入:**
|
||||
|
||||
```bash
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "mcp_server.h"
|
||||
#include "lamp_controller.h"
|
||||
#include "led/single_led.h"
|
||||
#include "esp_video.h"
|
||||
#include "esp32_camera.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
@@ -65,7 +65,7 @@ private:
|
||||
|
||||
Button boot_button_;
|
||||
LcdDisplay* display_;
|
||||
EspVideo* camera_;
|
||||
Esp32Camera* camera_;
|
||||
|
||||
void InitializeSpi() {
|
||||
spi_bus_config_t buscfg = {};
|
||||
@@ -125,47 +125,32 @@ private:
|
||||
}
|
||||
|
||||
void InitializeCamera() {
|
||||
static esp_cam_ctlr_dvp_pin_config_t dvp_pin_config = {
|
||||
.data_width = CAM_CTLR_DATA_WIDTH_8,
|
||||
.data_io = {
|
||||
[0] = CAMERA_PIN_D0,
|
||||
[1] = CAMERA_PIN_D1,
|
||||
[2] = CAMERA_PIN_D2,
|
||||
[3] = CAMERA_PIN_D3,
|
||||
[4] = CAMERA_PIN_D4,
|
||||
[5] = CAMERA_PIN_D5,
|
||||
[6] = CAMERA_PIN_D6,
|
||||
[7] = CAMERA_PIN_D7,
|
||||
},
|
||||
.vsync_io = CAMERA_PIN_VSYNC,
|
||||
.de_io = CAMERA_PIN_HREF,
|
||||
.pclk_io = CAMERA_PIN_PCLK,
|
||||
.xclk_io = CAMERA_PIN_XCLK,
|
||||
};
|
||||
|
||||
esp_video_init_sccb_config_t sccb_config = {
|
||||
.init_sccb = true,
|
||||
.i2c_config = {
|
||||
.port = 0,
|
||||
.scl_pin = CAMERA_PIN_SIOC,
|
||||
.sda_pin = CAMERA_PIN_SIOD,
|
||||
},
|
||||
.freq = 100000,
|
||||
};
|
||||
|
||||
esp_video_init_dvp_config_t dvp_config = {
|
||||
.sccb_config = sccb_config,
|
||||
.reset_pin = CAMERA_PIN_RESET,
|
||||
.pwdn_pin = CAMERA_PIN_PWDN,
|
||||
.dvp_pin = dvp_pin_config,
|
||||
.xclk_freq = XCLK_FREQ_HZ,
|
||||
};
|
||||
|
||||
esp_video_init_config_t video_config = {
|
||||
.dvp = &dvp_config,
|
||||
};
|
||||
|
||||
camera_ = new EspVideo(video_config);
|
||||
camera_config_t config = {};
|
||||
config.pin_d0 = CAMERA_PIN_D0;
|
||||
config.pin_d1 = CAMERA_PIN_D1;
|
||||
config.pin_d2 = CAMERA_PIN_D2;
|
||||
config.pin_d3 = CAMERA_PIN_D3;
|
||||
config.pin_d4 = CAMERA_PIN_D4;
|
||||
config.pin_d5 = CAMERA_PIN_D5;
|
||||
config.pin_d6 = CAMERA_PIN_D6;
|
||||
config.pin_d7 = CAMERA_PIN_D7;
|
||||
config.pin_xclk = CAMERA_PIN_XCLK;
|
||||
config.pin_pclk = CAMERA_PIN_PCLK;
|
||||
config.pin_vsync = CAMERA_PIN_VSYNC;
|
||||
config.pin_href = CAMERA_PIN_HREF;
|
||||
config.pin_sccb_sda = CAMERA_PIN_SIOD;
|
||||
config.pin_sccb_scl = CAMERA_PIN_SIOC;
|
||||
config.sccb_i2c_port = 0;
|
||||
config.pin_pwdn = CAMERA_PIN_PWDN;
|
||||
config.pin_reset = CAMERA_PIN_RESET;
|
||||
config.xclk_freq_hz = XCLK_FREQ_HZ;
|
||||
config.pixel_format = PIXFORMAT_RGB565;
|
||||
config.frame_size = FRAMESIZE_VGA;
|
||||
config.jpeg_quality = 12;
|
||||
config.fb_count = 1;
|
||||
config.fb_location = CAMERA_FB_IN_PSRAM;
|
||||
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
|
||||
camera_ = new Esp32Camera(config);
|
||||
camera_->SetHMirror(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ public:
|
||||
virtual bool Capture() = 0;
|
||||
virtual bool SetHMirror(bool enabled) = 0;
|
||||
virtual bool SetVFlip(bool enabled) = 0;
|
||||
virtual bool SetSwapBytes(bool enabled) { return false; } // Optional, default no-op
|
||||
virtual std::string Explain(const std::string& question) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ Esp32Camera::~Esp32Camera() {
|
||||
esp_camera_fb_return(current_fb_);
|
||||
current_fb_ = nullptr;
|
||||
}
|
||||
if (encode_buf_) {
|
||||
heap_caps_free(encode_buf_);
|
||||
encode_buf_ = nullptr;
|
||||
encode_buf_size_ = 0;
|
||||
}
|
||||
esp_camera_deinit();
|
||||
streaming_on_ = false;
|
||||
}
|
||||
@@ -72,30 +77,46 @@ bool Esp32Camera::Capture() {
|
||||
}
|
||||
}
|
||||
|
||||
// Perform byte swapping for RGB565 format and prepare preview image
|
||||
// Prepare encode buffer for RGB565 format (with optional byte swapping)
|
||||
if (current_fb_->format == PIXFORMAT_RGB565) {
|
||||
size_t pixel_count = current_fb_->width * current_fb_->height;
|
||||
size_t data_size = pixel_count * 2;
|
||||
|
||||
uint8_t *preview_data = (uint8_t *)heap_caps_malloc(data_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (preview_data == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for preview image");
|
||||
return false;
|
||||
// Allocate or reallocate encode buffer if needed
|
||||
if (encode_buf_size_ < data_size) {
|
||||
if (encode_buf_) {
|
||||
heap_caps_free(encode_buf_);
|
||||
}
|
||||
encode_buf_ = (uint8_t *)heap_caps_malloc(data_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (encode_buf_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for encode buffer");
|
||||
encode_buf_size_ = 0;
|
||||
return false;
|
||||
}
|
||||
encode_buf_size_ = data_size;
|
||||
}
|
||||
|
||||
// Copy data to encode buffer with optional byte swapping
|
||||
uint16_t *src = (uint16_t *)current_fb_->buf;
|
||||
uint16_t *dst = (uint16_t *)preview_data;
|
||||
for (size_t i = 0; i < pixel_count; i++) {
|
||||
// Copy data from driver buffer to preview buffer with byte swapping
|
||||
dst[i] = __builtin_bswap16(src[i]);
|
||||
uint16_t *dst = (uint16_t *)encode_buf_;
|
||||
if (swap_bytes_enabled_) {
|
||||
for (size_t i = 0; i < pixel_count; i++) {
|
||||
dst[i] = __builtin_bswap16(src[i]);
|
||||
}
|
||||
} else {
|
||||
memcpy(encode_buf_, current_fb_->buf, data_size);
|
||||
}
|
||||
|
||||
// Display preview image
|
||||
auto display = dynamic_cast<LvglDisplay *>(Board::GetInstance().GetDisplay());
|
||||
if (display != nullptr) {
|
||||
display->SetPreviewImage(std::make_unique<LvglAllocatedImage>(preview_data, data_size, current_fb_->width, current_fb_->height, current_fb_->width * 2, LV_COLOR_FORMAT_RGB565));
|
||||
} else {
|
||||
heap_caps_free(preview_data);
|
||||
// Allocate separate buffer for preview display
|
||||
uint8_t *preview_data = (uint8_t *)heap_caps_malloc(data_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (preview_data != nullptr) {
|
||||
memcpy(preview_data, encode_buf_, data_size);
|
||||
auto display = dynamic_cast<LvglDisplay *>(Board::GetInstance().GetDisplay());
|
||||
if (display != nullptr) {
|
||||
display->SetPreviewImage(std::make_unique<LvglAllocatedImage>(preview_data, data_size, current_fb_->width, current_fb_->height, current_fb_->width * 2, LV_COLOR_FORMAT_RGB565));
|
||||
} else {
|
||||
heap_caps_free(preview_data);
|
||||
}
|
||||
}
|
||||
} else if (current_fb_->format == PIXFORMAT_JPEG) {
|
||||
// JPEG format preview usually requires decoding, skip preview display for now, just log
|
||||
@@ -126,6 +147,11 @@ bool Esp32Camera::SetVFlip(bool enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Esp32Camera::SetSwapBytes(bool enabled) {
|
||||
swap_bytes_enabled_ = enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Esp32Camera::Explain(const std::string &question) {
|
||||
if (explain_url_.empty()) {
|
||||
throw std::runtime_error("Image explain URL or token is not set");
|
||||
@@ -172,7 +198,15 @@ std::string Esp32Camera::Explain(const std::string &question) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok = image_to_jpeg_cb(current_fb_->buf, current_fb_->len, w, h, enc_fmt, 80,
|
||||
// Use encode buffer for RGB565, otherwise use original frame buffer
|
||||
uint8_t *jpeg_src_buf = current_fb_->buf;
|
||||
size_t jpeg_src_len = current_fb_->len;
|
||||
if (current_fb_->format == PIXFORMAT_RGB565 && encode_buf_ != nullptr) {
|
||||
jpeg_src_buf = encode_buf_;
|
||||
jpeg_src_len = encode_buf_size_;
|
||||
}
|
||||
|
||||
bool ok = image_to_jpeg_cb(jpeg_src_buf, jpeg_src_len, w, h, enc_fmt, 80,
|
||||
[](void* arg, size_t index, const void* data, size_t len) -> size_t {
|
||||
auto jpeg_queue = static_cast<QueueHandle_t>(arg);
|
||||
JpegChunk chunk = {.data = nullptr, .len = len};
|
||||
|
||||
@@ -23,10 +23,13 @@ class Esp32Camera : public Camera
|
||||
{
|
||||
private:
|
||||
bool streaming_on_ = false;
|
||||
bool swap_bytes_enabled_ = true; // Swap pixel byte order for RGB565, enabled by default
|
||||
std::string explain_url_;
|
||||
std::string explain_token_;
|
||||
std::thread encoder_thread_;
|
||||
camera_fb_t *current_fb_ = nullptr;
|
||||
uint8_t *encode_buf_ = nullptr; // Buffer for JPEG encoding (with optional byte swap)
|
||||
size_t encode_buf_size_ = 0;
|
||||
|
||||
public:
|
||||
Esp32Camera(const camera_config_t &config);
|
||||
@@ -36,5 +39,6 @@ public:
|
||||
virtual bool Capture() override;
|
||||
virtual bool SetHMirror(bool enabled) override;
|
||||
virtual bool SetVFlip(bool enabled) override;
|
||||
virtual bool SetSwapBytes(bool enabled) override;
|
||||
virtual std::string Explain(const std::string &question) override;
|
||||
};
|
||||
|
||||
@@ -11,6 +11,10 @@ private:
|
||||
|
||||
public:
|
||||
LampController(gpio_num_t gpio_num) : gpio_num_(gpio_num) {
|
||||
if (gpio_num_ == GPIO_NUM_NC) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpio_config_t config = {
|
||||
.pin_bit_mask = (1ULL << gpio_num_),
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
|
||||
@@ -107,6 +107,9 @@ void Nt26Board::StartNetwork() {
|
||||
ScheduleAsyncStop();
|
||||
OnNetworkEvent(NetworkEvent::ModemErrorInitFailed);
|
||||
break;
|
||||
case UartEthModem::UartEthModemEvent::InFlightMode:
|
||||
ESP_LOGW(TAG, "Modem in flight mode");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -59,17 +59,16 @@ void WifiBoard::StartNetwork() {
|
||||
wifi_manager.Initialize(config);
|
||||
|
||||
// Set unified event callback - forward to NetworkEvent with SSID data
|
||||
wifi_manager.SetEventCallback([this, &wifi_manager](WifiEvent event) {
|
||||
std::string ssid = wifi_manager.GetSsid();
|
||||
wifi_manager.SetEventCallback([this](WifiEvent event, const std::string& data) {
|
||||
switch (event) {
|
||||
case WifiEvent::Scanning:
|
||||
OnNetworkEvent(NetworkEvent::Scanning);
|
||||
break;
|
||||
case WifiEvent::Connecting:
|
||||
OnNetworkEvent(NetworkEvent::Connecting, ssid);
|
||||
OnNetworkEvent(NetworkEvent::Connecting, data);
|
||||
break;
|
||||
case WifiEvent::Connected:
|
||||
OnNetworkEvent(NetworkEvent::Connected, ssid);
|
||||
OnNetworkEvent(NetworkEvent::Connected, data);
|
||||
break;
|
||||
case WifiEvent::Disconnected:
|
||||
OnNetworkEvent(NetworkEvent::Disconnected);
|
||||
|
||||
@@ -152,6 +152,8 @@ void K10AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gpio_
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_handle_, &tdm_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,95 +3,61 @@
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "assets.h"
|
||||
#include "assets/lang_config.h"
|
||||
#include "display/lvgl_display/emoji_collection.h"
|
||||
#include "display/lvgl_display/lvgl_image.h"
|
||||
#include "display/lvgl_display/lvgl_theme.h"
|
||||
#include "otto_emoji_gif.h"
|
||||
|
||||
#define TAG "ElectronEmojiDisplay"
|
||||
ElectronEmojiDisplay::ElectronEmojiDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y,
|
||||
bool swap_xy)
|
||||
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
||||
InitializeElectronEmojis();
|
||||
}
|
||||
|
||||
void ElectronEmojiDisplay::SetupUI() {
|
||||
// Prevent duplicate calls - parent SetupUI() will also check, but check here for early return
|
||||
if (setup_ui_called_) {
|
||||
ESP_LOGW(TAG, "SetupUI() called multiple times, skipping duplicate call");
|
||||
return;
|
||||
}
|
||||
|
||||
// Call parent SetupUI() first to create all lvgl objects (including container_)
|
||||
SpiLcdDisplay::SetupUI();
|
||||
|
||||
// Setup chat label after parent UI is initialized so that container_ is valid
|
||||
SetupChatLabel();
|
||||
|
||||
// Set default emotion after UI is initialized
|
||||
SetEmotion("staticstate");
|
||||
}
|
||||
|
||||
void ElectronEmojiDisplay::InitializeElectronEmojis() {
|
||||
ESP_LOGI(TAG, "初始化Electron GIF表情");
|
||||
|
||||
auto otto_emoji_collection = std::make_shared<EmojiCollection>();
|
||||
|
||||
// 中性/平静类表情 -> staticstate
|
||||
otto_emoji_collection->AddEmoji("staticstate", new LvglRawImage((void*)staticstate.data, staticstate.data_size));
|
||||
otto_emoji_collection->AddEmoji("neutral", new LvglRawImage((void*)staticstate.data, staticstate.data_size));
|
||||
otto_emoji_collection->AddEmoji("relaxed", new LvglRawImage((void*)staticstate.data, staticstate.data_size));
|
||||
otto_emoji_collection->AddEmoji("sleepy", new LvglRawImage((void*)staticstate.data, staticstate.data_size));
|
||||
otto_emoji_collection->AddEmoji("idle", new LvglRawImage((void*)staticstate.data, staticstate.data_size));
|
||||
|
||||
// 积极/开心类表情 -> happy
|
||||
otto_emoji_collection->AddEmoji("happy", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("laughing", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("funny", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("loving", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("confident", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("winking", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("cool", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("delicious", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("kissy", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
otto_emoji_collection->AddEmoji("silly", new LvglRawImage((void*)happy.data, happy.data_size));
|
||||
|
||||
// 悲伤类表情 -> sad
|
||||
otto_emoji_collection->AddEmoji("sad", new LvglRawImage((void*)sad.data, sad.data_size));
|
||||
otto_emoji_collection->AddEmoji("crying", new LvglRawImage((void*)sad.data, sad.data_size));
|
||||
|
||||
// 愤怒类表情 -> anger
|
||||
otto_emoji_collection->AddEmoji("anger", new LvglRawImage((void*)anger.data, anger.data_size));
|
||||
otto_emoji_collection->AddEmoji("angry", new LvglRawImage((void*)anger.data, anger.data_size));
|
||||
|
||||
// 惊讶类表情 -> scare
|
||||
otto_emoji_collection->AddEmoji("scare", new LvglRawImage((void*)scare.data, scare.data_size));
|
||||
otto_emoji_collection->AddEmoji("surprised", new LvglRawImage((void*)scare.data, scare.data_size));
|
||||
otto_emoji_collection->AddEmoji("shocked", new LvglRawImage((void*)scare.data, scare.data_size));
|
||||
|
||||
// 思考/困惑类表情 -> buxue
|
||||
otto_emoji_collection->AddEmoji("buxue", new LvglRawImage((void*)buxue.data, buxue.data_size));
|
||||
otto_emoji_collection->AddEmoji("thinking", new LvglRawImage((void*)buxue.data, buxue.data_size));
|
||||
otto_emoji_collection->AddEmoji("confused", new LvglRawImage((void*)buxue.data, buxue.data_size));
|
||||
otto_emoji_collection->AddEmoji("embarrassed", new LvglRawImage((void*)buxue.data, buxue.data_size));
|
||||
|
||||
// 将表情集合添加到主题中
|
||||
auto& theme_manager = LvglThemeManager::GetInstance();
|
||||
auto light_theme = theme_manager.GetTheme("light");
|
||||
auto dark_theme = theme_manager.GetTheme("dark");
|
||||
|
||||
if (light_theme != nullptr) {
|
||||
light_theme->set_emoji_collection(otto_emoji_collection);
|
||||
}
|
||||
if (dark_theme != nullptr) {
|
||||
dark_theme->set_emoji_collection(otto_emoji_collection);
|
||||
}
|
||||
|
||||
// 设置默认表情为staticstate
|
||||
SetEmotion("staticstate");
|
||||
|
||||
ESP_LOGI(TAG, "Electron GIF表情初始化完成");
|
||||
ESP_LOGI(TAG, "Electron表情初始化将由Assets系统处理");
|
||||
// 表情初始化已移至assets系统,通过DEFAULT_EMOJI_COLLECTION=otto-gif配置
|
||||
// assets.cc会从assets分区加载GIF表情并设置到theme
|
||||
// Note: Default emotion is now set in SetupUI() after LVGL objects are created
|
||||
}
|
||||
|
||||
void ElectronEmojiDisplay::SetupChatLabel() {
|
||||
DisplayLockGuard lock(this);
|
||||
// Create/recreate the chat label under the display lock
|
||||
{
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
if (chat_message_label_) {
|
||||
lv_obj_del(chat_message_label_);
|
||||
if (chat_message_label_) {
|
||||
lv_obj_del(chat_message_label_);
|
||||
}
|
||||
|
||||
chat_message_label_ = lv_label_create(container_);
|
||||
lv_label_set_text(chat_message_label_, "");
|
||||
lv_obj_set_width(chat_message_label_, width_ * 0.9);
|
||||
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
|
||||
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_CENTER, 0);
|
||||
lv_obj_set_style_text_color(chat_message_label_, lv_color_white(), 0);
|
||||
}
|
||||
|
||||
chat_message_label_ = lv_label_create(container_);
|
||||
lv_label_set_text(chat_message_label_, "");
|
||||
lv_obj_set_width(chat_message_label_, width_ * 0.9); // 限制宽度为屏幕宽度的 90%
|
||||
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); // 设置为自动换行模式
|
||||
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_CENTER, 0); // 设置文本居中对齐
|
||||
lv_obj_set_style_text_color(chat_message_label_, lv_color_white(), 0);
|
||||
// SetTheme acquires DisplayLockGuard internally, so call it after releasing the lock above
|
||||
SetTheme(LvglThemeManager::GetInstance().GetTheme("dark"));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class ElectronEmojiDisplay : public SpiLcdDisplay {
|
||||
|
||||
virtual ~ElectronEmojiDisplay() = default;
|
||||
virtual void SetStatus(const char* status) override;
|
||||
virtual void SetupUI() override;
|
||||
|
||||
private:
|
||||
void InitializeElectronEmojis();
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
{
|
||||
"name": "esp-box-3",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_USE_DEVICE_AEC=y",
|
||||
"CONFIG_USE_EMOTE_MESSAGE_STYLE=y",
|
||||
"CONFIG_FLASH_CUSTOM_ASSETS=y",
|
||||
"CONFIG_CUSTOM_ASSETS_FILE=\"https://dl.espressif.com/AE/wn9_nihaoxiaozhi_tts-font_puhui_common_20_4-esp-box-3.bin\""
|
||||
"CONFIG_USE_DEVICE_AEC=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -165,6 +165,8 @@ void BoxAudioCodecLite::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, g
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(rx_handle_, &tdm_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/i2s_tdm.h>
|
||||
#include "adc_mic.h"
|
||||
#include "driver/i2s_pdm.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
@@ -44,18 +43,19 @@ AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
|
||||
uint8_t adc_channel[1] = {0};
|
||||
adc_channel[0] = adc_mic_channel;
|
||||
|
||||
audio_codec_adc_cfg_t cfg = {
|
||||
.handle = NULL,
|
||||
.max_store_buf_size = 1024 * 2,
|
||||
.conv_frame_size = 1024,
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.adc_channel_list = adc_channel,
|
||||
.adc_channel_num = sizeof(adc_channel) / sizeof(adc_channel[0]),
|
||||
.sample_rate_hz = (uint32_t)input_sample_rate,
|
||||
};
|
||||
audio_codec_adc_cfg_t cfg = {};
|
||||
cfg.handle = NULL;
|
||||
cfg.continuous_cfg.max_store_buf_size = 1024 * 2;
|
||||
cfg.continuous_cfg.conv_frame_size = 1024;
|
||||
cfg.continuous_cfg.sample_freq_hz = (uint32_t)input_sample_rate;
|
||||
cfg.continuous_cfg.conv_mode = ADC_CONV_SINGLE_UNIT_1;
|
||||
cfg.continuous_cfg.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2;
|
||||
cfg.continuous_cfg.pattern_num = 1;
|
||||
cfg.continuous_cfg.cfg_mode = AUDIO_CODEC_ADC_CFG_MODE_SINGLE_UNIT;
|
||||
cfg.continuous_cfg.cfg.single_unit.unit_id = ADC_UNIT_1;
|
||||
cfg.continuous_cfg.cfg.single_unit.atten = ADC_ATTEN_DB_12;
|
||||
cfg.continuous_cfg.cfg.single_unit.bit_width = ADC_BITWIDTH_12;
|
||||
cfg.continuous_cfg.cfg.single_unit.channel_id[0] = (uint8_t)adc_mic_channel;
|
||||
const audio_codec_data_if_t *adc_if = audio_codec_new_adc_data(&cfg);
|
||||
|
||||
esp_codec_dev_cfg_t codec_dev_cfg = {
|
||||
@@ -81,6 +81,7 @@ AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate
|
||||
const i2s_pdm_tx_config_t *p_i2s_cfg = &pdm_cfg_default;
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_pdm_tx_mode(tx_handle_, p_i2s_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
.port = I2S_NUM_0,
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
"CONFIG_NEWLIB_NANO_FORMAT=y",
|
||||
"CONFIG_ESP_CONSOLE_NONE=y",
|
||||
"CONFIG_USE_ESP_WAKE_WORD=y",
|
||||
"CONFIG_COMPILER_OPTIMIZATION_SIZE=y"
|
||||
"CONFIG_COMPILER_OPTIMIZATION_SIZE=y",
|
||||
"CONFIG_CODEC_DATA_ADC_SUPPORT=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -186,7 +186,16 @@ private:
|
||||
},
|
||||
};
|
||||
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT1151_CONFIG();
|
||||
esp_lcd_panel_io_i2c_config_t tp_io_config = {
|
||||
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT1151_ADDRESS,
|
||||
.control_phase_bytes = 1,
|
||||
.dc_bit_offset = 0,
|
||||
.lcd_cmd_bits = 16,
|
||||
.flags =
|
||||
{
|
||||
.disable_control_phase = 1,
|
||||
}
|
||||
};
|
||||
tp_io_config.scl_speed_hz = 400 * 1000;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus_, &tp_io_config, &tp_io_handle));
|
||||
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt1151(tp_io_handle, &tp_cfg, &tp));
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/i2s_tdm.h>
|
||||
#include "adc_mic.h"
|
||||
#include "driver/i2s_pdm.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
@@ -44,18 +43,19 @@ AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
|
||||
uint8_t adc_channel[1] = {0};
|
||||
adc_channel[0] = adc_mic_channel;
|
||||
|
||||
audio_codec_adc_cfg_t cfg = {
|
||||
.handle = NULL,
|
||||
.max_store_buf_size = 1024 * 2,
|
||||
.conv_frame_size = 1024,
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.adc_channel_list = adc_channel,
|
||||
.adc_channel_num = sizeof(adc_channel) / sizeof(adc_channel[0]),
|
||||
.sample_rate_hz = (uint32_t)input_sample_rate,
|
||||
};
|
||||
audio_codec_adc_cfg_t cfg = {};
|
||||
cfg.handle = NULL;
|
||||
cfg.continuous_cfg.max_store_buf_size = 1024 * 2;
|
||||
cfg.continuous_cfg.conv_frame_size = 1024;
|
||||
cfg.continuous_cfg.sample_freq_hz = (uint32_t)input_sample_rate;
|
||||
cfg.continuous_cfg.conv_mode = ADC_CONV_SINGLE_UNIT_1;
|
||||
cfg.continuous_cfg.format = ADC_DIGI_OUTPUT_FORMAT_TYPE2;
|
||||
cfg.continuous_cfg.pattern_num = 1;
|
||||
cfg.continuous_cfg.cfg_mode = AUDIO_CODEC_ADC_CFG_MODE_SINGLE_UNIT;
|
||||
cfg.continuous_cfg.cfg.single_unit.unit_id = ADC_UNIT_1;
|
||||
cfg.continuous_cfg.cfg.single_unit.atten = ADC_ATTEN_DB_12;
|
||||
cfg.continuous_cfg.cfg.single_unit.bit_width = ADC_BITWIDTH_12;
|
||||
cfg.continuous_cfg.cfg.single_unit.channel_id[0] = (uint8_t)adc_mic_channel;
|
||||
const audio_codec_data_if_t *adc_if = audio_codec_new_adc_data(&cfg);
|
||||
|
||||
esp_codec_dev_cfg_t codec_dev_cfg = {
|
||||
@@ -81,6 +81,7 @@ AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate
|
||||
const i2s_pdm_tx_config_t *p_i2s_cfg = &pdm_cfg_default;
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_pdm_tx_mode(tx_handle_, p_i2s_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
.port = I2S_NUM_0,
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
"CONFIG_SR_WN_WN9S_HIESP=y",
|
||||
"CONFIG_USE_EMOTE_MESSAGE_STYLE=y",
|
||||
"CONFIG_FLASH_CUSTOM_ASSETS=y",
|
||||
"CONFIG_CUSTOM_ASSETS_FILE=\"https://dl.espressif.com/AE/wn9_nihaoxiaozhi_tts-font_puhui_common_20_4-echoear.bin\""
|
||||
"CONFIG_CUSTOM_ASSETS_FILE=\"https://dl.espressif.com/AE/wn9_nihaoxiaozhi_tts-font_puhui_common_20_4-echoear.bin\"",
|
||||
"CONFIG_CODEC_DATA_ADC_SUPPORT=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# EchoEar 喵伴
|
||||
# ESP-VoCat 喵伴
|
||||
|
||||
## 简介
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<a href="https://oshwhub.com/esp-college/echoear"><b> 立创开源平台 </b></a>
|
||||
</div>
|
||||
|
||||
EchoEar 喵伴是一款智能 AI 开发套件,搭载 ESP32-S3-WROOM-1 模组,1.85 寸 QSPI 圆形触摸屏,双麦阵列,支持离线语音唤醒与声源定位算法。硬件详情等可查看[立创开源项目](https://oshwhub.com/esp-college/echoear)。
|
||||
ESP-VoCat 喵伴是一款智能 AI 开发套件,搭载 ESP32-S3-WROOM-1 模组,1.85 寸 QSPI 圆形触摸屏,双麦阵列,支持离线语音唤醒与声源定位算法。硬件详情等可查看[立创开源项目](https://oshwhub.com/esp-college/echoear)。
|
||||
|
||||
## 配置、编译命令
|
||||
|
||||
@@ -25,11 +25,11 @@ idf.py menuconfig
|
||||
分别配置如下选项:
|
||||
|
||||
### 基本配置
|
||||
- `Xiaozhi Assistant` → `Board Type` → 选择 `EchoEar`
|
||||
- `Xiaozhi Assistant` → `Board Type` → 选择 `Espressif ESP-VoCat`
|
||||
|
||||
### UI风格选择
|
||||
|
||||
EchoEar 支持多种不同的 UI 显示风格,通过 menuconfig 配置选择:
|
||||
ESP-VoCat 支持多种不同的 UI 显示风格,通过 menuconfig 配置选择:
|
||||
|
||||
- `Xiaozhi Assistant` → `Select display style` → 选择显示风格
|
||||
|
||||
@@ -63,7 +63,7 @@ EchoEar 支持多种不同的 UI 显示风格,通过 menuconfig 配置选择
|
||||
- **适用**: 喜欢微信风格的用户
|
||||
- **类**: `SpiLcdDisplay`
|
||||
|
||||
> **说明**: EchoEar 使用16MB Flash,需要使用专门的分区表配置来合理分配存储空间给应用程序、OTA更新、资源文件等。
|
||||
> **说明**: ESP-VoCat 喵伴使用16MB Flash,需要使用专门的分区表配置来合理分配存储空间给应用程序、OTA更新、资源文件等。
|
||||
|
||||
按 `S` 保存,按 `Q` 退出。
|
||||
|
||||
@@ -75,7 +75,7 @@ idf.py build
|
||||
|
||||
**烧录**
|
||||
|
||||
将 EchoEar 连接至电脑,**注意打开电源**,并运行:
|
||||
将 ESP-VoCat 喵伴连接至电脑,**注意打开电源**,并运行:
|
||||
|
||||
```bash
|
||||
idf.py flash
|
||||
@@ -2,7 +2,7 @@
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "echoear",
|
||||
"name": "esp-vocat",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_USE_EMOTE_MESSAGE_STYLE=y",
|
||||
"CONFIG_MMAP_FILE_NAME_LENGTH=32",
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <freertos/semphr.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
#define TAG "EchoEar"
|
||||
#define TAG "ESP-VoCat"
|
||||
|
||||
|
||||
temperature_sensor_handle_t temp_sensor = NULL;
|
||||
@@ -282,7 +282,7 @@ public:
|
||||
// Create touch interrupt semaphore
|
||||
touch_isr_mux_ = xSemaphoreCreateBinary();
|
||||
if (touch_isr_mux_ == NULL) {
|
||||
ESP_LOGE("EchoEar", "Failed to create touch semaphore");
|
||||
ESP_LOGE(TAG, "Failed to create touch semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,15 +319,15 @@ public:
|
||||
// Press event (transition from not touched to touched)
|
||||
press_count_++;
|
||||
event = TOUCH_PRESS;
|
||||
ESP_LOGI("EchoEar", "TOUCH PRESS - count: %d, x: %d, y: %d", press_count_, tp_.x, tp_.y);
|
||||
ESP_LOGI(TAG, "TOUCH PRESS - count: %d, x: %d, y: %d", press_count_, tp_.x, tp_.y);
|
||||
} else if (!is_touched && was_touched_) {
|
||||
// Release event (transition from touched to not touched)
|
||||
event = TOUCH_RELEASE;
|
||||
ESP_LOGI("EchoEar", "TOUCH RELEASE - total presses: %d", press_count_);
|
||||
ESP_LOGI(TAG, "TOUCH RELEASE - total presses: %d", press_count_);
|
||||
} else if (is_touched && was_touched_) {
|
||||
// Continuous touch (hold)
|
||||
event = TOUCH_HOLD;
|
||||
ESP_LOGD("EchoEar", "TOUCH HOLD - x: %d, y: %d", tp_.x, tp_.y);
|
||||
ESP_LOGD(TAG, "TOUCH HOLD - x: %d, y: %d", tp_.x, tp_.y);
|
||||
}
|
||||
|
||||
// Update previous state
|
||||
@@ -380,7 +380,7 @@ private:
|
||||
SemaphoreHandle_t touch_isr_mux_;
|
||||
};
|
||||
|
||||
class EchoEar : public WifiBoard {
|
||||
class EspVocat : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
Cst816s* cst816s_;
|
||||
@@ -391,6 +391,8 @@ private:
|
||||
esp_timer_handle_t touchpad_timer_;
|
||||
esp_lcd_touch_handle_t tp; // LCD touch handle
|
||||
EspVideo* camera_ = nullptr;
|
||||
TaskHandle_t charge_task_handle_ = nullptr;
|
||||
TaskHandle_t touch_task_handle_ = nullptr;
|
||||
|
||||
void InitializeI2c()
|
||||
{
|
||||
@@ -416,10 +418,10 @@ private:
|
||||
uint8_t DetectPcbVersion()
|
||||
{
|
||||
esp_err_t ret = i2c_master_probe(i2c_bus_, 0x18, 100);
|
||||
uint8_t pcb_verison = 0;
|
||||
uint8_t pcb_version = 0;
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "PCB verison V1.0");
|
||||
pcb_verison = 0;
|
||||
ESP_LOGI(TAG, "PCB version V1.0");
|
||||
pcb_version = 0;
|
||||
} else {
|
||||
gpio_config_t gpio_conf = {
|
||||
.pin_bit_mask = (1ULL << GPIO_NUM_48),
|
||||
@@ -433,8 +435,8 @@ private:
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ret = i2c_master_probe(i2c_bus_, 0x18, 100);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "PCB verison V1.2");
|
||||
pcb_verison = 1;
|
||||
ESP_LOGI(TAG, "PCB version V1.2");
|
||||
pcb_version = 1;
|
||||
AUDIO_I2S_GPIO_DIN = AUDIO_I2S_GPIO_DIN_2;
|
||||
AUDIO_CODEC_PA_PIN = AUDIO_CODEC_PA_PIN_2;
|
||||
QSPI_PIN_NUM_LCD_RST = QSPI_PIN_NUM_LCD_RST_2;
|
||||
@@ -446,7 +448,7 @@ private:
|
||||
|
||||
}
|
||||
}
|
||||
return pcb_verison;
|
||||
return pcb_version;
|
||||
}
|
||||
|
||||
static void touch_isr_callback(void* arg)
|
||||
@@ -469,7 +471,7 @@ private:
|
||||
while (true) {
|
||||
if (touchpad->WaitForTouchEvent()) {
|
||||
auto &app = Application::GetInstance();
|
||||
auto &board = (EchoEar &)Board::GetInstance();
|
||||
auto &board = (EspVocat &)Board::GetInstance();
|
||||
|
||||
ESP_LOGD(TAG, "Touch event, TP_PIN_NUM_INT: %d", gpio_get_level(TP_PIN_NUM_INT));
|
||||
touchpad->UpdateTouchPoint();
|
||||
@@ -489,14 +491,14 @@ private:
|
||||
void InitializeCharge()
|
||||
{
|
||||
charge_ = new Charge(i2c_bus_, 0x55);
|
||||
xTaskCreatePinnedToCore(Charge::TaskFunction, "batterydecTask", 3 * 1024, charge_, 6, NULL, 0);
|
||||
xTaskCreatePinnedToCore(Charge::TaskFunction, "batterydecTask", 3 * 1024, charge_, 6, &charge_task_handle_, 0);
|
||||
}
|
||||
|
||||
void InitializeCst816sTouchPad()
|
||||
{
|
||||
cst816s_ = new Cst816s(i2c_bus_, 0x15);
|
||||
|
||||
xTaskCreatePinnedToCore(touch_event_task, "touch_task", 4 * 1024, cst816s_, 5, NULL, 1);
|
||||
xTaskCreatePinnedToCore(touch_event_task, "touch_task", 4 * 1024, cst816s_, 5, &touch_task_handle_, 1);
|
||||
|
||||
const gpio_config_t int_gpio_config = {
|
||||
.pin_bit_mask = (1ULL << TP_PIN_NUM_INT),
|
||||
@@ -507,7 +509,7 @@ private:
|
||||
gpio_config(&int_gpio_config);
|
||||
gpio_install_isr_service(0);
|
||||
gpio_intr_enable(TP_PIN_NUM_INT);
|
||||
gpio_isr_handler_add(TP_PIN_NUM_INT, EchoEar::touch_isr_callback, cst816s_);
|
||||
gpio_isr_handler_add(TP_PIN_NUM_INT, EspVocat::touch_isr_callback, cst816s_);
|
||||
}
|
||||
|
||||
void InitializeSpi()
|
||||
@@ -521,7 +523,7 @@ private:
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(QSPI_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void Initializest77916Display(uint8_t pcb_verison)
|
||||
void InitializeSt77916Display(uint8_t pcb_version)
|
||||
{
|
||||
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
@@ -541,7 +543,7 @@ private:
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
.bits_per_pixel = QSPI_LCD_BIT_PER_PIXEL,
|
||||
.flags = {
|
||||
.reset_active_high = pcb_verison,
|
||||
.reset_active_high = pcb_version,
|
||||
},
|
||||
.vendor_config = &vendor_config,
|
||||
};
|
||||
@@ -610,15 +612,43 @@ private:
|
||||
#endif // CONFIG_ESP_VIDEO_ENABLE_USB_UVC_VIDEO_DEVICE
|
||||
|
||||
public:
|
||||
EchoEar() : boot_button_(BOOT_BUTTON_GPIO)
|
||||
~EspVocat() {
|
||||
// Stop tasks
|
||||
if (charge_task_handle_ != nullptr) {
|
||||
vTaskDelete(charge_task_handle_);
|
||||
}
|
||||
if (touch_task_handle_ != nullptr) {
|
||||
vTaskDelete(touch_task_handle_);
|
||||
}
|
||||
|
||||
// Delete objects
|
||||
delete charge_;
|
||||
delete cst816s_;
|
||||
delete display_;
|
||||
// Note: backlight_ (PwmBacklight) and camera_ (EspVideo) are not deleted here
|
||||
// because their base classes (Backlight, Camera) don't have virtual destructors.
|
||||
// Since EspVocat is a singleton that lives for the device lifetime, this is acceptable.
|
||||
|
||||
// Remove GPIO ISR handler
|
||||
gpio_isr_handler_remove(TP_PIN_NUM_INT);
|
||||
|
||||
// Disable temperature sensor
|
||||
if (temp_sensor != NULL) {
|
||||
temperature_sensor_disable(temp_sensor);
|
||||
temperature_sensor_uninstall(temp_sensor);
|
||||
temp_sensor = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
EspVocat() : boot_button_(BOOT_BUTTON_GPIO)
|
||||
{
|
||||
InitializeI2c();
|
||||
uint8_t pcb_verison = DetectPcbVersion();
|
||||
uint8_t pcb_version = DetectPcbVersion();
|
||||
InitializeCharge();
|
||||
InitializeCst816sTouchPad();
|
||||
|
||||
InitializeSpi();
|
||||
Initializest77916Display(pcb_verison);
|
||||
InitializeSt77916Display(pcb_version);
|
||||
InitializeButtons();
|
||||
#ifdef CONFIG_ESP_VIDEO_ENABLE_USB_UVC_VIDEO_DEVICE
|
||||
InitializeCamera();
|
||||
@@ -663,4 +693,4 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(EchoEar);
|
||||
DECLARE_BOARD(EspVocat);
|
||||
@@ -0,0 +1,5 @@
|
||||
# Freenove ESP32-S3 ESP32 S3 Capacitive Touch Display CYD WiFi BT, 2.8 Inch 240x320 IPS Screen
|
||||
|
||||
[product](https://store.freenove.com/products/fnk0104)
|
||||
|
||||
Official github: [freenove-esp32s3-display-2.8-lcd](https://github.com/Freenove/Freenove_ESP32_S3_Display)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user