以下是一份基于代码实现整理的 WebSocket 通信协议文档,概述设备端与服务器之间如何通过 WebSocket 进行交互。
该文档仅基于所提供的代码推断,实际部署时可能需要结合服务器端实现进行进一步确认或补充。
Application:
Protocol 接口的 WebSocket 协议实例(WebsocketProtocol)OpenAudioChannel():
Authorization, Protocol-Version, Device-Id, Client-Id)Connect() 与服务器建立 WebSocket 连接json { "type": "hello", "version": 1, "features": { "mcp": true }, "transport": "websocket", "audio_params": { "format": "opus", "sample_rate": 16000, "channels": 1, "frame_duration": 60 } } features 字段为可选,内容根据设备编译配置自动生成。例如:"mcp": true 表示支持 MCP 协议。frame_duration 的值对应 OPUS_FRAME_DURATION_MS(例如 60ms)。"type": "hello" 的 JSON 消息,并检查 "transport": "websocket" 是否匹配。session_id 字段,设备端收到后会自动记录。json { "type": "hello", "transport": "websocket", "session_id": "xxx", "audio_params": { "format": "opus", "sample_rate": 24000, "channels": 1, "frame_duration": 60 } } 设备端和服务器端之间可发送两种主要类型的数据:
在代码里,接收回调主要分为:
OnData(...):binary 为 true 时,认为是音频帧;设备会将其当作 Opus 数据进行解码。binary 为 false 时,认为是 JSON 文本,需要在设备端用 cJSON 进行解析并做相应业务逻辑处理(如聊天、TTS、MCP 协议消息等)。当服务器或网络出现断连,回调 OnDisconnected() 被触发:
on_audio_channel_closed_(),并最终回到空闲状态。CloseAudioChannel() 主动断开连接,并回到空闲状态。在建立 WebSocket 连接时,代码示例中设置了以下请求头:
Authorization: 用于存放访问令牌,形如 "Bearer <token>"Protocol-Version: 协议版本号,与 hello 消息体内的 version 字段保持一致Device-Id: 设备物理网卡 MAC 地址Client-Id: 软件生成的 UUID(擦除 NVS 或重新烧录完整固件会重置)这些头会随着 WebSocket 握手一起发送到服务器,服务器可根据需求进行校验、认证等。
设备支持多种二进制协议版本,通过配置中的 version 字段指定:
直接发送 Opus 音频数据,无额外元数据。Websocket 协议会区分 text 与 binary。
使用 BinaryProtocol2 结构:c struct BinaryProtocol2 { uint16_t version; // 协议版本 uint16_t type; // 消息类型 (0: OPUS, 1: JSON) uint32_t reserved; // 保留字段 uint32_t timestamp; // 时间戳(毫秒,用于服务器端AEC) uint32_t payload_size; // 负载大小(字节) uint8_t payload[]; // 负载数据 } __attribute__((packed));
使用 BinaryProtocol3 结构:c struct BinaryProtocol3 { uint8_t type; // 消息类型 uint8_t reserved; // 保留字段 uint16_t payload_size; // 负载大小 uint8_t payload[]; // 负载数据 } __attribute__((packed));
WebSocket 文本帧以 JSON 方式传输,以下为常见的 "type" 字段及其对应业务逻辑。若消息里包含未列出的字段,可能为可选或特定实现细节。
json { "type": "hello", "version": 1, "features": { "mcp": true }, "transport": "websocket", "audio_params": { "format": "opus", "sample_rate": 16000, "channels": 1, "frame_duration": 60 } } "session_id":会话标识"type": "listen""state":"start", "stop", "detect"(唤醒检测已触发)"mode":"auto", "manual" 或 "realtime",表示识别模式。json { "session_id": "xxx", "type": "listen", "state": "start", "mode": "manual" } json { "session_id": "xxx", "type": "abort", "reason": "wake_word_detected" } reason 值可为 "wake_word_detected" 或其他。json { "session_id": "xxx", "type": "listen", "state": "detect", "text": "你好小明" } 推荐用于物联网控制的新一代协议。所有设备能力发现、工具调用等均通过 type: "mcp" 的消息进行,payload 内部为标准 JSON-RPC 2.0(详见 MCP 协议文档)。
设备端到服务器发送 result 的例子:
json { "session_id": "xxx", "type": "mcp", "payload": { "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "true" } ], "isError": false } } }
"type": "hello" 和 "transport": "websocket"。audio_params,表示服务器期望的音频参数,或与设备端对齐的配置。session_id 字段,设备端收到后会自动记录。{"session_id": "xxx", "type": "stt", "text": "..."}{"session_id": "xxx", "type": "llm", "emotion": "happy", "text": "😀"}{"session_id": "xxx", "type": "tts", "state": "start"}:服务器准备下发 TTS 音频,设备端进入 "speaking" 播放状态。{"session_id": "xxx", "type": "tts", "state": "stop"}:表示本次 TTS 结束。{"session_id": "xxx", "type": "tts", "state": "sentence_start", "text": "..."}
服务器通过 type: "mcp" 的消息下发物联网相关的控制指令或返回调用结果,payload 结构同上。
服务器到设备端发送 tools/call 的例子:
json { "session_id": "xxx", "type": "mcp", "payload": { "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "self.light.set_rgb", "arguments": { "r": 255, "g": 0, "b": 0 } }, "id": 1 } }
json { "session_id": "xxx", "type": "system", "command": "reboot" } "reboot":重启设备CONFIG_RECEIVE_CUSTOM_MESSAGE 启用时支持。json { "session_id": "xxx", "type": "custom", "payload": { "message": "自定义内容" } } 以下为常见设备端关键状态流转,与 WebSocket 消息对应:
OpenAudioChannel() → 建立 WebSocket 连接 → 发送 "type":"hello"。SendStartListening(...),则进入录音状态。此时设备会持续编码麦克风数据并发送到服务器。{"type":"tts","state":"start"}) → 停止录音并播放接收到的音频。{"type":"tts","state":"stop"}) → 音频播放结束。若未继续进入自动监听,则返回 Idle;如果配置了自动循环,则再度进入 Listening。SendAbortSpeaking(...) 或 CloseAudioChannel() → 中断会话 → 关闭 WebSocket → 状态回到 Idle。stateDiagram
direction TB
[*] --> kDeviceStateUnknown
kDeviceStateUnknown --> kDeviceStateStarting:初始化
kDeviceStateStarting --> kDeviceStateWifiConfiguring:配置WiFi
kDeviceStateStarting --> kDeviceStateActivating:激活设备
kDeviceStateActivating --> kDeviceStateUpgrading:检测到新版本
kDeviceStateActivating --> kDeviceStateIdle:激活完成
kDeviceStateIdle --> kDeviceStateConnecting:开始连接
kDeviceStateConnecting --> kDeviceStateIdle:连接失败
kDeviceStateConnecting --> kDeviceStateListening:连接成功
kDeviceStateListening --> kDeviceStateSpeaking:开始说话
kDeviceStateSpeaking --> kDeviceStateListening:结束说话
kDeviceStateListening --> kDeviceStateIdle:手动终止
kDeviceStateSpeaking --> kDeviceStateIdle:自动终止
stateDiagram
direction TB
[*] --> kDeviceStateUnknown
kDeviceStateUnknown --> kDeviceStateStarting:初始化
kDeviceStateStarting --> kDeviceStateWifiConfiguring:配置WiFi
kDeviceStateStarting --> kDeviceStateActivating:激活设备
kDeviceStateActivating --> kDeviceStateUpgrading:检测到新版本
kDeviceStateActivating --> kDeviceStateIdle:激活完成
kDeviceStateIdle --> kDeviceStateConnecting:开始连接
kDeviceStateConnecting --> kDeviceStateIdle:连接失败
kDeviceStateConnecting --> kDeviceStateListening:连接成功
kDeviceStateIdle --> kDeviceStateListening:开始监听
kDeviceStateListening --> kDeviceStateIdle:停止监听
kDeviceStateIdle --> kDeviceStateSpeaking:开始说话
kDeviceStateSpeaking --> kDeviceStateIdle:结束说话
Connect(url) 返回失败或在等待服务器 "hello" 消息时超时,触发 on_network_error_() 回调。设备会提示"无法连接到服务"或类似错误信息。OnDisconnected():
on_audio_channel_closed_()Authorization: Bearer <token> 提供鉴权,服务器端需验证是否有效。session_id,用于区分独立的对话或操作。服务端可根据需要对不同会话做分离处理。sample_rate = 16000,单声道。帧时长由 OPUS_FRAME_DURATION_MS 控制,一般为 60ms。可根据带宽或性能做适当调整。为了获得更好的音乐播放效果,服务器下行音频可能使用 24000 采样率。version 字段配置二进制协议版本(1、2 或 3){"type": ...},设备端会记录错误日志(ESP_LOGE(TAG, "Missing message type, data: %s", data);),不会执行任何业务。下面给出一个典型的双向消息示例(流程简化示意):
设备端 → 服务器(握手)
json { "type": "hello", "version": 1, "features": { "mcp": true }, "transport": "websocket", "audio_params": { "format": "opus", "sample_rate": 16000, "channels": 1, "frame_duration": 60 } }
服务器 → 设备端(握手应答)
json { "type": "hello", "transport": "websocket", "session_id": "xxx", "audio_params": { "format": "opus", "sample_rate": 16000 } }
设备端 → 服务器(开始监听)
json { "session_id": "xxx", "type": "listen", "state": "start", "mode": "auto" }
同时设备端开始发送二进制帧(Opus 数据)。
服务器 → 设备端(ASR 结果)
json { "session_id": "xxx", "type": "stt", "text": "用户说的话" }
服务器 → 设备端(TTS开始)
json { "session_id": "xxx", "type": "tts", "state": "start" }
接着服务器发送二进制音频帧给设备端播放。
服务器 → 设备端(TTS结束)
json { "session_id": "xxx", "type": "tts", "state": "stop" }
设备端停止播放音频,若无更多指令,则回到空闲状态。
本协议通过在 WebSocket 上层传输 JSON 文本与二进制音频帧,完成功能包括音频流上传、TTS 音频播放、语音识别与状态管理、MCP 指令下发等。其核心特征:
"type":"hello",等待服务器返回。"type" 为核心字段标识不同业务逻辑,包括 TTS、STT、MCP、WakeWord、System、Custom 等。服务器与设备端需提前约定各类消息的字段含义、时序逻辑以及错误处理规则,方能保证通信顺畅。上述信息可作为基础文档,便于后续对接、开发或扩展。