小智AI是一个基于ESP32平台开发的智能语音聊天机器人项目,支持多种开发板和通信协议。体验特别有趣,花几十元给娃买了个,玩的爱不释手。喜欢听它讲故事、讲笑话、唱歌等,情绪价值拉满分。推荐可以给自家娃买个,不错的陪伴与守护,哈哈。
本文猫哥将详细分析其系统架构、启动流程、通信机制以及硬件适配方案,帮助开发者更深入地理解和扩展这个项目。该项目支持二次开发,商用也免费。预言以后可真是AI智能玩具的天下,太有引力与可玩性啦!


开发板资源链接 :https://pan.baidu.com/s/1GrSZ9711QsAc0bDmn59BQg?pwd=ffku#list/path=%2F
一、系统架构概览
小智AI项目开源地址: https://github.com/78/xiaozhi-esp32
小智AI项目采用分层设计架构,主要包含以下几个核心层次:
应用层 (Application)
├── 协议层 (Protocol)
│ ├── MQTT协议
│ └── WebSocket协议
├── 业务层
│ ├── 音频处理
│ ├── 语音识别
│ ├── 显示控制
│ └── IoT设备管理
└── 硬件抽象层
├── 板级抽象 (Board)
├── 音频编解码器
├── 显示驱动
└── 网络接口
这种分层设计使得项目具有良好的可扩展性和可维护性,特别是在硬件适配方面表现出色,目前已支持50多种ESP32系列开发板。良好的项目架构,同时也是学习的典范。这种架构充分展示的c++面向对象思想在具体项目上的体现,代码结构分层清晰,便于扩展和维护。
二、详细启动流程分析
1. 系统入口点初始化
启动流程从main.cc中的app_main函数开始,这是ESP-IDF框架的标准入口点:
cpp
extern "C" void app_main(void)
{
// 初始化默认事件循环
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 初始化NVS闪存,用于WiFi配置存储
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_LOGW(TAG, "Erasing NVS flash to fix corruption");
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// 启动应用程序
Application::GetInstance().Start();
}
这一阶段完成了两个重要的系统初始化步骤:
- 事件循环初始化:创建默认事件循环,用于任务间通信和系统事件处理
- NVS初始化:初始化非易失性存储,用于保存WiFi配置等持久化数据,如果发现NVS损坏则进行擦除
2. 应用程序启动流程
Application::Start()方法是整个应用程序的核心启动函数,包含了复杂的初始化过程:
2.1 设备状态设置与硬件初始化
cpp
void Application::Start() {
auto& board = Board::GetInstance();
SetDeviceState(kDeviceStateStarting);
/* 设置显示 */
auto display = board.GetDisplay();
/* 设置音频编解码器 */
auto codec = board.GetAudioCodec();
opus_decoder_ = std::make_unique<OpusDecoderWrapper>(codec->output_sample_rate(), 1, OPUS_FRAME_DURATION_MS);
opus_encoder_ = std::make_unique<OpusEncoderWrapper>(16000, 1, OPUS_FRAME_DURATION_MS);
// 根据不同的板子类型和功能配置opus编码器复杂度
// ...
// 启动音频编解码器
codec->Start();
}
这一步骤中,系统获取了Board实例(通过工厂模式创建具体的板级实现),并初始化了显示和音频系统。值得注意的是,项目使用了Opus编解码器进行音频处理,支持多种复杂度过渡方案,以适应不同硬件平台的性能需求。
2.2 板级抽象与初始化
板级抽象是项目的一个重要特性,通过Board抽象基类和工厂模式实现对多种硬件平台的支持。从boards/README.md文档中,可以了解到板级初始化主要包括:
- I2C初始化:配置用于连接音频编解码器等外设的I2C总线
- SPI初始化:配置用于显示屏的SPI总线
- 按钮初始化:设置按钮的GPIO引脚和点击回调函数
- 显示屏初始化:初始化LCD/OLED显示面板
- IoT设备初始化:注册各种IoT设备(如扬声器、显示屏、电池等)
具体实现示例:
cpp
class MyCustomBoard : public WifiBoard {
private:
// 各种初始化方法
void InitializeI2c() { /* ... */ }
void InitializeSpi() { /* ... */ }
void InitializeButtons() { /* ... */ }
void InitializeDisplay() { /* ... */ }
void InitializeIot() { /* ... */ }
public:
// 构造函数中调用各种初始化方法
MyCustomBoard() {
InitializeI2c();
InitializeSpi();
InitializeDisplay();
InitializeButtons();
InitializeIot();
}
// 各种虚函数重写
virtual AudioCodec* GetAudioCodec() override { /* ... */ }
virtual Display* GetDisplay() override { /* ... */ }
virtual Backlight* GetBacklight() override { /* ... */ }
};
// 使用宏注册开发板
DECLARE_BOARD(MyCustomBoard);
2.3 音频处理初始化
cpp
// 创建音频处理循环任务
xTaskCreate([](void* arg) {
Application* app = (Application*)arg;
app->AudioLoop();
vTaskDelete(NULL);
}, "audio_loop", 4096 * 2, this, 8, &audio_loop_task_handle_);
系统创建了一个专门的音频处理任务,用于处理音频输入和输出。根据配置,可以选择将任务固定在特定的CPU核心上执行,以优化性能。
2.4 网络初始化
cpp
/* 等待网络准备就绪 */
board.StartNetwork();
根据不同的板型,StartNetwork()方法会初始化WiFi连接或4G网络(通过ML307模块)。
2.5 固件版本检查与OTA升级
cpp
// 检查新固件版本或获取MQTT代理地址
CheckNewVersion();
在CheckNewVersion()方法中,系统会:
- 检查是否有新版本固件可用
- 如果有新版本,进行OTA升级
- 如果需要激活码,显示激活码并等待用户激活
- 实现带有重试逻辑的循环过程
2.6 通信协议初始化
cpp
// 初始化协议
if (ota_.HasMqttConfig()) {
protocol_ = std::make_unique<MqttProtocol>();
} else if (ota_.HasWebsocketConfig()) {
protocol_ = std::make_unique<WebsocketProtocol>();
} else {
ESP_LOGW(TAG, "No protocol specified in the OTA config, using MQTT");
protocol_ = std::make_unique<MqttProtocol>();
}
// 设置各种协议回调函数
protocol_->OnNetworkError(...);
protocol_->OnIncomingAudio(...);
protocol_->OnAudioChannelOpened(...);
protocol_->OnAudioChannelClosed(...);
protocol_->OnIncomingJson(...);
bool protocol_started = protocol_->Start();
系统根据配置选择使用MQTT或WebSocket协议,并设置各种协议事件的回调函数。
3. WebSocket通信协议详解
根据docs/websocket.md文档,WebSocket协议是小智AI项目的重要通信机制之一。
其通信流程如下:
3.1 WebSocket连接建立流程
- 连接初始化 :设备调用
OpenAudioChannel(),根据配置获取WebSocket URL - 设置请求头 :包括
Authorization、Protocol-Version、Device-Id、Client-Id等 - 发送Hello消息:设备发送JSON格式的hello消息,包含音频参数
- 等待服务器响应:设备等待服务器返回hello消息,并验证transport字段
3.2 WebSocket消息类型
WebSocket通信支持两种主要数据类型:
- 二进制音频数据:Opus编码的音频帧
- 文本JSON消息:用于传输聊天状态、TTS/STT事件、IoT命令等
常见JSON消息类型包括:
| 消息类型 | 方向 | 作用 |
|---|---|---|
| hello | 双向 | 握手确认 |
| listen | 客户端→服务器 | 开始/停止录音监听 |
| stt | 服务器→客户端 | 语音转文本结果 |
| tts | 服务器→客户端 | TTS音频播放控制 |
| iot | 双向 | IoT设备描述/状态/命令 |
| abort | 客户端→服务器 | 终止当前会话 |
3.3 典型消息交互示例
- 客户端→服务器(握手)
json
{
"type": "hello",
"version": 1,
"transport": "websocket",
"audio_params": {
"format": "opus",
"sample_rate": 16000,
"channels": 1,
"frame_duration": 60
}
}
- 服务器→客户端(开始TTS)
json
{"type": "tts", "state": "start"}
- 服务器→客户端(STT结果)
json
{"type": "stt", "text": "用户说的话"}
4. 状态管理与事件循环
4.1 设备状态流转
设备有多个关键状态,与WebSocket消息对应:
- Idle → Connecting:用户触发或唤醒后,建立WebSocket连接
- Connecting → Listening:连接成功后开始录音
- Listening → Speaking:收到TTS Start消息后播放音频
- Speaking → Idle:收到TTS Stop消息后回到空闲状态
- 异常中断:遇到网络错误或主动中断会话,关闭WebSocket回到Idle
4.2 主事件循环
cpp
void Application::MainEventLoop() {
while (true) {
auto bits = xEventGroupWaitBits(event_group_, SCHEDULE_EVENT, pdTRUE, pdFALSE, portMAX_DELAY);
if (bits & SCHEDULE_EVENT) {
std::unique_lock<std::mutex> lock(mutex_);
std::list<std::function<void()>> tasks = std::move(main_tasks_);
lock.unlock();
for (auto& task : tasks) {
task();
}
}
}
}
主事件循环通过Schedule方法接收异步任务,并在适当的时候执行它们,是应用程序的核心控制逻辑。
三、硬件适配架构
小智AI项目的一个显著特点是强大的硬件适配能力,通过以下几个关键设计实现:
1. 板级抽象基类
cpp
class Board {
public:
static Board& GetInstance() {
static Board* instance = static_cast<Board*>(create_board());
return *instance;
}
// 各种虚函数接口
virtual std::string GetBoardType() = 0;
virtual AudioCodec* GetAudioCodec() = 0;
virtual Display* GetDisplay();
// ...
};
2. 板级继承体系
Board- 基础板级类WifiBoard- WiFi连接的开发板ML307Board- 使用4G模块的开发板DualNetworkBoard- 双网络(WiFi+4G)开发板
3. 工厂模式注册
通过DECLARE_BOARD宏简化开发板注册过程:
cpp
#define DECLARE_BOARD(BOARD_CLASS_NAME) \
void* create_board() { \
return new BOARD_CLASS_NAME(); \
}
4. 支持的硬件组件
4.1 显示屏
支持多种显示屏驱动,包括:
- ST7789 (SPI)
- ILI9341 (SPI)
- SH8601 (QSPI)
- OLED显示屏 (I2C)
4.2 音频编解码器
支持的编解码器包括:
- ES8311 (常用)
- ES7210 (麦克风阵列)
- AW88298 (功放)
- ES8374/ES8388
四、定制开发板指南
根据boards/README.md文档,添加新的开发板支持需要以下步骤:
1. 创建新的开发板目录
bash
mkdir main/boards/my-custom-board
2. 创建配置文件
2.1 config.h
定义所有硬件配置,包括:
- 音频采样率和I2S引脚配置
- 音频编解码芯片地址和I2C引脚配置
- 按钮和LED引脚配置
- 显示屏参数和引脚配置
2.2 config.json
定义编译配置:
json
{
"target": "esp32s3", // 目标芯片型号
"builds": [
{
"name": "my-custom-board", // 开发板名称
"sdkconfig_append": [
// 额外需要的编译配置
]
}
]
}
3. 编写板级初始化代码
实现继承自WifiBoard或ML307Board的开发板类,重写必要的虚函数。
4. 编译打包
使用专用脚本编译打包固件:
bash
python scripts/release.py [开发板目录名字]
五、常见问题与解决方案
根据文档和代码分析,以下是常见问题及其解决方法:
- 显示屏不正常:检查SPI配置、镜像设置和颜色反转设置
- 音频无输出:检查I2S配置、PA使能引脚和编解码器地址
- 无法连接网络:检查WiFi凭据和网络配置
- 无法与服务器通信:检查MQTT或WebSocket配置
- OTA升级问题:确保开发板标识唯一,避免被标准固件覆盖
六、总结
小智AI项目是一个结构良好、功能丰富的ESP32智能语音助手实现。其主要特点包括:
- 优秀的架构设计:采用分层架构,实现了高内聚低耦合
- 强大的硬件适配:通过抽象基类和工厂模式支持50多种开发板
- 灵活的通信协议:同时支持MQTT和WebSocket协议
- 完整的音频处理:支持Opus编解码、回声消除、降噪等功能
- 完善的状态管理:清晰的状态流转和事件驱动机制
这个项目不仅是一个功能完整的智能语音助手,也是学习ESP32应用开发、音频处理和IoT设备开发的优秀范例。通过本文的分析,希望能够帮助开发者更好地理解和扩展这个项目。
其他资源
项目链接:https://github.com/78/xiaozhi-esp32
后台实现:https://github.com/AnimeAIChat/xiaozhi-server-go
https://github.com/xinnan-tech/xiaozhi-esp32-server