Compare commits

...

38 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 969dde0d10 Fix crash when adjusting volume via button while audio device is disabled
When both input and output are disabled, UpdateDeviceState() sets dev_ to
nullptr. Pressing a volume button calls SetOutputVolume() which previously
called esp_codec_dev_set_out_vol(dev_, volume) without null-checking dev_,
causing a crash via ESP_ERROR_CHECK.

Fix: Add null guard for dev_ and mutex lock for thread safety. The volume
is still saved via AudioCodec::SetOutputVolume() and will be applied when
the device is reopened by UpdateDeviceState().

Agent-Logs-Url: https://github.com/78/xiaozhi-esp32/sessions/945c653a-ed16-49af-aefe-5cfb473402c6

Co-authored-by: 78 <4488133+78@users.noreply.github.com>
2026-04-07 10:25:15 +00:00
copilot-swe-agent[bot] 20d91a3507 Initial plan 2026-04-07 10:20:33 +00:00
radmir 7a7c74a747 Add touch screen support to freenove 2.8 (#1901)
* Add touch screen support to freenove 2.8

Volume and brightness levels on vertical/horizontal swipe
Listening, chat state and wifi config on press/long press

* Simplified

Removed swipes. Touch quiet unresponsive on Freenove board.

* Remove unused parts

* Modify long tap duration in TouchTask

Updated touch driver logic to change long tap duration from 600ms to 3000ms.

* Cosmetic fix
2026-04-07 17:18:24 +08:00
Y1hsiaochunnn cc7cbe78ba Merge pull request #1821 from ggc121238/main
feat: add waveshare esp32-s3-cam-xxxx support
2026-04-03 14:14:35 +08:00
ggc 314edc5c51 content: add ESP32-S3-CAM-OVxxxx 2026-04-02 16:37:24 +08:00
CQ793 7e7890183e Content: fixed ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG issue 2026-04-02 14:21:01 +08:00
CQ793 2c5d7757e5 Content: Removed some irrelevant code 2026-04-02 14:21:01 +08:00
CQ793 43b4d35b2e Content: Removed irrelevant code 2026-04-02 14:21:01 +08:00
CQ793 33ed917172 content: fixed config.json and config.h 2026-04-02 14:21:01 +08:00
CQ793 89d51fdc23 content: add ESP32-P4-WIFI6-Touch-LCD-3.5 2026-04-02 14:21:01 +08:00
radmir 022d9848de Add freenove-esp32s3-display-2.8-lcd (#1876)
* add freenove-esp32s3-display-2.8-lcd

from https://github.com/Freenove/xiaozhi-esp32 and updated to v2.2.4

* add Freenove_ESP32S3_DISPLAY_2_8_LCD to configs

* Update from manufacturer

https://github.com/Freenove/xiaozhi-esp32/tree/New

* Delete main/boards/freenove-esp32s3-display-2.8-lcd/_static/ReadMe/media-kit-2.8-lcd-top.jpg

orphan

* Delete main/boards/freenove-esp32s3-display-2.8-lcd/_static/ReadMe/media-kit-2.8-lcd-sideview.jpg

orphan

* Delete main/boards/freenove-esp32s3-display-2.8-lcd/_static/ReadMe directory

* readme update
2026-04-01 12:46:53 +08:00
Y1hsiaochunnn 8afebe560a Merge pull request #1895 from Y1hsiaochunnn/touchcompatible
fix: Default configuration of GT911
2026-03-31 10:22:59 +08:00
Y1hsiaochunnn a63f8bc08b fix: Default configuration of GT911 2026-03-31 10:10:12 +08:00
Y1hsiaochunnn 4dc1a8c75f Merge pull request #1892 from DrayxR3X/esp32-c6-touch-amoled-2.16
add waveshare esp32-c6-touch-amoled-2.16
2026-03-31 09:16:16 +08:00
Tomato Me 121a2d45e7 Merge branch '78:main' into esp32-c6-touch-amoled-2.16 2026-03-30 18:16:11 +08:00
Y1hsiaochunnn 06b3b7613c Merge pull request #1891 from Y1hsiaochunnn/gt911_err
fix: Compilation errors caused by default configuration changes of the touch chip
2026-03-30 16:17:58 +08:00
Y1hsiaochunnn addf5fcc64 fix: touch configuration security risk fixed 2026-03-30 15:29:31 +08:00
Y1hsiaochunnn a877d95f74 fix: touch_config errors 2026-03-30 15:12:22 +08:00
Tomato Me 76a5c19000 add waveshare esp32-c6-touch-amoled-2.16 2026-03-30 15:07:45 +08:00
Y1hsiaochunnn 7d63797dfa fix: GT911 config errors 2026-03-30 15:05:20 +08:00
laride 0f3199a812 feat: adapt to esp_codec_dev>=1.5.5 (#1878)
* feat: adapt to esp_codec_dev>=1.5.5

* fix: fix board config resolution in release script
2026-03-26 23:56:45 +08:00
Copilot 6d51b9dbde Bump version to 2.2.5 (#1882)
* Initial plan

* bump version to 2.2.5

Co-authored-by: 78 <4488133+78@users.noreply.github.com>
Agent-Logs-Url: https://github.com/78/xiaozhi-esp32/sessions/841b9fce-b34b-4cbd-b46a-20b48fd35d4c

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: 78 <4488133+78@users.noreply.github.com>
2026-03-26 23:52:38 +08:00
Xiaoxia d340efe58d Remove unused flag for playing popup sound on listening state in ContinueWakeWordInvoke() function (#1881) 2026-03-26 13:12:02 +08:00
Copilot d9f0ef13aa Fix GIF emoji flickering by merging lock scopes in SetEmotion() (#1880)
* Initial plan

* Fix GIF emoji flickering by merging lock scopes in SetEmotion()

Remove the separate first lock scope that hid emoji_image_ before
destroying gif_controller_ (added by PR #1848). Instead, move the
gif_controller_ cleanup into each lock scope where new content is set,
so destruction and creation happen atomically. This prevents both the
original race condition crash AND the visible flickering.

Co-authored-by: 78 <4488133+78@users.noreply.github.com>
Agent-Logs-Url: https://github.com/78/xiaozhi-esp32/sessions/2dd0ff3a-4556-47a6-8d40-a30e18c01923

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: 78 <4488133+78@users.noreply.github.com>
2026-03-26 12:53:06 +08:00
-10 358819bf2e change to use cJSON_ParseWithLength (#1873) 2026-03-23 21:05:06 +08:00
Xiaoxia cef581d723 Update dependencies in idf_component.yml to latest versions: esp-ml30… (#1867)
* Update dependencies in idf_component.yml to latest versions: esp-ml307 to ~3.6.5, uart-eth-modem to ~0.3.4, esp32-camera to ^2.1.5, lvgl to ~9.5.0, esp_lvgl_port to ~2.7.2, esp_mmap_assets to ^1.3.2, and adc_battery_estimation to ^0.2.1.

* Update esp-wifi-connect version and adjust partition sizes in 4m.csv
2026-03-22 13:05:42 +08:00
JasonYANG17 dbb8e1d409 fix(eda-robot-pro): Optimize gait algorithm (#1871) 2026-03-22 07:03:10 +08:00
-10 78008ab9d3 add refresh display theme param for Apply function (#1865) 2026-03-22 06:23:58 +08:00
Y1hsiaochunnn cab8a7cecf Merge pull request #1868 from shunian11/main
feat: add waveshare esp32-s3-touch-lcd-1.54 and waveshare esp32-s3-lcd-0.85
2026-03-20 19:00:43 +08:00
shunian11 74fa5413fc update config.json 2026-03-20 16:48:06 +08:00
shunian11 cc4e12fa74 add manufacturer in config.json 2026-03-20 15:10:46 +08:00
shunian11 1c195fea4f Update config.json 2026-03-20 11:11:24 +08:00
shunian11 a4bf13b30d add waveshare esp32-s3-lcd-0.85 2026-03-20 11:03:29 +08:00
shunian11 619dd2801c add waveshare esp32-s3-touch-lcd-1.54 2026-03-20 10:49:58 +08:00
Copilot 280b2ff856 Fix crash when LAMP_GPIO or BUILTIN_LED_GPIO is GPIO_NUM_NC (#1853)
* Initial plan

* Add GPIO_NUM_NC guards for LampController and SingleLed across all board files

Boards crash when LAMP_GPIO or BUILTIN_LED_GPIO is set to GPIO_NUM_NC because
LampController and SingleLed are instantiated unconditionally.

Fix: Guard LampController with `if (LAMP_GPIO != GPIO_NUM_NC)` in InitializeTools()
and guard SingleLed with `if (BUILTIN_LED_GPIO != GPIO_NUM_NC)` in GetLed(),
falling back to NoLed when GPIO is not connected.

Fixes the reported crash on bread-compact-wifi-lcd and hardens all similar boards.

Co-authored-by: 78 <4488133+78@users.noreply.github.com>

* Fix crash when LAMP_GPIO or BUILTIN_LED_GPIO is GPIO_NUM_NC (minimal 2-file fix)

Instead of adding guards in every board file (36 files), fix the root cause
in just 2 source files:

1. SingleLed constructor: replace assert(gpio != GPIO_NUM_NC) with graceful
   early return, leaving led_strip_ as nullptr (all methods already null-check it)
2. LampController constructor: add early return when gpio_num == GPIO_NUM_NC

This fixes the crash on bread-compact-wifi-lcd and any other board where
these GPIOs are set to GPIO_NUM_NC.

Co-authored-by: 78 <4488133+78@users.noreply.github.com>

* Remove esp_timer_delete from destructor to match original behavior

Co-authored-by: 78 <4488133+78@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: 78 <4488133+78@users.noreply.github.com>
2026-03-20 07:02:03 +08:00
Copilot 527cd1fd58 Restore README.md and update Discord links to https://discord.gg/C759fGMBcZ in all language versions (#1862)
* Initial plan

* Restore README.md and update Discord links to https://discord.gg/C759fGMBcZ in all language versions

Co-authored-by: 78 <4488133+78@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: 78 <4488133+78@users.noreply.github.com>
2026-03-19 03:54:13 +08:00
Xiaoxia 55698b2d74 Update Discord link in README.md 2026-03-19 03:25:59 +08:00
Y1hsiaochunnn e503a32d84 feat: Add Waevshare Product ESP32-P4-WIFI6-Touch-LCD-4.3 Support (#1858)
* feat: Add Waevshare Product ESP32-P4-WIFI6-Touch-LCD-4.3 Support

* feat: Supplement README

* feat: Remove redundant conf
2026-03-19 03:05:08 +08:00
72 changed files with 3664 additions and 349 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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.4")
set(PROJECT_VER "2.2.5")
project(xiaozhi)
+2 -2
View File
@@ -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
View File
@@ -156,7 +156,7 @@ Feishuドキュメントチュートリアルをご覧ください:
このプロジェクトを通じて、AIハードウェア開発を理解し、急速に進化する大規模言語モデルを実際のハードウェアデバイスに応用できるようになることを目指しています。
ご意見やご提案があれば、いつでもIssueを提出するか、[Discord](https://discord.gg/bXqgAfRm) または QQグループ:1011329060 にご参加ください。
ご意見やご提案があれば、いつでもIssueを提出するか、[Discord](https://discord.gg/C759fGMBcZ) または QQグループ:1011329060 にご参加ください。
## スター履歴
+1 -1
View File
@@ -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
+41
View File
@@ -273,6 +273,12 @@ elseif(CONFIG_BOARD_TYPE_WAVESHARE_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")
@@ -303,6 +309,12 @@ elseif(CONFIG_BOARD_TYPE_WAVESHARE_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_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_ESP32_S3_TOUCH_LCD_4B)
set(MANUFACTURER "waveshare")
set(BOARD_TYPE "esp32-s3-touch-lcd-4b")
@@ -387,6 +399,18 @@ elseif(CONFIG_BOARD_TYPE_WAVESHARE_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_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_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")
@@ -429,6 +453,12 @@ elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_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_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_ESP32_P4_WIFI6_TOUCH_LCD_7B)
set(MANUFACTURER "waveshare")
set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd")
@@ -441,6 +471,12 @@ elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C)
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")
@@ -728,6 +764,11 @@ 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()
if(MANUFACTURER)
+43 -2
View File
@@ -269,6 +269,9 @@ choice BOARD_TYPE
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
@@ -281,6 +284,9 @@ choice BOARD_TYPE
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_2_06
bool "Waveshare ESP32-C6-Touch-AMOLED-2.06"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_C6_TOUCH_AMOLED_2_16
bool "Waveshare ESP32-C6-Touch-AMOLED-2.16"
depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75
bool "Waveshare ESP32-S3-Touch-AMOLED-1.75"
depends on IDF_TARGET_ESP32S3
@@ -344,18 +350,30 @@ choice BOARD_TYPE
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_3_5B
bool "Waveshare ESP32-S3-Touch-LCD-3.5B"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_54
bool "Waveshare ESP32-S3-Touch-LCD-1.54"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_S3_LCD_0_85
bool "Waveshare ESP32-S3-LCD-0.85"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_WAVESHARE_ESP32_P4_NANO
bool "Waveshare ESP32-P4-NANO"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4B"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-7B"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_5
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5"
depends on IDF_TARGET_ESP32P4
config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4C"
depends on IDF_TARGET_ESP32P4
@@ -518,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
@@ -616,6 +637,25 @@ 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_ESP32_S3_AUDIO_BOARD
prompt "ESP32S3_AUDIO_BOARD LCD Type"
@@ -754,11 +794,12 @@ config USE_DEVICE_AEC
default n
depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE \
|| BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_ESP_KORVO2_V3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83\
|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B \
|| BOARD_TYPE_WAVESHARE_ESP32_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_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.
-3
View File
@@ -840,9 +840,6 @@ void Application::ContinueWakeWordInvoke(const std::string& wake_word) {
}
// Set the chat state to wake word detected
protocol_->SendWakeWordDetected(wake_word);
// Set flag to play popup sound after state changes to listening
play_popup_on_listening_ = true;
SetListeningMode(GetDefaultListeningMode());
#else
// Set flag to play popup sound after state changes to listening
+20 -18
View File
@@ -50,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() {
@@ -211,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)) {
@@ -332,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");
}
}
}
@@ -411,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();
+4 -4
View File
@@ -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;
+4 -1
View File
@@ -156,7 +156,10 @@ void Es8311AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gp
}
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);
}
+4
View File
@@ -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,
+13 -13
View File
@@ -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 = {
+2 -1
View File
@@ -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 = {
+2 -1
View File
@@ -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"
]
}
]
@@ -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)
@@ -0,0 +1,53 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
//音频iis部分
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_4 //MCLK
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5 //SCK
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6 //DIN
#define AUDIO_I2S_GPIO_WS GPIO_NUM_7 //LRC
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_8 //DOUT
#define AUDIO_CODEC_PA_PIN GPIO_NUM_1 //PA
//音频iic部分
#define AUDIO_CODEC_I2C_NUM I2C_NUM_0
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_15
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_16
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
//boot引脚
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define BUILTIN_LED_GPIO GPIO_NUM_42
//屏幕显示部分
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_45
#define DISPLAY_RST_PIN GPIO_NUM_NC
#define DISPLAY_SCK_PIN GPIO_NUM_12
#define DISPLAY_DC_PIN GPIO_NUM_46
#define DISPLAY_CS_PIN GPIO_NUM_10
#define DISPLAY_MOSI_PIN GPIO_NUM_11
#define DISPLAY_MIS0_PIN GPIO_NUM_13
#define DISPLAY_SPI_SCLK_HZ (20 * 1000 * 1000)
#define LCD_SPI_HOST SPI3_HOST
#define LCD_TYPE_ILI9341_SERIAL
#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 240
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_SWAP_XY true
#define DISPLAY_INVERT_COLOR true
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif // _BOARD_CONFIG_H_
@@ -0,0 +1,15 @@
{
"target": "esp32s3",
"builds": [
{
"name": "freenove-esp32s3-display-2.8-lcd",
"sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/16m.csv\"",
"CONFIG_LANGUAGE_EN_US=y"
"CONFIG_SR_WN_WN9S_HIESP=y",
"CONFIG_SR_WN_WN9_HIESP=y"
]
}
]
}
@@ -0,0 +1,229 @@
#include <esp_lcd_panel_vendor.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <wifi_station.h>
#include "wifi_board.h"
#include "codecs/es8311_audio_codec.h"
#include "display/lcd_display.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "mcp_server.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
#include <driver/spi_common.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_timer.h>
#include "led/single_led.h"
#include "system_reset.h"
#include "esp_lcd_ili9341.h"
#define TAG "FreenoveESP32S3Display"
class TouchDriver {
public:
TouchDriver() : dev_(nullptr) {}
bool Init(i2c_master_bus_handle_t bus, uint8_t addr) {
i2c_device_config_t cfg = {
.device_address = addr,
.scl_speed_hz = 400000,
.scl_wait_us = 0,
};
return i2c_master_bus_add_device(bus, &cfg, &dev_) == ESP_OK;
}
bool Read(bool &touched, uint16_t &x, uint16_t &y) {
touched = false;
x = y = 0;
if (!dev_) return false;
uint8_t reg = 0x02;
uint8_t buf[5];
if (i2c_master_transmit_receive(dev_, &reg, 1, buf, 5, 50) != ESP_OK) return false;
uint8_t points = buf[0] & 0x0F;
if (points == 0) return true;
touched = true;
x = ((buf[1] & 0x0F) << 8) | buf[2];
y = ((buf[3] & 0x0F) << 8) | buf[4];
return true;
}
private:
i2c_master_dev_handle_t dev_;
};
class FreenoveESP32S3Display : public WifiBoard {
private:
Button boot_button_;
LcdDisplay *display_;
i2c_master_bus_handle_t codec_i2c_bus_;
TouchDriver touch_;
static void TouchTask(void *arg) {
auto *self = static_cast<FreenoveESP32S3Display*>(arg);
auto &app = Application::GetInstance();
uint32_t last_tap = 0;
uint32_t down_start = 0;
bool down = false;
while (true) {
bool t;
uint16_t x, y;
self->touch_.Read(t, x, y);
uint32_t now = esp_timer_get_time() / 1000;
if (t) {
if (!down) {
down = true;
down_start = now;
}
}
if (!t && down) {
down = false;
uint32_t press = now - down_start;
// long tap
if (press > 3000) {
self->EnterWifiConfigMode();
} else {
// double tap
if (now - last_tap < 250) {
app.StartListening();
last_tap = 0;
} else {
// single tap
app.ToggleChatState();
last_tap = now;
}
}
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
void InitializeTouch() {
if (!touch_.Init(codec_i2c_bus_, 0x38)) return;
xTaskCreatePinnedToCore(TouchTask, "touch_task", 4096, this, 5, nullptr, 0);
}
void InitializeI2c() {
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = AUDIO_CODEC_I2C_NUM,
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.intr_priority = 0,
.trans_queue_depth = 0,
.flags = {
.enable_internal_pullup = 1,
},
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_));
}
void InitializeSpi() {
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
buscfg.miso_io_num = DISPLAY_MIS0_PIN;
buscfg.sclk_io_num = DISPLAY_SCK_PIN;
buscfg.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
ESP_ERROR_CHECK(spi_bus_initialize(LCD_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
auto &app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting) {
EnterWifiConfigMode();
}
app.ToggleChatState();
});
}
void InitializeLcdDisplay() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS_PIN;
io_config.dc_gpio_num = DISPLAY_DC_PIN;
io_config.spi_mode = DISPLAY_SPI_MODE;
io_config.pclk_hz = DISPLAY_SPI_SCLK_HZ;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(LCD_SPI_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片
ESP_LOGD(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
panel_config.bits_per_pixel = 16;
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel));
ESP_LOGI(TAG, "Install LCD driver ILI9341");
esp_lcd_panel_reset(panel);
esp_lcd_panel_init(panel);
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y,
DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
}
void InitializeTools() {
}
public:
FreenoveESP32S3Display(): boot_button_(BOOT_BUTTON_GPIO)
{
InitializeI2c();
InitializeSpi();
InitializeLcdDisplay();
InitializeTouch();
InitializeButtons();
InitializeTools();
GetBacklight()->SetBrightness(100);
}
virtual Led *GetLed() override {
static SingleLed led(BUILTIN_LED_GPIO);
return &led;
}
virtual AudioCodec* GetAudioCodec() override {
static Es8311AudioCodec audio_codec(codec_i2c_bus_, AUDIO_CODEC_I2C_NUM,
AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK,
AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN, AUDIO_CODEC_PA_PIN,
AUDIO_CODEC_ES8311_ADDR, true, true);
return &audio_codec;
}
virtual Display *GetDisplay() override { return display_; }
virtual Backlight *GetBacklight() override {
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
return &backlight;
}
};
DECLARE_BOARD(FreenoveESP32S3Display);
@@ -327,251 +327,164 @@ void EDARobotDog::LiftRightRearLeg(int period, int height) {
//-- DOG GAIT MOVEMENTS -----------------------------------------//
///////////////////////////////////////////////////////////////////
void EDARobotDog::Turn(float steps, int period, int dir) {
if (GetRestState() == true) {
SetRestState(false);
}
for (int step = 0; step < (int)steps; step++) {
if (dir == LEFT) {
int current_pos[SERVO_COUNT];
for (int i = 0; i < SERVO_COUNT; i++) {
if (servo_pins_[i] != -1) {
current_pos[i] = servo_[i].GetPosition();
} else {
current_pos[i] = LEG_HOME_POSITION;
}
}
current_pos[RIGHT_REAR_LEG] = 140; // servo3
current_pos[LEFT_REAR_LEG] = 40; // servo2
MoveServos(100, current_pos);
current_pos[RIGHT_FRONT_LEG] = 40; // servo4
current_pos[LEFT_FRONT_LEG] = 140; // servo1
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 90; // servo3
current_pos[LEFT_REAR_LEG] = 90; // servo2
MoveServos(100, current_pos);
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
current_pos[LEFT_FRONT_LEG] = 90; // servo1
MoveServos(100, current_pos);
current_pos[RIGHT_FRONT_LEG] = 140; // servo4
current_pos[LEFT_FRONT_LEG] = 40; // servo1
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 40; // servo3
current_pos[LEFT_REAR_LEG] = 140; // servo2
MoveServos(100, current_pos);
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
current_pos[LEFT_FRONT_LEG] = 90; // servo1
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 90; // servo3
current_pos[LEFT_REAR_LEG] = 90; // servo2
MoveServos(100, current_pos);
// 舵机方向分析(从 Stretch/Sleep 推断物理前后方向):
//
// Stretch: LF=0, LR=180, RF=180, RR=0
// → 前腿向前伸,后腿向后伸
//
// LEFT_FRONT_LEG (0): 减小=向前, 增大=向后
// LEFT_REAR_LEG (1): 减小=向前, 增大=向后
// RIGHT_FRONT_LEG (2): 增大=向前, 减小=向后
// RIGHT_REAR_LEG (3): 增大=向前, 减小=向后
//
// 即:左侧两腿同向(减=前),右侧两腿同向(增=前)
// 对角线A: 左前 + 右后 → 前进时左前减小, 右后减小(都是各自的"向前")
// 对角线B: 左后 + 右前 → 前进时左后减小, 右前增大(都是各自的"向前")
//
// 抬腿方向(从 Lift 函数):
// LEFT_FRONT_LEG: 0° (减小=抬)
// LEFT_REAR_LEG: 180° (增大=抬)
// RIGHT_FRONT_LEG: 180° (增大=抬)
// RIGHT_REAR_LEG: 0° (减小=抬)
// 辅助:获取当前所有舵机位置
void EDARobotDog::GetCurrentPositions(int pos[SERVO_COUNT]) {
for (int i = 0; i < SERVO_COUNT; i++) {
if (servo_pins_[i] != -1) {
pos[i] = servo_[i].GetPosition();
} else {
// 获取当前位置
int current_pos[SERVO_COUNT];
for (int i = 0; i < SERVO_COUNT; i++) {
if (servo_pins_[i] != -1) {
current_pos[i] = servo_[i].GetPosition();
} else {
current_pos[i] = LEG_HOME_POSITION;
}
}
current_pos[LEFT_REAR_LEG] = 140; // servo2
current_pos[RIGHT_REAR_LEG] = 40; // servo3
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 40; // servo1
current_pos[RIGHT_FRONT_LEG] = 140; // servo4
MoveServos(100, current_pos);
current_pos[LEFT_REAR_LEG] = 90; // servo2
current_pos[RIGHT_REAR_LEG] = 90; // servo3
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 90; // servo1
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 140; // servo1
current_pos[RIGHT_FRONT_LEG] = 40; // servo4
MoveServos(100, current_pos);
current_pos[LEFT_REAR_LEG] = 40; // servo2
current_pos[RIGHT_REAR_LEG] = 140; // servo3
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 90; // servo1
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
MoveServos(100, current_pos);
current_pos[LEFT_REAR_LEG] = 90; // servo2
current_pos[RIGHT_REAR_LEG] = 90; // servo3
MoveServos(100, current_pos);
pos[i] = LEG_HOME_POSITION;
}
}
}
void EDARobotDog::Walk(float steps, int period, int dir) {
void EDARobotDog::Turn(float steps, int period, int dir) {
if (GetRestState() == true) {
SetRestState(false);
}
// 转弯 = 左侧后退 + 右侧前进(左转),或反过来(右转)
// 从 Walk 的已验证方向推导:
// 前进: LF=90-s, LR=90-s, RF=90+s, RR=90+s
// 后退: LF=90+s, LR=90+s, RF=90-s, RR=90-s
//
// 左转(d=1): 左侧后退+右侧前进
// LF=90+s, LR=90+s, RF=90+s, RR=90+s → 全部 90+s
// 地面反推: 全部 90-s
//
// 右转(d=-1): 左侧前进+右侧后退
// LF=90-s, LR=90-s, RF=90-s, RR=90-s → 全部 90-s
// 地面反推: 全部 90+s
const int swing = 60;
const int lift = 25;
int t = period / 6;
if (t < 50) t = 50;
int d = (dir == LEFT) ? -1 : 1;
for (int step = 0; step < (int)steps; step++) {
if (dir == FORWARD) {
int pos[SERVO_COUNT];
GetCurrentPositions(pos);
// 获取当前位置
int current_pos[SERVO_COUNT];
for (int i = 0; i < SERVO_COUNT; i++) {
if (servo_pins_[i] != -1) {
current_pos[i] = servo_[i].GetPosition();
} else {
current_pos[i] = LEG_HOME_POSITION;
}
}
// 拍1: 抬ALF+RR
pos[LEFT_FRONT_LEG] = 90 - lift;
pos[RIGHT_REAR_LEG] = 90 - lift;
MoveServos(t, pos);
current_pos[LEFT_FRONT_LEG] = 100; // servo1
current_pos[RIGHT_FRONT_LEG] = 100; // servo4
MoveServos(100, current_pos);
// 拍2: A空中摆 + B地面反推
pos[LEFT_FRONT_LEG] = 90 - lift + d * swing; // LF空中
pos[RIGHT_REAR_LEG] = 90 - lift + d * swing; // RR空中
pos[LEFT_REAR_LEG] = 90 - d * swing; // LR地面反推
pos[RIGHT_FRONT_LEG] = 90 - d * swing; // RF地面反推
MoveServos(t, pos);
current_pos[RIGHT_REAR_LEG] = 60; // servo3
current_pos[LEFT_REAR_LEG] = 60; // servo2
MoveServos(100, current_pos);
// 拍3: 放下A
pos[LEFT_FRONT_LEG] = 90 + d * swing;
pos[RIGHT_REAR_LEG] = 90 + d * swing;
MoveServos(t / 2, pos);
current_pos[LEFT_FRONT_LEG] = 140; // servo1
current_pos[RIGHT_FRONT_LEG] = 140; // servo4
MoveServos(100, current_pos);
// 拍4: 抬BLR+RF
pos[LEFT_REAR_LEG] = 90 + lift;
pos[RIGHT_FRONT_LEG] = 90 + lift;
MoveServos(t, pos);
current_pos[RIGHT_REAR_LEG] = 40; // servo3
current_pos[LEFT_REAR_LEG] = 40; // servo2
MoveServos(100, current_pos);
// 拍5: B空中摆 + A地面反推
pos[LEFT_REAR_LEG] = 90 + lift + d * swing; // LR空中
pos[RIGHT_FRONT_LEG] = 90 + lift + d * swing; // RF空中
pos[LEFT_FRONT_LEG] = 90 - d * swing; // LF地面反推
pos[RIGHT_REAR_LEG] = 90 - d * swing; // RR地面反推
MoveServos(t, pos);
current_pos[RIGHT_REAR_LEG] = 90; // servo3
current_pos[LEFT_REAR_LEG] = 90; // servo2
current_pos[LEFT_FRONT_LEG] = 90; // servo1
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 80; // servo1
current_pos[RIGHT_FRONT_LEG] = 80; // servo4
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 120; // servo3
current_pos[LEFT_REAR_LEG] = 120; // servo2
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 90; // servo1
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 140; // servo3
current_pos[LEFT_REAR_LEG] = 140; // servo2
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 90; // servo3
current_pos[LEFT_REAR_LEG] = 90; // servo2
MoveServos(100, current_pos);
} else {
// 每次只移动指定的舵机,然后延时100ms
// 获取当前位置
int current_pos[SERVO_COUNT];
for (int i = 0; i < SERVO_COUNT; i++) {
if (servo_pins_[i] != -1) {
current_pos[i] = servo_[i].GetPosition();
} else {
current_pos[i] = LEG_HOME_POSITION;
}
}
current_pos[LEFT_FRONT_LEG] = 80; // servo1
current_pos[RIGHT_FRONT_LEG] = 80; // servo4
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 120; // servo3
current_pos[LEFT_REAR_LEG] = 120; // servo2
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 40; // servo1
current_pos[RIGHT_FRONT_LEG] = 40; // servo4
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 140; // servo3
current_pos[LEFT_REAR_LEG] = 140; // servo2
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 90; // servo3
current_pos[LEFT_REAR_LEG] = 90; // servo2
current_pos[LEFT_FRONT_LEG] = 90; // servo1
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 100; // servo1
current_pos[RIGHT_FRONT_LEG] = 100; // servo4
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 60; // servo3
current_pos[LEFT_REAR_LEG] = 60; // servo2
MoveServos(100, current_pos);
current_pos[LEFT_FRONT_LEG] = 90; // servo1
current_pos[RIGHT_FRONT_LEG] = 90; // servo4
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 40; // servo3
current_pos[LEFT_REAR_LEG] = 40; // servo2
MoveServos(100, current_pos);
current_pos[RIGHT_REAR_LEG] = 90; // servo3
current_pos[LEFT_REAR_LEG] = 90; // servo2
MoveServos(100, current_pos);
}
// 拍6: 放下B
pos[LEFT_REAR_LEG] = 90 + d * swing;
pos[RIGHT_FRONT_LEG] = 90 + d * swing;
MoveServos(t / 2, pos);
}
// 结束回中
int home[SERVO_COUNT] = {90, 90, 90, 90};
MoveServos(150, home);
}
void EDARobotDog::Walk(float steps, int period, int dir) {
if (GetRestState() == true) {
SetRestState(false);
}
// Trot 对角步态
// 方向: LF减=前, LR减=前, RF增=前, RR增=前
// 抬腿: LF减=抬, LR增=抬, RF增=抬, RR减=抬
const int lift = 25;
const int swing = 30;
int t = period / 6;
if (t < 50) t = 50;
int fwd = (dir == FORWARD) ? 1 : -1;
for (int step = 0; step < (int)steps; step++) {
int pos[SERVO_COUNT];
GetCurrentPositions(pos);
// 拍1: 抬起对角线ALF+RR
pos[LEFT_FRONT_LEG] = 90 - lift;
pos[RIGHT_REAR_LEG] = 90 - lift;
MoveServos(t, pos);
// 拍2: A空中前摆 + B地面后推(同时)
pos[LEFT_FRONT_LEG] = 90 - lift - fwd * swing; // LF: 保持抬 + 前摆
pos[RIGHT_REAR_LEG] = 90 - lift + fwd * swing; // RR: 保持抬 + 前摆(增=前)
pos[LEFT_REAR_LEG] = 90 + fwd * swing; // LR: 地面后推(增=后)
pos[RIGHT_FRONT_LEG] = 90 - fwd * swing; // RF: 地面后推(减=后)
MoveServos(t, pos);
// 拍3: 放下A(着地到前摆位置)
pos[LEFT_FRONT_LEG] = 90 - fwd * swing;
pos[RIGHT_REAR_LEG] = 90 + fwd * swing;
MoveServos(t / 2, pos);
// 拍4: 抬起对角线BLR+RF
pos[LEFT_REAR_LEG] = 90 + lift;
pos[RIGHT_FRONT_LEG] = 90 + lift;
MoveServos(t, pos);
// 拍5: B空中前摆 + A地面后推(同时)
pos[LEFT_REAR_LEG] = 90 + lift - fwd * swing; // LR: 保持抬 + 前摆(减=前)
pos[RIGHT_FRONT_LEG] = 90 + lift + fwd * swing; // RF: 保持抬 + 前摆(增=前)
pos[LEFT_FRONT_LEG] = 90 + fwd * swing; // LF: 地面后推(增=后)
pos[RIGHT_REAR_LEG] = 90 - fwd * swing; // RR: 地面后推(减=后)
MoveServos(t, pos);
// 拍6: 放下B(不回中,直接衔接下一步)
pos[LEFT_REAR_LEG] = 90 - fwd * swing;
pos[RIGHT_FRONT_LEG] = 90 + fwd * swing;
MoveServos(t / 2, pos);
}
// 结束回中
int home[SERVO_COUNT] = {90, 90, 90, 90};
MoveServos(150, home);
}
void EDARobotDog::Sit(int period) {
@@ -60,6 +60,7 @@ public:
void LiftRightRearLeg(int period = 1000, int height = 45); // 抬起右后腿
//-- Dog gait movements
void GetCurrentPositions(int pos[SERVO_COUNT]);
void Walk(float steps = 4, int period = 1000, int dir = FORWARD);
void Turn(float steps = 4, int period = 2000, int dir = LEFT);
void Sit(int period = 1500);
+10 -1
View File
@@ -184,7 +184,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_FT5x06_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.flags =
{
.disable_control_phase = 1,
}
};
tp_io_config.scl_speed_hz = 400000;
esp_lcd_new_panel_io_i2c(i2c_bus_, &tp_io_config, &tp_io_handle);
+10 -1
View File
@@ -178,7 +178,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_GT911_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 16,
.flags =
{
.disable_control_phase = 1,
}
};
tp_io_config.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP; // 更改 GT911 地址
tp_io_config.scl_speed_hz = 100000;
esp_lcd_new_panel_io_i2c(i2c_bus_, &tp_io_config, &tp_io_handle);
@@ -265,7 +265,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_FT5x06_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.flags =
{
.disable_control_phase = 1,
}
};
tp_io_config.scl_speed_hz = 400 * 1000;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(codec_i2c_bus_, &tp_io_config, &tp_io_handle));
ESP_LOGI(TAG, "Initialize touch controller");
@@ -0,0 +1,48 @@
# 产品链接
[微雪电子 ESP32-C6-Touch-AMOLED-2.16](https://www.waveshare.net/shop/ESP32-C6-Touch-AMOLED-2.16.htm)
# 编译配置命令
**克隆工程**
```bash
git clone https://github.com/78/xiaozhi-esp32.git
```
**进入工程**
```bash
cd xiaozhi-esp32
```
**配置编译目标为 ESP32C6**
```bash
idf.py set-target esp32c6
```
**打开 menuconfig**
```bash
idf.py menuconfig
```
**选择板子**
```bash
Xiaozhi Assistant -> Board Type -> Waveshare ESP32-C6-Touch-AMOLED-2.16
```
**编译**
```ba
idf.py build
```
**下载并打开串口终端**
```bash
idf.py build flash monitor
```
@@ -0,0 +1,46 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE false
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_19
#define AUDIO_I2S_GPIO_WS GPIO_NUM_22
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_20
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_21
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_23
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_8
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_7
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR
#define BOOT_BUTTON_GPIO GPIO_NUM_9
#define LCD_CS GPIO_NUM_15
#define LCD_PCLK GPIO_NUM_0
#define LCD_D0 GPIO_NUM_1
#define LCD_D1 GPIO_NUM_2
#define LCD_D2 GPIO_NUM_3
#define LCD_D3 GPIO_NUM_4
#define LCD_RST GPIO_NUM_NC
#define TP_RST_GPIO GPIO_NUM_11
#define TP_INT_GPIO GPIO_NUM_5
#define LCD_H_RES 480
#define LCD_V_RES 480
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_SWAP_XY false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#endif // _BOARD_CONFIG_H_
@@ -0,0 +1,14 @@
{
"manufacturer": "waveshare",
"target": "esp32c6",
"builds": [
{
"name": "esp32-c6-touch-amoled-2.16",
"sdkconfig_append": [
"CONFIG_USE_ESP_WAKE_WORD=y",
"CONFIG_USE_DEFAULT_MESSAGE_STYLE=y",
"CONFIG_USE_MULTILINE_CHAT_MESSAGE=y"
]
}
]
}
@@ -0,0 +1,352 @@
#include "display/lcd_display.h"
#include "esp_lcd_sh8601.h"
#include "wifi_board.h"
#include "application.h"
#include "axp2101.h"
#include "button.h"
#include "codecs/box_audio_codec.h"
#include "config.h"
#include "i2c_device.h"
#include "led/single_led.h"
#include "mcp_server.h"
#include "power_save_timer.h"
#include <driver/i2c_master.h>
#include <driver/spi_master.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_log.h>
#include <esp_ota_ops.h>
#include <esp_partition.h>
#include "settings.h"
#include <esp_lcd_touch_cst9217.h>
#include <esp_lvgl_port.h>
#include <lvgl.h>
#define TAG "WaveshareEsp32c6TouchAMOLED2inch16"
class Pmic : public Axp2101 {
public:
Pmic(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : Axp2101(i2c_bus, addr) {
WriteReg(0x22, 0b110); // PWRON > OFFLEVEL as POWEROFF Source enable
WriteReg(0x27, 0x10); // hold 4s to power off
// Disable All DCs but DC1
WriteReg(0x80, 0x01);
// Disable All LDOs
WriteReg(0x90, 0x00);
WriteReg(0x91, 0x00);
// Set DC1 to 3.3V
WriteReg(0x82, (3300 - 1500) / 100);
// Set ALDO1 to 3.3V
WriteReg(0x92, (3300 - 500) / 100);
WriteReg(0x93, (3300 - 500) / 100);
WriteReg(0x94, (3300 - 500) / 100);
WriteReg(0x95, (3300 - 500) / 100);
// Enable ALDO1(MIC)
WriteReg(0x90, 0x0F);
WriteReg(0x64, 0x02); // CV charger voltage setting to 4.1V
WriteReg(0x61, 0x02); // set Main battery precharge current to 50mA
WriteReg(0x62, 0x0A); // set Main battery charger current to 400mA ( 0x08-200mA,
// 0x09-300mA, 0x0A-400mA )
WriteReg(0x63, 0x01); // set Main battery term charge current to 25mA
}
void Pmic_SetAldo3(bool enable) {
uint8_t reg = ReadReg(0x90);
if (enable) {
reg |= 0x04;
} else {
reg &= ~0x04;
}
WriteReg(0x90, reg);
}
void Pmic_SetAldo2(bool enable) {
uint8_t reg = ReadReg(0x90);
if (enable) {
reg |= 0x02;
} else {
reg &= ~0x02;
}
WriteReg(0x90, reg);
}
};
static const sh8601_lcd_init_cmd_t vendor_specific_init[] = {
{0x11, (uint8_t[]){0x00}, 0, 120},
{0xC4, (uint8_t[]){0x80}, 1, 0},
{0x44, (uint8_t[]){0x01, 0xD1}, 2, 0},
{0x35, (uint8_t[]){0x00}, 1, 0},
{0x36, (uint8_t[]){0xA0}, 1, 0},
{0x53, (uint8_t[]){0x20}, 1, 10},
{0x63, (uint8_t[]){0xFF}, 1, 10},
{0x51, (uint8_t[]){0x00}, 1, 10},
{0x2A, (uint8_t[]){0x00, 0x16, 0x01, 0xAF}, 4, 0},
{0x2B, (uint8_t[]){0x00, 0x00, 0x01, 0xF5}, 4, 0},
{0x29, (uint8_t[]){0x00}, 0, 10},
{0x51, (uint8_t[]){0xFF}, 1, 0},
};
// 在waveshare_amoled_2_16类之前添加新的显示类
class CustomLcdDisplay : public SpiLcdDisplay {
public:
static void rounder_event_cb(lv_event_t* e) {
lv_area_t* area = (lv_area_t*)lv_event_get_param(e);
uint16_t x1 = area->x1;
uint16_t x2 = area->x2;
uint16_t y1 = area->y1;
uint16_t y2 = area->y2;
// round the start of coordinate down to the nearest 2M number
area->x1 = (x1 >> 1) << 1;
area->y1 = (y1 >> 1) << 1;
// round the end of coordinate up to the nearest 2N+1 number
area->x2 = ((x2 >> 1) << 1) + 1;
area->y2 = ((y2 >> 1) << 1) + 1;
}
CustomLcdDisplay(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t panel_handle,
int width, int height, int offset_x, int offset_y, bool mirror_x,
bool mirror_y, bool swap_xy)
: SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x,
mirror_y, swap_xy) {}
virtual void SetupUI() override {
// Call parent SetupUI() first to create all lvgl objects
SpiLcdDisplay::SetupUI();
DisplayLockGuard lock(this);
lv_obj_set_style_pad_left(top_bar_, 60, 0);
lv_obj_set_style_pad_right(top_bar_, 60, 0);
lv_display_add_event_cb(display_, rounder_event_cb, LV_EVENT_INVALIDATE_AREA, NULL);
}
};
class CustomBacklight : public Backlight {
public:
CustomBacklight(esp_lcd_panel_io_handle_t panel_io) : Backlight(), panel_io_(panel_io) {}
protected:
esp_lcd_panel_io_handle_t panel_io_;
virtual void SetBrightnessImpl(uint8_t brightness) override {
auto display = Board::GetInstance().GetDisplay();
DisplayLockGuard lock(display);
uint8_t data[1] = {((uint8_t)((255 * brightness) / 100))};
int lcd_cmd = 0x51;
lcd_cmd &= 0xff;
lcd_cmd <<= 8;
lcd_cmd |= 0x02UL << 24;
esp_lcd_panel_io_tx_param(panel_io_, lcd_cmd, &data, sizeof(data));
}
};
class WaveshareEsp32c6TouchAMOLED2inch16 : public WifiBoard {
private:
i2c_master_bus_handle_t i2c_bus_;
Pmic* pmic_ = nullptr;
Button boot_button_;
CustomLcdDisplay* display_;
CustomBacklight* backlight_;
PowerSaveTimer* power_save_timer_;
void InitializePowerSaveTimer() {
power_save_timer_ = new PowerSaveTimer(-1, 120, 1200);
power_save_timer_->OnEnterSleepMode([this]() {
GetDisplay()->SetPowerSaveMode(true);
GetBacklight()->SetBrightness(20);
});
power_save_timer_->OnExitSleepMode([this]() {
GetDisplay()->SetPowerSaveMode(false);
GetBacklight()->RestoreBrightness();
});
power_save_timer_->OnShutdownRequest([this]() { pmic_->PowerOff(); });
power_save_timer_->SetEnabled(true);
}
void InitializeCodecI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = I2C_NUM_0,
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
.clk_source = I2C_CLK_SRC_DEFAULT,
.flags =
{
.enable_internal_pullup = 1,
},
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
}
void InitializeAxp2101() {
ESP_LOGI(TAG, "Init AXP2101");
pmic_ = new Pmic(i2c_bus_, 0x34);
}
void InitializeSpi() {
spi_bus_config_t buscfg = {};
buscfg.sclk_io_num = LCD_PCLK;
buscfg.data0_io_num = LCD_D0;
buscfg.data1_io_num = LCD_D1;
buscfg.data2_io_num = LCD_D2;
buscfg.data3_io_num = LCD_D3;
buscfg.max_transfer_sz = LCD_H_RES * LCD_V_RES * sizeof(uint16_t);
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting) {
EnterWifiConfigMode();
return;
}
app.ToggleChatState();
});
}
int DisplayPort_DispReset() {
pmic_->Pmic_SetAldo3(1);
vTaskDelay(pdMS_TO_TICKS(100));
pmic_->Pmic_SetAldo3(0);
vTaskDelay(pdMS_TO_TICKS(100));
pmic_->Pmic_SetAldo3(1);
vTaskDelay(pdMS_TO_TICKS(100));
return ESP_OK;
}
void InitializeSH8601Display() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config =
SH8601_PANEL_IO_QSPI_CONFIG(LCD_CS, nullptr, nullptr);
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片
ESP_LOGD(TAG, "Install LCD driver");
const sh8601_vendor_config_t vendor_config = {
.init_cmds = &vendor_specific_init[0],
.init_cmds_size = sizeof(vendor_specific_init) / sizeof(sh8601_lcd_init_cmd_t),
.flags = {
.use_qspi_interface = 1,
}};
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = LCD_RST;
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
panel_config.bits_per_pixel = 16;
panel_config.vendor_config = (void*)&vendor_config;
ESP_ERROR_CHECK(esp_lcd_new_panel_sh8601(panel_io, &panel_config, &panel));
ESP_ERROR_CHECK(DisplayPort_DispReset());
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
display_ = new CustomLcdDisplay(panel_io, panel, LCD_H_RES, LCD_V_RES, DISPLAY_OFFSET_X,
DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y,
DISPLAY_SWAP_XY);
backlight_ = new CustomBacklight(panel_io);
backlight_->RestoreBrightness();
}
void InitializeTouch() {
esp_lcd_touch_handle_t tp;
esp_lcd_touch_config_t tp_cfg = {
.x_max = LCD_H_RES - 1,
.y_max = LCD_V_RES - 1,
.rst_gpio_num = TP_RST_GPIO,
.int_gpio_num = TP_INT_GPIO,
.levels =
{
.reset = 0,
.interrupt = 0,
},
.flags =
{
.swap_xy = 1,
.mirror_x = 0,
.mirror_y = 1,
},
};
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_CST9217_CONFIG();
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_LOGI(TAG, "Initialize touch controller");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_cst9217(tp_io_handle, &tp_cfg, &tp));
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = lv_display_get_default(),
.handle = tp,
};
lvgl_port_add_touch(&touch_cfg);
ESP_LOGI(TAG, "Touch panel initialized successfully");
}
// 初始化工具
void InitializeTools() {
auto& mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.system.reconfigure_wifi",
"End this conversation and enter WiFi configuration mode.\n"
"**CAUTION** You must ask the user to confirm this action.",
PropertyList(), [this](const PropertyList& properties) {
EnterWifiConfigMode();
return true;
});
}
public:
WaveshareEsp32c6TouchAMOLED2inch16() : boot_button_(BOOT_BUTTON_GPIO) {
InitializePowerSaveTimer();
InitializeCodecI2c();
InitializeAxp2101();
InitializeSpi();
InitializeSH8601Display();
InitializeTouch();
InitializeButtons();
InitializeTools();
}
virtual AudioCodec* GetAudioCodec() override {
static BoxAudioCodec audio_codec(
i2c_bus_, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, AUDIO_I2S_GPIO_MCLK,
AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN,
AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR, AUDIO_CODEC_ES7210_ADDR,
AUDIO_INPUT_REFERENCE);
return &audio_codec;
}
virtual Display* GetDisplay() override { return display_; }
virtual Backlight* GetBacklight() override { return backlight_; }
virtual bool GetBatteryLevel(int& level, bool& charging, bool& discharging) override {
static bool last_discharging = false;
charging = pmic_->IsCharging();
discharging = pmic_->IsDischarging();
if (discharging != last_discharging) {
power_save_timer_->SetEnabled(discharging);
last_discharging = discharging;
}
level = pmic_->GetBatteryLevel();
return true;
}
virtual void SetPowerSaveLevel(PowerSaveLevel level) override {
if (level != PowerSaveLevel::LOW_POWER) {
power_save_timer_->WakeUp();
}
WifiBoard::SetPowerSaveLevel(level);
}
};
DECLARE_BOARD(WaveshareEsp32c6TouchAMOLED2inch16);
@@ -171,7 +171,20 @@ 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_CST816S_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS,
.on_color_trans_done = 0,
.user_ctx = 0,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.lcd_param_bits = 0,
.flags =
{
.dc_low_on_data = 0,
.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_LOGI(TAG, "Initialize touch controller");
@@ -200,7 +200,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_GT911_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 16,
.flags =
{
.disable_control_phase = 1,
}
};
tp_io_config.scl_speed_hz = 100 * 1000;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(codec_i2c_bus_, &tp_io_config, &tp_io_handle));
ESP_LOGI(TAG, "Initialize touch controller");
@@ -0,0 +1,11 @@
# Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5
**ESP32-P4-WIFI6-Touch-LCD-3.5** is the ESP32-P4 +ESP32-C6 screen development board launched by waveshare.
## Configuration
Configuration in `menuconfig`.
Selection Board Type `Xiaozhi Assistant --> Board Type`
- Waveshare ESP32-P4-WIFI6-Touch-LCD-3.5
@@ -0,0 +1,45 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_13
#define AUDIO_I2S_GPIO_WS GPIO_NUM_10
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_12
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_11
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_9
#define AUDIO_CODEC_PA_PIN GPIO_NUM_53
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_7
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_8
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR
#define BOOT_BUTTON_GPIO GPIO_NUM_35
#define DISPLAY_WIDTH (320)
#define DISPLAY_HEIGHT (480)
#define PIN_NUM_LCD_RST GPIO_NUM_27
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_28
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define TOUCH_RST_PIN GPIO_NUM_29
#define TOUCH_INT_PIN GPIO_NUM_50
#define LCD_SPI_MOSI_PIN GPIO_NUM_20
#define LCD_SPI_CLK_PIN GPIO_NUM_21
#define LCD_SPI_CS_PIN GPIO_NUM_23
#define LCD_SPI_DC_PIN GPIO_NUM_26
#define DISPLAY_SWAP_XY false
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#endif // _BOARD_CONFIG_H_
@@ -0,0 +1,17 @@
{
"manufacturer": "waveshare",
"target": "esp32p4",
"builds": [
{
"name": "esp32-p4-wifi6-touch-lcd-3.5",
"sdkconfig_append": [
"CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0",
"CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH=y",
"CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_5=y",
"CONFIG_CAMERA_OV5647=y",
"CONFIG_CAMERA_OV5647_AUTO_DETECT_MIPI_INTERFACE_SENSOR=y",
"CONFIG_XIAOZHI_ENABLE_CAMERA_ENDIANNESS_SWAP=y"
]
}
]
}
@@ -0,0 +1,191 @@
#include "wifi_board.h"
#include "codecs/es8311_audio_codec.h"
#include "application.h"
#include "display/lcd_display.h"
#include "button.h"
#include "esp_video.h"
#include "esp_video_init.h"
#include "esp_lcd_panel_ops.h"
#include <driver/spi_master.h>
#include "esp_lcd_st7796.h"
#include "config.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
#include <esp_lvgl_port.h>
#include "esp_lcd_touch_ft5x06.h"
#define TAG "WaveshareEsp32p4"
class WaveshareEsp32p4 : public WifiBoard {
private:
i2c_master_bus_handle_t i2c_bus_;
Button boot_button_;
LcdDisplay *display_;
EspVideo* camera_ = nullptr;
esp_err_t i2c_device_probe(uint8_t addr) {
return i2c_master_probe(i2c_bus_, addr, 100);
}
void InitializeCodecI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = I2C_NUM_1,
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.intr_priority = 0,
.trans_queue_depth = 0,
.flags = {
.enable_internal_pullup = 1,
},
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
}
void InitializeLCD() {
esp_lcd_panel_io_handle_t io = NULL;
esp_lcd_panel_handle_t disp_panel = NULL;
spi_bus_config_t buscfg = {};
buscfg.sclk_io_num = LCD_SPI_CLK_PIN;
buscfg.mosi_io_num = LCD_SPI_MOSI_PIN;
buscfg.miso_io_num = GPIO_NUM_NC;
buscfg.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = (DISPLAY_WIDTH * DISPLAY_HEIGHT);
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.dc_gpio_num = LCD_SPI_DC_PIN;
io_config.cs_gpio_num = LCD_SPI_CS_PIN;
io_config.pclk_hz = (80 * 1000 * 1000);
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
io_config.spi_mode = 3;
io_config.trans_queue_depth = 10;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &io));
const esp_lcd_panel_dev_config_t lcd_dev_config = {
.reset_gpio_num = PIN_NUM_LCD_RST,
.color_space = ESP_LCD_COLOR_SPACE_BGR,
.bits_per_pixel = 16,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_st7796(io, &lcd_dev_config, &disp_panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(disp_panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(disp_panel));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(disp_panel, true));
esp_lcd_panel_disp_on_off(disp_panel, true);
ESP_ERROR_CHECK(esp_lcd_panel_mirror(disp_panel, true, false));
display_ = new SpiLcdDisplay(io, disp_panel, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
}
void InitializeTouch()
{
esp_lcd_touch_handle_t tp;
esp_lcd_touch_config_t tp_cfg = {
.x_max = DISPLAY_WIDTH,
.y_max = DISPLAY_HEIGHT,
.rst_gpio_num = TOUCH_RST_PIN,
.int_gpio_num = TOUCH_INT_PIN,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 1,
.mirror_x = 1,
.mirror_y = 0,
},
};
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
esp_lcd_panel_io_i2c_config_t tp_io_config = {};
tp_io_config.dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS;
tp_io_config.control_phase_bytes = 1;
tp_io_config.dc_bit_offset = 0;
tp_io_config.lcd_cmd_bits = 8;
tp_io_config.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_ft5x06(tp_io_handle, &tp_cfg, &tp));
lvgl_port_touch_cfg_t touch_cfg = {};
touch_cfg.disp = lv_display_get_default();
touch_cfg.handle = tp;
lvgl_port_add_touch(&touch_cfg);
}
void InitializeCamera() {
esp_video_init_csi_config_t base_csi_config = {
.sccb_config = {
.init_sccb = false,
.i2c_handle = i2c_bus_,
.freq = 400000,
},
.reset_pin = GPIO_NUM_NC,
.pwdn_pin = GPIO_NUM_NC,
};
esp_video_init_config_t cam_config = {
.csi = &base_csi_config,
};
camera_ = new EspVideo(cam_config);
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
auto state = app.GetDeviceState();
if (state == kDeviceStateStarting ||
state == kDeviceStateConnecting ||
state == kDeviceStateWifiConfiguring) {
EnterWifiConfigMode();
return;
}
esp_restart();
});
Application::GetInstance().StartListening();
}
public:
WaveshareEsp32p4() :
boot_button_(BOOT_BUTTON_GPIO) {
InitializeCodecI2c();
InitializeLCD();
InitializeTouch();
InitializeCamera();
InitializeButtons();
GetBacklight()->RestoreBrightness();
}
virtual AudioCodec* GetAudioCodec() override {
static Es8311AudioCodec audio_codec(
i2c_bus_,
I2C_NUM_1,
AUDIO_INPUT_SAMPLE_RATE,
AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_GPIO_MCLK,
AUDIO_I2S_GPIO_BCLK,
AUDIO_I2S_GPIO_WS,
AUDIO_I2S_GPIO_DOUT,
AUDIO_I2S_GPIO_DIN,
AUDIO_CODEC_PA_PIN,
AUDIO_CODEC_ES8311_ADDR,
true,
false);
return &audio_codec;
}
virtual Display *GetDisplay() override {
return display_;
}
virtual Camera* GetCamera() override {
return camera_;
}
virtual Backlight* GetBacklight() override {
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
return &backlight;
}
};
DECLARE_BOARD(WaveshareEsp32p4);
@@ -9,6 +9,7 @@ Configuration in `menuconfig`.
Selection Board Type `Xiaozhi Assistant --> Board Type`
- Waveshare ESP32-P4-WIFI6-Touch-LCD-4B
- Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3
- Waveshare ESP32-P4-WIFI6-Touch-LCD-7B
- Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C
- Waveshare ESP32-P4-WIFI6-Touch-LCD-4C
@@ -29,6 +29,13 @@
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_26
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true
#define LCD_MIPI_DSI_LANE_BITRATE_MBPS (480)
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3
#define DISPLAY_WIDTH (480)
#define DISPLAY_HEIGHT (800)
#define PIN_NUM_LCD_RST GPIO_NUM_27
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_26
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true
#define LCD_MIPI_DSI_LANE_BITRATE_MBPS (500)
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B
#define DISPLAY_WIDTH (1024)
#define DISPLAY_HEIGHT (600)
@@ -16,6 +16,20 @@
"CONFIG_XIAOZHI_ENABLE_CAMERA_ENDIANNESS_SWAP=y"
]
},
{
"name": "esp32-p4-wifi6-touch-lcd-4.3",
"sdkconfig_append": [
"CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0",
"CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH=y",
"CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3=y",
"CONFIG_USE_WECHAT_MESSAGE_STYLE=n",
"CONFIG_USE_DEVICE_AEC=y",
"CONFIG_CAMERA_OV5647=y",
"CONFIG_CAMERA_OV5647_AUTO_DETECT_MIPI_INTERFACE_SENSOR=y",
"CONFIG_CAMERA_OV5647_MIPI_RAW8_800X800_50FPS=y",
"CONFIG_XIAOZHI_ENABLE_CAMERA_ENDIANNESS_SWAP=y"
]
},
{
"name": "esp32-p4-wifi6-touch-lcd-7b",
"sdkconfig_append": [
@@ -15,6 +15,8 @@
#if CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B
#include "esp_lcd_st7703.h"
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3
#include "esp_lcd_st7701.h"
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B
#include "esp_lcd_ek79007.h"
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C || CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C \
@@ -136,6 +138,46 @@ private:
.vendor_config = &vendor_config,
};
esp_lcd_new_panel_st7703(io, &lcd_dev_config, &disp_panel);
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3
esp_lcd_dpi_panel_config_t dpi_config = {
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
.dpi_clock_freq_mhz = 30,
.pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565,
.num_fbs = 1,
.video_timing = {
.h_size = 480,
.v_size = 800,
.hsync_pulse_width = 12,
.hsync_back_porch = 42,
.hsync_front_porch = 42,
.vsync_pulse_width = 8,
.vsync_back_porch = 2,
.vsync_front_porch = 60,
},
.flags = {
.use_dma2d = true,
},
};
st7701_vendor_config_t vendor_config = {
.init_cmds = vendor_specific_init_default,
.init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(st7701_lcd_init_cmd_t),
.mipi_config = {
.dsi_bus = mipi_dsi_bus,
.dpi_config = &dpi_config,
},
.flags = {
.use_mipi_interface = 1,
},
};
const esp_lcd_panel_dev_config_t lcd_dev_config = {
.reset_gpio_num = PIN_NUM_LCD_RST,
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
.bits_per_pixel = 16,
.vendor_config = &vendor_config,
};
esp_lcd_new_panel_st7701(io, &lcd_dev_config, &disp_panel);
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B
esp_lcd_dpi_panel_config_t dpi_config = {
.dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT,
@@ -294,7 +336,7 @@ private:
esp_lcd_touch_config_t tp_cfg = {
.x_max = DISPLAY_WIDTH,
.y_max = DISPLAY_HEIGHT,
.rst_gpio_num = GPIO_NUM_23,
.rst_gpio_num = GPIO_NUM_NC,
.int_gpio_num = GPIO_NUM_NC,
.levels = {
.reset = 0,
@@ -307,8 +349,17 @@ 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_GT911_CONFIG();
if (ESP_OK == i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS)) {
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 16,
.flags =
{
.disable_control_phase = 1,
}
};
if (ESP_OK == i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS)) {
ESP_LOGI(TAG, "Touch panel found at address 0x%02X", ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS);
} else if (ESP_OK == i2c_device_probe(ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP)) {
ESP_LOGI(TAG, "Touch panel found at address 0x%02X", ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP);
@@ -1,5 +1,56 @@
#ifdef CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B
// Leave it blank
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3
static const st7701_lcd_init_cmd_t vendor_specific_init_default[] = {
{0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x13}, 5, 0},
{0xEF, (uint8_t[]){0x08}, 1, 0},
{0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x10}, 5, 0},
{0xC0, (uint8_t[]){0x63, 0x00}, 2, 0},
{0xC1, (uint8_t[]){0x0D, 0x02}, 2, 0},
{0xC2, (uint8_t[]){0x17, 0x08}, 2, 0},
{0xCC, (uint8_t[]){0x10}, 1, 0},
{0xB0, (uint8_t[]){0x40, 0xC9, 0x94, 0x0E, 0x10, 0x05, 0x0B, 0x09, 0x08, 0x26, 0x04, 0x52, 0x10, 0x69, 0x6B, 0x69}, 16, 0},
{0xB1, (uint8_t[]){0x40, 0xD2, 0x98, 0x0C, 0x92, 0x07, 0x09, 0x08, 0x07, 0x25, 0x02, 0x0E, 0x0C, 0x6E, 0x78, 0x55}, 16, 0},
{0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x11}, 5, 0},
{0xB0, (uint8_t[]){0x5D}, 1, 0},
{0xB1, (uint8_t[]){0x4E}, 1, 0},
{0xB2, (uint8_t[]){0x87}, 1, 0},
{0xB3, (uint8_t[]){0x80}, 1, 0},
{0xB5, (uint8_t[]){0x4E}, 1, 0},
{0xB7, (uint8_t[]){0x85}, 1, 0},
{0xB8, (uint8_t[]){0x21}, 1, 0},
{0xB9, (uint8_t[]){0x10, 0x1F}, 2, 0},
{0xBB, (uint8_t[]){0x03}, 1, 0},
{0xBC, (uint8_t[]){0x00}, 1, 0},
{0xC1, (uint8_t[]){0x78}, 1, 0},
{0xC2, (uint8_t[]){0x78}, 1, 0},
{0xD0, (uint8_t[]){0x88}, 1, 0},
{0xE0, (uint8_t[]){0x00, 0x3A, 0x02}, 3, 0},
{0xE1, (uint8_t[]){0x04, 0xA0, 0x00, 0xA0, 0x05, 0xA0, 0x00, 0xA0, 0x00, 0x40, 0x40}, 11, 0},
{0xE2, (uint8_t[]){0x30, 0x00, 0x40, 0x40, 0x32, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0xA0, 0x00}, 13, 0},
{0xE3, (uint8_t[]){0x00, 0x00, 0x33, 0x33}, 4, 0},
{0xE4, (uint8_t[]){0x44, 0x44}, 2, 0},
{0xE5, (uint8_t[]){0x09, 0x2E, 0xA0, 0xA0, 0x0B, 0x30, 0xA0, 0xA0, 0x05, 0x2A, 0xA0, 0xA0, 0x07, 0x2C, 0xA0, 0xA0}, 16, 0},
{0xE6, (uint8_t[]){0x00, 0x00, 0x33, 0x33}, 4, 0},
{0xE7, (uint8_t[]){0x44, 0x44}, 2, 0},
{0xE8, (uint8_t[]){0x08, 0x2D, 0xA0, 0xA0, 0x0A, 0x2F, 0xA0, 0xA0, 0x04, 0x29, 0xA0, 0xA0, 0x06, 0x2B, 0xA0, 0xA0}, 16, 0},
{0xEB, (uint8_t[]){0x00, 0x00, 0x4E, 0x4E, 0x00, 0x00, 0x00}, 7, 0},
{0xEC, (uint8_t[]){0x08, 0x01}, 2, 0},
{0xED, (uint8_t[]){0xB0, 0x2B, 0x98, 0xA4, 0x56, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x65, 0x4A, 0x89, 0xB2, 0x0B}, 16, 0},
{0xEF, (uint8_t[]){0x08, 0x08, 0x08, 0x45, 0x3F, 0x54}, 6, 0},
{0xFF, (uint8_t[]){0x77, 0x01, 0x00, 0x00, 0x00}, 5, 0},
{0x11, (uint8_t[]){0x00}, 0, 120},
{0x29, (uint8_t[]){0x00}, 0, 0},
};
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B
// Leave it blank
#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C
@@ -0,0 +1,6 @@
新增 微雪 开发板: ESP32-S3-CAM-XXXX
产品链接:
https://www.waveshare.net/shop/ESP32-S3-CAM-OV5640.htm
https://www.waveshare.net/shop/ESP32-S3-CAM-OV3660.htm
https://www.waveshare.net/shop/ESP32-S3-CAM-GC2145.htm
https://www.waveshare.net/shop/ESP32-S3-CAM-GC0308.htm
+115
View File
@@ -0,0 +1,115 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#include <driver/spi_master.h>
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_10
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_11
#define AUDIO_I2S_GPIO_WS GPIO_NUM_12
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_13
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_14
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR
#define I2C_SCL_IO GPIO_NUM_7
#define I2C_SDA_IO GPIO_NUM_8
#define BSP_IO_EXPANDER_I2C_ADDRESS CUSTOM_IO_EXPANDER_I2C_CH32V003_ADDRESS
#define DISPLAY_SDA_PIN I2C_SDA_IO
#define DISPLAY_SCL_PIN I2C_SCL_IO
#define DISPLAY_MISO_PIN GPIO_NUM_2
#define DISPLAY_MOSI_PIN GPIO_NUM_1
#define DISPLAY_SCLK_PIN GPIO_NUM_5
#define DISPLAY_CS_PIN GPIO_NUM_6
#define DISPLAY_DC_PIN GPIO_NUM_3
#define DISPLAY_RESET_PIN GPIO_NUM_NC
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_NC
#define DISPLAY_SPI_SCLK_HZ (40 * 1000 * 1000)
/* Camera pins */
#define CAMERA_PIN_PWDN GPIO_NUM_NC
#define CAMERA_PIN_RESET GPIO_NUM_NC
#define CAMERA_PIN_XCLK GPIO_NUM_38
#define CAMERA_PIN_SIOD GPIO_NUM_NC
#define CAMERA_PIN_SIOC GPIO_NUM_NC
#define CAMERA_PIN_D7 GPIO_NUM_21
#define CAMERA_PIN_D6 GPIO_NUM_39
#define CAMERA_PIN_D5 GPIO_NUM_40
#define CAMERA_PIN_D4 GPIO_NUM_42
#define CAMERA_PIN_D3 GPIO_NUM_46
#define CAMERA_PIN_D2 GPIO_NUM_48
#define CAMERA_PIN_D1 GPIO_NUM_47
#define CAMERA_PIN_D0 GPIO_NUM_45
#define CAMERA_PIN_VSYNC GPIO_NUM_17
#define CAMERA_PIN_HREF GPIO_NUM_18
#define CAMERA_PIN_PCLK GPIO_NUM_41
#define XCLK_FREQ_HZ 20000000
#ifdef CONFIG_BSP_LCD_SIZE_2INCH
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 320
#define DISPLAY_SWAP_XY false
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_INVERT_COLOR true
#define BACKLIGHT_INVERT false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#endif
#ifdef CONFIG_BSP_LCD_SIZE_2_8INCH
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 320
#define DISPLAY_SWAP_XY false
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_INVERT_COLOR true
#define BACKLIGHT_INVERT false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#endif
#ifdef CONFIG_BSP_LCD_SIZE_3_5INCH
#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 480
#define DISPLAY_SWAP_XY false
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y false
#define DISPLAY_INVERT_COLOR true
#define BACKLIGHT_INVERT false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#endif
#ifdef CONFIG_BSP_LCD_SIZE_1_83INCH
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 284
#define DISPLAY_SWAP_XY false
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_INVERT_COLOR true
#define BACKLIGHT_INVERT false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#endif
#endif // _BOARD_CONFIG_H_
@@ -0,0 +1,38 @@
{
"manufacturer": "waveshare",
"target": "esp32s3",
"builds": [
{
"name": "esp32-s3-cam-2",
"sdkconfig_append": [
"CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX=y",
"CONFIG_USE_DEVICE_AEC=y",
"CONFIG_BSP_LCD_SIZE_2INCH=y"
]
},
{
"name": "esp32-s3-cam-2.8",
"sdkconfig_append": [
"CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX=y",
"CONFIG_USE_DEVICE_AEC=y",
"CONFIG_BSP_LCD_SIZE_2_8INCH=y"
]
},
{
"name": "esp32-s3-cam-3.5",
"sdkconfig_append": [
"CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX=y",
"CONFIG_USE_DEVICE_AEC=y",
"CONFIG_BSP_LCD_SIZE_3_5INCH=y"
]
},
{
"name": "esp32-s3-cam-1.83",
"sdkconfig_append": [
"CONFIG_BOARD_TYPE_WAVESHARE_ESP32_S3_CAM_XXXX=y",
"CONFIG_USE_DEVICE_AEC=y",
"CONFIG_BSP_LCD_SIZE_1_83INCH=y"
]
}
]
}
@@ -0,0 +1,282 @@
#include "wifi_board.h"
#include "codecs/box_audio_codec.h"
#include "display/lcd_display.h"
#include "system_reset.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include <esp_log.h>
#include "i2c_device.h"
#include <driver/i2c_master.h>
#include <driver/ledc.h>
#include <wifi_station.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <esp_timer.h>
#include "esp32_camera.h"
#include "esp_lcd_panel_vendor.h"
#include "custom_io_expander_ch32v003.h"
#include "esp_lcd_st7796.h"
#include "esp_ota_ops.h"
#define TAG "waveshare_s3_cam_xxxx"
#define LCD_OPCODE_WRITE_CMD (0x02ULL)
#define LCD_OPCODE_READ_CMD (0x0BULL)
#define LCD_OPCODE_WRITE_COLOR (0x32ULL)
void switch_to_main(void)
{
const esp_partition_t *factory_part =
esp_partition_find_first(ESP_PARTITION_TYPE_APP,
ESP_PARTITION_SUBTYPE_APP_FACTORY,
NULL);
if (!factory_part) {
ESP_LOGE("APP_SWITCH", "未找到 factory 分区");
return;
}
esp_err_t err = esp_ota_set_boot_partition(factory_part);
if (err == ESP_OK) {
ESP_LOGI("APP_SWITCH", "已设置 factory 为启动分区,重启回到主程序");
esp_restart();
} else {
ESP_LOGE("APP_SWITCH", "设置启动分区失败: %s", esp_err_to_name(err));
}
}
class CustomBacklight : public Backlight {
public:
CustomBacklight(esp_io_expander_handle_t io_handle)
: Backlight(), io_handle_(io_handle) {}
protected:
esp_io_expander_handle_t io_handle_;
virtual void SetBrightnessImpl(uint8_t brightness) override {
if (brightness > 100) brightness = 100;
int flipped_brightness = 100 - brightness;
custom_io_expander_set_pwm(io_handle_, flipped_brightness * 255 / 100);
}
};
class CustomBoard : public WifiBoard {
private:
Button boot_button_;
i2c_master_bus_handle_t i2c_bus_;
LcdDisplay* display_;
Esp32Camera* camera_;
esp_io_expander_handle_t io_expander = NULL;
CustomBacklight *backlight_;
void InitializeI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = (i2c_port_t)1,
.sda_io_num = I2C_SDA_IO,
.scl_io_num = I2C_SCL_IO,
.clk_source = I2C_CLK_SRC_DEFAULT,
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
}
void Initialize_Expander(void)
{
custom_io_expander_new_i2c_ch32v003(i2c_bus_, BSP_IO_EXPANDER_I2C_ADDRESS, &io_expander);
esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1 | IO_EXPANDER_PIN_NUM_4 | IO_EXPANDER_PIN_NUM_6, IO_EXPANDER_OUTPUT);
esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1 , 1);
vTaskDelay(pdMS_TO_TICKS(10));
esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1 , 0);
vTaskDelay(pdMS_TO_TICKS(10));
esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1 , 1);
vTaskDelay(pdMS_TO_TICKS(10));
esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_4 | IO_EXPANDER_PIN_NUM_6 , 1);
//custom_io_expander_set_pwm(io_expander,100);
}
void InitializeSpi() {
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
buscfg.miso_io_num = GPIO_NUM_NC;
buscfg.sclk_io_num = DISPLAY_SCLK_PIN;
buscfg.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
}
void InitializeSt7789Display() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS_PIN;
io_config.dc_gpio_num = DISPLAY_DC_PIN;
io_config.spi_mode = 0;
io_config.pclk_hz = DISPLAY_SPI_SCLK_HZ;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = GPIO_NUM_NC;
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
panel_config.bits_per_pixel = 16;
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel,true));
display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
backlight_ = new CustomBacklight(io_expander);
backlight_->RestoreBrightness();
}
void InitializeSt7796Display() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS_PIN;
io_config.dc_gpio_num = DISPLAY_DC_PIN;
io_config.spi_mode = 0;
io_config.pclk_hz = DISPLAY_SPI_SCLK_HZ;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = GPIO_NUM_NC;
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR;
panel_config.bits_per_pixel = 16;
ESP_ERROR_CHECK(esp_lcd_new_panel_st7796(panel_io, &panel_config, &panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel,true));
display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
backlight_ = new CustomBacklight(io_expander);
backlight_->RestoreBrightness();
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting) {
EnterWifiConfigMode();
return;
}
app.ToggleChatState();
});
boot_button_.OnLongPress([this]() {
auto& app = Application::GetInstance();
switch_to_main();
});
#if CONFIG_USE_DEVICE_AEC
boot_button_.OnDoubleClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateIdle) {
app.SetAecMode(app.GetAecMode() == kAecOff ? kAecOnDeviceSide : kAecOff);
}
});
#endif
}
void InitializeCamera() {
camera_config_t camera_config = {
.pin_pwdn = CAMERA_PIN_PWDN,
.pin_reset = CAMERA_PIN_RESET,
.pin_xclk = CAMERA_PIN_XCLK,
.pin_sccb_sda = -1, // Use initialized I2C
.pin_sccb_scl = -1,
.pin_d7 = CAMERA_PIN_D7,
.pin_d6 = CAMERA_PIN_D6,
.pin_d5 = CAMERA_PIN_D5,
.pin_d4 = CAMERA_PIN_D4,
.pin_d3 = CAMERA_PIN_D3,
.pin_d2 = CAMERA_PIN_D2,
.pin_d1 = CAMERA_PIN_D1,
.pin_d0 = CAMERA_PIN_D0,
.pin_vsync = CAMERA_PIN_VSYNC,
.pin_href = CAMERA_PIN_HREF,
.pin_pclk = CAMERA_PIN_PCLK,
.xclk_freq_hz = XCLK_FREQ_HZ,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_RGB565,
.frame_size = FRAMESIZE_QVGA,
.jpeg_quality = 12,
.fb_count = 2,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
.sccb_i2c_port = (i2c_port_t)1,
};
camera_ = new Esp32Camera(camera_config);
if (camera_ != nullptr) {
camera_->SetVFlip(true);
}
}
public:
CustomBoard() :
boot_button_(BOOT_BUTTON_GPIO) {
InitializeI2c();
Initialize_Expander();
InitializeSpi();
InitializeButtons();
#ifdef CONFIG_BSP_LCD_SIZE_2INCH
InitializeSt7789Display();
#elif CONFIG_BSP_LCD_SIZE_2_8INCH
InitializeSt7789Display();
#elif CONFIG_BSP_LCD_SIZE_1_83INCH
InitializeSt7789Display();
#elif CONFIG_BSP_LCD_SIZE_3_5INCH
InitializeSt7796Display();
#endif
InitializeCamera();
}
virtual AudioCodec* GetAudioCodec() override {
static BoxAudioCodec audio_codec(i2c_bus_, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN, AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR, AUDIO_CODEC_ES7210_ADDR, AUDIO_INPUT_REFERENCE);
return &audio_codec;
}
virtual Display* GetDisplay() override {
return display_;
}
virtual Camera* GetCamera() override {
return camera_;
}
};
DECLARE_BOARD(CustomBoard);
@@ -0,0 +1,60 @@
# 产品链接
[微雪电子 ESP32-S3-LCD-0.85](https://www.waveshare.net/shop/ESP32-S3-LCD-0.85.htm)
# 编译配置命令
**克隆工程**
```bash
git clone https://github.com/78/xiaozhi-esp32.git
```
**进入工程**
```bash
cd xiaozhi-esp32
```
**配置编译目标为 ESP32C6**
```bash
idf.py set-target esp32S3
```
**打开 menuconfig**
```bash
idf.py menuconfig
```
**选择板子**
```bash
Xiaozhi Assistant -> Board Type -> Waveshare ESP32-S3-LCD-0.85
```
**修改 flash 大小:**
```
Serial flasher config -> Flash size -> 8 MB
```
**修改分区表:**
```
Partition Table -> Custom partition CSV file -> partitions/v2/8m.csv
```
**编译**
```ba
idf.py build
```
**下载并打开串口终端**
```bash
idf.py build flash monitor
```
@@ -0,0 +1,56 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#include <driver/spi_master.h>
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_8
#define AUDIO_I2S_GPIO_WS GPIO_NUM_10
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_9
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_11
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_12
#define AUDIO_CODEC_PA_PIN GPIO_NUM_7
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_42
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_41
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR
#define BUILTIN_LED_GPIO GPIO_NUM_48
#define PWR_BUTTON_GPIO GPIO_NUM_5
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_4
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_0
#define DISPLAY_SPI_MODE 3
#define DISPLAY_CS_PIN GPIO_NUM_21
#define DISPLAY_MOSI_PIN GPIO_NUM_39
#define DISPLAY_MISO_PIN GPIO_NUM_NC
#define DISPLAY_CLK_PIN GPIO_NUM_38
#define DISPLAY_DC_PIN GPIO_NUM_45
#define DISPLAY_RST_PIN GPIO_NUM_40
#define TOOUCH_INT_PIN GPIO_NUM_48
#define TOOUCH_RST_PIN GPIO_NUM_47
#define BATTERY_EN_PIN GPIO_NUM_2
#define BATTERY_ADC_PIN GPIO_NUM_1
#define BATTERY_CHARGING_PIN GPIO_NUM_3
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 128
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y true
#define DISPLAY_SWAP_XY false
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
#define DISPLAY_INVERT_COLOR true
#define DISPLAY_OFFSET_X 2
#define DISPLAY_OFFSET_Y 1
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_46
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#endif // _BOARD_CONFIG_H_
@@ -0,0 +1,15 @@
{
"manufacturer": "waveshare",
"target": "esp32s3",
"builds": [
{
"name": "esp32-s3-lcd-0.85",
"sdkconfig_append": [
"CONFIG_USE_DEVICE_AEC=y",
"CONFIG_USE_WECHAT_MESSAGE_STYLE=n",
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\""
]
}
]
}
@@ -0,0 +1,311 @@
#include "wifi_board.h"
#include "codecs/box_audio_codec.h"
#include "display/lcd_display.h"
#include "system_reset.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "mcp_server.h"
#include <esp_log.h>
#include "i2c_device.h"
#include <driver/i2c_master.h>
#include <driver/ledc.h>
#include <wifi_station.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <esp_timer.h>
#include "power_save_timer.h"
#include "assets/lang_config.h"
#include <esp_lvgl_port.h>
#include "driver/gpio.h"
#include "iot_button.h"
#include "power_manager.h"
#include "led/circular_strip.h"
#include "led_strip_control.h"
#define TAG "waveshare_lcd_0_85"
class CustomButton: public Button{
public:
void OnPressDownDel(void) {
if (button_handle_ == nullptr) {
return;
}
on_press_down_ = NULL;
iot_button_unregister_cb(button_handle_, BUTTON_PRESS_DOWN, nullptr);
}
void OnPressUpDel(void) {
if (button_handle_ == nullptr) {
return;
}
on_press_up_ = NULL;
iot_button_unregister_cb(button_handle_, BUTTON_PRESS_UP, nullptr);
}
};
class CustomBoard : public WifiBoard {
private:
CustomButton pwr_button_;
CustomButton volume_up_button_;
CustomButton volume_down_button_;
i2c_master_bus_handle_t i2c_bus_;
CircularStrip* led_strip_;
LcdDisplay* display_;
PowerManager* power_manager_ = nullptr;
PowerSaveTimer* power_save_timer_ = nullptr;
void InitializePowerManager() {
power_manager_ = new PowerManager(BATTERY_CHARGING_PIN, BATTERY_ADC_PIN, BATTERY_EN_PIN);
power_manager_->PowerON();
}
void InitializePowerSaveTimer() {
power_save_timer_ = new PowerSaveTimer(-1, 60, 300);
power_save_timer_->OnEnterSleepMode([this]() {
ESP_LOGI(TAG, "Enabling sleep mode");
auto display = GetDisplay();
display->SetChatMessage("system", "");
display->SetEmotion("sleepy");
GetBacklight()->SetBrightness(20);
});
power_save_timer_->OnExitSleepMode([this]() {
auto display = GetDisplay();
display->SetChatMessage("system", "");
display->SetEmotion("neutral");
GetBacklight()->RestoreBrightness();
});
power_save_timer_->OnShutdownRequest([this]() {
power_manager_->PowerOff();
});
power_save_timer_->SetEnabled(true);
}
void InitializeI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = (i2c_port_t)0,
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.intr_priority = 0,
.trans_queue_depth = 0,
.flags = {
.enable_internal_pullup = 1,
},
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
}
void InitializeSpi() {
ESP_LOGI(TAG, "Initialize QSPI bus");
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
buscfg.miso_io_num = DISPLAY_MISO_PIN;
buscfg.sclk_io_num = DISPLAY_CLK_PIN;
buscfg.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
}
void InitializeLed() {
led_strip_ = new CircularStrip(BUILTIN_LED_GPIO, 8);
new LedStripControl(led_strip_);
}
void InitializeLcdDisplay() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGI(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS_PIN;
io_config.dc_gpio_num = DISPLAY_DC_PIN;
io_config.spi_mode = DISPLAY_SPI_MODE;
io_config.pclk_hz = 40 * 1000 * 1000;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片
ESP_LOGI(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
panel_config.bits_per_pixel = 16;
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
esp_lcd_panel_reset(panel);
esp_lcd_panel_init(panel);
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
}
void InitializeButtons() {
pwr_button_.OnPressUp([this]() {
pwr_button_.OnClick([this]() {
auto& app = Application::GetInstance();
app.ToggleChatState();
});
pwr_button_.OnLongPress([this]() {
printf("Power button long press\n");
if (power_manager_ != nullptr){
power_manager_->PowerOff();
}
});
pwr_button_.OnDoubleClick([this]() {
static uint8_t brightness_last = 0;
auto backlight = Board::GetInstance().GetBacklight();
if (backlight->brightness() == 0) {
brightness_last = 0;
if (brightness_last == 0) {
backlight->SetBrightness(50, true);
} else {
backlight->SetBrightness(brightness_last, true);
}
} else {
brightness_last = backlight->brightness();
backlight->SetBrightness(0);
}
});
pwr_button_.OnMultipleClick([this]() {
EnterWifiConfigMode();
}, 3);
pwr_button_.OnPressUpDel();
});
volume_up_button_.OnClick([this]() {
auto codec = Board::GetInstance().GetAudioCodec();
auto volume = codec->output_volume() + 10;
if (volume > 100) {
volume = 100;
}
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume/10));
});
volume_up_button_.OnLongPress([this]() {
Board::GetInstance().GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
});
volume_down_button_.OnClick([this]() {
auto codec = Board::GetInstance().GetAudioCodec();
auto volume = codec->output_volume() - 10;
if (volume < 0) {
volume = 0;
}
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume/10));
});
volume_down_button_.OnLongPress([this]() {
Board::GetInstance().GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
});
#if CONFIG_USE_DEVICE_AEC
volume_down_button_.OnDoubleClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateIdle) {
app.SetAecMode(app.GetAecMode() == kAecOff ? kAecOnDeviceSide : kAecOff);
}
});
#endif
}
// 初始化工具
void InitializeTools() {
auto &mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.system.reconfigure_wifi",
"Reboot the device and enter WiFi configuration mode.\n"
"**CAUTION** You must ask the user to confirm this action.",
PropertyList(), [this](const PropertyList& properties) {
EnterWifiConfigMode();
return true;
});
}
public:
CustomBoard() :
pwr_button_(PWR_BUTTON_GPIO),
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
InitializePowerManager();
InitializePowerSaveTimer();
InitializeI2c();
InitializeSpi();
InitializeLcdDisplay();
InitializeButtons();
InitializeLed();
InitializeTools();
GetBacklight()->RestoreBrightness();
}
virtual AudioCodec* GetAudioCodec() override {
static BoxAudioCodec audio_codec(
i2c_bus_,
AUDIO_INPUT_SAMPLE_RATE,
AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_GPIO_MCLK,
AUDIO_I2S_GPIO_BCLK,
AUDIO_I2S_GPIO_WS,
AUDIO_I2S_GPIO_DOUT,
AUDIO_I2S_GPIO_DIN,
AUDIO_CODEC_PA_PIN,
AUDIO_CODEC_ES8311_ADDR,
AUDIO_CODEC_ES7210_ADDR,
true);
return &audio_codec;
}
virtual Led* GetLed() override {
return led_strip_;
}
virtual Display* GetDisplay() override {
return display_;
}
virtual Backlight* GetBacklight() override {
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
return &backlight;
}
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
static bool last_discharging = false;
charging = power_manager_->IsCharging();
discharging = power_manager_->IsDischarging();
if (discharging != last_discharging) {
power_save_timer_->SetEnabled(discharging);
last_discharging = discharging;
}
level = power_manager_->GetBatteryLevel();
return true;
}
virtual void SetPowerSaveLevel(PowerSaveLevel level) override {
if (level != PowerSaveLevel::LOW_POWER) {
power_save_timer_->WakeUp();
}
WifiBoard::SetPowerSaveLevel(level);
}
};
DECLARE_BOARD(CustomBoard);
@@ -0,0 +1,130 @@
#include "led_strip_control.h"
#include "settings.h"
#include "mcp_server.h"
#include <esp_log.h>
#define TAG "LedStripControl"
int LedStripControl::LevelToBrightness(int level) const {
if (level < 0) level = 0;
if (level > 8) level = 8;
return (1 << level) - 1; // 2^n - 1
}
StripColor LedStripControl::RGBToColor(int red, int green, int blue) {
if (red < 0) red = 0;
if (red > 255) red = 255;
if (green < 0) green = 0;
if (green > 255) green = 255;
if (blue < 0) blue = 0;
if (blue > 255) blue = 255;
return {static_cast<uint8_t>(red), static_cast<uint8_t>(green), static_cast<uint8_t>(blue)};
}
LedStripControl::LedStripControl(CircularStrip* led_strip)
: led_strip_(led_strip) {
// 从设置中读取亮度等级
Settings settings("led_strip");
brightness_level_ = settings.GetInt("brightness", 4); // 默认等级4
led_strip_->SetBrightness(LevelToBrightness(brightness_level_), 4);
auto& mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.led_strip.get_brightness",
"Get the brightness of the led strip (0-8)",
PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
return brightness_level_;
});
mcp_server.AddTool("self.led_strip.set_brightness",
"Set the brightness of the led strip (0-8)",
PropertyList({
Property("level", kPropertyTypeInteger, 0, 8)
}), [this](const PropertyList& properties) -> ReturnValue {
int level = properties["level"].value<int>();
ESP_LOGI(TAG, "Set LedStrip brightness level to %d", level);
brightness_level_ = level;
led_strip_->SetBrightness(LevelToBrightness(brightness_level_), 4);
// 保存设置
Settings settings("led_strip", true);
settings.SetInt("brightness", brightness_level_);
return true;
});
mcp_server.AddTool("self.led_strip.set_single_color",
"Set the color of a single led.",
PropertyList({
Property("index", kPropertyTypeInteger, 0, 7),
Property("red", kPropertyTypeInteger, 0, 255),
Property("green", kPropertyTypeInteger, 0, 255),
Property("blue", kPropertyTypeInteger, 0, 255)
}), [this](const PropertyList& properties) -> ReturnValue {
int index = properties["index"].value<int>();
int red = properties["red"].value<int>();
int green = properties["green"].value<int>();
int blue = properties["blue"].value<int>();
ESP_LOGI(TAG, "Set led strip single color %d to %d, %d, %d",
index, red, green, blue);
led_strip_->SetSingleColor(index, RGBToColor(green, red, blue));
return true;
});
mcp_server.AddTool("self.led_strip.set_all_color",
"Set the color of all leds.",
PropertyList({
Property("red", kPropertyTypeInteger, 0, 255),
Property("green", kPropertyTypeInteger, 0, 255),
Property("blue", kPropertyTypeInteger, 0, 255)
}), [this](const PropertyList& properties) -> ReturnValue {
int red = properties["red"].value<int>();
int green = properties["green"].value<int>();
int blue = properties["blue"].value<int>();
ESP_LOGI(TAG, "Set led strip all color to %d, %d, %d",
red, green, blue);
led_strip_->SetAllColor(RGBToColor(green, red, blue));
return true;
});
mcp_server.AddTool("self.led_strip.blink",
"Blink the led strip. (闪烁)",
PropertyList({
Property("red", kPropertyTypeInteger, 0, 255),
Property("green", kPropertyTypeInteger, 0, 255),
Property("blue", kPropertyTypeInteger, 0, 255),
Property("interval", kPropertyTypeInteger, 0, 1000)
}), [this](const PropertyList& properties) -> ReturnValue {
int red = properties["red"].value<int>();
int green = properties["green"].value<int>();
int blue = properties["blue"].value<int>();
int interval = properties["interval"].value<int>();
ESP_LOGI(TAG, "Blink led strip with color %d, %d, %d, interval %dms",
red, green, blue, interval);
led_strip_->Blink(RGBToColor(green, red, blue), interval);
return true;
});
mcp_server.AddTool("self.led_strip.scroll",
"Scroll the led strip. (跑马灯)",
PropertyList({
Property("red", kPropertyTypeInteger, 0, 255),
Property("green", kPropertyTypeInteger, 0, 255),
Property("blue", kPropertyTypeInteger, 0, 255),
Property("length", kPropertyTypeInteger, 1, 7),
Property("interval", kPropertyTypeInteger, 0, 1000)
}), [this](const PropertyList& properties) -> ReturnValue {
int red = properties["red"].value<int>();
int green = properties["green"].value<int>();
int blue = properties["blue"].value<int>();
int interval = properties["interval"].value<int>();
int length = properties["length"].value<int>();
ESP_LOGI(TAG, "Scroll led strip with color %d, %d, %d, length %d, interval %dms",
red, green, blue, length, interval);
StripColor low = RGBToColor(4, 4, 4);
StripColor high = RGBToColor(green, red, blue);
led_strip_->Scroll(low, high, length, interval);
return true;
});
}
@@ -0,0 +1,18 @@
#ifndef LED_STRIP_CONTROL_H
#define LED_STRIP_CONTROL_H
#include "led/circular_strip.h"
class LedStripControl {
private:
CircularStrip* led_strip_;
int brightness_level_; // 亮度等级 (0-8)
int LevelToBrightness(int level) const; // 将等级转换为实际亮度值
StripColor RGBToColor(int red, int green, int blue);
public:
explicit LedStripControl(CircularStrip* led_strip);
};
#endif // LED_STRIP_CONTROL_H
@@ -0,0 +1,212 @@
#pragma once
#include <vector>
#include <functional>
#include <esp_log.h>
#include <esp_timer.h>
#include <driver/gpio.h>
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include <math.h>
class PowerManager
{
private:
gpio_num_t charging_pin_ = GPIO_NUM_NC;
gpio_num_t bat_adc_pin_ = GPIO_NUM_NC;
gpio_num_t bat_power_pin_ = GPIO_NUM_NC;
adc_oneshot_unit_handle_t adc_handle_ = NULL;
adc_cali_handle_t adc_cali_handle_ = NULL;
adc_channel_t adc_channel_;
bool do_calibration = false;
bool adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
{
adc_cali_handle_t handle = NULL;
esp_err_t ret = ESP_FAIL;
bool calibrated = false;
if (!calibrated)
{
ESP_LOGI("power_manager", "calibration scheme version is %s", "Curve Fitting");
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = unit,
.chan = channel,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
if (ret == ESP_OK)
{
calibrated = true;
}
}
*out_handle = handle;
if (ret == ESP_OK)
{
ESP_LOGI("power_manager", "Calibration Success");
}
else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated)
{
ESP_LOGW("power_manager", "eFuse not burnt, skip software calibration");
}
else
{
ESP_LOGE("power_manager", "Invalid arg or no memory");
}
return calibrated;
}
public:
PowerManager(gpio_num_t charging_pin, gpio_num_t bat_adc_pin, gpio_num_t bat_power_pin) : charging_pin_(charging_pin),
bat_adc_pin_(bat_adc_pin),
bat_power_pin_(bat_power_pin)
{
// 初始化充电引脚
if (charging_pin_ != GPIO_NUM_NC)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = 1ULL << charging_pin_;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
}
// 初始化电池使能引脚
if (bat_power_pin_ != GPIO_NUM_NC)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = 1ULL << bat_power_pin_;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
}
// 初始化adc
if (bat_adc_pin_ != GPIO_NUM_NC)
{
adc_oneshot_unit_init_cfg_t init_config = {};
init_config.ulp_mode = ADC_ULP_MODE_DISABLE;
if (bat_adc_pin_ >= GPIO_NUM_1 && bat_adc_pin_ <= GPIO_NUM_10)
{
init_config.unit_id = ADC_UNIT_1;
adc_channel_ = (adc_channel_t)((int)bat_adc_pin_ - 1);
}
else if (bat_adc_pin_ >= GPIO_NUM_11 && bat_adc_pin_ <= GPIO_NUM_20)
{
init_config.unit_id = ADC_UNIT_2;
adc_channel_ = (adc_channel_t)((int)bat_adc_pin_ - 11);
}
else
{
return;
}
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_handle_));
adc_oneshot_chan_cfg_t config = {};
config.bitwidth = ADC_BITWIDTH_DEFAULT;
config.atten = ADC_ATTEN_DB_12;
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle_, adc_channel_, &config));
do_calibration = adc_calibration_init(init_config.unit_id, adc_channel_, config.atten, &adc_cali_handle_);
}
}
~PowerManager()
{
if (adc_handle_)
{
ESP_ERROR_CHECK(adc_oneshot_del_unit(adc_handle_));
}
// if (adc_cali_handle_)
// {
// ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(adc_cali_handle_));
// }
}
int GetBatteryLevel(void)
{
int adc_raw = 0;
int voltage_int = 0;
const float voltage_float_threshold = 0.1f;
float voltage_float = 0.0f;
static float last_voltage_float = 0.0f;
static int last_battery_level = 0;
if (adc_handle_ != nullptr)
{
ESP_ERROR_CHECK(adc_oneshot_read(adc_handle_, adc_channel_, &adc_raw));
if (do_calibration)
{
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc_cali_handle_, adc_raw, &voltage_int));
voltage_float = (voltage_int / 1000.0f) * 3.0;
if (fabs(voltage_float - last_voltage_float) >= voltage_float_threshold)
{
last_voltage_float = voltage_float;
if (voltage_float < 3.52) {
last_battery_level = 1;
} else if (voltage_float < 3.64) {
last_battery_level = 20;
} else if (voltage_float < 3.76) {
last_battery_level = 40;
} else if (voltage_float < 3.88) {
last_battery_level = 60;
} else if (voltage_float < 4.0) {
last_battery_level = 80;
} else {
last_battery_level = 100;
}
}
return last_battery_level;
}
}
return 100;
}
bool IsCharging(void)
{
if (charging_pin_ != GPIO_NUM_NC)
{
return gpio_get_level(charging_pin_) == 0 ? true : false;
}
return false;
}
bool IsDischarging(void)
{
if (charging_pin_ != GPIO_NUM_NC)
{
return gpio_get_level(charging_pin_) == 1;
}
return true;
}
bool IsChargingDone(void)
{
if (GetBatteryLevel() == 100)
{
return true;
}
return false;
}
void PowerOff(void)
{
if (bat_power_pin_ != GPIO_NUM_NC)
{
gpio_set_level(bat_power_pin_, 0);
}
}
void PowerON(void)
{
if (bat_power_pin_ != GPIO_NUM_NC)
{
gpio_set_level(bat_power_pin_, 1);
}
}
};
@@ -265,7 +265,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_FT5x06_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.flags =
{
.disable_control_phase = 1,
}
};
tp_io_config.scl_speed_hz = 400 * 1000;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(codec_i2c_bus_, &tp_io_config, &tp_io_handle));
ESP_LOGI(TAG, "Initialize touch controller");
@@ -267,7 +267,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_FT5x06_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.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_LOGI(TAG, "Initialize touch controller");
@@ -0,0 +1,49 @@
# 产品链接
[微雪电子 ESP32-S3-Touch-LCD-1.54](https://www.waveshare.net/shop/ESP32-S3-Touch-LCD-1.54.htm)
[微雪电子 ESP32-S3-LCD-1.54](https://www.waveshare.net/shop/ESP32-S3-LCD-1.54.htm)
# 编译配置命令
**克隆工程**
```bash
git clone https://github.com/78/xiaozhi-esp32.git
```
**进入工程**
```bash
cd xiaozhi-esp32
```
**配置编译目标为 ESP32S3**
```bash
idf.py set-target esp32s3
```
**打开 menuconfig**
```bash
idf.py menuconfig
```
**选择板子**
```bash
Xiaozhi Assistant -> Board Type -> Waveshare ESP32-S3-Touch-LCD-1.54
```
**编译**
```ba
idf.py build
```
**下载并打开串口终端**
```bash
idf.py build flash monitor
```
@@ -0,0 +1,56 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#include <driver/spi_master.h>
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_8
#define AUDIO_I2S_GPIO_WS GPIO_NUM_10
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_9
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_11
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_12
#define AUDIO_CODEC_PA_PIN GPIO_NUM_7
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_42
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_41
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR
#define BUILTIN_LED_GPIO GPIO_NUM_NC
#define PWR_BUTTON_GPIO GPIO_NUM_5
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_4
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_0
#define DISPLAY_SPI_MODE 3
#define DISPLAY_CS_PIN GPIO_NUM_21
#define DISPLAY_MOSI_PIN GPIO_NUM_39
#define DISPLAY_MISO_PIN GPIO_NUM_NC
#define DISPLAY_CLK_PIN GPIO_NUM_38
#define DISPLAY_DC_PIN GPIO_NUM_45
#define DISPLAY_RST_PIN GPIO_NUM_40
#define TOOUCH_INT_PIN GPIO_NUM_48
#define TOOUCH_RST_PIN GPIO_NUM_47
#define BATTERY_EN_PIN GPIO_NUM_2
#define BATTERY_ADC_PIN GPIO_NUM_1
#define BATTERY_CHARGING_PIN GPIO_NUM_3
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 240
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_SWAP_XY false
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
#define DISPLAY_INVERT_COLOR true
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_46
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#endif // _BOARD_CONFIG_H_
@@ -0,0 +1,13 @@
{
"manufacturer": "waveshare",
"target": "esp32s3",
"builds": [
{
"name": "esp32-s3-touch-lcd-1.54",
"sdkconfig_append": [
"CONFIG_USE_WECHAT_MESSAGE_STYLE=n",
"CONFIG_USE_DEVICE_AEC=y"
]
}
]
}
@@ -0,0 +1,355 @@
#include "wifi_board.h"
#include "codecs/box_audio_codec.h"
#include "display/lcd_display.h"
#include "system_reset.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "mcp_server.h"
#include <esp_log.h>
#include "i2c_device.h"
#include <driver/i2c_master.h>
#include <wifi_station.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <esp_timer.h>
#include "power_save_timer.h"
#include "assets/lang_config.h"
#include <esp_lcd_touch_cst816s.h>
#include <esp_lvgl_port.h>
#include "driver/gpio.h"
#include "iot_button.h"
#include "power_manager.h"
#include "esp_ota_ops.h"
#define TAG "waveshare_esp32_s3_touch_lcd_1_54"
class CustomButton: public Button{
public:
void OnPressDownDel(void) {
if (button_handle_ == nullptr) {
return;
}
on_press_down_ = NULL;
iot_button_unregister_cb(button_handle_, BUTTON_PRESS_DOWN, nullptr);
}
void OnPressUpDel(void) {
if (button_handle_ == nullptr) {
return;
}
on_press_up_ = NULL;
iot_button_unregister_cb(button_handle_, BUTTON_PRESS_UP, nullptr);
}
};
class CustomBoard : public WifiBoard {
private:
CustomButton pwr_button_;
CustomButton volume_up_button_;
CustomButton volume_down_button_;
i2c_master_bus_handle_t i2c_bus_;
LcdDisplay* display_;
PowerManager* power_manager_ = nullptr;
PowerSaveTimer* power_save_timer_ = nullptr;
void InitializePowerManager() {
power_manager_ = new PowerManager(BATTERY_CHARGING_PIN, BATTERY_ADC_PIN, BATTERY_EN_PIN);
power_manager_->PowerON();
}
void InitializePowerSaveTimer() {
power_save_timer_ = new PowerSaveTimer(-1, 60, 300);
power_save_timer_->OnEnterSleepMode([this]() {
auto display = GetDisplay();
display->SetChatMessage("system", "");
display->SetEmotion("sleepy");
GetBacklight()->SetBrightness(20);
});
power_save_timer_->OnExitSleepMode([this]() {
auto display = GetDisplay();
display->SetChatMessage("system", "");
display->SetEmotion("neutral");
GetBacklight()->RestoreBrightness();
});
power_save_timer_->OnShutdownRequest([this]() {
power_manager_->PowerOff();
});
power_save_timer_->SetEnabled(true);
}
void InitializeI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = (i2c_port_t)0,
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.intr_priority = 0,
.trans_queue_depth = 0,
.flags = {
.enable_internal_pullup = 1,
},
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
}
void InitializeSpi() {
ESP_LOGI(TAG, "Initialize QSPI bus");
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
buscfg.miso_io_num = DISPLAY_MISO_PIN;
buscfg.sclk_io_num = DISPLAY_CLK_PIN;
buscfg.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
}
void InitializeTouch()
{
esp_err_t ret = ESP_OK;
esp_lcd_touch_handle_t touch_handle;
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS,
.on_color_trans_done = 0,
.user_ctx = 0,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.lcd_param_bits = 0,
.flags =
{
.dc_low_on_data = 0,
.disable_control_phase = 1,
},
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus_, &tp_io_config, &tp_io_handle));
esp_lcd_touch_config_t tp_cfg = {
.x_max = DISPLAY_WIDTH,
.y_max = DISPLAY_HEIGHT,
.rst_gpio_num = TOOUCH_RST_PIN,
.int_gpio_num = TOOUCH_INT_PIN,
.flags = {
.swap_xy = 0,
.mirror_x = 0,
.mirror_y = 0,
},
};
ESP_LOGI(TAG, "Initialize touch controller CST816");
ret = esp_lcd_touch_new_i2c_cst816s(tp_io_handle, &tp_cfg, &touch_handle);
if (ret == ESP_OK)
{
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = lv_display_get_default(),
.handle = touch_handle,
};
lvgl_port_add_touch(&touch_cfg);
ESP_LOGI(TAG, "Touch panel initialized successfully");
}
}
void InitializeLcdDisplay() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGI(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS_PIN;
io_config.dc_gpio_num = DISPLAY_DC_PIN;
io_config.spi_mode = DISPLAY_SPI_MODE;
io_config.pclk_hz = 40 * 1000 * 1000;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片
ESP_LOGI(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
panel_config.bits_per_pixel = 16;
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
esp_lcd_panel_reset(panel);
esp_lcd_panel_init(panel);
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY
);
}
void InitializeButtons() {
pwr_button_.OnPressUp([this]() {
pwr_button_.OnClick([this]() {
auto& app = Application::GetInstance();
app.ToggleChatState();
});
pwr_button_.OnLongPress([this]() {
printf("Power button long press\n");
// gpio_set_level(BATTERY_EN_PIN, 0);
if (power_manager_ != nullptr){
power_manager_->PowerOff();
}
});
pwr_button_.OnDoubleClick([this]() {
static uint8_t brightness_last = 0;
auto backlight = Board::GetInstance().GetBacklight();
if (backlight->brightness() == 0) {
brightness_last = 0;
if (brightness_last == 0) {
backlight->SetBrightness(50, true);
} else {
backlight->SetBrightness(brightness_last, true);
}
} else {
brightness_last = backlight->brightness();
backlight->SetBrightness(0);
}
});
pwr_button_.OnMultipleClick([this]() {
EnterWifiConfigMode();
}, 3);
pwr_button_.OnPressUpDel();
});
volume_up_button_.OnClick([this]() {
auto codec = Board::GetInstance().GetAudioCodec();
auto volume = codec->output_volume() + 10;
if (volume > 100) {
volume = 100;
}
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume/10));
});
volume_up_button_.OnLongPress([this]() {
Board::GetInstance().GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
});
volume_down_button_.OnClick([this]() {
auto codec = Board::GetInstance().GetAudioCodec();
auto volume = codec->output_volume() - 10;
if (volume < 0) {
volume = 0;
}
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume/10));
});
volume_down_button_.OnLongPress([this]() {
Board::GetInstance().GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
});
#if CONFIG_USE_DEVICE_AEC
volume_down_button_.OnDoubleClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateIdle) {
app.SetAecMode(app.GetAecMode() == kAecOff ? kAecOnDeviceSide : kAecOff);
}
});
#endif
}
// 初始化工具
void InitializeTools() {
auto &mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.system.reconfigure_wifi",
"Reboot the device and enter WiFi configuration mode.\n"
"**CAUTION** You must ask the user to confirm this action.",
PropertyList(), [this](const PropertyList& properties) {
EnterWifiConfigMode();
return true;
});
}
public:
CustomBoard() :
pwr_button_(PWR_BUTTON_GPIO),
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
InitializePowerManager();
InitializePowerSaveTimer();
InitializeI2c();
InitializeSpi();
InitializeLcdDisplay();
InitializeTouch();
InitializeButtons();
InitializeTools();
GetBacklight()->RestoreBrightness();
}
virtual AudioCodec* GetAudioCodec() override {
static BoxAudioCodec audio_codec(
i2c_bus_,
AUDIO_INPUT_SAMPLE_RATE,
AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_GPIO_MCLK,
AUDIO_I2S_GPIO_BCLK,
AUDIO_I2S_GPIO_WS,
AUDIO_I2S_GPIO_DOUT,
AUDIO_I2S_GPIO_DIN,
AUDIO_CODEC_PA_PIN,
AUDIO_CODEC_ES8311_ADDR,
AUDIO_CODEC_ES7210_ADDR,
true);
return &audio_codec;
}
virtual Display* GetDisplay() override {
return display_;
}
virtual Backlight* GetBacklight() override {
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
return &backlight;
}
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging) override {
static bool last_discharging = false;
charging = power_manager_->IsCharging();
discharging = power_manager_->IsDischarging();
if (discharging != last_discharging) {
power_save_timer_->SetEnabled(discharging);
last_discharging = discharging;
}
level = power_manager_->GetBatteryLevel();
return true;
}
virtual void SetPowerSaveLevel(PowerSaveLevel level) override {
if (level != PowerSaveLevel::LOW_POWER) {
power_save_timer_->WakeUp();
}
WifiBoard::SetPowerSaveLevel(level);
}
};
DECLARE_BOARD(CustomBoard);
@@ -0,0 +1,212 @@
#pragma once
#include <vector>
#include <functional>
#include <esp_log.h>
#include <esp_timer.h>
#include <driver/gpio.h>
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include <math.h>
class PowerManager
{
private:
gpio_num_t charging_pin_ = GPIO_NUM_NC;
gpio_num_t bat_adc_pin_ = GPIO_NUM_NC;
gpio_num_t bat_power_pin_ = GPIO_NUM_NC;
adc_oneshot_unit_handle_t adc_handle_ = NULL;
adc_cali_handle_t adc_cali_handle_ = NULL;
adc_channel_t adc_channel_;
bool do_calibration = false;
bool adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
{
adc_cali_handle_t handle = NULL;
esp_err_t ret = ESP_FAIL;
bool calibrated = false;
if (!calibrated)
{
ESP_LOGI("power_manager", "calibration scheme version is %s", "Curve Fitting");
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = unit,
.chan = channel,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
if (ret == ESP_OK)
{
calibrated = true;
}
}
*out_handle = handle;
if (ret == ESP_OK)
{
ESP_LOGI("power_manager", "Calibration Success");
}
else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated)
{
ESP_LOGW("power_manager", "eFuse not burnt, skip software calibration");
}
else
{
ESP_LOGE("power_manager", "Invalid arg or no memory");
}
return calibrated;
}
public:
PowerManager(gpio_num_t charging_pin, gpio_num_t bat_adc_pin, gpio_num_t bat_power_pin) : charging_pin_(charging_pin),
bat_adc_pin_(bat_adc_pin),
bat_power_pin_(bat_power_pin)
{
// 初始化充电引脚
if (charging_pin_ != GPIO_NUM_NC)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pin_bit_mask = 1ULL << charging_pin_;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&io_conf);
}
// 初始化电池使能引脚
if (bat_power_pin_ != GPIO_NUM_NC)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = 1ULL << bat_power_pin_;
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
}
// 初始化adc
if (bat_adc_pin_ != GPIO_NUM_NC)
{
adc_oneshot_unit_init_cfg_t init_config = {};
init_config.ulp_mode = ADC_ULP_MODE_DISABLE;
if (bat_adc_pin_ >= GPIO_NUM_1 && bat_adc_pin_ <= GPIO_NUM_10)
{
init_config.unit_id = ADC_UNIT_1;
adc_channel_ = (adc_channel_t)((int)bat_adc_pin_ - 1);
}
else if (bat_adc_pin_ >= GPIO_NUM_11 && bat_adc_pin_ <= GPIO_NUM_20)
{
init_config.unit_id = ADC_UNIT_2;
adc_channel_ = (adc_channel_t)((int)bat_adc_pin_ - 11);
}
else
{
return;
}
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_handle_));
adc_oneshot_chan_cfg_t config = {};
config.bitwidth = ADC_BITWIDTH_DEFAULT;
config.atten = ADC_ATTEN_DB_12;
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle_, adc_channel_, &config));
do_calibration = adc_calibration_init(init_config.unit_id, adc_channel_, config.atten, &adc_cali_handle_);
}
}
~PowerManager()
{
if (adc_handle_)
{
ESP_ERROR_CHECK(adc_oneshot_del_unit(adc_handle_));
}
// if (adc_cali_handle_)
// {
// ESP_ERROR_CHECK(adc_cali_delete_scheme_line_fitting(adc_cali_handle_));
// }
}
int GetBatteryLevel(void)
{
int adc_raw = 0;
int voltage_int = 0;
const float voltage_float_threshold = 0.1f;
float voltage_float = 0.0f;
static float last_voltage_float = 0.0f;
static int last_battery_level = 0;
if (adc_handle_ != nullptr)
{
ESP_ERROR_CHECK(adc_oneshot_read(adc_handle_, adc_channel_, &adc_raw));
if (do_calibration)
{
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc_cali_handle_, adc_raw, &voltage_int));
voltage_float = (voltage_int / 1000.0f) * 3.0;
if (fabs(voltage_float - last_voltage_float) >= voltage_float_threshold)
{
last_voltage_float = voltage_float;
if (voltage_float < 3.52) {
last_battery_level = 1;
} else if (voltage_float < 3.64) {
last_battery_level = 20;
} else if (voltage_float < 3.76) {
last_battery_level = 40;
} else if (voltage_float < 3.88) {
last_battery_level = 60;
} else if (voltage_float < 4.0) {
last_battery_level = 80;
} else {
last_battery_level = 100;
}
}
return last_battery_level;
}
}
return 100;
}
bool IsCharging(void)
{
if (charging_pin_ != GPIO_NUM_NC)
{
return gpio_get_level(charging_pin_) == 0 ? true : false;
}
return false;
}
bool IsDischarging(void)
{
if (charging_pin_ != GPIO_NUM_NC)
{
return gpio_get_level(charging_pin_) == 1;
}
return true;
}
bool IsChargingDone(void)
{
if (GetBatteryLevel() == 100)
{
return true;
}
return false;
}
void PowerOff(void)
{
if (bat_power_pin_ != GPIO_NUM_NC)
{
gpio_set_level(bat_power_pin_, 0);
}
}
void PowerON(void)
{
if (bat_power_pin_ != GPIO_NUM_NC)
{
gpio_set_level(bat_power_pin_, 1);
}
}
};
@@ -172,7 +172,20 @@ 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_CST816S_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_CST816S_ADDRESS,
.on_color_trans_done = 0,
.user_ctx = 0,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.lcd_param_bits = 0,
.flags =
{
.dc_low_on_data = 0,
.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_LOGI(TAG, "Initialize touch controller");
@@ -232,7 +232,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_FT5x06_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.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_LOGI(TAG, "Initialize touch controller");
@@ -168,7 +168,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_GT911_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_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_lcd_new_panel_io_i2c(i2c_bus_, &tp_io_config, &tp_io_handle);
@@ -276,7 +276,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_GT911_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_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_LOGI(TAG, "Initialize touch controller");
@@ -190,7 +190,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_FT5x06_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_FT5x06_ADDRESS,
.control_phase_bytes = 1,
.dc_bit_offset = 0,
.lcd_cmd_bits = 8,
.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_LOGI(TAG, "Initialize touch controller");
@@ -152,7 +152,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_GT911_CONFIG();
esp_lcd_panel_io_i2c_config_t tp_io_config = {
.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_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_LOGI(TAG, "Initialize touch controller");
+10 -12
View File
@@ -1075,18 +1075,6 @@ void LcdDisplay::SetEmotion(const char* emotion) {
if (!setup_ui_called_) {
ESP_LOGW(TAG, "SetEmotion('%s') called before SetupUI() - emotion will not be displayed!", emotion);
}
// Stop any running GIF animation
if (gif_controller_) {
DisplayLockGuard lock(this);
gif_controller_->Stop();
// Hide image before destroying GIF controller to prevent LVGL from
// accessing freed image data during rendering between lock scopes
if (emoji_image_) {
lv_obj_add_flag(emoji_image_, LV_OBJ_FLAG_HIDDEN);
}
gif_controller_.reset();
}
if (emoji_image_ == nullptr) {
if (setup_ui_called_) {
ESP_LOGW(TAG, "SetEmotion('%s') failed: emoji_image_ is nullptr (SetupUI() was called but emoji image not created)", emotion);
@@ -1100,6 +1088,10 @@ void LcdDisplay::SetEmotion(const char* emotion) {
const char* utf8 = font_awesome_get_utf8(emotion);
if (utf8 != nullptr && emoji_label_ != nullptr) {
DisplayLockGuard lock(this);
if (gif_controller_) {
gif_controller_->Stop();
gif_controller_.reset();
}
lv_label_set_text(emoji_label_, utf8);
lv_obj_add_flag(emoji_image_, LV_OBJ_FLAG_HIDDEN);
lv_obj_remove_flag(emoji_label_, LV_OBJ_FLAG_HIDDEN);
@@ -1108,6 +1100,12 @@ void LcdDisplay::SetEmotion(const char* emotion) {
}
DisplayLockGuard lock(this);
// Stop any running GIF animation in the same lock scope as setting new image
// to prevent LVGL from accessing freed image data between operations
if (gif_controller_) {
gif_controller_->Stop();
gif_controller_.reset();
}
if (image->IsGif()) {
// Create new GIF controller
gif_controller_ = std::make_unique<LvglGif>(image->image_dsc());
+11 -12
View File
@@ -20,22 +20,22 @@ dependencies:
waveshare/custom_io_expander_ch32v003: ^1.0.0
espressif/esp_lcd_panel_io_additions: ^1.0.1
78/esp_lcd_nv3023: ~1.0.0
78/esp-wifi-connect: ~3.1.1
78/esp-wifi-connect: ~3.1.2
espressif/esp_audio_effects: ~1.2.1
espressif/esp_audio_codec: ~2.4.1
78/esp-ml307: ~3.6.4
78/esp-ml307: ~3.6.5
78/uart-eth-modem:
version: ~0.3.3
version: ~0.3.4
rules:
- if: target not in [esp32]
78/xiaozhi-fonts: ~1.6.0
espressif/led_strip: ~3.0.2
espressif/esp_codec_dev: ~1.5.4
espressif/esp_codec_dev: ~1.5.6
espressif/esp-sr: ~2.3.0
espressif/button: ~4.1.5
espressif/knob: ^1.0.0
espressif/esp32-camera:
version: ^2.1.4
version: ^2.1.5
rules:
- if: target in [esp32s3]
espressif/esp_video:
@@ -47,23 +47,22 @@ dependencies:
version: ^1.0.1
rules:
- if: target not in [esp32]
espressif/esp_lcd_touch_ft5x06: ~1.0.7
espressif/esp_lcd_touch_ft5x06: ~1
espressif/esp_lcd_touch_gt911: ^1
espressif/esp_lcd_touch_gt1151: ^1
waveshare/esp_lcd_touch_cst9217: ^1.0.3
espressif/esp_lcd_touch_cst816s: ^1.0.6
lvgl/lvgl: ~9.4.0
esp_lvgl_port: ~2.7.0
espressif/esp_lcd_touch_cst816s: ^1
lvgl/lvgl: ~9.5.0
esp_lvgl_port: ~2.7.2
espressif/esp_io_expander_tca95xx_16bit: ^2.0.0
espressif2022/image_player: ^1.1.1
espressif2022/esp_emote_expression: ^0.1.0
espressif/adc_mic: ^0.2.1
espressif/esp_mmap_assets: '>=1.2'
espressif/esp_mmap_assets: ^1.3.2
txp666/otto-emoji-gif-component:
version: ^1.1.1
rules:
- if: target in [esp32s3]
espressif/adc_battery_estimation: ^0.2.0
espressif/adc_battery_estimation: ^0.2.1
espressif/esp_new_jpeg: ^0.6.1
# SenseCAP Watcher Board
+7 -3
View File
@@ -12,8 +12,10 @@
SingleLed::SingleLed(gpio_num_t gpio) {
// If the gpio is not connected, you should use NoLed class
assert(gpio != GPIO_NUM_NC);
if (gpio == GPIO_NUM_NC) {
ESP_LOGW(TAG, "SingleLed initialized with GPIO_NUM_NC, LED will not function");
return;
}
led_strip_config_t strip_config = {};
strip_config.strip_gpio_num = gpio;
@@ -41,7 +43,9 @@ SingleLed::SingleLed(gpio_num_t gpio) {
}
SingleLed::~SingleLed() {
esp_timer_stop(blink_timer_);
if (blink_timer_ != nullptr) {
esp_timer_stop(blink_timer_);
}
if (led_strip_ != nullptr) {
led_strip_del(led_strip_);
}
+2 -2
View File
@@ -147,7 +147,7 @@ bool WebsocketProtocol::OpenAudioChannel() {
}
} else {
// Parse JSON data
auto root = cJSON_Parse(data);
auto root = cJSON_ParseWithLength(data, len);
auto type = cJSON_GetObjectItem(root, "type");
if (cJSON_IsString(type)) {
if (strcmp(type->valuestring, "hello") == 0) {
@@ -158,7 +158,7 @@ bool WebsocketProtocol::OpenAudioChannel() {
}
}
} else {
ESP_LOGE(TAG, "Missing message type, data: %s", data);
ESP_LOGE(TAG, "Missing message type, data: %s", std::string(data, len).c_str());
}
cJSON_Delete(root);
}
+2 -2
View File
@@ -3,5 +3,5 @@
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 0x2CE000,
assets, data, spiffs, 0x2DE000, 0x122000,
factory, app, factory, 0x10000, 0x2F0000,
assets, data, spiffs, 0x300000, 0x100000,
1 # ESP-IDF Partition Table
3 nvs, data, nvs, 0x9000, 0x4000,
4 otadata, data, ota, 0xd000, 0x2000,
5 phy_init, data, phy, 0xf000, 0x1000,
6 factory, app, factory, 0x10000, 0x2CE000, factory, app, factory, 0x10000, 0x2F0000,
7 assets, data, spiffs, 0x2DE000, 0x122000, assets, data, spiffs, 0x300000, 0x100000,
+110 -13
View File
@@ -3,6 +3,7 @@ import os
import json
import zipfile
import argparse
import re
from pathlib import Path
from typing import Optional
@@ -139,26 +140,113 @@ def _collect_variants(config_filename: str = "config.json") -> list[dict[str, st
def _find_board_config(board_type: str) -> Optional[str]:
"""Find the corresponding CONFIG_BOARD_TYPE_xxx for the given board_type
Search backwards from 'set(BOARD_TYPE "xxx")' to find the nearest if(CONFIG_BOARD_TYPE_).
"""
def _find_board_config_candidates(board_type: str) -> list[str]:
"""Find all CONFIG_BOARD_TYPE_xxx candidates for the given board_type."""
board_leaf = board_type.split("/")[-1]
pattern = f'set(BOARD_TYPE "{board_leaf}")'
cmake_file = Path("main/CMakeLists.txt")
lines = cmake_file.read_text(encoding="utf-8").splitlines()
candidates: list[str] = []
for idx, line in enumerate(lines):
if pattern in line:
# Found the BOARD_TYPE line, search backwards for the config
# Found the BOARD_TYPE line, search backwards for the nearest config guard
for back_idx in range(idx - 1, -1, -1):
back_line = lines[back_idx]
if "if(CONFIG_BOARD_TYPE_" in back_line:
return back_line.strip().split("if(")[1].split(")")[0]
candidates.append(back_line.strip().split("if(")[1].split(")")[0])
break
return candidates
def _extract_board_config_from_sdkconfig_append(sdkconfig_append: list[str]) -> Optional[str]:
"""Extract explicit CONFIG_BOARD_TYPE_xxx=y from sdkconfig_append, if present."""
pattern = re.compile(r"^(CONFIG_BOARD_TYPE_[A-Z0-9_]+)=y$")
matches = []
for item in sdkconfig_append:
m = pattern.match(item.strip())
if m:
matches.append(m.group(1))
if not matches:
return None
uniq = list(dict.fromkeys(matches))
if len(uniq) > 1:
raise ValueError(f"Multiple board type configs found in sdkconfig_append: {uniq}")
return uniq[0]
def _symbol_supports_target(symbol: str, target: str) -> bool:
"""Check whether Kconfig symbol depends on given target (e.g. esp32c5)."""
kconfig_file = Path("main/Kconfig.projbuild")
if not kconfig_file.exists():
return False
target_flag = f"IDF_TARGET_{target.upper()}"
lines = kconfig_file.read_text(encoding="utf-8").splitlines()
in_symbol = False
for line in lines:
stripped = line.strip()
if stripped.startswith("config "):
curr_symbol = stripped.split("config ", 1)[1].strip()
in_symbol = curr_symbol == symbol
continue
if in_symbol and stripped.startswith(("config ", "choice ", "endchoice", "menu ", "endmenu")):
break
return None
if in_symbol and "depends on" in stripped and target_flag in stripped:
return True
return False
def _resolve_board_config(board_type: str, target: str, sdkconfig_append: list[str]) -> str:
"""Resolve CONFIG_BOARD_TYPE_xxx for current board build."""
explicit = _extract_board_config_from_sdkconfig_append(sdkconfig_append)
if explicit:
return explicit
candidates = _find_board_config_candidates(board_type)
if not candidates:
raise ValueError(f"Cannot find board config symbol for {board_type}")
if len(candidates) == 1:
return candidates[0]
by_target = [c for c in candidates if _symbol_supports_target(c, target)]
if len(by_target) == 1:
return by_target[0]
if len(by_target) > 1:
selected = by_target[0]
print(
f"[WARN] Ambiguous board config for {board_type} (target={target}), "
f"target-matched candidates={by_target}, selecting first: {selected}",
file=sys.stderr,
)
return selected
target_u = target.upper()
target_short = target_u.replace("ESP32", "")
by_name = [
c for c in candidates
if target_u in c or f"_{target_short}" in c
]
if len(by_name) == 1:
return by_name[0]
if len(by_name) > 1:
selected = by_name[0]
print(
f"[WARN] Ambiguous board config for {board_type} (target={target}), "
f"name-matched candidates={by_name}, selecting first: {selected}",
file=sys.stderr,
)
return selected
selected = candidates[0]
print(
f"[WARN] Ambiguous board config for {board_type} (target={target}), "
f"candidates={candidates}, selecting first: {selected}",
file=sys.stderr,
)
return selected
# Kconfig "select" entries are not automatically applied when we simply append
@@ -258,9 +346,18 @@ def release(board_type: str, config_filename: str = "config.json", *, filter_nam
continue
# Process sdkconfig_append
board_type_config = _find_board_config(board_type)
sdkconfig_append = [f"{board_type_config}=y"]
sdkconfig_append.extend(build.get("sdkconfig_append", []))
build_sdkconfig_append = build.get("sdkconfig_append", [])
explicit_board_cfg = _extract_board_config_from_sdkconfig_append(build_sdkconfig_append)
if explicit_board_cfg:
print(
f"[INFO] Board config explicitly set in config.json: {explicit_board_cfg}, "
"skip auto-select.",
)
sdkconfig_append = list(build_sdkconfig_append)
else:
board_type_config = _resolve_board_config(board_type, target, build_sdkconfig_append)
sdkconfig_append = [f"{board_type_config}=y"]
sdkconfig_append.extend(build_sdkconfig_append)
sdkconfig_append = _apply_auto_selects(sdkconfig_append)
print("-" * 80)
Submodule
+1
Submodule xiaozhi-esp32 added at 8fde7f3c62