链接:https://github.com/78/xiaozhi-esp32
相关project:咸鱼平台的智能客服
docs:xiaozhi-esp32
本项目为运行于ESP32
微控制器的AI语音助手系统。
应用核心作为中央控制单元
,通过*开发板抽象层
*协调硬件组件交互
包括音频编解码器(麦克风与扬声器)和显示屏。
用户语音经音频处理模块(负责降噪、唤醒词检测等)处理后,通过通信协议层发送至
AI服务器
。应用核心通过
协议层接收AI响应
(如文本指令),更新显示界面并控制已注册的物联网设备。通过配置管理模块实现设备设置的
持久化
存储。
系统架构图
章节导航
基于 MCP 控制万物
小智 AI 聊天机器人作为一个语音交互入口,利用 Qwen / DeepSeek
等大模型的 AI 能力,通过 MCP 协议实现多端控制
。
通过MCP控制万物
对于协议感兴趣的,之前有手写过rpc协议实现Json-Rpc
已实现功能
Wi-Fi / ML307 Cat.1 4G
- 无线局域网连接 / ML307 Cat.1 4G
- 离线语音唤醒
ESP-SR
- 支持两种通信协议(
Websocket
或 MQTT+UDP) - 采用
OPUS
音频编解码 - 基于流式
ASR + LLM + TTS
架构的语音交互 - 声纹识别,识别当前说话人的身份
3D Speaker
OLED / LCD
显示屏,支持表情显示- 电量显示与电源管理
- 支持多语言(中文、英文、日文)
- 支持 ESP32-C3、ESP32-S3、ESP32-P4 芯片平台
- 通过
设备端 MCP
实现设备控制(音量、灯光、电机、GPIO 等) - 通过
云端 MCP
扩展大模型能力(智能家居控制、PC桌面操作、知识搜索、邮件收发等)
开发环境
- VSCode
- 安装
ESP-IDF
插件,选择 SDK 版本 5.4 或以上 - Linux 比 Windows 更好,编译速度快,也免去驱动问题的困扰
- 本项目使用
Google C++
代码风格
第一章:开发板抽象层
欢迎进入xiaozhi-esp32
项目!
本项目旨在通过多功能
ESP32芯片
构建智能AI设备。然而ESP32生态中存在形态各异的开发板,它们配备的硬件组合(屏幕、麦克风、扬声器、按钮、网络芯片等)存在显著差异
核心挑战在于:如何让项目的核心大脑——负责AI交互的应用层能在任意开发板上无缝运行?
该层无需知晓
开发板A的扬声器引脚编号,也无需适配
开发板B的特定显示屏型号。
这如同在国际旅行中使用通用电源适配器:无需为每个国家的插座定制设备,适配器即可实现跨平台兼容。
本项目中的"开发板抽象层"正是这样的技术适配器。
开发板抽象层的本质
开发板抽象层是硬件细节的抽象化实现。所谓抽象化,即通过标准化接口隐藏
底层复杂性,仅暴露
核心功能特征
在本项目中,Board
抽象层屏蔽了具体ESP32开发板
的硬件连接细节,为应用层提供标准化的硬件访问接口,具体实现:
统一获取
显示屏、音频系统、网络接口等硬件组件集中管理
板级配置与操作(如网络启动)- 支持核心应用逻辑无需修改即可
跨平台运行
抽象层示例
应用层代码通过Board::GetInstance()
获取当前开发板实例,实现硬件无关调用。以下为屏幕消息显示示例:
// 获取当前开发板适配器
Board& current_board = Board::GetInstance();
// 从开发板获取显示组件
Display* screen = current_board.GetDisplay();
// 调用显示组件的标准方法
screen->ShowNotification("来自小智的问候!");
代码解析:
Board::GetInstance()
:获取当前开发板的实例化对象
GetDisplay()
:通过抽象接口获取
显示组件(无论LCD/OLED)ShowNotification()
:调用
显示组件的标准化方法
应用层仅与Board
接口及组件接口交互,完全隔离硬件引脚与芯片寄存器操作
抽象层的内部实现机制
开发板抽象层通过继承体系实现多态
,具体架构:
基类定义
Board
基类(main/boards/common/board.h
)声明虚拟方法:class Board { public: virtual Display* GetDisplay() = 0; virtual AudioCodec* GetAudioCodec() = 0; virtual void StartNetwork() = 0; // 其他标准方法 };
具体开发板实现
各开发板继承基类并实现具体硬件操作,如:- 正晨1.54寸屏开发板(
zhengchen-1.54tft-wifi.cc
)
class ZHENGCHEN_1_54TFT_WIFI : public WifiBoard { Display* GetDisplay() override { // 初始化ST7789显示屏驱动 InitializeSpi(); return new ZHENGCHEN_LcdDisplay(...); } };
- KevinBox开发板(
kevin_box_board.cc
)
class KevinBoxBoard : public Ml307Board { Display* GetDisplay() override { // 初始化SSD1306 OLED驱动 InitializeI2c(); return new OledDisplay(...); } };
- 正晨1.54寸屏开发板(
override
override
用于明确标记
派生类中重写
基类virtual
虚函数的成员函数,确保函数签名与基类一致,避免因拼写或参数错误导致未重写的意外情况。
static
在C++中,static
关键字的主要作用取决于它的使用位置:
- 在函数内:让局部变量“记住”上次的值,即使
函数调用结束也不会被销毁
。 - 在类中:表示该成员
属于类本身
,而非某个对象,所有对象共享同一份数据。 - 在全局作用域:限制变量或函数
仅在当前文件
可见,避免命名冲突。
- 实例化机制
通过工厂模式创建具体实例:// 开发板注册宏 DECLARE_BOARD(ZHENGCHEN_1_54TFT_WIFI) // 实例获取入口 Board& Board::GetInstance() { static Board* instance = create_board(); return *instance; }
系统启动时,create_board()
根据编译配置实例化对应开发板类
实现要点
注册宏
DECLARE_BOARD
宏可能用于注册具体开发板类型(如ZHENGCHEN_1_54TFT_WIFI
),隐含了工厂的创建逻辑。单例与工厂结合
GetInstance()
通过调用create_board()
工厂方法生成实例,既保证单例唯一性,又隐藏具体实现类。隐藏实现细节
用户只需关注Board
接口,无需知道具体开发板的构造方式,符合开闭原则。
应用场景
- 需要动态切换
不同硬件
配置(如多种开发板型号) 隔离
具体产品类的创建逻辑- 实现
插件式架构
时管理模块实例
类继承体系结构
为优化网络模块复用,抽象层采用分级继承:
该结构实现:
- 通用WiFi功能在
WifiBoard
实现 - 4G模块功能在
Ml307Board
实现 - 具体开发板继承对应网络基类
结语
开发板抽象层是xiaozhi-esp32
项目的硬件兼容核心,通过标准化接口实现:
- 硬件组件的
统一访问
- 网络模块的
功能复用
- 应用逻辑的
跨平台
运行
理解该抽象机制后,我们将进入项目通信协议的分析:下一章:通信协议层
第二章:通信协议层
在第一章:开发板抽象层中,我们解析了Board
如何像万能适配器般实现硬件无关
操作。
本章将深入项目与外部AI服务器的通信机制
,该机制通过协议抽象层实现网络通信标准化
协议抽象层的本质
协议抽象层是网络通信细节的标准化封装,其核心功能包括:
统一管理
音频流传输(如用户语音数据)- 规范
控制指令
的收发格式(JSON
格式) - 自动维护服务器
连接状态
- 支持多种网络协议(
WebSocket/MQTT/UDP
)无缝切换
类比国际快递服务:用户只需填写标准面单,无需关心包裹通过空运/海运
等具体运输方式。
协议接口应用范式
应用层通过统一接口实现网络无关调用,典型语音会话流程:
// 获取协议实例(根据配置自动选择WebSocket/MQTT)
Protocol* protocol = ProtocolManager::GetCurrentProtocol();
// 注册回调函数处理服务器响应
protocol->OnIncomingJson([](cJSON* json) {
// 解析"stt"类型的语音转文字结果
cJSON* type = cJSON_GetObjectItem(json, "type");
if (type && strcmp(type->valuestring, "stt") == 0) {
DisplayText(cJSON_GetObjectItem(json, "text")->valuestring);
}
cJSON_Delete(json); // 释放JSON内存
});
protocol->OnIncomingAudio( //lambda
[](AudioStreamPacket packet) {
// 播放服务器返回的语音响应
AudioCodec::Play(packet.data, packet.size);
});
// 建立音频通道
if (protocol->OpenAudioChannel()) {
// 发送唤醒词检测事件
protocol->SendWakeWordDetected("小智");
// 循环发送麦克风采集的音频
while (IsRecording()) {
AudioStreamPacket pkt = MicRecorder::GetEncodedPacket();
protocol->SendAudio(pkt);
}
protocol->CloseAudioChannel();
}
关键接口解析:
OpenAudioChannel()
:建立协议专用连接通道(WebSocket连接/MQTT会话等)SendAudio()
:发送OPUS编码的音频数据包
(自动适配协议格式)OnIncomingJson
:接收服务器JSON指令(如设备控制、对话响应,eg服务器AI生成的返回信息
)
协议实现机制
协议抽象层通过继承体系实现多态
,核心架构如下:
基类定义(protocol.h)
class Protocol {
public:
virtual bool OpenAudioChannel() = 0; // 纯虚函数
virtual void SendAudio(const AudioStreamPacket&) = 0;
virtual void OnIncomingJson(JsonCallback) = 0;
// ... 其他标准接口
};
WebSocket
协议实现
class WebsocketProtocol : public Protocol {
bool OpenAudioChannel() override {
websocket_ = Board::GetInstance().CreateWebSocket();
websocket_->Connect("wss://ai-server.com/voice");
// 设置二进制数据传输回调
websocket_->OnBinaryData([this](const uint8_t* data, size_t len){
HandleServerAudio(data, len);
});
return true;
}
void SendAudio(const AudioStreamPacket& pkt) override {
websocket_->SendBinary(pkt.data, pkt.size); // WebSocket二进制帧传输
}
};
MQTT+UDP
协议实现
class MqttProtocol : public Protocol {
bool OpenAudioChannel() override {
mqtt_->Connect("mqtt.aiserver.com");
// 通过MQTT协商UDP参数
mqtt_->Subscribe("/voice/udp_params", [this](const string& params){
udp_->Connect(ParseIP(params), ParsePort(params));
});
return true;
}
void SendAudio(const AudioStreamPacket& pkt) override {
uint8_t encrypted[256];
AES_Encrypt(pkt.data, pkt.size, encrypted); // UDP传输加密
udp_->SendTo(encrypted, sizeof(encrypted), server_addr_);
}
};
协议实现对比分析
特性 | WebSocket协议实现 | MQTT+UDP协议实现 |
---|---|---|
连接方式 | 单一长连接 |
MQTT控制通道 + UDP数据通道 |
传输层安全 | TLS 加密 |
应用层AES加密 |
音频延迟 | 100-300ms | 50-150ms |
适用场景 | 公网 环境 |
高实时性局域网 环境 |
数据包格式 | 二进制帧 | 加密UDP数据报 |
该对比显示不同协议在安全策略web好
和传输效率udp好
上的取舍
通信时序可视化
该时序图展示WebSocket协议
下语音交互的全流程
结语
协议抽象层是xiaozhi-esp32
实现云端智能的关键桥梁,其设计亮点包括:
- 双通道分离:控制指令与音频流分通道传输保障实时性
- 加密可扩展:支持TLS与应用层加密的灵活组合
- 协议热切换:通过配置即可切换通信协议无需代码修改
下一章将深入解析整合硬件与网络能力的应用层核心。