Skip to Content
Esp32固件 WebSocket 音频传输协议规范

固件 WebSocket 音频传输协议规范

本文档描述了小智固件通过 WebSocket 向服务端发送音频数据的二进制协议格式。服务端可据此文档解析和处理固件上传的音频数据。

1. 概述

固件支持三种二进制协议版本,通过配置选择使用:

协议版本说明
版本 1直接发送 Opus 音频数据,无额外元数据
版本 2包含完整的消息头,用于服务端 AEC 处理
版本 3精简版消息头,减少带宽占用

默认使用 版本 1

2. 音频参数

所有协议版本使用相同的音频参数:

参数
采样率16000 Hz
声道数1 (单声道)
帧时长60 ms
编码格式Opus

3. 协议格式详解

3.1 版本 1 (BinaryProtocol1)

说明:直接发送 Opus 音频数据,无任何头部信息。

┌────────────────────────────┐ │ Payload │ │ (Opus 音频数据) │ │ N 字节 │ └────────────────────────────┘

特点

  • 消息体即为纯 Opus 音频数据
  • 服务端收到后直接进行 Opus 解码
  • 适用于网络带宽充足的场景

3.2 版本 2 (BinaryProtocol2)

说明:包含完整的消息头,适用于需要服务端进行回声消除(AEC)的场景。

数据结构

struct BinaryProtocol2 { uint16_t version; // 协议版本,值为 2 uint16_t type; // 消息类型:0 = Opus 音频, 1 = JSON 文本 uint32_t reserved; // 保留字段,用于未来扩展 uint32_t timestamp; // 时间戳(毫秒),用于服务端 AEC 对齐 uint32_t payload_size; // Payload 负载大小(字节) uint8_t payload[]; // 负载数据(Opus 音频数据) } __attribute__((packed));

消息结构图

┌──────────┬──────┬──────────┬──────────┬──────────┬──────────┐ │ version │ type │ reserved │ timestamp│pay size │ payload │ │ (2B) │ (2B) │ (4B) │ (4B) │ (4B) │ (N B) │ └──────────┴──────┴──────────┴──────────┴──────────┴──────────┘ │ 头部 (16 字节) │ └────────────────────────────────────────────────────────────┘ ↑ 真正的 Opus 音频数据

字段说明

字段字节序大小说明
字节序 (大端)version网络 2 字节协议版本号,固定为 2
type网络字节序 (大端)2 字节消息类型:0 = Opus 音频,1 = JSON 文本
reserved网络字节序 (大端)4 字节保留字段,当前设为 0
timestamp网络字节序 (大端)4 字节时间戳(毫秒),用于服务端 AEC 对齐
payload_size网络字节序 (大端)4 字节payload 数据的长度(字节)
payload-N 字节实际的 Opus 编码音频数据

服务端解析步骤

