Compare commits

...

68 Commits

Author SHA1 Message Date
Terrence
0f95444d62 Bump to 1.4.0 2025-03-03 21:41:41 +08:00
Terrence
01aab961ca set 20px font size 2025-03-03 15:25:55 +08:00
Terrence
7945da0c84 1.3.1 Updates
- Add startup and network failure sound effects
- 12864 OLED scroll text
- Internalization of volume actions
2025-03-03 07:29:22 +08:00
Terrence
36d98ce1a4 reboot => recheck 2025-03-03 07:17:32 +08:00
yusuhua
a872ca1bff 增加LilyGo T-CameraPlus-S3的支持及修复LilyGo T-Circle-S3编译i2s_std_clk_config_t报'i2s_std_clk_config_t::ext_clk_freq_hz' does not match declaration (#239)
* 增加LILYGO T-CameraPlus-S3支持

* 增加LILYGO T-CameraPlus-S3支持

* Remove .DS_Store files

* Remove .DS_Store files

* 将T-Circle-S3与T-CamerPlus-S3的codec放入boards下

* 移除T-CameraPlus-S的README3图片展示

* revert README files

* delete lilygo-t-cameraplus-s3.jpg

---------

Co-authored-by: yusuhua <yusuhua@baidu.com>
2025-03-01 22:06:53 +08:00
ZhouKe
7c2a64cc86 add support for GC9A01_240X240 ST7735 _128X128 ST7789_240X240_7PIN (#244)
* add GC9A01 240*240
add ST7735  128*128
add ST7789   240*240 7pin

* fix config for ILI9341

---------

Co-authored-by: zk <982145@qq.com>
2025-03-01 02:38:11 +08:00
WMnologo
cbf479f636 为星智开发板四个板级配置分别集成新的display文件,将oled的聊天信息显示改为滚动显示,加入了电量显示和睡眠功能 (#236) 2025-02-26 22:09:57 +08:00
Y1hsiaochunnn
e55166b3b3 Add backlight control logic code to Waveshare ESP32-S3-Touch-AMOLED-1.8 (#235)
* Add backlight control logic code to Waveshare ESP32-S3-Touch-AMOLED-1.8

* Update esp32-s3-touch-amoled-1.8.cc

---------

Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-26 21:59:33 +08:00
Terrence
9116dd6c07 Update QQ group number 2025-02-25 03:01:30 +08:00
Terrence
77db38ca4c lichuang c3 use smaller font to fit in partition 2025-02-25 02:32:40 +08:00
Terrence
80fd7736d5 fix sparkbot emotion color 2025-02-25 02:32:09 +08:00
Y1hsiaochunnn
799302e1e6 Added functionality to Waveshare ESP32-S3-Touch-AMOLED-1.8 (#228)
* Added functionality to Waveshare ESP32-S3-Touch-AMOLED-1.8

* Update board_control.cc

不要提交没有使用的功能到main分支

---------

Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-24 18:25:11 +08:00
Terrence
ffe0b21d18 Fix yuying compiling 2025-02-24 17:55:11 +08:00
Terrence
de783b7c57 Modify error messages 2025-02-24 15:47:44 +08:00
Terrence
6b855eae53 Fix SpiLcdDisplay 2025-02-24 15:46:29 +08:00
Terrence
a23a88cc5d Bump to 1.3.0 2025-02-24 14:41:34 +08:00
Terrence
c08a1a5310 fix compile warnings 2025-02-24 14:15:49 +08:00
Kevincoooool
b0f0f4d4db 增加LCD屏幕类型\RGB屏参考\增加鱼鹰3.13寸LCD开发板 (#191)
* 增加LCD屏幕类型\RGB屏参考\增加鱼鹰3.13寸LCD开发板

1、增加LCD屏幕类型判断,lvgl port对不同屏幕初始化不一样
2、增加RGB屏使用参考
3、增加鱼鹰3.13寸LCD开发板

* Update lcd_display.h

* 修改xingzhi两个board为SpiLcdDisplay
2025-02-24 11:07:28 +08:00
MakerM0
20d52def0b add 神奇按钮2.5和c3-v2 (#206)
* add 神奇按钮2.5

* add 神奇按钮 c3-v2
2025-02-22 03:12:39 +08:00
WMnologo
dc118d2f24 在README.md中加入了星智开发板。 修改了星智开发板的分支,对0.96oled、1.54tft屏幕细分了wifi和4g两个版本 (#210)
* Update README.md

* Add files via upload

* Update README.md

* Update README.md

* Update README.md

* 修改了星智的版级配置,对0.96oled、1.54tft屏幕细分了wifi和4g两个版本

* 修改了星知开发板支持,对0.96oled、1.54tft屏幕都细分了4g和wifi两个版本

---------

Co-authored-by: WMnologo <200~limaohui2016@icloud.com>
2025-02-22 02:55:26 +08:00
MakerM0
63d8b30c02 修改神奇按钮2.4板子的LED和按键功能 (#203)
* 修改LED和按键功能

* Update magiclick_2p4_board.cc

---------

Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-21 04:32:09 +08:00
Kevincoooool
add225ba56 改korvo板lcd cs引脚为NC,使用TCA9554扩展的IO3来拉低控制LCD CS (#199)
* 改korvo板lcd cs引脚为NC,使用TCA9554扩展的IO3来拉低控制LCD CS

* Update esp32s3_korvo2_v3_board.cc

---------

Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-21 02:15:13 +08:00
Terrence
216ebc16bf fix compiling error on windows 2025-02-20 18:38:24 +08:00
飞雪无情
e123e37f7d add issue template 2025-02-20 15:22:29 +08:00
Terrence
cd2fbf2a25 增加唤醒词播放添加设备验证码 2025-02-20 14:47:03 +08:00
Terrence
8659087f5d fix oled not displaying text 2025-02-20 14:21:41 +08:00
Terrence
455c71167e 给 xmini-c3 增加按键模式的控制 2025-02-20 05:04:35 +08:00
Terrence
01e82ba32e update restriction to release.py 2025-02-20 04:28:39 +08:00
Terrence
d0e3426d9f Update docs 2025-02-20 04:15:56 +08:00
Terrence
1d1fe3dd2e Sync language option to wifi config page 2025-02-20 00:26:24 +08:00
Terrence
939564b175 Add English system sounds 2025-02-19 23:54:59 +08:00
VinJay
d5594d01a3 添加多国语言支持 2025-02-18 19:33:07 +08:00
Kevincoooool
140aab8999 Update kevin-sp-v3_board.cc (#179)
* Update kevin-sp-v3_board.cc

* Update kevin-sp-v3_board.cc

---------

Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-18 13:54:48 +08:00
Terrence
898109b348 add default role to system 2025-02-18 13:40:56 +08:00
Terrence
10a05db4a1 Bump to 1.2.1 2025-02-18 05:31:30 +08:00
Terrence
35940f8bda fix network error 2025-02-18 05:14:53 +08:00
Terrence
db712fa078 remove unused code 2025-02-18 04:42:55 +08:00
Terrence
ade1e3193d 优化c3上的内存使用 2025-02-18 03:05:00 +08:00
Terrence
93915cd624 add config.json to boards for auto building 2025-02-18 00:52:37 +08:00
Terrence
ff18da5a29 fix display abort 2025-02-18 00:52:37 +08:00
Terrence
53cf361b58 remove unused code 2025-02-18 00:52:37 +08:00
zhou
bbdbbc4f28 rename main/boards/xingzhi-cube-tft/compact_wifi_board_lcd.cc to main/boards/xingzhi-cube-tft/xingzhi-cube-tft.cc (#175)
* Update CMakeLists.txt

* Update Kconfig.projbuild

* Add files via upload

* Update CMakeLists.txt

* Update Kconfig.projbuild

* update

* update

* update

* update xingzhi-cube-tft

* add xingzhi-cube-tft backlight control

* rename main/boards/xingzhi-cube-tft/compact_wifi_board_lcd.cc to main/boards/xingzhi-cube-tft/xingzhi-cube-tft.cc
2025-02-17 20:33:00 +08:00
ooxxU
6b2752a498 外接唤醒模组的支持,可以有多种自定义唤醒词,进行唤醒小智,ESP32(非C3,非S3) 面包板上已支持 (#172)
* 外接唤醒模组的支持,可以有多种自定义唤醒词,进行唤醒小智
ESP32(非C3,非S3) 面包板上已支持

* Update application.cc

外接唤醒模组的支持,好处是可以有多种自定义唤醒词,进行唤醒小智
唤醒模组需要一个GPIO Pin,设置成输出模式+高电平
对该Pin进行唤醒设置,1秒内的低电平脉冲,也就是小智的一个Click
可以参考 ESP32 面包板中的 asr_button_ 按钮的功能函数调用
本人测试采用ASR-ProV1.0版本的唤醒模组,测试内容包括:自定义唤醒词,唤醒词打断,唤醒词回应
此代码兼容其他型号的唤醒模组,并没做限制模组型号,方便大家使用
Modify By MarsBear

* Update esp32_bread_board.cc

---------

Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-17 18:47:21 +08:00
Xiaoxia
da2168ea73 Update README.md 2025-02-17 01:11:11 +08:00
Terrence
9ec5f2f908 shorter message to display on small LCD 2025-02-17 00:56:40 +08:00
Terrence
3a71c1e895 Enhance device activation and OTA update process
- Add support for device activation with audio feedback
- Refactor OTA update flow to include activation code handling
- Update asset management for localized sound resources
- Improve error handling and device state management
- Reorganize binary asset includes and CMake configuration
2025-02-16 06:59:19 +08:00
Terrence
14a89cae33 update ml307 version 2025-02-15 02:09:26 +08:00
dujianmin
3a2f6acc9a 修改代码语音控制LCD屏幕的亮度 (#165)
* 添加了嘟嘟电路板chatx

* 多写了一个空格。修改了一下

* 修改了屏幕的右边和下边有条纹的问题

* 修改屏幕亮度可以语言控制
2025-02-15 00:30:36 +08:00
二九
5bce5c3f70 Added several boards for Waveshare (#159)
* Added SPD2010 display adaptation

Added SPD2010 display adaptation

* Added other channel configurations

Added functions for other channel applications

* Add new boards

Add new boards

* Add new boards

Add new boards

* Update display compatibility

Update display compatibility

* The lcd display.cc changes are restored

The lcd display.cc changes are restored

* Modify the SPD2010 adaptation to the board file

Modify the SPD2010 adaptation to the board file

* The lcd display.cc changes are restored

The lcd display.cc changes are restored

* New backlight control

New backlight control

* New backlight control

New backlight control

* Add backlight controls

Add backlight controls

* Delete main/boards/esp32-s3-touch-lcd-1.85c/esp32-s3-touch-lcd-1.85c directory

Add path error

* Add backlight controls

Add backlight controls

* Update variable name

Update variable name

* Eliminate unnecessary programs

Eliminate unnecessary programs

* Update esp32-s3-touch-lcd-1.46.cc

* Update esp32-s3-touch-lcd-1.85.cc

Eliminate unnecessary programs

* Update esp32-s3-touch-lcd-1.85c.cc

Eliminate unnecessary programs

* Update no_audio_codec.cc

* Update esp32-s3-touch-lcd-1.46.cc

* Update esp32-s3-touch-lcd-1.85.cc

* Update esp32-s3-touch-lcd-1.85c.cc

---------

Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-14 17:53:22 +08:00
Kevincoooool
730275e4a7 增加Kevin SP V3开发板 (#160)
* 增加Kevin SP V3开发板

* Update config.h

* Update kevin-sp-v3_board.cc

---------

Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-14 17:37:30 +08:00
Forairaaaaa
f183b881b7 fix display rgb order (#158) 2025-02-14 14:00:42 +08:00
zhou
af79b70fb5 add xingzhi-cube-tft backlight control (#157)
* Update CMakeLists.txt

* Update Kconfig.projbuild

* Add files via upload

* Update CMakeLists.txt

* Update Kconfig.projbuild

* update

* update

* update

* update xingzhi-cube-tft

* add xingzhi-cube-tft backlight control
2025-02-14 13:56:04 +08:00
Terrence
1d1a0c43ba fix xmini-c3 toggle state 2025-02-14 05:20:59 +08:00
Terrence
8f6691859c remove unused code 2025-02-14 02:12:54 +08:00
MOV
4114ff213e fix; Moji GPIO conflict (#156)
* fix:Modify the README and add Moji images

* fix: Moji LCD initialization configuration.

* fix: DISPLAY_MIRROR_X false >> true

* fix: Moji GPIO conflict

* fix: Moji GPIO conflict
2025-02-14 01:49:18 +08:00
zhou
83d6fa9d26 删除无名科技ESP32S3星智开发板未使用的TOUCH_BUTTON;同时添加星智的TFT版本 (#155)
* Update CMakeLists.txt

* Update Kconfig.projbuild

* Add files via upload

* Update CMakeLists.txt

* Update Kconfig.projbuild

* update

* update

* update

* update xingzhi-cube-tft
2025-02-14 01:45:56 +08:00
Forairaaaaa
d7f41b4b4d add atoms3 + atomic echo base support (#154) 2025-02-14 01:36:08 +08:00
Li Junru
fed8cb4d86 feat: Add ESP SparkBot tracked chassis Support (#144)
* feat: Add ESP SparkBot tracked chassis Support

* feat: remove chassis.cc file to esp-sparkbot

---------

Co-authored-by: Liu Ruichao <liuruichao@espressif.com>
Co-authored-by: Xiaoxia <terrence@tenclass.com>
2025-02-14 01:30:00 +08:00
Forairaaaaa
bafc9def1a add no echo base check (#152) 2025-02-14 01:19:48 +08:00
Terrence
8ace1095e9 使用定时器调节屏幕亮度 2025-02-14 01:15:10 +08:00
Kevincoooool
9b8db4a551 iot增加屏幕背光亮度调节 (#150) 2025-02-14 00:21:46 +08:00
Terrence
b889355d8d 修复唤醒后亮红灯不说话的bug 2025-02-12 05:37:34 +08:00
ooxxU
5e406b481c 新增 ESP32 系列开发板 对 OLED-0.96 SSD1306 屏幕显示的支持 (#143)
1. 支持 ESP32 系列开发板: DevKitC / NodeMcu-32S / GoouuuESP32 / ESP32 DoIt / ESP-32S
2. 注意: 非ESP32-C3 / 非ESP32-S3
2025-02-12 02:41:23 +08:00
zhou
e19604b21e 增加无名科技的ESP32S3星智开发板 (#142)
* Update CMakeLists.txt

* Update Kconfig.projbuild

* Add files via upload

* Update CMakeLists.txt

* Update Kconfig.projbuild

* update

* update
2025-02-12 02:40:02 +08:00
Terrence
ac93e88d17 add uuid to board 2025-02-11 16:56:23 +08:00
Terrence
2d718a0cbc graceful code 2025-02-09 01:32:26 +08:00
Wang Yihua
24c10a607c feat: support JC3636W518 of taiji-pi-s3 board. (#131) 2025-02-09 00:55:30 +08:00
dujianmin
380f702637 修复了LCD屏幕底部条纹 (#132)
* 添加了嘟嘟电路板chatx

* 多写了一个空格。修改了一下

* 修改了屏幕的右边和下边有条纹的问题
2025-02-08 14:03:33 +08:00
205 changed files with 10467 additions and 478 deletions

View File

@@ -0,0 +1,103 @@
name: Installation or build bug report
description: Report installation or build bugs
labels: ['bug']
body:
- type: checkboxes
id: checklist
attributes:
label: Answers checklist.
description: Before submitting a new issue, please follow the checklist and try to find the answer.
options:
- label: I have read the documentation [XiaoZhi AI Programming Guide](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb) and the issue is not addressed there.
required: true
- label: I have updated my branch (master or release) to the latest version and checked that the issue is present there.
required: true
- label: I have searched the issue tracker for a similar issue and not found a similar issue.
required: true
- type: input
id: xiaozhi_ai_version
attributes:
label: XiaoZhi AI version.
description: On which XiaoZhi AI version does this issue occur on? Run `git describe --tags` to find it.
placeholder: ex. v1.1.0-44-g140aab8
validations:
required: true
- type: dropdown
id: operating_system
attributes:
label: Operating System used.
multiple: false
options:
- Windows
- Linux
- macOS
validations:
required: true
- type: dropdown
id: build
attributes:
label: How did you build your project?
multiple: false
options:
- Command line with CMake
- Command line with idf.py
- CLion IDE
- VS Code IDE/Cursor
- Other (please specify in More Information)
validations:
required: true
- type: dropdown
id: windows_comand_line
attributes:
label: If you are using Windows, please specify command line type.
multiple: false
options:
- PowerShell
- CMD
validations:
required: false
- type: textarea
id: expected
attributes:
label: What is the expected behavior?
description: Please provide a clear and concise description of the expected behavior.
placeholder: I expected it to...
validations:
required: true
- type: textarea
id: actual
attributes:
label: What is the actual behavior?
description: Please describe actual behavior.
placeholder: Instead it...
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce.
description: 'How do you trigger this bug? Please walk us through it step by step. If this is build bug, please attach sdkconfig file (from your project folder). Please attach your code here.'
value: |
1. Step
2. Step
3. Step
...
validations:
required: true
- type: textarea
id: debug_logs
attributes:
label: Build or installation Logs.
description: Build or installation log goes here, should contain the backtrace, as well as the reset source if it is a crash.
placeholder: Your log goes here.
render: plain
validations:
required: false
- type: textarea
id: more-info
attributes:
label: More Information.
description: Do you have any other information from investigating this?
placeholder: ex. Any more.
validations:
required: false

View File

@@ -0,0 +1,115 @@
name: Runtime bug report
description: Report runtime bugs
labels: ['bug']
body:
- type: checkboxes
id: checklist
attributes:
label: Answers checklist.
description: Before submitting a new issue, please follow the checklist and try to find the answer.
options:
- label: I have read the documentation [XiaoZhi AI Programming Guide](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb) and the issue is not addressed there.
required: true
- label: I have updated my firmware to the latest version and checked that the issue is present there.
required: true
- label: I have searched the issue tracker for a similar issue and not found a similar issue.
required: true
- type: input
id: xiaozhi_ai_firmware_version
attributes:
label: XiaoZhi AI firmware version.
description: On which firmware version does this issue occur on?
placeholder: ex. v1.2.1_bread-compact-wifi
validations:
required: true
- type: dropdown
id: operating_system
attributes:
label: Operating System used.
multiple: false
options:
- Windows
- Linux
- macOS
validations:
required: true
- type: dropdown
id: build
attributes:
label: How did you build your project?
multiple: false
options:
- Command line with CMake
- Command line with idf.py
- CLion IDE
- VS Code IDE/Cursor
- Other (please specify in More Information)
validations:
required: true
- type: dropdown
id: windows_comand_line
attributes:
label: If you are using Windows, please specify command line type.
multiple: false
options:
- PowerShell
- CMD
validations:
required: false
- type: dropdown
id: power_supply
attributes:
label: Power Supply used.
multiple: false
options:
- USB
- External 5V
- External 3.3V
- Battery
validations:
required: true
- type: textarea
id: expected
attributes:
label: What is the expected behavior?
description: Please provide a clear and concise description of the expected behavior.
placeholder: I expected it to...
validations:
required: true
- type: textarea
id: actual
attributes:
label: What is the actual behavior?
description: Please describe actual behavior.
placeholder: Instead it...
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce.
description: 'How do you trigger this bug? Please walk us through it step by step. Please attach your code here.'
value: |
1. Step
2. Step
3. Step
...
validations:
required: true
- type: textarea
id: debug_logs
attributes:
label: Debug Logs.
description: Debug log goes here, should contain the backtrace, as well as the reset source if it is a crash.
placeholder: Your log goes here.
render: plain
validations:
required: false
- type: textarea
id: more-info
attributes:
label: More Information.
description: Do you have any other information from investigating this?
placeholder: ex. Any more.
validations:
required: false

View File

@@ -0,0 +1,34 @@
name: Feature request
description: Suggest an idea for this project.
labels: ['enhancement']
body:
- type: markdown
attributes:
value: |
* We welcome any ideas or feature requests! Its helpful if you can explain exactly why the feature would be useful.
* There are usually some outstanding feature requests in the [existing issues list](https://github.com/78/xiaozhi-esp32/labels/enhancement), feel free to add comments to them.
* If you would like to contribute, please read the [contributions guide](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb).
- type: textarea
id: problem-related
attributes:
label: Is your feature request related to a problem?
description: Please provide a clear and concise description of what the problem is.
placeholder: ex. I'm always frustrated when ...
- type: textarea
id: solution
attributes:
label: Describe the solution you'd like.
description: Please provide a clear and concise description of what you want to happen.
placeholder: ex. When using XiaoZhi ...
- type: textarea
id: alternatives
attributes:
label: Describe alternatives you've considered.
description: Please provide a clear and concise description of any alternative solutions or features you've considered.
placeholder: ex. Choosing other approach wouldn't work, because ...
- type: textarea
id: context
attributes:
label: Additional context.
description: Please add any other context or screenshots about the feature request here.
placeholder: ex. This would work only when ...

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: 小智 AI 官方网站
url: https://xiaozhi.me/
about: 激活设备、配置 AI、声纹识别、声音克隆等应有尽有DIY 属于你自己的小智
- name: 小智 AI 聊天机器人百科全书
url: https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb
about: 开发文档、硬件制作、烧录教程、FAQ尽在小智百科

4
.gitignore vendored
View File

@@ -8,4 +8,6 @@ sdkconfig.old
sdkconfig
dependencies.lock
.env
releases/
releases/
main/assets/lang_config.h
.DS_Store

View File

@@ -4,7 +4,7 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
set(PROJECT_VER "1.1.2")
set(PROJECT_VER "1.4.0")
# Add this line to disable the specific warning
add_compile_options(-Wno-missing-field-initializers)

View File

@@ -4,19 +4,21 @@
这是虾哥的第一个硬件作品。
👉 [ESP32+SenseVoice+Qwen72B打造你的AI聊天伴侣【bilibili】](https://www.bilibili.com/video/BV11msTenEH3/?share_source=copy_web&vd_source=ee1aafe19d6e60cf22e60a93881faeba)
👉 [ESP32+SenseVoice+Qwen72B打造你的AI聊天伴侣【bilibili】](https://www.bilibili.com/video/BV11msTenEH3/)
👉 [给小智装上 DeepSeek 的聪明大脑【bilibili】](https://www.bilibili.com/video/BV1GQP6eNEFG/)
👉 [手工打造你的 AI 女友新手入门教程【bilibili】](https://www.bilibili.com/video/BV1XnmFYLEJN/)
## 项目目的
本项目基于乐鑫的 ESP-IDF 进行开发
本项目是一个开源项目,以 MIT 许可证发布,允许任何人免费使用,并可以用于商业用途
本项目是一个开源项目,主要用于教学目的。我们希望通过这个项目,能够帮助更多人入门 AI 硬件开发,了解如何将当下飞速发展的大语言模型应用到实际的硬件设备中。无论你是对 AI 感兴趣的学生,还是想要探索新技术的开发者,都可以通过这个项目获得宝贵的学习经验。
我们希望通过这个项目,能够帮助更多人入门 AI 硬件开发,了解如何将当下飞速发展的大语言模型应用到实际的硬件设备中。无论你是对 AI 感兴趣的学生,还是想要探索新技术的开发者,都可以通过这个项目获得宝贵的学习经验。
欢迎所有人参与到项目的开发和改进中来。如果你有任何想法或建议,请随时提出 Issue 或加入群聊。
学习交流 QQ 群:946599635
学习交流 QQ 群:376893254
## 已实现功能
@@ -27,11 +29,12 @@
- 支持国语、粤语、英语、日语、韩语 5 种语言识别 [SenseVoice](https://github.com/FunAudioLLM/SenseVoice)
- 声纹识别,识别是谁在喊 AI 的名字 [3D Speaker](https://github.com/modelscope/3D-Speaker)
- 大模型 TTS火山引擎 或 CosyVoice
- 大模型 LLMQwen2.5 72B 或 豆包 API
- 大模型 LLMQwen, DeepSeek, Doubao
- 可配置的提示词和音色(自定义角色)
- 短期记忆,每轮对话后自我总结
- OLED / LCD 显示屏,显示信号强弱或对话内容
- 支持 LCD 显示图片表情
- 支持多语言(中文、英文)
## 硬件部分
@@ -57,7 +60,8 @@
- <a href="https://github.com/Xinyuan-LilyGO/T-Circle-S3" target="_blank" title="LILYGO T-Circle-S3">LILYGO T-Circle-S3</a>
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="虾哥 Mini C3">虾哥 Mini C3</a>
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Moji 小智AI衍生版</a>
- <a href="https://github.com/WMnologo/xingzhi-ai" target="_blank" title="无名科技Nologo-星智-1.54">无名科技Nologo-星智-1.54TFT</a>
- <a href="https://github.com/WMnologo/xingzhi-ai" target="_blank" title="无名科技Nologo-星智-0.96">无名科技Nologo-星智-0.96TFT</a>
<div style="display: flex; justify-content: space-between;">
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="立创·实战派 ESP32-S3 开发板">
<img src="docs/v1/lichuang-s3.jpg" width="240" />
@@ -71,9 +75,6 @@
<a href="docs/v1/atoms3r.jpg" target="_blank" title="AtomS3R + Echo Base">
<img src="docs/v1/atoms3r.jpg" width="240" />
</a>
<a href="docs/AtomMatrix-echo-base.jpg" target="_blank" title="AtomMatrix-echo-base + Echo Base">
<img src="docs/AtomMatrix-echo-base.jpg" width="240" />
</a>
<a href="docs/v1/magiclick.jpg" target="_blank" title="神奇按钮 2.4">
<img src="docs/v1/magiclick.jpg" width="240" />
</a>
@@ -89,13 +90,21 @@
<a href="docs/v1/movecall-moji-esp32s3.jpg" target="_blank" title="Movecall Moji 小智AI衍生版">
<img src="docs/v1/movecall-moji-esp32s3.jpg" width="240" />
</a>
<a href="docs/v1/wmnologo_xingzhi_1.54.jpg" target="_blank" title="无名科技Nologo-星智-1.54">
<img src="docs/v1/wmnologo_xingzhi_1.54.jpg" width="240" />
</a>
<a href="docs/v1/wmnologo_xingzhi_0.96.jpg" target="_blank" title="无名科技Nologo-星智-0.96">
<img src="docs/v1/wmnologo_xingzhi_0.96.jpg" width="240" />
</a>
</div>
## 固件部分
### 免开发环境烧录
新手第一次操作建议先不要搭建开发环境,直接使用免开发环境烧录的固件。固件使用的是作者友情提供的测试服,目前开放免费使用,请勿用于商业用途。
新手第一次操作建议先不要搭建开发环境,直接使用免开发环境烧录的固件。
固件默认接入 [xiaozhi.me](https://xiaozhi.me) 官方服务器,目前个人用户注册账号可以免费使用 Qwen 实时模型。
👉 [Flash烧录固件无IDF开发环境](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)
@@ -105,13 +114,20 @@
- Cursor 或 VSCode
- 安装 ESP-IDF 插件,选择 SDK 版本 5.3 或以上
- Linux 比 Windows 更好,编译速度快,也免去驱动问题的困扰
- 使用 Google C++ 代码风格,提交代码时请确保符合规范
## AI 角色配置
## 智能体配置
如果你已经拥有一个小智 AI 聊天机器人,可以参考 👉 [后台操作视频教程](https://www.bilibili.com/video/BV1jUCUY2EKM/)
如果你已经拥有一个小智 AI 聊天机器人设备,可以登录 [xiaozhi.me](https://xiaozhi.me) 控制台进行配置。
详细的使用说明以及测试服的注意事项,请参考 👉 [小智测试服的帮助说明](https://xiaozhi.me/help)
👉 [后台操作视频教程(旧版界面)](https://www.bilibili.com/video/BV1jUCUY2EKM/)
## 技术原理与私有化部署
👉 [一份详细的 WebSocket 通信协议文档](docs/websocket.md)
在个人电脑上部署服务器,可以参考另一位作者同样以 MIT 许可证开源的项目 [xiaozhi-esp32-server](https://github.com/xinnan-tech/xiaozhi-esp32-server)
## Star History

View File

@@ -1,62 +1,65 @@
# XiaoZhi AI Chatbot
[中文](README.md) | English | [日本語](README_ja.md)
([中文](README.md) | English | [日本語](README_ja.md))
This is Terrence's first hardware project.
👉 [Build your AI chat companion with ESP32+SenseVoice+Qwen72B! [bilibili]](https://www.bilibili.com/video/BV11msTenEH3/?share_source=copy_web&vd_source=ee1aafe19d6e60cf22e60a93881faeba)
👉 [Build your AI chat companion with ESP32+SenseVoice+Qwen72B!bilibili](https://www.bilibili.com/video/BV11msTenEH3/)
👉 [DIY Your AI Companion - Beginner's Tutorial [bilibili]](https://www.bilibili.com/video/BV1XnmFYLEJN/)
👉 [Equipping XiaoZhi with DeepSeek's smart brain【bilibili](https://www.bilibili.com/video/BV1GQP6eNEFG/)
👉 [Build your own AI companion, a beginner's guide【bilibili】](https://www.bilibili.com/video/BV1XnmFYLEJN/)
## Project Purpose
This project is developed based on Espressif's ESP-IDF.
This is an open-source project released under the MIT license, allowing anyone to use it freely, including for commercial purposes.
This is an open-source project primarily for educational purposes. Through this project, we aim to help more people get started with AI hardware development and understand how to integrate rapidly evolving large language models into actual hardware devices. Whether you're a student interested in AI or a developer looking to explore new technologies, this project offers valuable learning experiences.
Through this project, we aim to help more people get started with AI hardware development and understand how to implement rapidly evolving large language models in actual hardware devices. Whether you're a student interested in AI or a developer exploring new technologies, this project offers valuable learning experiences.
Everyone is welcome to participate in the project's development and improvement. If you have any ideas or suggestions, please feel free to raise an Issue or join our chat group.
Everyone is welcome to participate in the project's development and improvement. If you have any ideas or suggestions, please feel free to raise an Issue or join the chat group.
Learning & Discussion QQ Group: 946599635
Learning & Discussion QQ Group: 376893254
## Implemented Features
- Wi-Fi / ML307 Cat.1 4G
- BOOT button wake-up and interrupt, supporting both click and long-press triggers
- BOOT button wake-up and interruption, supporting both click and long-press triggers
- Offline voice wake-up [ESP-SR](https://github.com/espressif/esp-sr)
- Streaming voice dialogue (WebSocket or UDP protocol)
- Support for 5 languages: Mandarin, Cantonese, English, Japanese, Korean [SenseVoice](https://github.com/FunAudioLLM/SenseVoice)
- Voice print recognition to identify who's calling AI's name [3D Speaker](https://github.com/modelscope/3D-Speaker)
- Large model TTS (Volcengine or CosyVoice)
- Large Language Model (Qwen2.5 72B or Doubao API)
- Large model TTS (Volcano Engine or CosyVoice)
- Large Language Models (Qwen, DeepSeek, Doubao)
- Configurable prompts and voice tones (custom characters)
- Short-term memory with self-summary after each conversation round
- Short-term memory, self-summarizing after each conversation round
- OLED / LCD display showing signal strength or conversation content
- Support for displaying emoji images on LCD
- Support for LCD image expressions
- Multi-language support (Chinese, English)
## Hardware Section
### Breadboard Practice
### Breadboard DIY Practice
For detailed tutorial, see the Feishu document:
See the Feishu document tutorial:
👉 [XiaoZhi AI Chatbot Encyclopedia](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb?from=from_copylink)
Breadboard setup shown below:
Breadboard demonstration:
![Breadboard Setup](docs/wiring2.jpg)
![Breadboard Demo](docs/wiring2.jpg)
### Supported Open-Source Hardware
### Supported Open Source Hardware
- <a href="https://oshwhub.com/li-chuang-kai-fa-ban/li-chuang-shi-zhan-pai-esp32-s3-kai-fa-ban" target="_blank" title="LiChuang ESP32-S3 Development Board">LiChuang ESP32-S3 Development Board</a>
- <a href="https://github.com/espressif/esp-box" target="_blank" title="Espressif ESP32-S3-BOX3">Espressif ESP32-S3-BOX3</a>
- <a href="https://docs.m5stack.com/zh_CN/core/CoreS3" target="_blank" title="M5Stack CoreS3">M5Stack CoreS3</a>
- <a href="https://docs.m5stack.com/en/atom/Atomic%20Echo%20Base" target="_blank" title="AtomS3R + Echo Base">AtomS3R + Echo Base</a>
- <a href="https://docs.m5stack.com/en/core/ATOM%20Matrix" target="_blank" title="AtomMatrix + Echo Base">AtomMatrix + Echo Base</a>
- <a href="https://gf.bilibili.com/item/detail/1108782064" target="_blank" title="MagiClick 2.4">MagiClick 2.4</a>
- <a href="https://gf.bilibili.com/item/detail/1108782064" target="_blank" title="Magic Button 2.4">Magic Button 2.4</a>
- <a href="https://www.waveshare.net/shop/ESP32-S3-Touch-AMOLED-1.8.htm" target="_blank" title="Waveshare ESP32-S3-Touch-AMOLED-1.8">Waveshare ESP32-S3-Touch-AMOLED-1.8</a>
- <a href="https://github.com/Xinyuan-LilyGO/T-Circle-S3" target="_blank" title="LILYGO T-Circle-S3">LILYGO T-Circle-S3</a>
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="Xmini C3">Xmini C3</a>
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Movecall Moji ESP32S3</a>
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="XiaGe Mini C3">XiaGe Mini C3</a>
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Moji XiaoZhi AI Derivative Version</a>
<div style="display: flex; justify-content: space-between;">
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 Development Board">
@@ -95,21 +98,30 @@ Breadboard setup shown below:
### Flashing Without Development Environment
For beginners, it's recommended to first try flashing the firmware without setting up a development environment. The firmware uses a test server provided by the author, currently available for free use (not for commercial purposes).
For beginners, it's recommended to first use the firmware that can be flashed without setting up a development environment.
👉 [Flash Firmware Guide (No IDF Environment Required)](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)
The firmware connects to the official [xiaozhi.me](https://xiaozhi.me) server by default. Currently, personal users can register an account to use the Qwen real-time model for free.
👉 [Flash Firmware Guide (No IDF Environment)](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)
### Development Environment
- Cursor or VSCode
- Install ESP-IDF plugin, select SDK version 5.3 or above
- Linux is preferred over Windows for faster compilation and fewer driver issues
- Use Google C++ code style, ensure compliance when submitting code
## AI Character Configuration
## AI Agent Configuration
If you already have a XiaoZhi AI chatbot, please refer to 👉 [Backend Operation Video Tutorial](https://www.bilibili.com/video/BV1jUCUY2EKM/)
If you already have a XiaoZhi AI chatbot device, you can configure it through the [xiaozhi.me](https://xiaozhi.me) console.
For detailed usage instructions and test server notes, please refer to 👉 [XiaoZhi Test Server Help Guide](https://xiaozhi.me/help).
👉 [Backend Operation Tutorial (Old Interface)](https://www.bilibili.com/video/BV1jUCUY2EKM/)
## Technical Principles and Private Deployment
👉 [Detailed WebSocket Communication Protocol Documentation](docs/websocket.md)
For server deployment on personal computers, refer to another MIT-licensed project [xiaozhi-esp32-server](https://github.com/xinnan-tech/xiaozhi-esp32-server)
## Star History

View File

@@ -1,49 +1,52 @@
# XiaoZhi AI チャットボット
# シャオジー AI チャットボット
[中文](README.md) | [English](README_en.md) | 日本語
([中文](README.md) | [English](README_en.md) | 日本語)
これはテレンスの最初のハードウェアプロジェクトです。
これは シャーガーTerrenceの最初のハードウェア作品です。
👉 [ESP32+SenseVoice+Qwen72BでAIチャットコンパニオンを作ろう【bilibili】](https://www.bilibili.com/video/BV11msTenEH3/?share_source=copy_web&vd_source=ee1aafe19d6e60cf22e60a93881faeba)
👉 [ESP32+SenseVoice+Qwen72Bで AI チャット仲間を作ろう【bilibili】](https://www.bilibili.com/video/BV11msTenEH3/)
👉 [AIコンパニオンをDIYする - 初心者向けチュートリアル【bilibili】](https://www.bilibili.com/video/BV1XnmFYLEJN/)
👉 [シャオジーに DeepSeek のスマートな頭脳を搭載【bilibili】](https://www.bilibili.com/video/BV1GQP6eNEFG/)
👉 [自分だけの AI パートナーを作る、初心者向けガイド【bilibili】](https://www.bilibili.com/video/BV1XnmFYLEJN/)
## プロジェクトの目的
このプロジェクトはEspressifのESP-IDFに基づいて開発されています。
このプロジェクトは MIT ライセンスの下で公開されているオープンソースプロジェクトで、商用利用を含め、誰でも自由に使用することができます。
このプロジェクトは主に教育目的のためのオープンソースプロジェクトです。このプロジェクトを通じて、より多くの人々がAIハードウェア開発を始め、急速に進化る大規模言語モデルを実際のハードウェアデバイスに統合する方法を理解する手助けをすることを目指しています。AIに興味のある学生新しい技術を探求したい開発者にとって、このプロジェクト貴重な学習験を提供します。
このプロジェクトを通じて、より多くの人々が AI ハードウェア開発を始め、急速に進化している大規模言語モデルを実際のハードウェアデバイスに実装する方法を理解できるようになることを目指しています。AI に興味のある学生でも、新しい技術を探求する開発者でも、このプロジェクトから貴重な学習験を得ることができます。
プロジェクトの開発と改善に参加することを歓迎します。アイデアや提案があれば、Issueを提起するかチャットグループに参加してください。
プロジェクトの開発と改善には誰でも参加できます。アイデアや提案がありましたら、Issue を立てるかチャットグループに参加ください。
学習・ディスカッションQQグループ: 946599635
学習・交流 QQ グループ376893254
## 実装された機能
## 実装済みの機能
- Wi-Fi / ML307 Cat.1 4G
- BOOTボタンのウェイクアップと割り込み、クリックと長押しの両方のトリガーをサポート
- オフライン音声ウェイクアップ [ESP-SR](https://github.com/espressif/esp-sr)
- ストリーミング音声対話WebSocketまたはUDPプロトコル
- 5つの言語をサポート:標準中国語、広東語、英語、日本語、韓国語 [SenseVoice](https://github.com/FunAudioLLM/SenseVoice)
- 音声認識AIの名前を呼んでいる人を識別 [3D Speaker](https://github.com/modelscope/3D-Speaker)
- 大規模モデルTTSVolcengineまたはCosyVoice
- 大規模言語モデルQwen2.5 72BまたはDoubao API
- カスタマイズ可能なプロンプトと音声トーン(カスタムキャラクター)
- 短期記憶、各話ラウンド後の自己要約
- 信号強度や対話内容を表示するOLED / LCDディスプレイ
- LCDディスプレイでの絵文字表示をサポート
- BOOT ボタンによる起動と中断、クリックと長押しの2種類のトリガーに対応
- オフライン音声起動 [ESP-SR](https://github.com/espressif/esp-sr)
- ストリーミング音声対話WebSocket または UDP プロトコル)
- 5言語対応:標準中国語、広東語、英語、日本語、韓国語 [SenseVoice](https://github.com/FunAudioLLM/SenseVoice)
- 話者認識AI の名前を呼んでいる人を識別 [3D Speaker](https://github.com/modelscope/3D-Speaker)
- 大規模モデル TTSVolcano Engine または CosyVoice
- 大規模言語モデルQwen, DeepSeek, Doubao
- 設定可能なプロンプトと音声トーン(カスタムキャラクター)
- 短期記憶、各話ラウンド後の自己要約
- OLED / LCD ディスプレイ、信号強度や会話内容を表示
- LCD での画像表情表示に対応
- 多言語対応(中国語、英語)
## ハードウェアセクション
## ハードウェア部分
### ブレッドボードの練習
### ブレッドボード DIY 実践
詳細なチュートリアルについては、Feishuドキュメントを参照してください:
Feishu ドキュメントチュートリアルをご覧ください:
👉 [XiaoZhi AI チャットボット百事典](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb?from=from_copylink)
👉 [シャオジー AI チャットボット百事典](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb?from=from_copylink)
以下にブレッドボードのセットアップを示します
ブレッドボードのデモ
![ブレッドボードのセットアップ](docs/wiring2.jpg)
![ブレッドボードデモ](docs/wiring2.jpg)
### サポートされているオープンソースハードウェア
@@ -51,11 +54,12 @@
- <a href="https://github.com/espressif/esp-box" target="_blank" title="Espressif ESP32-S3-BOX3">Espressif ESP32-S3-BOX3</a>
- <a href="https://docs.m5stack.com/zh_CN/core/CoreS3" target="_blank" title="M5Stack CoreS3">M5Stack CoreS3</a>
- <a href="https://docs.m5stack.com/en/atom/Atomic%20Echo%20Base" target="_blank" title="AtomS3R + Echo Base">AtomS3R + Echo Base</a>
- <a href="https://gf.bilibili.com/item/detail/1108782064" target="_blank" title="MagiClick 2.4">MagiClick 2.4</a>
- <a href="https://docs.m5stack.com/en/core/ATOM%20Matrix" target="_blank" title="AtomMatrix + Echo Base">AtomMatrix + Echo Base</a>
- <a href="https://gf.bilibili.com/item/detail/1108782064" target="_blank" title="マジックボタン 2.4">マジックボタン 2.4</a>
- <a href="https://www.waveshare.net/shop/ESP32-S3-Touch-AMOLED-1.8.htm" target="_blank" title="Waveshare ESP32-S3-Touch-AMOLED-1.8">Waveshare ESP32-S3-Touch-AMOLED-1.8</a>
- <a href="https://github.com/Xinyuan-LilyGO/T-Circle-S3" target="_blank" title="LILYGO T-Circle-S3">LILYGO T-Circle-S3</a>
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="Xmini C3">Xmini C3</a>
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Movecall Moji ESP32S3</a>
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="XiaGe Mini C3">XiaGe Mini C3</a>
- <a href="https://oshwhub.com/movecall/moji-xiaozhi-ai-derivative-editi" target="_blank" title="Movecall Moji ESP32S3">Moji シャオジー AI 派生版</a>
<div style="display: flex; justify-content: space-between;">
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 開発ボード">
@@ -87,27 +91,36 @@
</a>
</div>
## ファームウェアセクション
## ファームウェア部分
### 開発環境なしのフラッシュ
### 開発環境なしのフラッシュ
初心者には、最初に開発環境を設定せずにファームウェアをフラッシュすることをお勧めします。ファームウェアは著者が提供するテストサーバーを使用しており、現在無料で使用できます(商業目的では使用しないでください)
初心者の方は、まず開発環境のセットアップなしでフラッシュできるファームウェアを使用することをお勧めします
👉 [開発環境なしでのフラッシュガイド](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)
ファームウェアはデフォルトで公式 [xiaozhi.me](https://xiaozhi.me) サーバーに接続します。現在、個人ユーザーはアカウントを登録することで、Qwen リアルタイムモデルを無料で使用できます。
👉 [フラッシュファームウェアガイドIDF環境なし](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)
### 開発環境
- CursorまたはVSCode
- ESP-IDFプラグインをインストール、SDKバージョン5.3以上を選択
- LinuxWindowsよりも優れており、コンパイルが速く、ドライバの問題も少ない
- Cursor または VSCode
- ESP-IDF プラグインをインストール、SDK バージョン 5.3 以上を選択
- LinuxWindows より好ましい(コンパイルが速く、ドライバの問題も少ない
- Google C++ コードスタイルを使用、コード提出時にはコンプライアンスを確認
## AIキャラクターの設定
## AI エージェント設定
すでにXiaoZhi AIチャットボットをお持ちの場合は、👉 [バックエンド操作ビデオチュートリアル](https://www.bilibili.com/video/BV1jUCUY2EKM/)を参照してください
シャオジー AI チャットボットデバイスをお持ちの場合は、[xiaozhi.me](https://xiaozhi.me) コンソールで設定できます
詳細な使用方法とテストサーバーの注意事項については、👉 [XiaoZhiテストサーバーヘルプガイド](https://xiaozhi.me/help)を参照してください。
👉 [バックエンド操作チュートリアル(旧インターフェース)](https://www.bilibili.com/video/BV1jUCUY2EKM/)
## Star History
## 技術原理とプライベートデプロイメント
👉 [詳細な WebSocket 通信プロトコルドキュメント](docs/websocket.md)
個人のコンピュータでのサーバーデプロイメントについては、同じく MIT ライセンスで公開されている別のプロジェクト [xiaozhi-esp32-server](https://github.com/xinnan-tech/xiaozhi-esp32-server) を参照してください。
## スター履歴
<a href="https://star-history.com/#78/xiaozhi-esp32&Date">
<picture>

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

338
docs/websocket.md Normal file
View File

@@ -0,0 +1,338 @@
以下是一份基于代码实现整理的 WebSocket 通信协议文档,概述客户端(设备)与服务器之间如何通过 WebSocket 进行交互。该文档仅基于所提供的代码推断,实际部署时可能需要结合服务器端实现进行进一步确认或补充。
---
## 1. 总体流程概览
1. **设备端初始化**
- 设备上电、初始化 `Application`
- 初始化音频编解码器、显示屏、LED 等
- 连接网络
- 创建并初始化实现 `Protocol` 接口的 WebSocket 协议实例(`WebsocketProtocol`
- 进入主循环等待事件(音频输入、音频输出、调度任务等)。
2. **建立 WebSocket 连接**
- 当设备需要开始语音会话时(例如用户唤醒、手动按键触发等),调用 `OpenAudioChannel()`
- 根据编译配置获取 WebSocket URL`CONFIG_WEBSOCKET_URL`
- 设置若干请求头(`Authorization`, `Protocol-Version`, `Device-Id`, `Client-Id`
- 调用 `Connect()` 与服务器建立 WebSocket 连接
3. **发送客户端 “hello” 消息**
- 连接成功后,设备会发送一条 JSON 消息,示例结构如下:
```json
{
"type": "hello",
"version": 1,
"transport": "websocket",
"audio_params": {
"format": "opus",
"sample_rate": 16000,
"channels": 1,
"frame_duration": 60
}
}
```
- 其中 `"frame_duration"` 的值对应 `OPUS_FRAME_DURATION_MS`(例如 60ms
4. **服务器回复 “hello”**
- 设备等待服务器返回一条包含 `"type": "hello"` 的 JSON 消息,并检查 `"transport": "websocket"` 是否匹配。
- 如果匹配,则认为服务器已就绪,标记音频通道打开成功。
- 如果在超时时间(默认 10 秒)内未收到正确回复,认为连接失败并触发网络错误回调。
5. **后续消息交互**
- 设备端和服务器端之间可发送两种主要类型的数据:
1. **二进制音频数据**Opus 编码)
2. **文本 JSON 消息**用于传输聊天状态、TTS/STT 事件、IoT 命令等)
- 在代码里,接收回调主要分为:
- `OnData(...)`:
- 当 `binary` 为 `true` 时,认为是音频帧;设备会将其当作 Opus 数据进行解码。
- 当 `binary` 为 `false` 时,认为是 JSON 文本,需要在设备端用 cJSON 进行解析并做相应业务逻辑处理(见下文消息结构)。
- 当服务器或网络出现断连,回调 `OnDisconnected()` 被触发:
- 设备会调用 `on_audio_channel_closed_()`,并最终回到空闲状态。
6. **关闭 WebSocket 连接**
- 设备在需要结束语音会话时,会调用 `CloseAudioChannel()` 主动断开连接,并回到空闲状态。
- 或者如果服务器端主动断开,也会引发同样的回调流程。
---
## 2. 通用请求头
在建立 WebSocket 连接时,代码示例中设置了以下请求头:
- `Authorization`: 用于存放访问令牌,形如 `"Bearer <token>"`
- `Protocol-Version`: 固定示例中为 `"1"`
- `Device-Id`: 设备物理网卡 MAC 地址
- `Client-Id`: 设备 UUID可在应用中唯一标识设备
这些头会随着 WebSocket 握手一起发送到服务器,服务器可根据需求进行校验、认证等。
---
## 3. JSON 消息结构
WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及其对应业务逻辑。若消息里包含未列出的字段,可能为可选或特定实现细节。
### 3.1 客户端→服务器
1. **Hello**
- 连接成功后,由客户端发送,告知服务器基本参数。
- 例:
```json
{
"type": "hello",
"version": 1,
"transport": "websocket",
"audio_params": {
"format": "opus",
"sample_rate": 16000,
"channels": 1,
"frame_duration": 60
}
}
```
2. **Listen**
- 表示客户端开始或停止录音监听。
- 常见字段:
- `"session_id"`:会话标识
- `"type": "listen"`
- `"state"``"start"`, `"stop"`, `"detect"`(唤醒检测已触发)
- `"mode"``"auto"`, `"manual"` 或 `"realtime"`,表示识别模式。
- 例:开始监听
```json
{
"session_id": "xxx",
"type": "listen",
"state": "start",
"mode": "manual"
}
```
3. **Abort**
- 终止当前说话TTS 播放)或语音通道。
- 例:
```json
{
"session_id": "xxx",
"type": "abort",
"reason": "wake_word_detected"
}
```
- `reason` 值可为 `"wake_word_detected"` 或其他。
4. **Wake Word Detected**
- 用于客户端向服务器告知检测到唤醒词。
- 例:
```json
{
"session_id": "xxx",
"type": "listen",
"state": "detect",
"text": "你好小明"
}
```
5. **IoT**
- 发送当前设备的物联网相关信息:
- **Descriptors**(描述设备功能、属性等)
- **States**(设备状态的实时更新)
- 例:
```json
{
"session_id": "xxx",
"type": "iot",
"descriptors": { ... }
}
```
```json
{
"session_id": "xxx",
"type": "iot",
"states": { ... }
}
```
---
### 3.2 服务器→客户端
1. **Hello**
- 服务器端返回的握手确认消息。
- 必须包含 `"type": "hello"` 和 `"transport": "websocket"`。
- 可能会带有 `audio_params`,表示服务器期望的音频参数,或与客户端对齐的配置。
- 成功接收后客户端会设置事件标志,表示 WebSocket 通道就绪。
2. **STT**
- `{"type": "stt", "text": "..."}`
- 表示服务器端识别到了用户语音。(例如语音转文本结果)
- 设备可能将此文本显示到屏幕上,后续再进入回答等流程。
3. **LLM**
- `{"type": "llm", "emotion": "happy", "text": "😀"}`
- 服务器指示设备调整表情动画 / UI 表达。
4. **TTS**
- `{"type": "tts", "state": "start"}`:服务器准备下发 TTS 音频,客户端进入 “speaking” 播放状态。
- `{"type": "tts", "state": "stop"}`:表示本次 TTS 结束。
- `{"type": "tts", "state": "sentence_start", "text": "..."}`
- 让设备在界面上显示当前要播放或朗读的文本片段(例如用于显示给用户)。
5. **IoT**
- `{"type": "iot", "commands": [ ... ]}`
- 服务器向设备发送物联网的动作指令,设备解析并执行(如打开灯、设置温度等)。
6. **音频数据:二进制帧**
- 当服务器发送音频二进制帧Opus 编码)时,客户端解码并播放。
- 若客户端正在处于 “listening” (录音)状态,收到的音频帧会被忽略或清空以防冲突。
---
## 4. 音频编解码
1. **客户端发送录音数据**
- 音频输入经过可能的回声消除、降噪或音量增益后,通过 Opus 编码打包为二进制帧发送给服务器。
- 如果客户端每次编码生成的二进制帧大小为 N 字节,则会通过 WebSocket 的 **binary** 消息发送这块数据。
2. **客户端播放收到的音频**
- 收到服务器的二进制帧时,同样认定是 Opus 数据。
- 设备端会进行解码,然后交由音频输出接口播放。
- 如果服务器的音频采样率与设备不一致,会在解码后再进行重采样。
---
## 5. 常见状态流转
以下简述设备端关键状态流转,与 WebSocket 消息对应:
1. **Idle** → **Connecting**
- 用户触发或唤醒后,设备调用 `OpenAudioChannel()` → 建立 WebSocket 连接 → 发送 `"type":"hello"`。
2. **Connecting** → **Listening**
- 成功建立连接后,若继续执行 `SendStartListening(...)`,则进入录音状态。此时设备会持续编码麦克风数据并发送到服务器。
3. **Listening** → **Speaking**
- 收到服务器 TTS Start 消息 (`{"type":"tts","state":"start"}`) → 停止录音并播放接收到的音频。
4. **Speaking** → **Idle**
- 服务器 TTS Stop (`{"type":"tts","state":"stop"}`) → 音频播放结束。若未继续进入自动监听,则返回 Idle如果配置了自动循环则再度进入 Listening。
5. **Listening** / **Speaking** → **Idle**(遇到异常或主动中断)
- 调用 `SendAbortSpeaking(...)` 或 `CloseAudioChannel()` → 中断会话 → 关闭 WebSocket → 状态回到 Idle。
---
## 6. 错误处理
1. **连接失败**
- 如果 `Connect(url)` 返回失败或在等待服务器 “hello” 消息时超时,触发 `on_network_error_()` 回调。设备会提示“无法连接到服务”或类似错误信息。
2. **服务器断开**
- 如果 WebSocket 异常断开,回调 `OnDisconnected()`
- 设备回调 `on_audio_channel_closed_()`
- 切换到 Idle 或其他重试逻辑。
---
## 7. 其它注意事项
1. **鉴权**
- 设备通过设置 `Authorization: Bearer <token>` 提供鉴权,服务器端需验证是否有效。
- 如果令牌过期或无效,服务器可拒绝握手或在后续断开。
2. **会话控制**
- 代码中部分消息包含 `session_id`用于区分独立的对话或操作。服务端可根据需要对不同会话做分离处理WebSocket 协议为空。
3. **音频负载**
- 代码里默认使用 Opus 格式,并设置 `sample_rate = 16000`,单声道。帧时长由 `OPUS_FRAME_DURATION_MS` 控制,一般为 60ms。可根据带宽或性能做适当调整。
4. **IoT 指令**
- `"type":"iot"` 的消息用户端代码对接 `thing_manager` 执行具体命令,因设备定制而不同。服务器端需确保下发格式与客户端保持一致。
5. **错误或异常 JSON**
- 当 JSON 中缺少必要字段,例如 `{"type": ...}`,客户端会记录错误日志(`ESP_LOGE(TAG, "Missing message type, data: %s", data);`),不会执行任何业务。
---
## 8. 消息示例
下面给出一个典型的双向消息示例(流程简化示意):
1. **客户端 → 服务器**(握手)
```json
{
"type": "hello",
"version": 1,
"transport": "websocket",
"audio_params": {
"format": "opus",
"sample_rate": 16000,
"channels": 1,
"frame_duration": 60
}
}
```
2. **服务器 → 客户端**(握手应答)
```json
{
"type": "hello",
"transport": "websocket",
"audio_params": {
"sample_rate": 16000
}
}
```
3. **客户端 → 服务器**(开始监听)
```json
{
"session_id": "",
"type": "listen",
"state": "start",
"mode": "auto"
}
```
同时客户端开始发送二进制帧Opus 数据)。
4. **服务器 → 客户端**ASR 结果)
```json
{
"type": "stt",
"text": "用户说的话"
}
```
5. **服务器 → 客户端**TTS开始
```json
{
"type": "tts",
"state": "start"
}
```
接着服务器发送二进制音频帧给客户端播放。
6. **服务器 → 客户端**TTS结束
```json
{
"type": "tts",
"state": "stop"
}
```
客户端停止播放音频,若无更多指令,则回到空闲状态。
---
## 9. 总结
本协议通过在 WebSocket 上层传输 JSON 文本与二进制音频帧完成功能包括音频流上传、TTS 音频播放、语音识别与状态管理、IoT 指令下发等。其核心特征:
- **握手阶段**:发送 `"type":"hello"`,等待服务器返回。
- **音频通道**:采用 Opus 编码的二进制帧双向传输语音流。
- **JSON 消息**:使用 `"type"` 为核心字段标识不同业务逻辑,包括 TTS、STT、IoT、WakeWord 等。
- **扩展性**:可根据实际需求在 JSON 消息中添加字段,或在 headers 里进行额外鉴权。
服务器与客户端需提前约定各类消息的字段含义、时序逻辑以及错误处理规则,方能保证通信顺畅。上述信息可作为基础文档,便于后续对接、开发或扩展。

View File

@@ -4,17 +4,13 @@ set(SOURCES "audio_codecs/audio_codec.cc"
"audio_codecs/es8311_audio_codec.cc"
"audio_codecs/es8388_audio_codec.cc"
"audio_codecs/cores3_audio_codec.cc"
"audio_codecs/tcircles3_audio_codec.cc"
"led/single_led.cc"
"led/circular_strip.cc"
"display/display.cc"
"display/no_display.cc"
"display/lcd_display.cc"
"display/ssd1306_display.cc"
"boards/lilygo-t-circle-s3/esp_lcd_gc9d01n.c"
"protocols/protocol.cc"
"protocols/mqtt_protocol.cc"
"protocols/websocket_protocol.cc"
"iot/thing.cc"
"iot/thing_manager.cc"
"system_info.cc"
@@ -50,17 +46,27 @@ elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_1)
elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_2)
set(BOARD_TYPE "kevin-box-2")
elseif(CONFIG_BOARD_TYPE_KEVIN_C3)
set(BOARD_TYPE "kevin-c3")
set(BOARD_TYPE "kevin-c3")
elseif(CONFIG_BOARD_TYPE_KEVIN_SP_V3_DEV)
set(BOARD_TYPE "kevin-sp-v3-dev")
elseif(CONFIG_BOARD_TYPE_KEVIN_YUYING_313LCD)
set(BOARD_TYPE "kevin-yuying-313lcd")
elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV)
set(BOARD_TYPE "lichuang-dev")
elseif(CONFIG_BOARD_TYPE_LICHUANG_C3_DEV)
set(BOARD_TYPE "lichuang-c3-dev")
elseif(CONFIG_BOARD_TYPE_MAGICLICK_2P4)
set(BOARD_TYPE "magiclick-2p4")
elseif(CONFIG_BOARD_TYPE_MAGICLICK_2P5)
set(BOARD_TYPE "magiclick-2p5")
elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3)
set(BOARD_TYPE "magiclick-c3")
elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3_V2)
set(BOARD_TYPE "magiclick-c3-v2")
elseif(CONFIG_BOARD_TYPE_M5STACK_CORE_S3)
set(BOARD_TYPE "m5stack-core-s3")
elseif(CONFIG_BOARD_TYPE_ATOMS3_ECHO_BASE)
set(BOARD_TYPE "atoms3-echo-base")
elseif(CONFIG_BOARD_TYPE_ATOMS3R_ECHO_BASE)
set(BOARD_TYPE "atoms3r-echo-base")
elseif(CONFIG_BOARD_TYPE_ATOMMATRIX_ECHO_BASE)
@@ -73,12 +79,20 @@ elseif(CONFIG_BOARD_TYPE_ESP_SPARKBOT)
set(BOARD_TYPE "esp-sparkbot")
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8)
set(BOARD_TYPE "esp32-s3-touch-amoled-1.8")
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85C)
set(BOARD_TYPE "esp32-s3-touch-lcd-1.85c")
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85)
set(BOARD_TYPE "esp32-s3-touch-lcd-1.85")
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_46)
set(BOARD_TYPE "esp32-s3-touch-lcd-1.46")
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_LCD)
set(BOARD_TYPE "bread-compact-wifi-lcd")
elseif(CONFIG_BOARD_TYPE_TUDOUZI)
set(BOARD_TYPE "tudouzi")
elseif(CONFIG_BOARD_TYPE_LILYGO_T_CIRCLE_S3)
set(BOARD_TYPE "lilygo-t-circle-s3")
elseif(CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3)
set(BOARD_TYPE "lilygo-t-cameraplus-s3")
elseif(CONFIG_BOARD_TYPE_MOVECALL_MOJI_ESP32S3)
set(BOARD_TYPE "movecall-moji-esp32s3")
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3)
@@ -87,14 +101,46 @@ elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX)
set(BOARD_TYPE "atk-dnesp32s3-box")
elseif(CONFIG_BOARD_TYPE_DU_CHATX)
set(BOARD_TYPE "du-chatx")
elseif(CONFIG_BOARD_TYPE_ESP32S3_Taiji_Pi)
set(BOARD_TYPE "taiji-pi-s3")
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_WIFI)
set(BOARD_TYPE "xingzhi-cube-0.96oled-wifi")
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_ML307)
set(BOARD_TYPE "xingzhi-cube-0.96oled-ml307")
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_WIFI)
set(BOARD_TYPE "xingzhi-cube-1.54tft-wifi")
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_ML307)
set(BOARD_TYPE "xingzhi-cube-1.54tft-ml307")
endif()
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
file(GLOB BOARD_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c
)
list(APPEND SOURCES ${BOARD_SOURCES})
if(CONFIG_CONNECTION_TYPE_MQTT_UDP)
list(APPEND SOURCES "protocols/mqtt_protocol.cc")
elseif(CONFIG_CONNECTION_TYPE_WEBSOCKET)
list(APPEND SOURCES "protocols/websocket_protocol.cc")
endif()
if(CONFIG_USE_AUDIO_PROCESSING)
list(APPEND SOURCES "audio_processing/audio_processor.cc" "audio_processing/wake_word_detect.cc")
endif()
# 根据Kconfig选择语言目录
if(CONFIG_LANGUAGE_ZH_CN)
set(LANG_DIR "zh-CN")
elseif(CONFIG_LANGUAGE_EN_US)
set(LANG_DIR "en-US")
endif()
# 定义生成路径
set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/language.json")
set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h")
file(GLOB LANG_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/*.p3)
file(GLOB COMMON_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/common/*.p3)
# 如果目标芯片是 ESP32则排除特定文件
if(CONFIG_IDF_TARGET_ESP32)
# 排除 "audio_codecs/box_audio_codec.cc" 和 "audio_codecs/cores3_audio_codec.cc"
@@ -104,12 +150,29 @@ if(CONFIG_IDF_TARGET_ESP32)
endif()
idf_component_register(SRCS ${SOURCES}
EMBED_FILES "assets/err_reg.p3" "assets/err_pin.p3" "assets/wificonfig.p3" "assets/upgrade.p3"
EMBED_FILES ${LANG_SOUNDS} ${COMMON_SOUNDS}
INCLUDE_DIRS ${INCLUDE_DIRS}
WHOLE_ARCHIVE
)
# 使用 target_compile_definitions 来定义 BOARD_TYPE
# 使用 target_compile_definitions 来定义 BOARD_TYPE, BOARD_NAME
target_compile_definitions(${COMPONENT_LIB}
PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\"
PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" BOARD_NAME=\"${BOARD_NAME}\"
)
# 添加生成规则
add_custom_command(
OUTPUT ${LANG_HEADER}
COMMAND python ${PROJECT_DIR}/scripts/gen_lang.py
--input "${LANG_JSON}"
--output "${LANG_HEADER}"
DEPENDS
${LANG_JSON}
${PROJECT_DIR}/scripts/gen_lang.py
COMMENT "Generating ${LANG_DIR} language config"
)
# 强制建立生成依赖
add_custom_target(lang_header ALL
DEPENDS ${LANG_HEADER}
)

View File

@@ -6,6 +6,20 @@ config OTA_VERSION_URL
help
The application will access this URL to check for updates.
choice
prompt "语言选择"
default LANGUAGE_ZH_CN
help
Select device display language
config LANGUAGE_ZH_CN
bool "Chinese"
config LANGUAGE_EN_US
bool "English"
endchoice
choice CONNECTION_TYPE
prompt "Connection Type"
default CONNECTION_TYPE_MQTT_UDP
@@ -52,16 +66,26 @@ choice BOARD_TYPE
bool "Kevin Box 2"
config BOARD_TYPE_KEVIN_C3
bool "Kevin C3"
config BOARD_TYPE_KEVIN_SP_V3_DEV
bool "Kevin SP V3开发板"
config BOARD_TYPE_KEVIN_YUYING_313LCD
bool "鱼鹰科技3.13LCD开发板"
config BOARD_TYPE_LICHUANG_DEV
bool "立创·实战派ESP32-S3开发板"
config BOARD_TYPE_LICHUANG_C3_DEV
bool "立创·实战派ESP32-C3开发板"
config BOARD_TYPE_MAGICLICK_2P4
bool "神奇按钮 Magiclick_2.4"
bool "神奇按钮 Magiclick_2.4"
config BOARD_TYPE_MAGICLICK_2P5
bool "神奇按钮 Magiclick_2.5"
config BOARD_TYPE_MAGICLICK_C3
bool "神奇按钮 Magiclick_C3"
bool "神奇按钮 Magiclick_C3"
config BOARD_TYPE_MAGICLICK_C3_V2
bool "神奇按钮 Magiclick_C3_v2"
config BOARD_TYPE_M5STACK_CORE_S3
bool "M5Stack CoreS3"
config BOARD_TYPE_ATOMS3_ECHO_BASE
bool "AtomS3 + Echo Base"
config BOARD_TYPE_ATOMS3R_ECHO_BASE
bool "AtomS3R + Echo Base"
config BOARD_TYPE_ATOMMATRIX_ECHO_BASE
@@ -74,10 +98,18 @@ choice BOARD_TYPE
bool "ESP-SparkBot开发板"
config BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8
bool "Waveshare ESP32-S3-Touch-AMOLED-1.8"
config BOARD_TYPE_ESP32S3_Touch_LCD_1_85C
bool "Waveshare ESP32-S3-Touch-LCD-1.85C"
config BOARD_TYPE_ESP32S3_Touch_LCD_1_85
bool "Waveshare ESP32-S3-Touch-LCD-1.85"
config BOARD_TYPE_ESP32S3_Touch_LCD_1_46
bool "Waveshare ESP32-S3-Touch-LCD-1.46"
config BOARD_TYPE_TUDOUZI
bool "土豆子"
config BOARD_TYPE_LILYGO_T_CIRCLE_S3
bool "LILYGO T-Circle-S3"
bool "LILYGO T-Circle-S3"
config BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3
bool "LILYGO T-CameraPlus-S3"
config BOARD_TYPE_MOVECALL_MOJI_ESP32S3
bool "Movecall Moji 小智AI衍生版"
config BOARD_TYPE_ATK_DNESP32S3
@@ -86,6 +118,28 @@ choice BOARD_TYPE
bool "正点原子DNESP32S3-BOX"
config BOARD_TYPE_DU_CHATX
bool "嘟嘟开发板CHATX(wifi)"
config BOARD_TYPE_ESP32S3_Taiji_Pi
bool "太极小派esp32s3"
config BOARD_TYPE_XINGZHI_Cube_0_96OLED_WIFI
bool "无名科技星智0.96(WIFI)"
config BOARD_TYPE_XINGZHI_Cube_0_96OLED_ML307
bool "无名科技星智0.96(ML307)"
config BOARD_TYPE_XINGZHI_Cube_1_54TFT_WIFI
bool "无名科技星智1.54(WIFI)"
config BOARD_TYPE_XINGZHI_Cube_1_54TFT_ML307
bool "无名科技星智1.54(ML307)"
endchoice
choice DISPLAY_OLED_TYPE
depends on BOARD_TYPE_BREAD_COMPACT_WIFI || BOARD_TYPE_BREAD_COMPACT_ML307 || BOARD_TYPE_BREAD_COMPACT_ESP32
prompt "OLED Type"
default OLED_SSD1306_128X32
help
OLED 屏幕类型选择
config OLED_SSD1306_128X32
bool "SSD1306, 分辨率128*32"
config OLED_SSD1306_128X64
bool "SSD1306, 分辨率128*64"
endchoice
choice DISPLAY_LCD_TYPE
@@ -106,16 +160,22 @@ choice DISPLAY_LCD_TYPE
bool "ST7789, 分辨率240*280"
config LCD_ST7789_240X240
bool "ST7789, 分辨率240*240"
config LCD_ST7789_240X240_7PIN
bool "ST7789, 分辨率240*240, 7PIN"
config LCD_ST7789_240X135
bool "ST7789, 分辨率240*135"
config LCD_ST7735_128X160
bool "ST7735, 分辨率128*160"
config LCD_ST7735_128X128
bool "ST7735, 分辨率128*128"
config LCD_ST7796_320X480
bool "ST7796, 分辨率320*480"
config LCD_ILI9341_240X320
bool "ILI9341, 分辨率240*320"
config LCD_ILI9341_240X320_NO_IPS
bool "ILI9341, 分辨率240*320, 非IPS"
config LCD_GC9A01_240X240
bool "GC9A01, 分辨率240*240, 圆屏"
config LCD_CUSTOM
bool "自定义屏幕参数"
endchoice

View File

@@ -8,6 +8,7 @@
#include "websocket_protocol.h"
#include "font_awesome_symbols.h"
#include "iot/thing_manager.h"
#include "assets/lang_config.h"
#include <cstring>
#include <esp_log.h>
@@ -17,14 +18,6 @@
#define TAG "Application"
extern const char p3_err_reg_start[] asm("_binary_err_reg_p3_start");
extern const char p3_err_reg_end[] asm("_binary_err_reg_p3_end");
extern const char p3_err_pin_start[] asm("_binary_err_pin_p3_start");
extern const char p3_err_pin_end[] asm("_binary_err_pin_p3_end");
extern const char p3_wificonfig_start[] asm("_binary_wificonfig_p3_start");
extern const char p3_wificonfig_end[] asm("_binary_wificonfig_p3_end");
extern const char p3_upgrade_start[] asm("_binary_upgrade_p3_start");
extern const char p3_upgrade_end[] asm("_binary_upgrade_p3_end");
static const char* const STATE_STRINGS[] = {
"unknown",
@@ -35,6 +28,7 @@ static const char* const STATE_STRINGS[] = {
"listening",
"speaking",
"upgrading",
"activating",
"fatal_error",
"invalid_state"
};
@@ -43,11 +37,23 @@ Application::Application() {
event_group_ = xEventGroupCreate();
background_task_ = new BackgroundTask(4096 * 8);
ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
esp_timer_create_args_t clock_timer_args = {
.callback = [](void* arg) {
Application* app = (Application*)arg;
app->OnClockTimer();
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "clock_timer"
};
esp_timer_create(&clock_timer_args, &clock_timer_handle_);
}
Application::~Application() {
if (clock_timer_handle_ != nullptr) {
esp_timer_stop(clock_timer_handle_);
esp_timer_delete(clock_timer_handle_);
}
if (background_task_ != nullptr) {
delete background_task_;
}
@@ -60,80 +66,160 @@ void Application::CheckNewVersion() {
// Check if there is a new firmware version available
ota_.SetPostData(board.GetJson());
const int MAX_RETRY = 10;
int retry_count = 0;
while (true) {
if (ota_.CheckVersion()) {
if (ota_.HasNewVersion()) {
Alert("Info", "正在升级固件");
// Wait for the chat state to be idle
do {
vTaskDelay(pdMS_TO_TICKS(3000));
} while (GetDeviceState() != kDeviceStateIdle);
// Use main task to do the upgrade, not cancelable
Schedule([this, &board, display]() {
SetDeviceState(kDeviceStateUpgrading);
display->SetIcon(FONT_AWESOME_DOWNLOAD);
display->SetStatus("新版本 " + ota_.GetFirmwareVersion());
board.SetPowerSaveMode(false);
#if CONFIG_USE_AUDIO_PROCESSING
wake_word_detect_.StopDetection();
#endif
// 预先关闭音频输出,避免升级过程有音频操作
board.GetAudioCodec()->EnableOutput(false);
{
std::lock_guard<std::mutex> lock(mutex_);
audio_decode_queue_.clear();
}
background_task_->WaitForCompletion();
delete background_task_;
background_task_ = nullptr;
vTaskDelay(pdMS_TO_TICKS(1000));
ota_.StartUpgrade([display](int progress, size_t speed) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "%d%% %zuKB/s", progress, speed / 1024);
display->SetStatus(buffer);
});
// If upgrade success, the device will reboot and never reach here
display->SetStatus("更新失败");
ESP_LOGI(TAG, "Firmware upgrade failed...");
vTaskDelay(pdMS_TO_TICKS(3000));
esp_restart();
});
} else {
ota_.MarkCurrentVersionValid();
display->ShowNotification("版本 " + ota_.GetCurrentVersion());
if (!ota_.CheckVersion()) {
retry_count++;
if (retry_count >= MAX_RETRY) {
ESP_LOGE(TAG, "Too many retries, exit version check");
return;
}
ESP_LOGW(TAG, "Check new version failed, retry in %d seconds (%d/%d)", 60, retry_count, MAX_RETRY);
vTaskDelay(pdMS_TO_TICKS(60000));
continue;
}
retry_count = 0;
if (ota_.HasNewVersion()) {
Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "happy", Lang::Sounds::P3_UPGRADE);
// Wait for the chat state to be idle
do {
vTaskDelay(pdMS_TO_TICKS(3000));
} while (GetDeviceState() != kDeviceStateIdle);
// Use main task to do the upgrade, not cancelable
Schedule([this, display]() {
SetDeviceState(kDeviceStateUpgrading);
display->SetIcon(FONT_AWESOME_DOWNLOAD);
std::string message = std::string(Lang::Strings::NEW_VERSION) + ota_.GetFirmwareVersion();
display->SetChatMessage("system", message.c_str());
auto& board = Board::GetInstance();
board.SetPowerSaveMode(false);
#if CONFIG_USE_AUDIO_PROCESSING
wake_word_detect_.StopDetection();
#endif
// 预先关闭音频输出,避免升级过程有音频操作
auto codec = board.GetAudioCodec();
codec->EnableInput(false);
codec->EnableOutput(false);
{
std::lock_guard<std::mutex> lock(mutex_);
audio_decode_queue_.clear();
}
background_task_->WaitForCompletion();
delete background_task_;
background_task_ = nullptr;
vTaskDelay(pdMS_TO_TICKS(1000));
ota_.StartUpgrade([display](int progress, size_t speed) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "%d%% %zuKB/s", progress, speed / 1024);
display->SetChatMessage("system", buffer);
});
// If upgrade success, the device will reboot and never reach here
display->SetStatus(Lang::Strings::UPGRADE_FAILED);
ESP_LOGI(TAG, "Firmware upgrade failed...");
vTaskDelay(pdMS_TO_TICKS(3000));
Reboot();
});
return;
}
// Check again in 60 seconds
vTaskDelay(pdMS_TO_TICKS(60000));
// No new version, mark the current version as valid
ota_.MarkCurrentVersionValid();
std::string message = std::string(Lang::Strings::VERSION) + ota_.GetCurrentVersion();
display->ShowNotification(message.c_str());
if (ota_.HasActivationCode()) {
// Activation code is valid
SetDeviceState(kDeviceStateActivating);
ShowActivationCode();
// Check again in 60 seconds or until the device is idle
for (int i = 0; i < 60; ++i) {
if (device_state_ == kDeviceStateIdle) {
break;
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
continue;
}
SetDeviceState(kDeviceStateIdle);
display->SetChatMessage("system", "");
PlaySound(Lang::Sounds::P3_SUCCESS);
// Exit the loop if upgrade or idle
break;
}
}
void Application::Alert(const std::string& title, const std::string& message) {
ESP_LOGW(TAG, "Alert: %s, %s", title.c_str(), message.c_str());
void Application::ShowActivationCode() {
auto& message = ota_.GetActivationMessage();
auto& code = ota_.GetActivationCode();
struct digit_sound {
char digit;
const std::string_view& sound;
};
static const std::array<digit_sound, 10> digit_sounds{{
digit_sound{'0', Lang::Sounds::P3_0},
digit_sound{'1', Lang::Sounds::P3_1},
digit_sound{'2', Lang::Sounds::P3_2},
digit_sound{'3', Lang::Sounds::P3_3},
digit_sound{'4', Lang::Sounds::P3_4},
digit_sound{'5', Lang::Sounds::P3_5},
digit_sound{'6', Lang::Sounds::P3_6},
digit_sound{'7', Lang::Sounds::P3_7},
digit_sound{'8', Lang::Sounds::P3_8},
digit_sound{'9', Lang::Sounds::P3_9}
}};
// This sentence uses 9KB of SRAM, so we need to wait for it to finish
Alert(Lang::Strings::ACTIVATION, message.c_str(), "happy", Lang::Sounds::P3_ACTIVATION);
vTaskDelay(pdMS_TO_TICKS(1000));
background_task_->WaitForCompletion();
for (const auto& digit : code) {
auto it = std::find_if(digit_sounds.begin(), digit_sounds.end(),
[digit](const digit_sound& ds) { return ds.digit == digit; });
if (it != digit_sounds.end()) {
PlaySound(it->sound);
}
}
}
void Application::Alert(const char* status, const char* message, const char* emotion, const std::string_view& sound) {
ESP_LOGW(TAG, "Alert %s: %s [%s]", status, message, emotion);
auto display = Board::GetInstance().GetDisplay();
display->ShowNotification(message);
if (message == "进入配网模式") {
PlayLocalFile(p3_wificonfig_start, p3_wificonfig_end - p3_wificonfig_start);
} else if (message == "正在升级固件") {
PlayLocalFile(p3_upgrade_start, p3_upgrade_end - p3_upgrade_start);
} else if (message == "请插入SIM卡") {
PlayLocalFile(p3_err_pin_start, p3_err_pin_end - p3_err_pin_start);
} else if (message == "无法接入网络,请检查流量卡状态") {
PlayLocalFile(p3_err_reg_start, p3_err_reg_end - p3_err_reg_start);
display->SetStatus(status);
display->SetEmotion(emotion);
display->SetChatMessage("system", message);
if (!sound.empty()) {
PlaySound(sound);
}
}
void Application::PlayLocalFile(const char* data, size_t size) {
ESP_LOGI(TAG, "PlayLocalFile: %zu bytes", size);
void Application::DismissAlert() {
if (device_state_ == kDeviceStateIdle) {
auto display = Board::GetInstance().GetDisplay();
display->SetStatus(Lang::Strings::STANDBY);
display->SetEmotion("neutral");
display->SetChatMessage("system", "");
}
}
void Application::PlaySound(const std::string_view& sound) {
auto codec = Board::GetInstance().GetAudioCodec();
codec->EnableOutput(true);
SetDecodeSampleRate(16000);
const char* data = sound.data();
size_t size = sound.size();
for (const char* p = data; p < data + size; ) {
auto p3 = (BinaryProtocol3*)p;
p += sizeof(BinaryProtocol3);
@@ -150,67 +236,79 @@ void Application::PlayLocalFile(const char* data, size_t size) {
}
void Application::ToggleChatState() {
Schedule([this]() {
if (!protocol_) {
ESP_LOGE(TAG, "Protocol not initialized");
return;
}
if (device_state_ == kDeviceStateActivating) {
SetDeviceState(kDeviceStateIdle);
return;
}
if (device_state_ == kDeviceStateIdle) {
if (!protocol_) {
ESP_LOGE(TAG, "Protocol not initialized");
return;
}
if (device_state_ == kDeviceStateIdle) {
Schedule([this]() {
SetDeviceState(kDeviceStateConnecting);
if (!protocol_->OpenAudioChannel()) {
Alert("Error", "Failed to open audio channel");
SetDeviceState(kDeviceStateIdle);
return;
}
keep_listening_ = true;
protocol_->SendStartListening(kListeningModeAutoStop);
SetDeviceState(kDeviceStateListening);
} else if (device_state_ == kDeviceStateSpeaking) {
});
} else if (device_state_ == kDeviceStateSpeaking) {
Schedule([this]() {
AbortSpeaking(kAbortReasonNone);
} else if (device_state_ == kDeviceStateListening) {
});
} else if (device_state_ == kDeviceStateListening) {
Schedule([this]() {
protocol_->CloseAudioChannel();
}
});
});
}
}
void Application::StartListening() {
Schedule([this]() {
if (!protocol_) {
ESP_LOGE(TAG, "Protocol not initialized");
return;
}
keep_listening_ = false;
if (device_state_ == kDeviceStateIdle) {
if (device_state_ == kDeviceStateActivating) {
SetDeviceState(kDeviceStateIdle);
return;
}
if (!protocol_) {
ESP_LOGE(TAG, "Protocol not initialized");
return;
}
keep_listening_ = false;
if (device_state_ == kDeviceStateIdle) {
Schedule([this]() {
if (!protocol_->IsAudioChannelOpened()) {
SetDeviceState(kDeviceStateConnecting);
if (!protocol_->OpenAudioChannel()) {
SetDeviceState(kDeviceStateIdle);
Alert("Error", "Failed to open audio channel");
return;
}
}
protocol_->SendStartListening(kListeningModeManualStop);
SetDeviceState(kDeviceStateListening);
} else if (device_state_ == kDeviceStateSpeaking) {
});
} else if (device_state_ == kDeviceStateSpeaking) {
Schedule([this]() {
AbortSpeaking(kAbortReasonNone);
protocol_->SendStartListening(kListeningModeManualStop);
// FIXME: Wait for the speaker to empty the buffer
vTaskDelay(pdMS_TO_TICKS(120));
SetDeviceState(kDeviceStateListening);
}
});
});
}
}
void Application::StopListening() {
Schedule([this]() {
if (device_state_ == kDeviceStateListening) {
if (device_state_ == kDeviceStateListening) {
Schedule([this]() {
protocol_->SendStopListening();
SetDeviceState(kDeviceStateIdle);
}
});
});
}
}
void Application::Start() {
@@ -262,14 +360,15 @@ void Application::Start() {
board.StartNetwork();
// Initialize the protocol
display->SetStatus("初始化协议");
display->SetStatus(Lang::Strings::LOADING_PROTOCOL);
#ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET
protocol_ = std::make_unique<WebsocketProtocol>();
#else
protocol_ = std::make_unique<MqttProtocol>();
#endif
protocol_->OnNetworkError([this](const std::string& message) {
Alert("Error", std::move(message));
SetDeviceState(kDeviceStateIdle);
Alert(Lang::Strings::ERROR, message.c_str(), "sad", Lang::Sounds::P3_EXCLAMATION);
});
protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) {
std::lock_guard<std::mutex> lock(mutex_);
@@ -280,11 +379,11 @@ void Application::Start() {
protocol_->OnAudioChannelOpened([this, codec, &board]() {
board.SetPowerSaveMode(false);
if (protocol_->server_sample_rate() != codec->output_sample_rate()) {
ESP_LOGW(TAG, "服务器的音频采样率 %d 与设备输出的采样率 %d 不一致,重采样后可能会失真",
ESP_LOGW(TAG, "Server sample rate %d does not match device output sample rate %d, resampling may cause distortion",
protocol_->server_sample_rate(), codec->output_sample_rate());
}
SetDecodeSampleRate(protocol_->server_sample_rate());
// 物联网设备描述符
// IoT device descriptors
last_iot_states_.clear();
auto& thing_manager = iot::ThingManager::GetInstance();
protocol_->SendIotDescriptors(thing_manager.GetDescriptorsJson());
@@ -293,7 +392,7 @@ void Application::Start() {
board.SetPowerSaveMode(true);
Schedule([this]() {
auto display = Board::GetInstance().GetDisplay();
display->SetChatMessage("", "");
display->SetChatMessage("system", "");
SetDeviceState(kDeviceStateIdle);
});
});
@@ -326,7 +425,7 @@ void Application::Start() {
if (text != NULL) {
ESP_LOGI(TAG, "<< %s", text->valuestring);
Schedule([this, display, message = std::string(text->valuestring)]() {
display->SetChatMessage("assistant", message);
display->SetChatMessage("assistant", message.c_str());
});
}
}
@@ -335,14 +434,14 @@ void Application::Start() {
if (text != NULL) {
ESP_LOGI(TAG, ">> %s", text->valuestring);
Schedule([this, display, message = std::string(text->valuestring)]() {
display->SetChatMessage("user", message);
display->SetChatMessage("user", message.c_str());
});
}
} else if (strcmp(type->valuestring, "llm") == 0) {
auto emotion = cJSON_GetObjectItem(root, "emotion");
if (emotion != NULL) {
Schedule([this, display, emotion_str = std::string(emotion->valuestring)]() {
display->SetEmotion(emotion_str);
display->SetEmotion(emotion_str.c_str());
});
}
} else if (strcmp(type->valuestring, "iot") == 0) {
@@ -356,8 +455,14 @@ void Application::Start() {
}
}
});
protocol_->Start();
// Check for new firmware version or get the MQTT broker address
ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
ota_.SetHeader("Client-Id", board.GetUuid());
ota_.SetHeader("Accept-Language", Lang::CODE);
xTaskCreate([](void* arg) {
Application* app = (Application*)arg;
app->CheckNewVersion();
@@ -399,8 +504,6 @@ void Application::Start() {
wake_word_detect_.EncodeWakeWordData();
if (!protocol_->OpenAudioChannel()) {
ESP_LOGE(TAG, "Failed to open audio channel");
SetDeviceState(kDeviceStateIdle);
wake_word_detect_.StartDetection();
return;
}
@@ -417,6 +520,8 @@ void Application::Start() {
SetDeviceState(kDeviceStateListening);
} else if (device_state_ == kDeviceStateSpeaking) {
AbortSpeaking(kAbortReasonWakeWordDetected);
} else if (device_state_ == kDeviceStateActivating) {
SetDeviceState(kDeviceStateIdle);
}
// Resume detection
@@ -427,6 +532,33 @@ void Application::Start() {
#endif
SetDeviceState(kDeviceStateIdle);
esp_timer_start_periodic(clock_timer_handle_, 1000000);
}
void Application::OnClockTimer() {
static int count = 0;
count++;
// Print the debug info every 10 seconds
if (count % 10 == 0) {
// SystemInfo::PrintRealTimeStats(pdMS_TO_TICKS(1000));
int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
int min_free_sram = heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL);
ESP_LOGI(TAG, "Free internal: %u minimal internal: %u", free_sram, min_free_sram);
// If we have synchronized server time, set the status to clock "HH:MM" if the device is idle
if (ota_.HasServerTime()) {
if (device_state_ == kDeviceStateIdle) {
Schedule([this]() {
// Set status to clock "HH:MM"
time_t now = time(NULL);
char time_str[64];
strftime(time_str, sizeof(time_str), "%H:%M ", localtime(&now));
Board::GetInstance().GetDisplay()->SetStatus(time_str);
});
}
}
}
}
void Application::Schedule(std::function<void()> callback) {
@@ -468,7 +600,6 @@ void Application::ResetDecoder() {
opus_decoder_->ResetState();
audio_decode_queue_.clear();
last_output_time_ = std::chrono::steady_clock::now();
Board::GetInstance().GetAudioCodec()->EnableOutput(true);
}
void Application::OutputAudio() {
@@ -587,23 +718,27 @@ void Application::SetDeviceState(DeviceState state) {
// The state is changed, wait for all background tasks to finish
background_task_->WaitForCompletion();
auto display = Board::GetInstance().GetDisplay();
auto led = Board::GetInstance().GetLed();
auto& board = Board::GetInstance();
auto codec = board.GetAudioCodec();
auto display = board.GetDisplay();
auto led = board.GetLed();
led->OnStateChanged();
switch (state) {
case kDeviceStateUnknown:
case kDeviceStateIdle:
display->SetStatus("待命");
display->SetStatus(Lang::Strings::STANDBY);
display->SetEmotion("neutral");
#ifdef CONFIG_USE_AUDIO_PROCESSING
audio_processor_.Stop();
#endif
break;
case kDeviceStateConnecting:
display->SetStatus("连接中...");
display->SetStatus(Lang::Strings::CONNECTING);
display->SetEmotion("neutral");
display->SetChatMessage("system", "");
break;
case kDeviceStateListening:
display->SetStatus("聆听中...");
display->SetStatus(Lang::Strings::LISTENING);
display->SetEmotion("neutral");
ResetDecoder();
opus_encoder_->ResetState();
@@ -613,8 +748,9 @@ void Application::SetDeviceState(DeviceState state) {
UpdateIotStates();
break;
case kDeviceStateSpeaking:
display->SetStatus("说话中...");
display->SetStatus(Lang::Strings::SPEAKING);
ResetDecoder();
codec->EnableOutput(true);
#if CONFIG_USE_AUDIO_PROCESSING
audio_processor_.Stop();
#endif
@@ -649,3 +785,29 @@ void Application::UpdateIotStates() {
protocol_->SendIotStates(states);
}
}
void Application::Reboot() {
ESP_LOGI(TAG, "Rebooting...");
esp_restart();
}
void Application::WakeWordInvoke(const std::string& wake_word) {
if (device_state_ == kDeviceStateIdle) {
ToggleChatState();
Schedule([this, wake_word]() {
if (protocol_) {
protocol_->SendWakeWordDetected(wake_word);
}
});
} else if (device_state_ == kDeviceStateSpeaking) {
Schedule([this]() {
AbortSpeaking(kAbortReasonNone);
});
} else if (device_state_ == kDeviceStateListening) {
Schedule([this]() {
if (protocol_) {
protocol_->CloseAudioChannel();
}
});
}
}

View File

@@ -4,6 +4,7 @@
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <freertos/task.h>
#include <esp_timer.h>
#include <string>
#include <mutex>
@@ -35,6 +36,7 @@ enum DeviceState {
kDeviceStateListening,
kDeviceStateSpeaking,
kDeviceStateUpgrading,
kDeviceStateActivating,
kDeviceStateFatalError
};
@@ -55,12 +57,16 @@ public:
bool IsVoiceDetected() const { return voice_detected_; }
void Schedule(std::function<void()> callback);
void SetDeviceState(DeviceState state);
void Alert(const std::string& title, const std::string& message);
void Alert(const char* status, const char* message, const char* emotion = "", const std::string_view& sound = "");
void DismissAlert();
void AbortSpeaking(AbortReason reason);
void ToggleChatState();
void StartListening();
void StopListening();
void UpdateIotStates();
void Reboot();
void WakeWordInvoke(const std::string& wake_word);
void PlaySound(const std::string_view& sound);
private:
Application();
@@ -74,7 +80,8 @@ private:
std::mutex mutex_;
std::list<std::function<void()>> main_tasks_;
std::unique_ptr<Protocol> protocol_;
EventGroupHandle_t event_group_;
EventGroupHandle_t event_group_ = nullptr;
esp_timer_handle_t clock_timer_handle_ = nullptr;
volatile DeviceState device_state_ = kDeviceStateUnknown;
bool keep_listening_ = false;
bool aborted_ = false;
@@ -100,8 +107,8 @@ private:
void ResetDecoder();
void SetDecodeSampleRate(int sample_rate);
void CheckNewVersion();
void PlayLocalFile(const char* data, size_t size);
void ShowActivationCode();
void OnClockTimer();
};
#endif // _APPLICATION_H_

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
main/assets/en-US/0.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/1.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/2.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/3.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/4.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/5.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/6.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/7.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/8.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/9.p3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,51 @@
{
"language": {
"type": "en-US"
},
"strings": {
"WARNING": "Warning",
"INFO": "Information",
"ERROR": "Error",
"VERSION": "Ver ",
"LOADING_PROTOCOL": "Loading Protocol...",
"INITIALIZING": "Initializing...",
"PIN_ERROR": "Please insert SIM card",
"REG_ERROR": "Unable to access network, please check SIM card status",
"DETECTING_MODULE": "Detecting module...",
"REGISTERING_NETWORK": "Waiting for network...",
"STANDBY": "Standby",
"CONNECT_TO": "Connect to ",
"CONNECTING": "Connecting...",
"CONNECTION_SUCCESSFUL": "Connection Successful",
"CONNECTED_TO": "Connected to ",
"LISTENING": "Listening...",
"SPEAKING": "Speaking...",
"SERVER_NOT_FOUND": "Looking for available service",
"SERVER_NOT_CONNECTED": "Unable to connect to service, please try again later",
"SERVER_TIMEOUT": "Waiting for response timeout",
"SERVER_ERROR": "Sending failed, please check the network",
"CONNECT_TO_HOTSPOT": "Hotspot: ",
"ACCESS_VIA_BROWSER": " Config URL: ",
"WIFI_CONFIG_MODE": "Wi-Fi Configuration Mode",
"ENTERING_WIFI_CONFIG_MODE": "Entering Wi-Fi configuration mode...",
"SCANNING_WIFI": "Scanning Wi-Fi...",
"NEW_VERSION": "New version ",
"OTA_UPGRADE": "OTA Upgrade",
"UPGRADING": "System is upgrading...",
"UPGRADE_FAILED": "Upgrade failed",
"ACTIVATION": "Activation",
"BATTERY_LOW": "Low battery",
"BATTERY_CHARGING": "Charging",
"BATTERY_FULL": "Battery full",
"VOLUME": "Volume ",
"MUTED": "Muted",
"MAX_VOLUME": "Max volume"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
main/assets/zh-CN/0.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/1.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/2.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/3.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/4.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/5.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/6.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/7.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/8.p3 Normal file

Binary file not shown.

BIN
main/assets/zh-CN/9.p3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,50 @@
{
"language": {
"type" :"zh-CN"
},
"strings": {
"WARNING":"警告",
"INFO":"信息",
"ERROR":"错误",
"VERSION": "版本 ",
"LOADING_PROTOCOL":"加载协议...",
"INITIALIZING":"正在初始化...",
"PIN_ERROR":"请插入 SIM 卡",
"REG_ERROR":"无法接入网络,请检查流量卡状态",
"DETECTING_MODULE":"检测模组...",
"REGISTERING_NETWORK":"等待网络...",
"STANDBY":"待命",
"CONNECT_TO":"连接 ",
"CONNECTING":"连接中...",
"CONNECTED_TO":"已连接 ",
"LISTENING":"聆听中...",
"SPEAKING":"说话中...",
"SERVER_NOT_FOUND":"正在寻找可用服务",
"SERVER_NOT_CONNECTED":"无法连接服务,请稍后再试",
"SERVER_TIMEOUT":"等待响应超时",
"SERVER_ERROR":"发送失败,请检查网络",
"CONNECT_TO_HOTSPOT":"手机连接热点 ",
"ACCESS_VIA_BROWSER":",浏览器访问 ",
"WIFI_CONFIG_MODE":"配网模式",
"ENTERING_WIFI_CONFIG_MODE":"进入配网模式...",
"SCANNING_WIFI":"扫描 Wi-Fi...",
"NEW_VERSION": "新版本 ",
"OTA_UPGRADE":"OTA 升级",
"UPGRADING":"正在升级系统...",
"UPGRADE_FAILED":"升级失败",
"ACTIVATION":"激活设备",
"BATTERY_LOW":"电量不足",
"BATTERY_CHARGING":"正在充电",
"BATTERY_FULL":"电量已满",
"VOLUME":"音量 ",
"MUTED":"已静音",
"MAX_VOLUME":"最大音量"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -14,7 +14,6 @@ NoAudioCodec::~NoAudioCodec() {
}
}
NoAudioCodecDuplex::NoAudioCodecDuplex(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din) {
duplex_ = true;
input_sample_rate_ = input_sample_rate;
@@ -201,6 +200,76 @@ NoAudioCodecSimplex::NoAudioCodecSimplex(int input_sample_rate, int output_sampl
ESP_LOGI(TAG, "Simplex channels created");
}
NoAudioCodecSimplex::NoAudioCodecSimplex(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, i2s_std_slot_mask_t spk_slot_mask, gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din, i2s_std_slot_mask_t mic_slot_mask){
duplex_ = false;
input_sample_rate_ = input_sample_rate;
output_sample_rate_ = output_sample_rate;
// Create a new channel for speaker
i2s_chan_config_t chan_cfg = {
.id = (i2s_port_t)0,
.role = I2S_ROLE_MASTER,
.dma_desc_num = 6,
.dma_frame_num = 240,
.auto_clear_after_cb = true,
.auto_clear_before_cb = false,
.intr_priority = 0,
};
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, nullptr));
i2s_std_config_t std_cfg = {
.clk_cfg = {
.sample_rate_hz = (uint32_t)output_sample_rate_,
.clk_src = I2S_CLK_SRC_DEFAULT,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
#ifdef I2S_HW_VERSION_2
.ext_clk_freq_hz = 0,
#endif
},
.slot_cfg = {
.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO,
.slot_mode = I2S_SLOT_MODE_MONO,
.slot_mask = spk_slot_mask,
.ws_width = I2S_DATA_BIT_WIDTH_32BIT,
.ws_pol = false,
.bit_shift = true,
#ifdef I2S_HW_VERSION_2
.left_align = true,
.big_endian = false,
.bit_order_lsb = false
#endif
},
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = spk_bclk,
.ws = spk_ws,
.dout = spk_dout,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false
}
}
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg));
// Create a new channel for MIC
chan_cfg.id = (i2s_port_t)1;
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, nullptr, &rx_handle_));
std_cfg.clk_cfg.sample_rate_hz = (uint32_t)input_sample_rate_;
std_cfg.slot_cfg.slot_mask = mic_slot_mask;
std_cfg.gpio_cfg.bclk = mic_sck;
std_cfg.gpio_cfg.ws = mic_ws;
std_cfg.gpio_cfg.dout = I2S_GPIO_UNUSED;
std_cfg.gpio_cfg.din = mic_din;
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg));
ESP_LOGI(TAG, "Simplex channels created");
}
NoAudioCodecSimplexPdm::NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_din) {
duplex_ = false;
input_sample_rate_ = input_sample_rate;

View File

@@ -28,6 +28,7 @@ public:
class NoAudioCodecSimplex : public NoAudioCodec {
public:
NoAudioCodecSimplex(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din);
NoAudioCodecSimplex(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, i2s_std_slot_mask_t spk_slot_mask, gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din, i2s_std_slot_mask_t mic_slot_mask);
};
class NoAudioCodecSimplexPdm : public NoAudioCodec {

View File

@@ -20,7 +20,10 @@ BackgroundTask::~BackgroundTask() {
void BackgroundTask::Schedule(std::function<void()> callback) {
std::lock_guard<std::mutex> lock(mutex_);
if (active_tasks_ >= 30) {
ESP_LOGW(TAG, "active_tasks_ == %u", active_tasks_.load());
int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
if (free_sram < 10000) {
ESP_LOGW(TAG, "active_tasks_ == %u, free_sram == %u", active_tasks_.load(), free_sram);
}
}
active_tasks_++;
main_tasks_.emplace_back([this, cb = std::move(callback)]() {

View File

@@ -152,7 +152,7 @@ private:
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true));
display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
display_ = new SpiLcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_20_4,

View File

@@ -0,0 +1,9 @@
{
"target": "esp32s3",
"builds": [
{
"name": "atk-dnesp32s3-box",
"sdkconfig_append": []
}
]
}

View File

@@ -132,7 +132,7 @@ private:
esp_lcd_panel_invert_color(panel, true);
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
display_ = new SpiLcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_20_4,

View File

@@ -0,0 +1,9 @@
{
"target": "esp32s3",
"builds": [
{
"name": "atk-dnesp32s3",
"sdkconfig_append": []
}
]
}

View File

@@ -0,0 +1,12 @@
{
"target": "esp32",
"builds": [
{
"name": "atommatrix-echo-base",
"sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_4M.csv\""
]
}
]
}

View File

@@ -0,0 +1,49 @@
# 编译配置命令
**配置编译目标为 ESP32S3**
```bash
idf.py set-target esp32s3
```
**打开 menuconfig**
```bash
idf.py menuconfig
```
**选择板子:**
```
Xiaozhi Assistant -> Board Type -> AtomS3 + Echo Base
```
**关闭语音唤醒:**
```
Xiaozhi Assistant -> [ ] 启用语音唤醒与音频处理 -> Unselect
```
**修改 flash 大小:**
```
Serial flasher config -> Flash size -> 8 MB
```
**修改分区表:**
```
Partition Table -> Custom partition CSV file -> partitions_8M.csv
```
**关闭片外 PSRAM**
```
Component config -> ESP PSRAM -> [ ] Support for external, SPI-connected RAM -> Unselect
```
**编译:**
```bash
idf.py build
```

View File

@@ -0,0 +1,238 @@
#include "wifi_board.h"
#include "audio_codecs/es8311_audio_codec.h"
#include "display/lcd_display.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "i2c_device.h"
#include "iot/thing_manager.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
#include <wifi_station.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <esp_lcd_gc9a01.h>
#define TAG "AtomS3+EchoBase"
LV_FONT_DECLARE(font_puhui_16_4);
LV_FONT_DECLARE(font_awesome_16_4);
static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
// {cmd, { data }, data_size, delay_ms}
{0xfe, (uint8_t[]){0x00}, 0, 0},
{0xef, (uint8_t[]){0x00}, 0, 0},
{0xb0, (uint8_t[]){0xc0}, 1, 0},
{0xb2, (uint8_t[]){0x2f}, 1, 0},
{0xb3, (uint8_t[]){0x03}, 1, 0},
{0xb6, (uint8_t[]){0x19}, 1, 0},
{0xb7, (uint8_t[]){0x01}, 1, 0},
{0xac, (uint8_t[]){0xcb}, 1, 0},
{0xab, (uint8_t[]){0x0e}, 1, 0},
{0xb4, (uint8_t[]){0x04}, 1, 0},
{0xa8, (uint8_t[]){0x19}, 1, 0},
{0xb8, (uint8_t[]){0x08}, 1, 0},
{0xe8, (uint8_t[]){0x24}, 1, 0},
{0xe9, (uint8_t[]){0x48}, 1, 0},
{0xea, (uint8_t[]){0x22}, 1, 0},
{0xc6, (uint8_t[]){0x30}, 1, 0},
{0xc7, (uint8_t[]){0x18}, 1, 0},
{0xf0,
(uint8_t[]){0x1f, 0x28, 0x04, 0x3e, 0x2a, 0x2e, 0x20, 0x00, 0x0c, 0x06,
0x00, 0x1c, 0x1f, 0x0f},
14, 0},
{0xf1,
(uint8_t[]){0x00, 0x2d, 0x2f, 0x3c, 0x6f, 0x1c, 0x0b, 0x00, 0x00, 0x00,
0x07, 0x0d, 0x11, 0x0f},
14, 0},
};
class AtomS3EchoBaseBoard : public WifiBoard {
private:
i2c_master_bus_handle_t i2c_bus_;
Display* display_;
Button boot_button_;
bool is_echo_base_connected_ = false;
void InitializeI2c() {
// 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 I2cDetect() {
is_echo_base_connected_ = false;
uint8_t echo_base_connected_flag = 0x00;
uint8_t address;
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
for (int i = 0; i < 128; i += 16) {
printf("%02x: ", i);
for (int j = 0; j < 16; j++) {
fflush(stdout);
address = i + j;
esp_err_t ret = i2c_master_probe(i2c_bus_, address, pdMS_TO_TICKS(200));
if (ret == ESP_OK) {
printf("%02x ", address);
if (address == 0x18) {
echo_base_connected_flag |= 0xF0;
} else if (address == 0x43) {
echo_base_connected_flag |= 0x0F;
}
} else if (ret == ESP_ERR_TIMEOUT) {
printf("UU ");
} else {
printf("-- ");
}
}
printf("\r\n");
}
is_echo_base_connected_ = (echo_base_connected_flag == 0xFF);
}
void CheckEchoBaseConnection() {
if (is_echo_base_connected_) {
return;
}
// Pop error page
InitializeSpi();
InitializeGc9107Display();
InitializeButtons();
display_->SetStatus(Lang::Strings::ERROR);
display_->SetEmotion("sad");
display_->SetChatMessage("system", "Echo Base\nnot connected");
while (1) {
ESP_LOGE(TAG, "Atomic Echo Base is disconnected");
vTaskDelay(pdMS_TO_TICKS(1000));
// Rerun detection
I2cDetect();
if (is_echo_base_connected_) {
vTaskDelay(pdMS_TO_TICKS(500));
I2cDetect();
if (is_echo_base_connected_) {
ESP_LOGI(TAG, "Atomic Echo Base is reconnected");
vTaskDelay(pdMS_TO_TICKS(200));
esp_restart();
}
}
}
}
void InitializeSpi() {
ESP_LOGI(TAG, "Initialize SPI bus");
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = GPIO_NUM_21;
buscfg.miso_io_num = GPIO_NUM_NC;
buscfg.sclk_io_num = GPIO_NUM_17;
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 InitializeGc9107Display() {
ESP_LOGI(TAG, "Init GC9107 display");
ESP_LOGI(TAG, "Install panel IO");
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = GPIO_NUM_15;
io_config.dc_gpio_num = GPIO_NUM_33;
io_config.spi_mode = 0;
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, &io_handle));
ESP_LOGI(TAG, "Install GC9A01 panel driver");
esp_lcd_panel_handle_t panel_handle = NULL;
gc9a01_vendor_config_t gc9107_vendor_config = {
.init_cmds = gc9107_lcd_init_cmds,
.init_cmds_size = sizeof(gc9107_lcd_init_cmds) / sizeof(gc9a01_lcd_init_cmd_t),
};
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = GPIO_NUM_34; // Set to -1 if not use
panel_config.rgb_endian = LCD_RGB_ENDIAN_BGR;
panel_config.bits_per_pixel = 16; // Implemented by LCD command `3Ah` (16/18)
panel_config.vendor_config = &gc9107_vendor_config;
ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(io_handle, &panel_config, &panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
display_ = new SpiLcdDisplay(io_handle, panel_handle, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_16_4,
.icon_font = &font_awesome_16_4,
.emoji_font = font_emoji_32_init(),
});
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
ResetWifiConfiguration();
}
app.ToggleChatState();
});
}
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
}
public:
AtomS3EchoBaseBoard() : boot_button_(BOOT_BUTTON_GPIO) {
InitializeI2c();
I2cDetect();
CheckEchoBaseConnection();
InitializeSpi();
InitializeGc9107Display();
InitializeButtons();
InitializeIot();
}
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_GPIO_PA,
AUDIO_CODEC_ES8311_ADDR,
false);
return &audio_codec;
}
virtual Display* GetDisplay() override {
return display_;
}
};
DECLARE_BOARD(AtomS3EchoBaseBoard);

View File

@@ -0,0 +1,43 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
// AtomS3+EchoBase Board configuration
#include <driver/gpio.h>
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_NC
#define AUDIO_I2S_GPIO_WS GPIO_NUM_6
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_8
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_7
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_5
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_38
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_39
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define AUDIO_CODEC_GPIO_PA GPIO_NUM_NC
#define BUILTIN_LED_GPIO GPIO_NUM_NC
#define BOOT_BUTTON_GPIO GPIO_NUM_41
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
#define DISPLAY_SDA_PIN GPIO_NUM_NC
#define DISPLAY_SCL_PIN GPIO_NUM_NC
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 128
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_SWAP_XY false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 32
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_16
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#endif // _BOARD_CONFIG_H_

View File

@@ -0,0 +1,14 @@
{
"target": "esp32s3",
"builds": [
{
"name": "atoms3-echo-base",
"sdkconfig_append": [
"CONFIG_SPIRAM=n",
"CONFIG_USE_AUDIO_PROCESSING=n",
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_8M.csv\""
]
}
]
}

View File

@@ -1,12 +1,12 @@
#include "wifi_board.h"
#include "audio_codecs/es8311_audio_codec.h"
#include "display/lcd_display.h"
#include "display/no_display.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "i2c_device.h"
#include "iot/thing_manager.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
@@ -96,6 +96,7 @@ private:
Lp5562* lp5562_;
Display* display_;
Button boot_button_;
bool is_echo_base_connected_ = false;
void InitializeI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
@@ -119,6 +120,8 @@ private:
}
void I2cDetect() {
is_echo_base_connected_ = false;
uint8_t echo_base_connected_flag = 0x00;
uint8_t address;
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
for (int i = 0; i < 128; i += 16) {
@@ -129,6 +132,11 @@ private:
esp_err_t ret = i2c_master_probe(i2c_bus_, address, pdMS_TO_TICKS(200));
if (ret == ESP_OK) {
printf("%02x ", address);
if (address == 0x18) {
echo_base_connected_flag |= 0xF0;
} else if (address == 0x43) {
echo_base_connected_flag |= 0x0F;
}
} else if (ret == ESP_ERR_TIMEOUT) {
printf("UU ");
} else {
@@ -137,6 +145,40 @@ private:
}
printf("\r\n");
}
is_echo_base_connected_ = (echo_base_connected_flag == 0xFF);
}
void CheckEchoBaseConnection() {
if (is_echo_base_connected_) {
return;
}
// Pop error page
InitializeLp5562();
InitializeSpi();
InitializeGc9107Display();
InitializeButtons();
display_->SetStatus(Lang::Strings::ERROR);
display_->SetEmotion("sad");
display_->SetChatMessage("system", "Echo Base\nnot connected");
while (1) {
ESP_LOGE(TAG, "Atomic Echo Base is disconnected");
vTaskDelay(pdMS_TO_TICKS(1000));
// Rerun detection
I2cDetect();
if (is_echo_base_connected_) {
vTaskDelay(pdMS_TO_TICKS(500));
I2cDetect();
if (is_echo_base_connected_) {
ESP_LOGI(TAG, "Atomic Echo Base is reconnected");
vTaskDelay(pdMS_TO_TICKS(200));
esp_restart();
}
}
}
}
void InitializePi4ioe() {
@@ -195,7 +237,7 @@ private:
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
display_ = new LcdDisplay(io_handle, panel_handle, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
display_ = new SpiLcdDisplay(io_handle, panel_handle, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_16_4,
@@ -224,6 +266,7 @@ public:
AtomS3rEchoBaseBoard() : boot_button_(BOOT_BUTTON_GPIO) {
InitializeI2c();
I2cDetect();
CheckEchoBaseConnection();
InitializePi4ioe();
InitializeLp5562();
InitializeSpi();

View File

@@ -0,0 +1,12 @@
{
"target": "esp32s3",
"builds": [
{
"name": "atoms3r-echo-base",
"sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_8M.csv\""
]
}
]
}

View File

@@ -30,7 +30,22 @@
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define TOUCH_BUTTON_GPIO GPIO_NUM_5
#define ASR_BUTTON_GPIO GPIO_NUM_19
#define BUILTIN_LED_GPIO GPIO_NUM_2
#define DISPLAY_SDA_PIN GPIO_NUM_4
#define DISPLAY_SCL_PIN GPIO_NUM_15
#define DISPLAY_WIDTH 128
#if CONFIG_OLED_SSD1306_128X32
#define DISPLAY_HEIGHT 32
#elif CONFIG_OLED_SSD1306_128X64
#define DISPLAY_HEIGHT 64
#else
#error "未选择 OLED 屏幕类型"
#endif
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y true
#endif // _BOARD_CONFIG_H_

View File

@@ -0,0 +1,21 @@
{
"target": "esp32",
"builds": [
{
"name": "bread-compact-esp32",
"sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_4M.csv\"",
"CONFIG_OLED_SSD1306_128X64=y"
]
},
{
"name": "bread-compact-esp32-128x32",
"sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions_4M.csv\"",
"CONFIG_OLED_SSD1306_128X32=y"
]
}
]
}

View File

@@ -6,18 +6,40 @@
#include "config.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "display/ssd1306_display.h"
#include <wifi_station.h>
#include <esp_log.h>
#include <driver/i2c_master.h>
#define TAG "ESP32-MarsbearSupport"
LV_FONT_DECLARE(font_puhui_14_1);
LV_FONT_DECLARE(font_awesome_14_1);
class CompactWifiBoard : public WifiBoard {
private:
Button boot_button_;
Button touch_button_;
Button asr_button_;
i2c_master_bus_handle_t display_i2c_bus_;
void InitializeDisplayI2c() {
i2c_master_bus_config_t bus_config = {
.i2c_port = (i2c_port_t)0,
.sda_io_num = DISPLAY_SDA_PIN,
.scl_io_num = DISPLAY_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(&bus_config, &display_i2c_bus_));
}
void InitializeButtons() {
@@ -39,6 +61,12 @@ private:
gpio_set_level(BUILTIN_LED_GPIO, 1);
app.ToggleChatState();
});
asr_button_.OnClick([this]() {
std::string wake_word="你好小智";
Application::GetInstance().WakeWordInvoke(wake_word);
});
touch_button_.OnPressDown([this]() {
gpio_set_level(BUILTIN_LED_GPIO, 1);
Application::GetInstance().StartListening();
@@ -57,8 +85,9 @@ private:
}
public:
CompactWifiBoard() : boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO)
CompactWifiBoard() : boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO)
{
InitializeDisplayI2c();
InitializeButtons();
InitializeIot();
}
@@ -75,6 +104,12 @@ public:
return &audio_codec;
}
virtual Display* GetDisplay() override {
static Ssd1306Display display(display_i2c_bus_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y,
&font_puhui_14_1, &font_awesome_14_1);
return &display;
}
};
DECLARE_BOARD(CompactWifiBoard);

View File

@@ -7,6 +7,7 @@
#include "config.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
@@ -58,12 +59,12 @@ private:
volume = 100;
}
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume));
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
});
volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量");
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
});
volume_down_button_.OnClick([this]() {
@@ -73,12 +74,12 @@ private:
volume = 0;
}
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume));
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
});
volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音");
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
});
}

View File

@@ -36,7 +36,15 @@
#define DISPLAY_SDA_PIN GPIO_NUM_41
#define DISPLAY_SCL_PIN GPIO_NUM_42
#define DISPLAY_WIDTH 128
#if CONFIG_OLED_SSD1306_128X32
#define DISPLAY_HEIGHT 32
#elif CONFIG_OLED_SSD1306_128X64
#define DISPLAY_HEIGHT 64
#else
#error "未选择 OLED 屏幕类型"
#endif
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y true

View File

@@ -0,0 +1,17 @@
{
"target": "esp32s3",
"builds": [
{
"name": "bread-compact-ml307",
"sdkconfig_append": [
"CONFIG_OLED_SSD1306_128X32=y"
]
},
{
"name": "bread-compact-ml307-128x64",
"sdkconfig_append": [
"CONFIG_OLED_SSD1306_128X64=y"
]
}
]
}

View File

@@ -12,11 +12,50 @@
#include <esp_log.h>
#include <driver/i2c_master.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <driver/spi_common.h>
#if defined(LCD_ILI9341_240X320) || defined(LCD_ILI9341_240X320_NO_IPS)
#if defined(LCD_TYPE_ILI9341_SERIAL)
#include "esp_lcd_ili9341.h"
#endif
#if defined(LCD_TYPE_GC9A01_SERIAL)
#include "esp_lcd_gc9a01.h"
static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
// {cmd, { data }, data_size, delay_ms}
{0xfe, (uint8_t[]){0x00}, 0, 0},
{0xef, (uint8_t[]){0x00}, 0, 0},
{0xb0, (uint8_t[]){0xc0}, 1, 0},
{0xb1, (uint8_t[]){0x80}, 1, 0},
{0xb2, (uint8_t[]){0x27}, 1, 0},
{0xb3, (uint8_t[]){0x13}, 1, 0},
{0xb6, (uint8_t[]){0x19}, 1, 0},
{0xb7, (uint8_t[]){0x05}, 1, 0},
{0xac, (uint8_t[]){0xc8}, 1, 0},
{0xab, (uint8_t[]){0x0f}, 1, 0},
{0x3a, (uint8_t[]){0x05}, 1, 0},
{0xb4, (uint8_t[]){0x04}, 1, 0},
{0xa8, (uint8_t[]){0x08}, 1, 0},
{0xb8, (uint8_t[]){0x08}, 1, 0},
{0xea, (uint8_t[]){0x02}, 1, 0},
{0xe8, (uint8_t[]){0x2A}, 1, 0},
{0xe9, (uint8_t[]){0x47}, 1, 0},
{0xe7, (uint8_t[]){0x5f}, 1, 0},
{0xc6, (uint8_t[]){0x21}, 1, 0},
{0xc7, (uint8_t[]){0x15}, 1, 0},
{0xf0,
(uint8_t[]){0x1D, 0x38, 0x09, 0x4D, 0x92, 0x2F, 0x35, 0x52, 0x1E, 0x0C,
0x04, 0x12, 0x14, 0x1f},
14, 0},
{0xf1,
(uint8_t[]){0x16, 0x40, 0x1C, 0x54, 0xA9, 0x2D, 0x2E, 0x56, 0x10, 0x0D,
0x0C, 0x1A, 0x14, 0x1E},
14, 0},
{0xf4, (uint8_t[]){0x00, 0x00, 0xFF}, 3, 0},
{0xba, (uint8_t[]){0xFF, 0xFF}, 2, 0},
};
#endif
#define TAG "CompactWifiBoardLCD"
@@ -48,7 +87,7 @@ private:
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.spi_mode = DISPLAY_SPI_MODE;
io_config.pclk_hz = 40 * 1000 * 1000;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
@@ -61,8 +100,14 @@ private:
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
panel_config.bits_per_pixel = 16;
#if defined(LCD_ILI9341_240X320) || defined(LCD_ILI9341_240X320_NO_IPS)
#if defined(LCD_TYPE_ILI9341_SERIAL)
ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(panel_io, &panel_config, &panel));
#elif defined(LCD_TYPE_GC9A01_SERIAL)
ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(panel_io, &panel_config, &panel));
gc9a01_vendor_config_t gc9107_vendor_config = {
.init_cmds = gc9107_lcd_init_cmds,
.init_cmds_size = sizeof(gc9107_lcd_init_cmds) / sizeof(gc9a01_lcd_init_cmd_t),
};
#else
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
#endif
@@ -74,7 +119,10 @@ private:
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 LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
#ifdef LCD_TYPE_GC9A01_SERIAL
panel_config.vendor_config = &gc9107_vendor_config;
#endif
display_ = new SpiLcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_16_4,
@@ -99,6 +147,7 @@ private:
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Backlight"));
}
public:

View File

@@ -43,9 +43,8 @@
#define DISPLAY_CS_PIN GPIO_NUM_41
#ifdef CONFIG_LCD_ST7789_240X320
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 320
#define DISPLAY_MIRROR_X false
@@ -59,6 +58,7 @@
#endif
#ifdef CONFIG_LCD_ST7789_240X320_NO_IPS
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 320
#define DISPLAY_MIRROR_X false
@@ -69,9 +69,11 @@
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#ifdef CONFIG_LCD_ST7789_170X320
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 170
#define DISPLAY_HEIGHT 320
#define DISPLAY_MIRROR_X false
@@ -82,9 +84,11 @@
#define DISPLAY_OFFSET_X 35
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#ifdef CONFIG_LCD_ST7789_172X320
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 172
#define DISPLAY_HEIGHT 320
#define DISPLAY_MIRROR_X false
@@ -95,9 +99,11 @@
#define DISPLAY_OFFSET_X 34
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#ifdef CONFIG_LCD_ST7789_240X280
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 280
#define DISPLAY_MIRROR_X false
@@ -108,9 +114,11 @@
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 20
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#ifdef CONFIG_LCD_ST7789_240X240
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 240
#define DISPLAY_MIRROR_X false
@@ -121,9 +129,26 @@
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#ifdef CONFIG_LCD_ST7789_240X240_7PIN
#define LCD_TYPE_ST7789_SERIAL
#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_INVERT_COLOR true
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 2
#endif
#ifdef CONFIG_LCD_ST7789_240X135
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 135
#define DISPLAY_MIRROR_X true
@@ -134,9 +159,11 @@
#define DISPLAY_OFFSET_X 40
#define DISPLAY_OFFSET_Y 53
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#ifdef CONFIG_LCD_ST7735_128X160
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 160
#define DISPLAY_MIRROR_X true
@@ -149,7 +176,23 @@
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#endif
#ifdef CONFIG_LCD_ST7735_128X128
#define LCD_TYPE_ST7789_SERIAL
#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_INVERT_COLOR false
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_BGR
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 32
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#ifdef CONFIG_LCD_ST7796_320X480
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 480
#define DISPLAY_MIRROR_X false
@@ -160,32 +203,52 @@
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#ifdef CONFIG_LCD_ILI9341_240X320
#define LCD_TYPE_ILI9341_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 320
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y false
#define DISPLAY_SWAP_XY false
#define DISPLAY_INVERT_COLOR true
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
#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
#ifdef CONFIG_LCD_ILI9341_240X320_NO_IPS
#define LCD_TYPE_ILI9341_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 320
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y false
#define DISPLAY_SWAP_XY false
#define DISPLAY_INVERT_COLOR false
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
#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
#ifdef CONFIG_LCD_GC9A01_240X240
#define LCD_TYPE_GC9A01_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 240
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y false
#define DISPLAY_SWAP_XY false
#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
#ifdef CONFIG_LCD_CUSTOM
@@ -199,6 +262,7 @@
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define DISPLAY_SPI_MODE 0
#endif
#endif // _BOARD_CONFIG_H_

View File

@@ -7,6 +7,7 @@
#include "config.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "assets/lang_config.h"
#include <wifi_station.h>
#include <esp_log.h>
@@ -63,12 +64,12 @@ private:
volume = 100;
}
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume));
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
});
volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量");
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
});
volume_down_button_.OnClick([this]() {
@@ -78,12 +79,12 @@ private:
volume = 0;
}
codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume));
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
});
volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音");
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
});
}

View File

@@ -37,7 +37,15 @@
#define DISPLAY_SDA_PIN GPIO_NUM_41
#define DISPLAY_SCL_PIN GPIO_NUM_42
#define DISPLAY_WIDTH 128
#if CONFIG_OLED_SSD1306_128X32
#define DISPLAY_HEIGHT 32
#elif CONFIG_OLED_SSD1306_128X64
#define DISPLAY_HEIGHT 64
#else
#error "未选择 OLED 屏幕类型"
#endif
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y true

View File

@@ -0,0 +1,17 @@
{
"target": "esp32s3",
"builds": [
{
"name": "bread-compact-wifi",
"sdkconfig_append": [
"CONFIG_OLED_SSD1306_128X32=y"
]
},
{
"name": "bread-compact-wifi-128x64",
"sdkconfig_append": [
"CONFIG_OLED_SSD1306_128X64=y"
]
}
]
}

View File

@@ -1,14 +1,47 @@
#include "board.h"
#include "system_info.h"
#include "settings.h"
#include "display/no_display.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <esp_ota_ops.h>
#include <esp_chip_info.h>
#include <esp_random.h>
#define TAG "Board"
Board::Board() {
Settings settings("board", true);
uuid_ = settings.GetString("uuid");
if (uuid_.empty()) {
uuid_ = GenerateUuid();
settings.SetString("uuid", uuid_);
}
ESP_LOGI(TAG, "UUID=%s SKU=%s", uuid_.c_str(), BOARD_NAME);
}
std::string Board::GenerateUuid() {
// UUID v4 需要 16 字节的随机数据
uint8_t uuid[16];
// 使用 ESP32 的硬件随机数生成器
esp_fill_random(uuid, sizeof(uuid));
// 设置版本 (版本 4) 和变体位
uuid[6] = (uuid[6] & 0x0F) | 0x40; // 版本 4
uuid[8] = (uuid[8] & 0x3F) | 0x80; // 变体 1
// 将字节转换为标准的 UUID 字符串格式
char uuid_str[37];
snprintf(uuid_str, sizeof(uuid_str),
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid[0], uuid[1], uuid[2], uuid[3],
uuid[4], uuid[5], uuid[6], uuid[7],
uuid[8], uuid[9], uuid[10], uuid[11],
uuid[12], uuid[13], uuid[14], uuid[15]);
return std::string(uuid_str);
}
bool Board::GetBatteryLevel(int &level, bool& charging) {
@@ -28,10 +61,12 @@ Led* Board::GetLed() {
std::string Board::GetJson() {
/*
{
"version": 2,
"flash_size": 4194304,
"psram_size": 0,
"minimum_free_heap_size": 123456,
"mac_address": "00:00:00:00:00:00",
"uuid": "00000000-0000-0000-0000-000000000000",
"chip_model_name": "esp32s3",
"chip_info": {
"model": 1,
@@ -57,13 +92,19 @@ std::string Board::GetJson() {
],
"ota": {
"label": "ota_0"
},
"board": {
...
}
}
*/
std::string json = "{";
json += "\"version\":2,";
json += "\"language\":\"" + std::string(Lang::CODE) + "\",";
json += "\"flash_size\":" + std::to_string(SystemInfo::GetFlashSize()) + ",";
json += "\"minimum_free_heap_size\":" + std::to_string(SystemInfo::GetMinimumFreeHeapSize()) + ",";
json += "\"mac_address\":\"" + SystemInfo::GetMacAddress() + "\",";
json += "\"uuid\":\"" + uuid_ + "\",";
json += "\"chip_model_name\":\"" + SystemInfo::GetChipModelName() + "\",";
json += "\"chip_info\":{";

View File

@@ -20,6 +20,10 @@ private:
protected:
Board();
std::string GenerateUuid();
// 软件生成的设备唯一标识
std::string uuid_;
public:
static Board& GetInstance() {
@@ -29,6 +33,7 @@ public:
virtual ~Board() = default;
virtual std::string GetBoardType() = 0;
virtual std::string GetUuid() { return uuid_; }
virtual Led* GetLed();
virtual AudioCodec* GetAudioCodec() = 0;
virtual Display* GetDisplay();

View File

@@ -3,6 +3,7 @@
#include "application.h"
#include "display.h"
#include "font_awesome_symbols.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <esp_timer.h>
@@ -24,7 +25,7 @@ std::string Ml307Board::GetBoardType() {
void Ml307Board::StartNetwork() {
auto display = Board::GetInstance().GetDisplay();
display->SetStatus("初始化模块");
display->SetStatus(Lang::Strings::DETECTING_MODULE);
modem_.SetDebug(false);
modem_.SetBaudRate(921600);
@@ -44,13 +45,13 @@ void Ml307Board::StartNetwork() {
void Ml307Board::WaitForNetworkReady() {
auto& application = Application::GetInstance();
auto display = Board::GetInstance().GetDisplay();
display->SetStatus("等待网络...");
display->SetStatus(Lang::Strings::REGISTERING_NETWORK);
int result = modem_.WaitForNetworkReady();
if (result == -1) {
application.Alert("Error", "请插入SIM卡");
application.Alert(Lang::Strings::ERROR, Lang::Strings::PIN_ERROR, "sad", Lang::Sounds::P3_ERR_PIN);
return;
} else if (result == -2) {
application.Alert("Error", "无法接入网络,请检查流量卡状态");
application.Alert(Lang::Strings::ERROR, Lang::Strings::REG_ERROR, "sad", Lang::Sounds::P3_ERR_REG);
return;
}
@@ -105,8 +106,8 @@ const char* Ml307Board::GetNetworkStateIcon() {
std::string Ml307Board::GetBoardJson() {
// Set the board type for OTA
std::string board_type = BOARD_TYPE;
std::string board_json = std::string("{\"type\":\"" + board_type + "\",");
std::string board_json = std::string("{\"type\":\"" BOARD_TYPE "\",");
board_json += "\"name\":\"" BOARD_NAME "\",";
board_json += "\"revision\":\"" + modem_.GetModuleName() + "\",";
board_json += "\"carrier\":\"" + modem_.GetCarrierName() + "\",";
board_json += "\"csq\":\"" + std::to_string(modem_.GetCsq()) + "\",";

View File

@@ -5,6 +5,7 @@
#include "system_info.h"
#include "font_awesome_symbols.h"
#include "settings.h"
#include "assets/lang_config.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
@@ -37,23 +38,22 @@ std::string WifiBoard::GetBoardType() {
void WifiBoard::EnterWifiConfigMode() {
auto& application = Application::GetInstance();
auto display = Board::GetInstance().GetDisplay();
application.SetDeviceState(kDeviceStateWifiConfiguring);
auto& wifi_ap = WifiConfigurationAp::GetInstance();
wifi_ap.SetLanguage(Lang::CODE);
wifi_ap.SetSsidPrefix("Xiaozhi");
wifi_ap.Start();
// 显示 WiFi 配置 AP 的 SSID 和 Web 服务器 URL
std::string hint = "请在手机上连接热点 ";
std::string hint = Lang::Strings::CONNECT_TO_HOTSPOT;
hint += wifi_ap.GetSsid();
hint += ",然后打开浏览器访问 ";
hint += Lang::Strings::ACCESS_VIA_BROWSER;
hint += wifi_ap.GetWebServerUrl();
display->SetStatus(hint);
hint += "\n\n";
// 播报配置 WiFi 的提示
application.Alert("Info", "进入配网模式");
application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "", Lang::Sounds::P3_WIFICONFIG);
// Wait forever until reset after configuration
while (true) {
@@ -83,15 +83,20 @@ void WifiBoard::StartNetwork() {
auto& wifi_station = WifiStation::GetInstance();
wifi_station.OnScanBegin([this]() {
auto display = Board::GetInstance().GetDisplay();
display->ShowNotification("正在扫描 WiFi 网络", 30000);
display->ShowNotification(Lang::Strings::SCANNING_WIFI, 30000);
});
wifi_station.OnConnect([this](const std::string& ssid) {
auto display = Board::GetInstance().GetDisplay();
display->ShowNotification(std::string("正在连接 ") + ssid, 30000);
std::string notification = Lang::Strings::CONNECT_TO;
notification += ssid;
notification += "...";
display->ShowNotification(notification.c_str(), 30000);
});
wifi_station.OnConnected([this](const std::string& ssid) {
auto display = Board::GetInstance().GetDisplay();
display->ShowNotification(std::string("已连接 ") + ssid);
std::string notification = Lang::Strings::CONNECTED_TO;
notification += ssid;
display->ShowNotification(notification.c_str(), 30000);
});
wifi_station.Start();
@@ -149,8 +154,8 @@ const char* WifiBoard::GetNetworkStateIcon() {
std::string WifiBoard::GetBoardJson() {
// Set the board type for OTA
auto& wifi_station = WifiStation::GetInstance();
std::string board_type = BOARD_TYPE;
std::string board_json = std::string("{\"type\":\"" + board_type + "\",");
std::string board_json = std::string("{\"type\":\"" BOARD_TYPE "\",");
board_json += "\"name\":\"" BOARD_NAME "\",";
if (!wifi_config_mode_) {
board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\",";
board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ",";
@@ -167,12 +172,12 @@ void WifiBoard::SetPowerSaveMode(bool enabled) {
}
void WifiBoard::ResetWifiConfiguration() {
// Reset the wifi station
// Set a flag and reboot the device to enter the network configuration mode
{
Settings settings("wifi", true);
settings.SetInt("force_ap", 1);
}
GetDisplay()->ShowNotification("进入配网模式...");
GetDisplay()->ShowNotification(Lang::Strings::ENTERING_WIFI_CONFIG_MODE);
vTaskDelay(pdMS_TO_TICKS(1000));
// Reboot the device
esp_restart();

View File

@@ -6,11 +6,6 @@
#define AUDIO_INPUT_SAMPLE_RATE 16000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
// 如果使用 Duplex I2S 模式,请注释下面一行
#define AUDIO_I2S_METHOD_SIMPLEX
#ifdef AUDIO_I2S_METHOD_SIMPLEX
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_39
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_38
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_7
@@ -18,15 +13,6 @@
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_42
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_2
#else
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
#endif
#define BUILTIN_LED_GPIO GPIO_NUM_48
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define TOUCH_BUTTON_GPIO GPIO_NUM_NC
@@ -47,8 +33,8 @@
#define DISPLAY_SWAP_XY false
#define DISPLAY_INVERT_COLOR false
#define DISPLAY_RGB_ORDER LCD_RGB_ELEMENT_ORDER_RGB
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_OFFSET_X 2
#define DISPLAY_OFFSET_Y 1
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#endif // _BOARD_CONFIG_H_

View File

@@ -0,0 +1,9 @@
{
"target": "esp32s3",
"builds": [
{
"name": "du-chatx",
"sdkconfig_append": []
}
]
}

View File

@@ -74,7 +74,7 @@ private:
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 LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
display_ = new SpiLcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_16_4,
@@ -99,6 +99,7 @@ private:
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Backlight"));
}
public:
@@ -116,13 +117,8 @@ public:
}
virtual AudioCodec* GetAudioCodec() override {
#ifdef AUDIO_I2S_METHOD_SIMPLEX
static NoAudioCodecSimplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN);
#else
static NoAudioCodecDuplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN);
#endif
return &audio_codec;
}

View File

@@ -0,0 +1,9 @@
{
"target": "esp32s3",
"builds": [
{
"name": "esp-box-3",
"sdkconfig_append": []
}
]
}

View File

@@ -121,7 +121,7 @@ private:
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
esp_lcd_panel_disp_on_off(panel, true);
display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
display_ = new SpiLcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_20_4,
@@ -134,6 +134,7 @@ private:
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Backlight"));
}
public:

View File

@@ -0,0 +1,98 @@
/*
ESP-SparkBot 的底座
https://gitee.com/esp-friends/esp_sparkbot/tree/master/example/tank/c2_tracked_chassis
*/
#include "sdkconfig.h"
#include "iot/thing.h"
#include "board.h"
#include <driver/gpio.h>
#include <driver/uart.h>
#include <esp_log.h>
#include <cstring>
#include "boards/esp-sparkbot/config.h"
#define TAG "Chassis"
namespace iot {
class Chassis : public Thing {
private:
light_mode_t light_mode_ = LIGHT_MODE_ALWAYS_ON;
void SendUartMessage(const char * command_str) {
uint8_t len = strlen(command_str);
uart_write_bytes(ECHO_UART_PORT_NUM, command_str, len);
ESP_LOGI(TAG, "Sent command: %s", command_str);
}
void InitializeEchoUart() {
uart_config_t uart_config = {
.baud_rate = ECHO_UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
int intr_alloc_flags = 0;
ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, UART_ECHO_TXD, UART_ECHO_RXD, UART_ECHO_RTS, UART_ECHO_CTS));
SendUartMessage("w2");
}
public:
Chassis() : Thing("Chassis", "小机器人的底座:有履带可以移动;可以调整灯光效果"), light_mode_(LIGHT_MODE_ALWAYS_ON) {
InitializeEchoUart();
// 定义设备的属性
properties_.AddNumberProperty("light_mode", "灯光效果编号", [this]() -> int {
return (light_mode_ - 2 <= 0) ? 1 : light_mode_ - 2;
});
// 定义设备可以被远程执行的指令
methods_.AddMethod("GoForward", "向前走", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("x0.0 y1.0");
});
methods_.AddMethod("GoBack", "向后退", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("x0.0 y-1.0");
});
methods_.AddMethod("TurnLeft", "向左转", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("x-1.0 y0.0");
});
methods_.AddMethod("TurnRight", "向右转", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("x1.0 y0.0");
});
methods_.AddMethod("Dance", "跳舞", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("d1");
light_mode_ = LIGHT_MODE_MAX;
});
methods_.AddMethod("SwitchLightMode", "打开灯", ParameterList({
Parameter("lightmode", "1到6之间的整数", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
char command_str[5] = {'w', 0, 0};
char mode = static_cast<char>(parameters["lightmode"].number()) + 2;
ESP_LOGI(TAG, "Input Light Mode: %c", (mode + '0'));
if (mode >= 3 && mode <= 8) {
command_str[1] = mode + '0';
SendUartMessage(command_str);
}
});
}
};
} // namespace iot
DECLARE_THING(Chassis);

View File

@@ -2,6 +2,7 @@
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#include <driver/uart.h>
#define AUDIO_INPUT_SAMPLE_RATE 16000
#define AUDIO_OUTPUT_SAMPLE_RATE 16000
@@ -42,5 +43,31 @@
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_46
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
#define UART_ECHO_TXD GPIO_NUM_38
#define UART_ECHO_RXD GPIO_NUM_48
#define UART_ECHO_RTS (-1)
#define UART_ECHO_CTS (-1)
#define MOTOR_SPEED_MAX 100
#define MOTOR_SPEED_80 80
#define MOTOR_SPEED_60 60
#define MOTOR_SPEED_MIN 0
#define ECHO_UART_PORT_NUM UART_NUM_1
#define ECHO_UART_BAUD_RATE (115200)
#define BUF_SIZE (1024)
typedef enum {
LIGHT_MODE_CHARGING_BREATH = 0,
LIGHT_MODE_POWER_LOW,
LIGHT_MODE_ALWAYS_ON,
LIGHT_MODE_BLINK,
LIGHT_MODE_WHITE_BREATH_SLOW,
LIGHT_MODE_WHITE_BREATH_FAST,
LIGHT_MODE_FLOWING,
LIGHT_MODE_SHOW,
LIGHT_MODE_SLEEP,
LIGHT_MODE_MAX
} light_mode_t;
#endif // _BOARD_CONFIG_H_

View File

@@ -0,0 +1,9 @@
{
"target": "esp32s3",
"builds": [
{
"name": "esp-sparkbot",
"sdkconfig_append": []
}
]
}

View File

@@ -111,9 +111,9 @@ private:
esp_lcd_panel_reset(panel);
esp_lcd_panel_init(panel);
esp_lcd_panel_invert_color(panel, false);
esp_lcd_panel_invert_color(panel, true);
esp_lcd_panel_disp_on_off(panel, true);
display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
display_ = new SpiLcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
{
.text_font = &font_puhui_20_4,
@@ -126,6 +126,8 @@ private:
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Backlight"));
thing_manager.AddThing(iot::CreateThing("Chassis"));
}
public:

View File

@@ -0,0 +1,59 @@
#include "axp2101.h"
#include "board.h"
#include "display.h"
#include <esp_log.h>
#define TAG "Axp2101"
Axp2101::Axp2101(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
// ** EFUSE defaults **
WriteReg(0x22, 0b110); // PWRON > OFFLEVEL as POWEROFF Source enable
WriteReg(0x27, 0x10); // hold 4s to power off
WriteReg(0x93, 0x1C); // 配置 aldo2 输出为 3.3V
uint8_t value = ReadReg(0x90); // XPOWERS_AXP2101_LDO_ONOFF_CTRL0
value = value | 0x02; // set bit 1 (ALDO2)
WriteReg(0x90, value); // and power channels now enabled
WriteReg(0x64, 0x03); // CV charger voltage setting to 4.2V
WriteReg(0x61, 0x05); // set Main battery precharge current to 125mA
WriteReg(0x62, 0x0A); // set Main battery charger current to 400mA ( 0x08-200mA, 0x09-300mA, 0x0A-400mA )
WriteReg(0x63, 0x15); // set Main battery term charge current to 125mA
WriteReg(0x14, 0x00); // set minimum system voltage to 4.1V (default 4.7V), for poor USB cables
WriteReg(0x15, 0x00); // set input voltage limit to 3.88v, for poor USB cables
WriteReg(0x16, 0x05); // set input current limit to 2000mA
WriteReg(0x24, 0x01); // set Vsys for PWROFF threshold to 3.2V (default - 2.6V and kill battery)
WriteReg(0x50, 0x14); // set TS pin to EXTERNAL input (not temperature)
}
int Axp2101::GetBatteryCurrentDirection() {
return (ReadReg(0x01) & 0b01100000) >> 5;
}
bool Axp2101::IsCharging() {
return GetBatteryCurrentDirection() == 1;
}
bool Axp2101::IsDischarging() {
return GetBatteryCurrentDirection() == 2;
}
bool Axp2101::IsChargingDone() {
uint8_t value = ReadReg(0x01);
return (value & 0b00000111) == 0b00000100;
}
int Axp2101::GetBatteryLevel() {
return ReadReg(0xA4);
}
void Axp2101::PowerOff() {
uint8_t value = ReadReg(0x10);
value = value | 0x01;
WriteReg(0x10, value);
}

View File

@@ -0,0 +1,19 @@
#ifndef __AXP2101_H__
#define __AXP2101_H__
#include "i2c_device.h"
class Axp2101 : public I2cDevice {
public:
Axp2101(i2c_master_bus_handle_t i2c_bus, uint8_t addr);
bool IsCharging();
bool IsDischarging();
bool IsChargingDone();
int GetBatteryLevel();
void PowerOff();
private:
int GetBatteryCurrentDirection();
};
#endif

View File

@@ -0,0 +1,50 @@
#include <freertos/FreeRTOS.h>
#include <freertos/timers.h>
#include <freertos/task.h>
#include <esp_log.h>
#include "board.h"
#include "boards/common/wifi_board.h"
#include "boards/esp32-s3-touch-amoled-1.8/config.h"
#include "iot/thing.h"
#define TAG "BoardControl"
namespace iot {
class BoardControl : public Thing {
public:
BoardControl() : Thing("BoardControl", "当前 AI 机器人管理和控制") {
// 添加电池电量属性
properties_.AddNumberProperty("BatteryLevel", "当前电池电量百分比", [this]() -> int {
int level = 0;
bool charging = false;
Board::GetInstance().GetBatteryLevel(level, charging);
ESP_LOGI(TAG, "当前电池电量: %d%%, 充电状态: %s", level, charging ? "充电中" : "未充电");
return level;
});
// 添加充电状态属性
properties_.AddBooleanProperty("Charging", "是否正在充电", [this]() -> bool {
int level = 0;
bool charging = false;
Board::GetInstance().GetBatteryLevel(level, charging);
ESP_LOGI(TAG, "当前电池电量: %d%%, 充电状态: %s", level, charging ? "充电中" : "未充电");
return charging;
});
// 修改重新配网
methods_.AddMethod("ResetWifiConfiguration", "重新配网", ParameterList(),
[this](const ParameterList& parameters) {
ESP_LOGI(TAG, "ResetWifiConfiguration");
auto board = static_cast<WifiBoard*>(&Board::GetInstance());
if (board && board->GetBoardType() == "wifi") {
board->ResetWifiConfiguration();
}
});
}
};
} // namespace iot
DECLARE_THING(BoardControl);

View File

@@ -17,6 +17,7 @@
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_14
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define I2C_ADDRESS ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define EXAMPLE_PIN_NUM_LCD_CS GPIO_NUM_12

View File

@@ -0,0 +1,9 @@
{
"target": "esp32s3",
"builds": [
{
"name": "esp32-s3-touch-amoled-1.8",
"sdkconfig_append": []
}
]
}

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