[xiaozhi-esp32] 构建智能AI设备 | 开发板抽象层 | 通信协议层

发布于:2025-06-24 ⋅ 阅读:(18) ⋅ 点赞:(0)

链接:https://github.com/78/xiaozhi-esp32
相关project:咸鱼平台的智能客服

docs:xiaozhi-esp32

本项目为运行于ESP32微控制器的AI语音助手系统
应用核心作为中央控制单元,通过*开发板抽象层*协调硬件组件交互

  • 包括音频编解码器(麦克风与扬声器)和显示屏

  • 用户语音经音频处理模块(负责降噪、唤醒词检测等)处理后,通过通信协议层发送至AI服务器

  • 应用核心通过协议层接收AI响应(如文本指令),更新显示界面并控制已注册的物联网设备

  • 通过配置管理模块实现设备设置的持久化存储。

系统架构图

在这里插入图片描述

章节导航

  1. 开发板抽象层
  2. 通信协议层
  3. 应用核心
  4. 音频编解码器
  5. 音频处理模块
  6. 显示屏驱动
  7. 物联网设备管理
  8. 配置管理

基于 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("来自小智的问候!");

代码解析:

  1. Board::GetInstance():获取当前开发板的实例化对象
  2. GetDisplay():通过抽象接口获取显示组件(无论LCD/OLED)
  3. ShowNotification()调用显示组件的标准化方法

应用层仅与Board接口及组件接口交互,完全隔离硬件引脚与芯片寄存器操作


抽象层的内部实现机制

开发板抽象层通过继承体系实现多态,具体架构:

  1. 基类定义
    Board基类(main/boards/common/board.h)声明虚拟方法:

    class Board {
    public:
        virtual Display* GetDisplay() = 0;
        virtual AudioCodec* GetAudioCodec() = 0;
        virtual void StartNetwork() = 0;
        // 其他标准方法
    };
    
  2. 具体开发板实现
    各开发板继承基类并实现具体硬件操作,如:

    • 正晨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(...);
        }
    };
    
override

override用于明确标记派生类中重写基类virtual虚函数的成员函数,确保函数签名与基类一致,避免因拼写或参数错误导致未重写的意外情况。

static

在C++中,static关键字的主要作用取决于它的使用位置:

  • 在函数内:让局部变量“记住”上次的值,即使函数调用结束也不会被销毁
  • 在类中:表示该成员属于类本身,而非某个对象,所有对象共享同一份数据
  • 在全局作用域:限制变量或函数仅在当前文件可见,避免命名冲突。

  1. 实例化机制
    通过工厂模式创建具体实例:
    // 开发板注册宏
    DECLARE_BOARD(ZHENGCHEN_1_54TFT_WIFI)
    
    // 实例获取入口
    Board& Board::GetInstance() {
        static Board* instance = create_board();
        return *instance;
    }
    

系统启动时,create_board()根据编译配置实例化对应开发板类

实现要点

  1. 注册宏
    DECLARE_BOARD 宏可能用于注册具体开发板类型(如ZHENGCHEN_1_54TFT_WIFI),隐含了工厂的创建逻辑。

  2. 单例与工厂结合
    GetInstance() 通过调用 create_board() 工厂方法生成实例,既保证单例唯一性,又隐藏具体实现类。

  3. 隐藏实现细节
    用户只需关注 Board 接口,无需知道具体开发板的构造方式,符合开闭原则。

应用场景

  • 需要动态切换不同硬件配置(如多种开发板型号)
  • 隔离具体产品类的创建逻辑
  • 实现插件式架构时管理模块实例

类继承体系结构

为优化网络模块复用,抽象层采用分级继承
在这里插入图片描述

该结构实现:

  • 通用WiFi功能在WifiBoard实现
  • 4G模块功能在Ml307Board实现
  • 具体开发板继承对应网络基类

结语

开发板抽象层是xiaozhi-esp32项目的硬件兼容核心,通过标准化接口实现:

  • 硬件组件的统一访问
  • 网络模块的功能复用
  • 应用逻辑的跨平台运行

理解该抽象机制后,我们将进入项目通信协议的分析:下一章:通信协议层


第二章:通信协议层

第一章:开发板抽象层中,我们解析了Board如何像万能适配器般实现硬件无关操作

本章将深入项目与外部AI服务器的通信机制,该机制通过协议抽象层实现网络通信标准化

协议抽象层的本质

协议抽象层是网络通信细节的标准化封装,其核心功能包括:

  1. 统一管理音频流传输(如用户语音数据)
  2. 规范控制指令的收发格式(JSON格式)
  3. 自动维护服务器连接状态
  4. 支持多种网络协议(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();
}

关键接口解析:

  1. OpenAudioChannel()建立协议专用连接通道(WebSocket连接/MQTT会话等)
  2. SendAudio():发送OPUS编码的音频数据包(自动适配协议格式)
  3. 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实现云端智能的关键桥梁,其设计亮点包括:

  1. 双通道分离:控制指令与音频流分通道传输保障实时性
  2. 加密可扩展:支持TLS与应用层加密的灵活组合
  3. 协议热切换:通过配置即可切换通信协议无需代码修改

下一章将深入解析整合硬件与网络能力的应用层核心


网站公告

今日签到

点亮在社区的每一天
去签到