  1. 读取前 16 字节:读取消息头
  2. 解析头部字段
    • 将前 2 字节转换为整数得到 version(需使用 ntohs()
    • 下 2 字节转换为整数得到 type(需使用 ntohs()
    • 下 4 字节转换为整数得到 reserved
    • 下 4 字节转换为整数得到 timestamp(需使用 ntohl()
    • 下 4 字节转换为整数得到 payload_size(需使用 ntohl()
  3. 验证 version:确认 version == 2
  4. 验证 type:确认 type == 0(Opus 音频)
  5. 提取 payload:从偏移量 16 字节处开始,读取 payload_size 字节的数据,即为 Opus 音频数据
  6. 解码 Opus:使用 Opus 解码器将 payload 解码为 PCM 原始音频

时间戳用途

timestamp 字段表示音频帧相对于会话开始的时间偏移(毫秒),服务端可利用此字段:

  • 将服务端音频与麦克风输入进行时间对齐
  • 用于回声消除(AEC)算法
  • 检测音频帧是否连续或存在丢包

3.3 版本 3 (BinaryProtocol3)

说明:精简版消息头,减少带宽占用,适用于对带宽敏感的场景。

数据结构

struct BinaryProtocol3 { uint8_t type; // 消息类型:0 = Opus 音频, 1 = JSON 文本 uint8_t reserved; // 保留字段 uint16_t payload_size; // Payload 负载大小(字节) uint8_t payload[]; // 负载数据(Opus 音频数据) } __attribute__((packed));

消息结构图

┌────────┬──────────┬─────────────┬──────────┐ │ type │ reserved │ pay size │ payload │ │ (1 B) │ ved(1B) │ (2 B) │ (N B) │ └────────┴──────────┴─────────────┴──────────┘ │ 头部 (4 字节) │ └────────────────────────────────────────────┘ ↑ 真正的 Opus 音频数据

字段说明

字段字节序大小说明
type-1 字节消息类型:0 = Opus 音频,1 = JSON 文本
reserved-1 字节保留字段,当前设为 0
payload_size网络字节序 (大端)2 字节payload 数据的长度(字节)
payload-N 字节实际的 Opus 编码音频数据

服务端解析步骤

  1. 读取前 4 字节:读取消息头
  2. 解析头部字段
    • 第 1 字节为 type
    • 第 2 字节为 reserved
    • 下 2 字节转换为整数得到 payload_size(需使用 ntohs()
  3. 验证 type:确认 type == 0(Opus 音频)
  4. 提取 payload:从偏移量 4 字节处开始,读取 payload_size 字节的数据,即为 Opus 音频数据
  5. 解码 Opus:使用 Opus 解码器将 payload 解码为 PCM 原始音频

4. JSON 文本消息

type 字段值为 1 时,payload 为 JSON 文本格式,用于传输控制消息。

唤醒词检测通知

当用户说出唤醒词后,固件会发送以下 JSON 消息:

{ "session_id": "xxx", "type": "listen", "state": "detect", "text": "小智" }

字段说明

字段类型说明
session_idstring会话 ID
typestring消息类型
statestring状态:detect 表示检测到唤醒词
textstring唤醒词内容

5. 连接建立流程

5.1 Hello 消息

设备连接 WebSocket 后,首先发送 Hello 消息,包含音频参数:

{ "type": "hello", "version": 1, "features": { "mcp": true }, "transport": "websocket", "audio_params": { "format": "opus", "sample_rate": 16000, "channels": 1, "frame_duration": 60 } }

服务端可根据 audio_params 确认音频参数。

5.2 协议版本协商

服务端可在响应中指定使用的协议版本。固件默认使用版本 1,可通过配置切换为版本 2 或版本 3。


6. 音频数据处理流程

服务端处理固件上传音频数据的完整流程:

┌─────────────────┐ │ 接收 WebSocket │ │ 二进制数据 │ └────────┬────────┘ ┌─────────────────┐ │ 判断协议版本 │ │ (根据配置或解析) │ └────────┬────────┘ ┌─────────────────┐ │ 解析消息头部 │ │ 提取 payload │ └────────┬────────┘ ┌─────────────────┐ │ 使用 Opus │ │ 解码器解码 │ └────────┬────────┘ ┌─────────────────┐ │ 得到 PCM │ │ 原始音频数据 │ └─────────────────┘

7. 注意事项

  1. 字节序:所有多字节整数均使用网络字节序(大端序),服务端解析时需要使用 ntohs()ntohl() 进行转换

  2. 最小帧大小:每帧 Opus 数据最小约 10 字节,最大约 120 字节(取决于压缩率和音频内容)

  3. 帧间隔:每帧时长为 60ms,服务端可据此计算音频时长:duration = payload_size_frames * 60ms

  4. 版本兼容性:建议服务端支持所有三个版本,以便在不同配置下都能正常工作

  5. 错误处理

    • version 字段不是 1/2/3,应断开连接
    • type 字段不是 0/1,应忽略该消息
    • payload_size 超过合理范围(如 > 1000 字节),应视为异常

8. 参考资料

  • 固件源码:main/protocols/websocket_protocol.cc
  • 协议定义:main/protocols/protocol.h
  • Opus 编码库:https://opus-codec.org/ 
Last updated on