forked from xiaozhi/xiaozhi-esp32
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
678147a9bd | ||
|
|
679130c0ae | ||
|
|
bfdee9b721 | ||
|
|
3655fc89d2 | ||
|
|
a868d7e5d1 | ||
|
|
b3e39b6a22 | ||
|
|
6bb9ee3441 | ||
|
|
02079a35ab | ||
|
|
3832337b78 | ||
|
|
ddf8738c00 | ||
|
|
c177ae176b | ||
|
|
7de0fc7d54 | ||
|
|
1d1bec1320 | ||
|
|
bf483d0768 | ||
|
|
e7ec0ed30e | ||
|
|
a8bceee3c7 | ||
|
|
b3dd2461d2 | ||
|
|
9a4deeb78f | ||
|
|
9adb9f66a2 | ||
|
|
7cca1d80c1 | ||
|
|
6be4cdc559 |
@@ -4,7 +4,7 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VER "0.9.8")
|
||||
set(PROJECT_VER "0.9.9")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(xiaozhi)
|
||||
|
||||
8
README.md
Executable file → Normal file
8
README.md
Executable file → Normal file
@@ -1,4 +1,6 @@
|
||||
# 小智 AI 聊天机器人
|
||||
# 小智 AI 聊天机器人 (XiaoZhi AI Chatbot)
|
||||
|
||||
(中文 | [English](README_en.md) | [日本語](README_ja.md))
|
||||
|
||||
这是虾哥的第一个硬件作品。
|
||||
|
||||
@@ -48,7 +50,7 @@
|
||||
- <a href="https://github.com/espressif/esp-box" target="_blank" title="乐鑫 ESP32-S3-BOX3">乐鑫 ESP32-S3-BOX3</a>
|
||||
- <a href="https://docs.m5stack.com/zh_CN/core/CoreS3" target="_blank" title="M5Stack CoreS3">M5Stack CoreS3</a>
|
||||
- 神奇按钮 2.4
|
||||
- 虾哥定制版 Mini C3
|
||||
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="虾哥 Mini C3">虾哥 Mini C3</a>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<a href="docs/lichuang-s3.jpg" target="_blank" title="立创·实战派 ESP32-S3 开发板">
|
||||
@@ -63,7 +65,7 @@
|
||||
<a href="docs/magiclick-2p4.jpg" target="_blank" title="神奇按钮 2.4">
|
||||
<img src="docs/magiclick-2p4.jpg" width="240" />
|
||||
</a>
|
||||
<a href="docs/xmini-c3.jpg" target="_blank" title="虾哥定制版 Mini C3">
|
||||
<a href="docs/xmini-c3.jpg" target="_blank" title="虾哥 Mini C3">
|
||||
<img src="docs/xmini-c3.jpg" width="240" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
101
README_en.md
Normal file
101
README_en.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# XiaoZhi AI Chatbot
|
||||
|
||||
([中文](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)
|
||||
|
||||
👉 [DIY Your AI Companion - Beginner's Tutorial [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 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.
|
||||
|
||||
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.
|
||||
|
||||
Learning & Discussion QQ Group: 946599635
|
||||
|
||||
## Implemented Features
|
||||
|
||||
- Wi-Fi / ML307 Cat.1 4G
|
||||
- BOOT button wake-up and interrupt, 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)
|
||||
- Configurable prompts and voice tones (custom characters)
|
||||
- Short-term memory with self-summary after each conversation round
|
||||
- OLED / LCD display showing signal strength or conversation content
|
||||
|
||||
## Hardware Section
|
||||
|
||||
### Breadboard Practice
|
||||
|
||||
For detailed tutorial, see the Feishu document:
|
||||
|
||||
👉 [XiaoZhi AI Chatbot Encyclopedia](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb?from=from_copylink)
|
||||
|
||||
Breadboard setup shown below:
|
||||
|
||||

|
||||
|
||||
### 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>
|
||||
- MagiClick 2.4
|
||||
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="Xmini C3">Xmini C3</a>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<a href="docs/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 Development Board">
|
||||
<img src="docs/lichuang-s3.jpg" />
|
||||
</a>
|
||||
<a href="docs/esp32s3-box3.jpg" target="_blank" title="Espressif ESP32-S3-BOX3">
|
||||
<img src="docs/esp32s3-box3.jpg" />
|
||||
</a>
|
||||
<a href="docs/m5stack-cores3.jpg" target="_blank" title="M5Stack CoreS3">
|
||||
<img src="docs/m5stack-cores3.jpg" />
|
||||
</a>
|
||||
<a href="docs/magiclick-2p4.jpg" target="_blank" title="MagiClick 2.4">
|
||||
<img src="docs/magiclick-2p4.jpg" />
|
||||
</a>
|
||||
<a href="docs/xmini-c3.jpg" target="_blank" title="Xmini C3">
|
||||
<img src="docs/xmini-c3.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Firmware Section
|
||||
|
||||
### 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).
|
||||
|
||||
👉 [Flash Firmware Guide (No IDF Environment Required)](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
|
||||
|
||||
## AI Character Configuration
|
||||
|
||||
If you already have a XiaoZhi AI chatbot, please refer to 👉 [Backend Operation Video Tutorial](https://www.bilibili.com/video/BV1jUCUY2EKM/)
|
||||
|
||||
For detailed usage instructions and test server notes, please refer to 👉 [XiaoZhi Test Server Help Guide](https://xiaozhi.me/help).
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://star-history.com/#78/xiaozhi-esp32&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
101
README_ja.md
Normal file
101
README_ja.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# XiaoZhi AI チャットボット
|
||||
|
||||
([中文](README.md) | [English](README_en.md) | 日本語)
|
||||
|
||||
これはテレンスの最初のハードウェアプロジェクトです。
|
||||
|
||||
👉 [ESP32+SenseVoice+Qwen72BでAIチャットコンパニオンを作ろう!【bilibili】](https://www.bilibili.com/video/BV11msTenEH3/?share_source=copy_web&vd_source=ee1aafe19d6e60cf22e60a93881faeba)
|
||||
|
||||
👉 [AIコンパニオンをDIYする - 初心者向けチュートリアル【bilibili】](https://www.bilibili.com/video/BV1XnmFYLEJN/)
|
||||
|
||||
## プロジェクトの目的
|
||||
|
||||
このプロジェクトはEspressifのESP-IDFに基づいて開発されています。
|
||||
|
||||
このプロジェクトは主に教育目的のためのオープンソースプロジェクトです。このプロジェクトを通じて、より多くの人々がAIハードウェア開発を始め、急速に進化する大規模言語モデルを実際のハードウェアデバイスに統合する方法を理解する手助けをすることを目指しています。AIに興味のある学生や新しい技術を探求したい開発者にとって、このプロジェクトは貴重な学習体験を提供します。
|
||||
|
||||
プロジェクトの開発と改善に参加することを歓迎します。アイデアや提案があれば、Issueを提起するか、チャットグループに参加してください。
|
||||
|
||||
学習・ディスカッションQQグループ: 946599635
|
||||
|
||||
## 実装された機能
|
||||
|
||||
- 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)
|
||||
- 大規模モデルTTS(VolcengineまたはCosyVoice)
|
||||
- 大規模言語モデル(Qwen2.5 72BまたはDoubao API)
|
||||
- カスタマイズ可能なプロンプトと音声トーン(カスタムキャラクター)
|
||||
- 短期記憶、各対話ラウンド後の自己要約
|
||||
- 信号強度や対話内容を表示するOLED / LCDディスプレイ
|
||||
|
||||
## ハードウェアセクション
|
||||
|
||||
### ブレッドボードの練習
|
||||
|
||||
詳細なチュートリアルについては、Feishuドキュメントを参照してください:
|
||||
|
||||
👉 [XiaoZhi AI チャットボット百<E38388><E799BE>事典](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb?from=from_copylink)
|
||||
|
||||
以下にブレッドボードのセットアップを示します:
|
||||
|
||||

|
||||
|
||||
### サポートされているオープンソースハードウェア
|
||||
|
||||
- <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 開発ボード">LiChuang ESP32-S3 開発ボード</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>
|
||||
- MagiClick 2.4
|
||||
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="Xmini C3">Xmini C3</a>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
<a href="docs/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 開発ボード">
|
||||
<img src="docs/lichuang-s3.jpg" />
|
||||
</a>
|
||||
<a href="docs/esp32s3-box3.jpg" target="_blank" title="Espressif ESP32-S3-BOX3">
|
||||
<img src="docs/esp32s3-box3.jpg" />
|
||||
</a>
|
||||
<a href="docs/m5stack-cores3.jpg" target="_blank" title="M5Stack CoreS3">
|
||||
<img src="docs/m5stack-cores3.jpg" />
|
||||
</a>
|
||||
<a href="docs/magiclick-2p4.jpg" target="_blank" title="MagiClick 2.4">
|
||||
<img src="docs/magiclick-2p4.jpg" />
|
||||
</a>
|
||||
<a href="docs/xmini-c3.jpg" target="_blank" title="Xmini C3">
|
||||
<img src="docs/xmini-c3.jpg" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## ファームウェアセクション
|
||||
|
||||
### 開発環境なしでのフラッシュ
|
||||
|
||||
初心者には、最初に開発環境を設定せずにファームウェアをフラッシュすることをお勧めします。ファームウェアは著者が提供するテストサーバーを使用しており、現在無料で使用できます(商業目的では使用しないでください)。
|
||||
|
||||
👉 [開発環境なしでのフラッシュガイド](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)
|
||||
|
||||
### 開発環境
|
||||
|
||||
- CursorまたはVSCode
|
||||
- ESP-IDFプラグインをインストールし、SDKバージョン5.3以上を選択
|
||||
- LinuxはWindowsよりも優れており、コンパイルが速く、ドライバの問題も少ない
|
||||
|
||||
## AIキャラクターの設定
|
||||
|
||||
すでにXiaoZhi AIチャットボットをお持ちの場合は、👉 [バックエンド操作ビデオチュートリアル](https://www.bilibili.com/video/BV1jUCUY2EKM/)を参照してください。
|
||||
|
||||
詳細な使用方法とテストサーバーの注意事項については、👉 [XiaoZhiテストサーバーヘルプガイド](https://xiaozhi.me/help)を参照してください。
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://star-history.com/#78/xiaozhi-esp32&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
2
flash.sh
2
flash.sh
@@ -1,2 +1,2 @@
|
||||
#!/bin/sh
|
||||
esptool.py -p /dev/ttyACM0 -b 2000000 write_flash 0 releases/v0.6.2_ML307/merged-binary.bin
|
||||
esptool.py -p /dev/ttyACM0 -b 2000000 write_flash 0 releases/v0.9.9_bread-compact-wifi/merged-binary.bin
|
||||
|
||||
@@ -53,12 +53,16 @@ elseif(CONFIG_BOARD_TYPE_KEVIN_C3)
|
||||
set(BOARD_TYPE "kevin-c3")
|
||||
elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV)
|
||||
set(BOARD_TYPE "lichuang-dev")
|
||||
elseif(CONFIG_BOARD_TYPE_TERRENCE_C3_DEV)
|
||||
set(BOARD_TYPE "terrence-c3-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_M5STACK_CORE_S3)
|
||||
set(BOARD_TYPE "m5stack-core-s3")
|
||||
elseif(CONFIG_BOARD_TYPE_XMINI_C3)
|
||||
set(BOARD_TYPE "xmini-c3")
|
||||
elseif(CONFIG_BOARD_TYPE_ESP32S3_KORVO2_V3)
|
||||
set(BOARD_TYPE "esp32s3-korvo2-v3")
|
||||
endif()
|
||||
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
|
||||
list(APPEND SOURCES ${BOARD_SOURCES})
|
||||
|
||||
@@ -49,11 +49,17 @@ choice BOARD_TYPE
|
||||
config BOARD_TYPE_KEVIN_C3
|
||||
bool "Kevin C3"
|
||||
config BOARD_TYPE_LICHUANG_DEV
|
||||
bool "立创开发板"
|
||||
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_M5STACK_CORE_S3
|
||||
bool "M5Stack CoreS3"
|
||||
config BOARD_TYPE_XMINI_C3
|
||||
bool "虾哥 Mini C3"
|
||||
config BOARD_TYPE_ESP32S3_KORVO2_V3
|
||||
bool "ESP32S3_KORVO2_V3开发板"
|
||||
endchoice
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -324,6 +324,8 @@ void Application::Start() {
|
||||
protocol_->OnAudioChannelClosed([this, &board]() {
|
||||
board.SetPowerSaveMode(true);
|
||||
Schedule([this]() {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->SetChatMessage("", "");
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
});
|
||||
});
|
||||
@@ -548,7 +550,6 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
case kDeviceStateIdle:
|
||||
display->SetStatus("待命");
|
||||
display->SetEmotion("neutral");
|
||||
display->SetChatMessage("", "");
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
audio_processor_.Stop();
|
||||
#endif
|
||||
|
||||
@@ -96,8 +96,8 @@ void BoxAudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gpio_
|
||||
i2s_chan_config_t chan_cfg = {
|
||||
.id = I2S_NUM_0,
|
||||
.role = I2S_ROLE_MASTER,
|
||||
.dma_desc_num = 2,
|
||||
.dma_frame_num = 240 * 3,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 240,
|
||||
.auto_clear_after_cb = true,
|
||||
.auto_clear_before_cb = false,
|
||||
.intr_priority = 0,
|
||||
|
||||
@@ -99,8 +99,8 @@ void CoreS3AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gp
|
||||
i2s_chan_config_t chan_cfg = {
|
||||
.id = I2S_NUM_0,
|
||||
.role = I2S_ROLE_MASTER,
|
||||
.dma_desc_num = 2,
|
||||
.dma_frame_num = 240 * 3,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 240,
|
||||
.auto_clear_after_cb = true,
|
||||
.auto_clear_before_cb = false,
|
||||
.intr_priority = 0,
|
||||
|
||||
@@ -79,8 +79,8 @@ void Es8311AudioCodec::CreateDuplexChannels(gpio_num_t mclk, gpio_num_t bclk, gp
|
||||
i2s_chan_config_t chan_cfg = {
|
||||
.id = I2S_NUM_0,
|
||||
.role = I2S_ROLE_MASTER,
|
||||
.dma_desc_num = 2,
|
||||
.dma_frame_num = 240 * 3,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 240,
|
||||
.auto_clear_after_cb = true,
|
||||
.auto_clear_before_cb = false,
|
||||
.intr_priority = 0,
|
||||
|
||||
@@ -15,7 +15,7 @@ NoAudioCodec::~NoAudioCodec() {
|
||||
}
|
||||
|
||||
|
||||
NoAudioCodec::NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din) {
|
||||
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;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
@@ -23,8 +23,8 @@ NoAudioCodec::NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_n
|
||||
i2s_chan_config_t chan_cfg = {
|
||||
.id = I2S_NUM_0,
|
||||
.role = I2S_ROLE_MASTER,
|
||||
.dma_desc_num = 2,
|
||||
.dma_frame_num = 240 * 3,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 240,
|
||||
.auto_clear_after_cb = false,
|
||||
.auto_clear_before_cb = false,
|
||||
.intr_priority = 0,
|
||||
@@ -68,7 +68,7 @@ NoAudioCodec::NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_n
|
||||
ESP_LOGI(TAG, "Duplex channels created");
|
||||
}
|
||||
|
||||
NoAudioCodec::NoAudioCodec(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::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) {
|
||||
duplex_ = false;
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
@@ -77,8 +77,8 @@ NoAudioCodec::NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_n
|
||||
i2s_chan_config_t chan_cfg = {
|
||||
.id = (i2s_port_t)0,
|
||||
.role = I2S_ROLE_MASTER,
|
||||
.dma_desc_num = 2,
|
||||
.dma_frame_num = 240 * 3,
|
||||
.dma_desc_num = 6,
|
||||
.dma_frame_num = 240,
|
||||
.auto_clear_after_cb = true,
|
||||
.auto_clear_before_cb = false,
|
||||
.intr_priority = 0,
|
||||
@@ -131,15 +131,15 @@ NoAudioCodec::NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_n
|
||||
ESP_LOGI(TAG, "Simplex channels created");
|
||||
}
|
||||
|
||||
NoAudioCodec::NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_din) {
|
||||
NoAudioCodecSimplexPdm::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;
|
||||
output_sample_rate_ = output_sample_rate;
|
||||
|
||||
// Create a new channel for speaker
|
||||
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG((i2s_port_t)1, I2S_ROLE_MASTER);
|
||||
tx_chan_cfg.dma_desc_num = 2;
|
||||
tx_chan_cfg.dma_frame_num = 240 * 3;
|
||||
tx_chan_cfg.dma_desc_num = 6;
|
||||
tx_chan_cfg.dma_frame_num = 240;
|
||||
tx_chan_cfg.auto_clear_after_cb = true;
|
||||
tx_chan_cfg.auto_clear_before_cb = false;
|
||||
tx_chan_cfg.intr_priority = 0;
|
||||
|
||||
@@ -5,19 +5,29 @@
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <driver/i2s_pdm.h>
|
||||
|
||||
class NoAudioCodec : public AudioCodec {
|
||||
private:
|
||||
virtual int Write(const int16_t* data, int samples) override;
|
||||
virtual int Read(int16_t* dest, int samples) override;
|
||||
|
||||
public:
|
||||
// Duplex
|
||||
NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din);
|
||||
// Simplex
|
||||
NoAudioCodec(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);
|
||||
// Simplex_PDM
|
||||
NoAudioCodec(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);
|
||||
virtual ~NoAudioCodec();
|
||||
};
|
||||
|
||||
class NoAudioCodecDuplex : public NoAudioCodec {
|
||||
public:
|
||||
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);
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
class NoAudioCodecSimplexPdm : public NoAudioCodec {
|
||||
public:
|
||||
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);
|
||||
};
|
||||
|
||||
#endif // _NO_AUDIO_CODEC_H
|
||||
|
||||
@@ -66,11 +66,11 @@ AudioProcessor::~AudioProcessor() {
|
||||
void AudioProcessor::Input(const std::vector<int16_t>& data) {
|
||||
input_buffer_.insert(input_buffer_.end(), data.begin(), data.end());
|
||||
|
||||
auto chunk_size = esp_afe_vc_v1.get_feed_chunksize(afe_communication_data_) * channels_;
|
||||
while (input_buffer_.size() >= chunk_size) {
|
||||
auto feed_size = esp_afe_vc_v1.get_feed_chunksize(afe_communication_data_) * channels_;
|
||||
while (input_buffer_.size() >= feed_size) {
|
||||
auto chunk = input_buffer_.data();
|
||||
esp_afe_vc_v1.feed(afe_communication_data_, chunk);
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + chunk_size);
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + feed_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,10 @@ void AudioProcessor::OnOutput(std::function<void(std::vector<int16_t>&& data)> c
|
||||
}
|
||||
|
||||
void AudioProcessor::AudioProcessorTask() {
|
||||
int chunk_size = esp_afe_vc_v1.get_fetch_chunksize(afe_communication_data_);
|
||||
ESP_LOGI(TAG, "Audio communication task started, chunk size: %d", chunk_size);
|
||||
auto fetch_size = esp_afe_sr_v1.get_fetch_chunksize(afe_communication_data_);
|
||||
auto feed_size = esp_afe_sr_v1.get_feed_chunksize(afe_communication_data_);
|
||||
ESP_LOGI(TAG, "Audio communication task started, feed size: %d fetch size: %d",
|
||||
feed_size, fetch_size);
|
||||
|
||||
while (true) {
|
||||
xEventGroupWaitBits(event_group_, PROCESSOR_RUNNING, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
|
||||
@@ -114,16 +114,18 @@ bool WakeWordDetect::IsDetectionRunning() {
|
||||
void WakeWordDetect::Feed(const std::vector<int16_t>& data) {
|
||||
input_buffer_.insert(input_buffer_.end(), data.begin(), data.end());
|
||||
|
||||
auto chunk_size = esp_afe_sr_v1.get_feed_chunksize(afe_detection_data_) * channels_;
|
||||
while (input_buffer_.size() >= chunk_size) {
|
||||
auto feed_size = esp_afe_sr_v1.get_feed_chunksize(afe_detection_data_) * channels_;
|
||||
while (input_buffer_.size() >= feed_size) {
|
||||
esp_afe_sr_v1.feed(afe_detection_data_, input_buffer_.data());
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + chunk_size);
|
||||
input_buffer_.erase(input_buffer_.begin(), input_buffer_.begin() + feed_size);
|
||||
}
|
||||
}
|
||||
|
||||
void WakeWordDetect::AudioDetectionTask() {
|
||||
auto chunk_size = esp_afe_sr_v1.get_fetch_chunksize(afe_detection_data_);
|
||||
ESP_LOGI(TAG, "Audio detection task started, chunk size: %d", chunk_size);
|
||||
auto fetch_size = esp_afe_sr_v1.get_fetch_chunksize(afe_detection_data_);
|
||||
auto feed_size = esp_afe_sr_v1.get_feed_chunksize(afe_detection_data_);
|
||||
ESP_LOGI(TAG, "Audio detection task started, feed size: %d fetch size: %d",
|
||||
feed_size, fetch_size);
|
||||
|
||||
while (true) {
|
||||
xEventGroupWaitBits(event_group_, DETECTION_RUNNING_EVENT, pdFALSE, pdTRUE, portMAX_DELAY);
|
||||
|
||||
@@ -109,10 +109,10 @@ public:
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
#ifdef AUDIO_I2S_METHOD_SIMPLEX
|
||||
static NoAudioCodec audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
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 NoAudioCodec audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
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;
|
||||
|
||||
@@ -114,10 +114,10 @@ public:
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
#ifdef AUDIO_I2S_METHOD_SIMPLEX
|
||||
static NoAudioCodec audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
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 NoAudioCodec audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
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;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <wifi_station.h>
|
||||
#include <wifi_configuration_ap.h>
|
||||
#include <ssid_manager.h>
|
||||
|
||||
static const char *TAG = "WifiBoard";
|
||||
|
||||
@@ -35,39 +36,81 @@ static std::string rssi_to_string(int rssi) {
|
||||
}
|
||||
}
|
||||
|
||||
void WifiBoard::StartNetwork() {
|
||||
WifiBoard::WifiBoard() {
|
||||
Settings settings("wifi", true);
|
||||
wifi_config_mode_ = settings.GetInt("force_ap") == 1;
|
||||
if (wifi_config_mode_) {
|
||||
ESP_LOGI(TAG, "force_ap is set to 1, reset to 0");
|
||||
settings.SetInt("force_ap", 0);
|
||||
}
|
||||
}
|
||||
|
||||
void WifiBoard::EnterWifiConfigMode() {
|
||||
auto& application = Application::GetInstance();
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
application.SetDeviceState(kDeviceStateWifiConfiguring);
|
||||
|
||||
auto& wifi_ap = WifiConfigurationAp::GetInstance();
|
||||
wifi_ap.SetSsidPrefix("Xiaozhi");
|
||||
wifi_ap.Start();
|
||||
|
||||
// 播报配置 WiFi 的提示
|
||||
application.Alert("Info", "Configuring WiFi");
|
||||
|
||||
// 显示 WiFi 配置 AP 的 SSID 和 Web 服务器 URL
|
||||
std::string hint = "请在手机上连接热点 ";
|
||||
hint += wifi_ap.GetSsid();
|
||||
hint += ",然后打开浏览器访问 ";
|
||||
hint += wifi_ap.GetWebServerUrl();
|
||||
|
||||
display->SetStatus(hint);
|
||||
|
||||
// Wait forever until reset after configuration
|
||||
while (true) {
|
||||
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);
|
||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||||
}
|
||||
}
|
||||
|
||||
void WifiBoard::StartNetwork() {
|
||||
// User can press BOOT button while starting to enter WiFi configuration mode
|
||||
if (wifi_config_mode_) {
|
||||
EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
|
||||
// If no WiFi SSID is configured, enter WiFi configuration mode
|
||||
auto& ssid_manager = SsidManager::GetInstance();
|
||||
auto ssid_list = ssid_manager.GetSsidList();
|
||||
if (ssid_list.empty()) {
|
||||
wifi_config_mode_ = true;
|
||||
EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
|
||||
auto& wifi_station = WifiStation::GetInstance();
|
||||
wifi_station.OnScanBegin([this]() {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->ShowNotification("正在扫描 WiFi 网络", 30000);
|
||||
});
|
||||
wifi_station.OnConnect([this](const std::string& ssid) {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->ShowNotification(std::string("正在连接 ") + ssid, 30000);
|
||||
});
|
||||
wifi_station.OnConnected([this](const std::string& ssid) {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->ShowNotification(std::string("已连接 ") + ssid);
|
||||
});
|
||||
wifi_station.Start();
|
||||
|
||||
// Try to connect to WiFi, if failed, launch the WiFi configuration AP
|
||||
auto& wifi_station = WifiStation::GetInstance();
|
||||
display->SetStatus(std::string("正在连接 ") + wifi_station.GetSsid());
|
||||
wifi_station.Start();
|
||||
if (!wifi_station.IsConnected()) {
|
||||
application.SetDeviceState(kDeviceStateWifiConfiguring);
|
||||
|
||||
auto& wifi_ap = WifiConfigurationAp::GetInstance();
|
||||
wifi_ap.SetSsidPrefix("Xiaozhi");
|
||||
wifi_ap.Start();
|
||||
|
||||
// 播报配置 WiFi 的提示
|
||||
application.Alert("Info", "Configuring WiFi");
|
||||
|
||||
// 显示 WiFi 配置 AP 的 SSID 和 Web 服务器 URL
|
||||
std::string hint = "请在手机上连接热点 ";
|
||||
hint += wifi_ap.GetSsid();
|
||||
hint += ",然后打开浏览器访问 ";
|
||||
hint += wifi_ap.GetWebServerUrl();
|
||||
|
||||
display->SetStatus(hint);
|
||||
|
||||
// Wait forever until reset after configuration
|
||||
while (true) {
|
||||
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);
|
||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||||
}
|
||||
if (!wifi_station.WaitForConnected(60 * 1000)) {
|
||||
wifi_station.Stop();
|
||||
wifi_config_mode_ = true;
|
||||
EnterWifiConfigMode();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +198,9 @@ void WifiBoard::ResetWifiConfiguration() {
|
||||
// Reset the wifi station
|
||||
{
|
||||
Settings settings("wifi", true);
|
||||
settings.EraseAll();
|
||||
settings.SetInt("force_ap", 1);
|
||||
}
|
||||
GetDisplay()->ShowNotification("已重置 WiFi...");
|
||||
GetDisplay()->ShowNotification("进入配网模式...");
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
// Reboot the device
|
||||
esp_restart();
|
||||
|
||||
@@ -7,6 +7,8 @@ class WifiBoard : public Board {
|
||||
protected:
|
||||
bool wifi_config_mode_ = false;
|
||||
|
||||
WifiBoard();
|
||||
void EnterWifiConfigMode();
|
||||
virtual std::string GetBoardJson() override;
|
||||
|
||||
public:
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_DEFAULT_OUTPUT_VOLUME 80
|
||||
|
||||
#define AUDIO_INPUT_REFERENCE true
|
||||
|
||||
@@ -12,14 +12,10 @@
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <wifi_station.h>
|
||||
|
||||
#define TAG "EspBox3Board"
|
||||
|
||||
// Can move to display/st7789_display.h
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
LV_FONT_DECLARE(font_awesome_30_1);
|
||||
LV_FONT_DECLARE(font_awesome_14_1);
|
||||
|
||||
// Init ili9341 by custom cmd
|
||||
static const ili9341_lcd_init_cmd_t vendor_specific_init[] = {
|
||||
{0xC8, (uint8_t []){0xFF, 0x93, 0x42}, 3, 0},
|
||||
@@ -41,70 +37,11 @@ static const ili9341_lcd_init_cmd_t vendor_specific_init[] = {
|
||||
{0, (uint8_t []){0}, 0xff, 0},
|
||||
};
|
||||
|
||||
// Example Display and UI overwrite in different board
|
||||
class Ili9341Display : public LcdDisplay {
|
||||
private:
|
||||
lv_obj_t *user_messge_label_ = nullptr;
|
||||
lv_obj_t *ai_messge_label_ = nullptr;
|
||||
public:
|
||||
Ili9341Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel,
|
||||
gpio_num_t backlight_pin, bool backlight_output_invert,
|
||||
int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy)
|
||||
: LcdDisplay(panel_io, panel, backlight_pin, backlight_output_invert, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {}
|
||||
|
||||
void SetChatMessage(const std::string &role, const std::string &content) override {
|
||||
if (ai_messge_label_== nullptr || user_messge_label_== nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayLockGuard lock(this);
|
||||
ESP_LOGI(TAG, "role:%s", role.c_str());
|
||||
if(role == "assistant") {
|
||||
std::string new_content = "AI: " + content;
|
||||
lv_label_set_text(ai_messge_label_, new_content.c_str());
|
||||
} else if(role == "user") {
|
||||
std::string new_content = "User: " + content;
|
||||
lv_label_set_text(user_messge_label_, new_content.c_str());
|
||||
} else{
|
||||
lv_label_set_text(ai_messge_label_, "AI: ");
|
||||
lv_label_set_text(user_messge_label_, "User: ");
|
||||
}
|
||||
}
|
||||
|
||||
void SetupUI() override {
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
lv_obj_del(chat_message_label_);
|
||||
|
||||
lv_obj_set_flex_align(content_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_EVENLY); // 子对象居中对齐,等距分布
|
||||
|
||||
lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_1, 0);
|
||||
lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP);
|
||||
lv_obj_align(emotion_label_, LV_ALIGN_TOP_MID, 0, -10); // 左侧居中,向右偏移10个单位
|
||||
|
||||
static lv_style_t style_msg;
|
||||
lv_style_init(&style_msg);
|
||||
lv_style_set_width(&style_msg, LV_HOR_RES - 25);
|
||||
|
||||
user_messge_label_ = lv_label_create(content_);
|
||||
lv_obj_set_style_text_font(user_messge_label_, &font_puhui_14_1, 0);
|
||||
lv_label_set_text(user_messge_label_, "User: ");
|
||||
lv_obj_add_style(user_messge_label_, &style_msg, 0);
|
||||
lv_obj_align(user_messge_label_, LV_ALIGN_TOP_LEFT, 2, 25);
|
||||
|
||||
ai_messge_label_ = lv_label_create(content_);
|
||||
lv_obj_set_style_text_font(ai_messge_label_, &font_puhui_14_1, 0);
|
||||
lv_label_set_text(ai_messge_label_, "AI: ");
|
||||
lv_obj_add_style(ai_messge_label_, &style_msg, 0);
|
||||
lv_obj_align(ai_messge_label_, LV_ALIGN_TOP_LEFT, 2, 77);
|
||||
}
|
||||
};
|
||||
|
||||
class EspBox3Board : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
Button boot_button_;
|
||||
Ili9341Display* display_;
|
||||
LcdDisplay* display_;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
@@ -136,7 +73,11 @@ private:
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
Application::GetInstance().ToggleChatState();
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -173,13 +114,12 @@ private:
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, true);
|
||||
esp_lcd_panel_invert_color(panel, false);
|
||||
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 Ili9341Display(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT,
|
||||
display_ = new LcdDisplay(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);
|
||||
display_->SetupUI();
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
|
||||
45
main/boards/esp32s3-korvo2-v3/config.h
Normal file
45
main/boards/esp32s3-korvo2-v3/config.h
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_DEFAULT_OUTPUT_VOLUME 90
|
||||
|
||||
#define AUDIO_INPUT_REFERENCE true
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_16
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_45
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_9
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_10
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_8
|
||||
|
||||
#define AUDIO_CODEC_PA_PIN GPIO_NUM_48
|
||||
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_17
|
||||
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_18
|
||||
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
|
||||
#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_NC
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_5
|
||||
#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 280
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_SWAP_XY true
|
||||
#define DISPLAY_MIRROR_X false
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
#define BACKLIGHT_INVERT false
|
||||
|
||||
#define DISPLAY_OFFSET_X 20
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_NC
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
136
main/boards/esp32s3-korvo2-v3/esp32s3_korvo2_v3_board.cc
Normal file
136
main/boards/esp32s3-korvo2-v3/esp32s3_korvo2_v3_board.cc
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/box_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 <esp_log.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <wifi_station.h>
|
||||
|
||||
#define TAG "esp32s3_korvo2_v3"
|
||||
|
||||
class esp32s3_korvo2_v3_board : public WifiBoard
|
||||
{
|
||||
private:
|
||||
Button boot_button_;
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
LcdDisplay* display_;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = (i2c_port_t)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 InitializeSpi() {
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = GPIO_NUM_0;
|
||||
buscfg.miso_io_num = GPIO_NUM_NC;
|
||||
buscfg.sclk_io_num = GPIO_NUM_1;
|
||||
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 InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeSt7789Display() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = GPIO_NUM_46;
|
||||
io_config.dc_gpio_num = GPIO_NUM_2;
|
||||
io_config.spi_mode = 0;
|
||||
io_config.pclk_hz = 60 * 1000 * 1000;
|
||||
io_config.trans_queue_depth = 10;
|
||||
io_config.lcd_cmd_bits = 8;
|
||||
io_config.lcd_param_bits = 8;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io));
|
||||
|
||||
// 初始化液晶屏驱动芯片ST7789
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = GPIO_NUM_NC;
|
||||
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y));
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, true));
|
||||
|
||||
display_ = new LcdDisplay(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);
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
esp32s3_korvo2_v3_board() : boot_button_(BOOT_BUTTON_GPIO)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing esp32s3_korvo2_v3 Board");
|
||||
InitializeI2c();
|
||||
InitializeSpi();
|
||||
InitializeButtons();
|
||||
InitializeSt7789Display();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static BoxAudioCodec* audio_codec = nullptr;
|
||||
if (audio_codec == nullptr) {
|
||||
audio_codec = new BoxAudioCodec(i2c_bus_, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN,
|
||||
AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR, AUDIO_CODEC_ES7210_ADDR, AUDIO_INPUT_REFERENCE);
|
||||
audio_codec->SetOutputVolume(AUDIO_DEFAULT_OUTPUT_VOLUME);
|
||||
}
|
||||
return audio_codec;
|
||||
}
|
||||
|
||||
virtual Display *GetDisplay() override
|
||||
{
|
||||
return display_;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(esp32s3_korvo2_v3_board);
|
||||
@@ -38,7 +38,7 @@ private:
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateUnknown && !WifiStation::GetInstance().IsConnected()) {
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
11
main/boards/lichuang-c3-dev/README.md
Normal file
11
main/boards/lichuang-c3-dev/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## 立创·实战派ESP32-C3开发板
|
||||
|
||||
1、开发板资料:https://wiki.lckfb.com/zh-hans/szpi-esp32c3
|
||||
|
||||
2、该开发板 flash 大小为 8MB,编译时注意选择合适的分区表:
|
||||
|
||||
```
|
||||
Partition Table --->
|
||||
Partition Table (Custom partition table CSV) --->
|
||||
(partitions_4M.csv) Custom partition CSV file
|
||||
```
|
||||
47
main/boards/lichuang-c3-dev/config.h
Normal file
47
main/boards/lichuang-c3-dev/config.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_DEFAULT_OUTPUT_VOLUME 80
|
||||
#define AUDIO_INPUT_REFERENCE true
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_10
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_12
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_8
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_7
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_11
|
||||
|
||||
#define AUDIO_CODEC_USE_PCA9557
|
||||
#define AUDIO_CODEC_PA_PIN GPIO_NUM_13
|
||||
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_0
|
||||
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_1
|
||||
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
|
||||
#define AUDIO_CODEC_ES7210_ADDR 0x82
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_NC
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_9
|
||||
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
|
||||
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
|
||||
|
||||
#define DISPLAY_SPI_SCK_PIN GPIO_NUM_3
|
||||
#define DISPLAY_SPI_MOSI_PIN GPIO_NUM_5
|
||||
#define DISPLAY_DC_PIN GPIO_NUM_6
|
||||
#define DISPLAY_SPI_CS_PIN GPIO_NUM_4
|
||||
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 240
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y false
|
||||
#define DISPLAY_SWAP_XY true
|
||||
|
||||
#define DISPLAY_OFFSET_X 0
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_2
|
||||
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true
|
||||
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
131
main/boards/lichuang-c3-dev/lichuang_c3_dev_board.cc
Normal file
131
main/boards/lichuang-c3-dev/lichuang_c3_dev_board.cc
Normal file
@@ -0,0 +1,131 @@
|
||||
#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 <esp_log.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/spi_common.h>
|
||||
#include <wifi_station.h>
|
||||
|
||||
#define TAG "LichuangC3DevBoard"
|
||||
|
||||
class LichuangC3DevBoard : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||
Button boot_button_;
|
||||
LcdDisplay* display_;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = I2C_NUM_0,
|
||||
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
|
||||
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_));
|
||||
}
|
||||
|
||||
void InitializeSpi() {
|
||||
spi_bus_config_t buscfg = {};
|
||||
buscfg.mosi_io_num = DISPLAY_SPI_MOSI_PIN;
|
||||
buscfg.miso_io_num = GPIO_NUM_NC;
|
||||
buscfg.sclk_io_num = DISPLAY_SPI_SCK_PIN;
|
||||
buscfg.quadwp_io_num = GPIO_NUM_NC;
|
||||
buscfg.quadhd_io_num = GPIO_NUM_NC;
|
||||
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
void InitializeSt7789Display() {
|
||||
esp_lcd_panel_io_handle_t panel_io = nullptr;
|
||||
esp_lcd_panel_handle_t panel = nullptr;
|
||||
// 液晶屏控制IO初始化
|
||||
ESP_LOGD(TAG, "Install panel IO");
|
||||
esp_lcd_panel_io_spi_config_t io_config = {};
|
||||
io_config.cs_gpio_num = DISPLAY_SPI_CS_PIN;
|
||||
io_config.dc_gpio_num = DISPLAY_DC_PIN;
|
||||
io_config.spi_mode = 2;
|
||||
io_config.pclk_hz = 80 * 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(SPI2_HOST, &io_config, &panel_io));
|
||||
|
||||
// 初始化液晶屏驱动芯片ST7789
|
||||
ESP_LOGD(TAG, "Install LCD driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = GPIO_NUM_NC;
|
||||
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
|
||||
panel_config.bits_per_pixel = 16;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
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_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
}
|
||||
|
||||
public:
|
||||
LichuangC3DevBoard() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||
InitializeI2c();
|
||||
InitializeSpi();
|
||||
InitializeSt7789Display();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static Es8311AudioCodec* audio_codec = nullptr;
|
||||
if (audio_codec == nullptr) {
|
||||
audio_codec = new Es8311AudioCodec(codec_i2c_bus_, I2C_NUM_0, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN,
|
||||
AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR);
|
||||
audio_codec->SetOutputVolume(AUDIO_DEFAULT_OUTPUT_VOLUME);
|
||||
}
|
||||
return audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(LichuangC3DevBoard);
|
||||
@@ -73,7 +73,7 @@ private:
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateUnknown && !WifiStation::GetInstance().IsConnected()) {
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#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_ili9341.h"
|
||||
@@ -244,7 +245,11 @@ private:
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
Application::GetInstance().ToggleChatState();
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
app.ToggleChatState();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateUnknown && !WifiStation::GetInstance().IsConnected()) {
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
|
||||
28
main/boards/xmini-c3/config.h
Normal file
28
main/boards/xmini-c3/config.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 24000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_10
|
||||
#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_PA_PIN GPIO_NUM_11
|
||||
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_3
|
||||
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_4
|
||||
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_2
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_9
|
||||
|
||||
#define DISPLAY_WIDTH 128
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
88
main/boards/xmini-c3/xmini_c3_board.cc
Normal file
88
main/boards/xmini-c3/xmini_c3_board.cc
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "wifi_board.h"
|
||||
#include "audio_codecs/es8311_audio_codec.h"
|
||||
#include "display/ssd1306_display.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "led/single_led.h"
|
||||
|
||||
#include <wifi_station.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_efuse_table.h>
|
||||
#include <driver/i2c_master.h>
|
||||
|
||||
#define TAG "XminiC3Board"
|
||||
|
||||
class XminiC3Board : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||
Button boot_button_;
|
||||
|
||||
void InitializeCodecI2c() {
|
||||
// Initialize I2C peripheral
|
||||
i2c_master_bus_config_t i2c_bus_cfg = {
|
||||
.i2c_port = I2C_NUM_0,
|
||||
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
|
||||
.scl_io_num = AUDIO_CODEC_I2C_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_));
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
}
|
||||
});
|
||||
boot_button_.OnPressDown([this]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
boot_button_.OnPressUp([this]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeIot() {
|
||||
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||
}
|
||||
|
||||
public:
|
||||
XminiC3Board() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||
// 把 ESP32C3 的 VDD SPI 引脚作为普通 GPIO 口使用
|
||||
esp_efuse_write_field_bit(ESP_EFUSE_VDD_SPI_AS_GPIO);
|
||||
|
||||
InitializeCodecI2c();
|
||||
InitializeButtons();
|
||||
InitializeIot();
|
||||
}
|
||||
|
||||
virtual Led* GetLed() override {
|
||||
static SingleLed led_strip(BUILTIN_LED_GPIO);
|
||||
return &led_strip;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
static Ssd1306Display display(codec_i2c_bus_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
return &display;
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
static Es8311AudioCodec audio_codec(codec_i2c_bus_, I2C_NUM_0, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN,
|
||||
AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR);
|
||||
return &audio_codec;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(XminiC3Board);
|
||||
@@ -63,6 +63,8 @@ void Display::SetStatus(const std::string &status) {
|
||||
}
|
||||
DisplayLockGuard lock(this);
|
||||
lv_label_set_text(status_label_, status.c_str());
|
||||
lv_obj_clear_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
void Display::ShowNotification(const std::string ¬ification, int duration_ms) {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
dependencies:
|
||||
espressif/esp_lcd_ili9341: "==1.2.0"
|
||||
78/esp_lcd_nv3023: "~1.0.0"
|
||||
78/esp-wifi-connect: "~1.4.1"
|
||||
78/esp-wifi-connect: "~2.0.1"
|
||||
78/esp-opus-encoder: "~2.0.0"
|
||||
78/esp-ml307: "~1.7.0"
|
||||
78/esp-ml307: "~1.7.1"
|
||||
espressif/led_strip: "^2.4.1"
|
||||
espressif/esp_codec_dev: "~1.3.2"
|
||||
espressif/esp-sr: "^1.9.0"
|
||||
|
||||
@@ -31,6 +31,9 @@ std::string Settings::GetString(const std::string& key, const std::string& defau
|
||||
std::string value;
|
||||
value.resize(length);
|
||||
ESP_ERROR_CHECK(nvs_get_str(nvs_handle_, key.c_str(), value.data(), &length));
|
||||
while (value.back() == '\0') {
|
||||
value.pop_back();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -66,7 +69,10 @@ void Settings::SetInt(const std::string& key, int32_t value) {
|
||||
|
||||
void Settings::EraseKey(const std::string& key) {
|
||||
if (read_write_) {
|
||||
ESP_ERROR_CHECK(nvs_erase_key(nvs_handle_, key.c_str()));
|
||||
auto ret = nvs_erase_key(nvs_handle_, key.c_str());
|
||||
if (ret != ESP_ERR_NVS_NOT_FOUND) {
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Namespace %s is not open for writing", ns_.c_str());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user