基于代码实现整理的 MQTT + UDP 混合通信协议文档,概述设备端与服务器之间如何通过 MQTT 进行控制消息传输,通过 UDP 进行音频数据传输的交互方式。
本协议采用混合传输方式:
- MQTT:用于控制消息、状态同步、JSON 数据交换
- UDP:用于实时音频数据传输,支持加密
sequenceDiagram
participant Device as ESP32 设备
participant MQTT as MQTT 服务器
participant UDP as UDP 服务器
Note over Device, UDP: 1. 建立 MQTT 连接
Device->>MQTT: MQTT Connect
MQTT->>Device: Connected
Note over Device, UDP: 2. 请求音频通道
Device->>MQTT: Hello Message (type: "hello", transport: "udp")
MQTT->>Device: Hello Response (UDP 连接信息 + 加密密钥)
Note over Device, UDP: 3. 建立 UDP 连接
Device->>UDP: UDP Connect
UDP->>Device: Connected
Note over Device, UDP: 4. 音频数据传输
loop 音频流传输
Device->>UDP: 加密音频数据 (Opus)
UDP->>Device: 加密音频数据 (Opus)
end
Note over Device, UDP: 5. 控制消息交换
par 控制消息
Device->>MQTT: Listen/TTS/MCP 消息
MQTT->>Device: STT/TTS/MCP 响应
end
Note over Device, UDP: 6. 关闭连接
Device->>MQTT: Goodbye Message
Device->>UDP: Disconnect
设备通过 MQTT 连接到服务器,连接参数包括:
- Endpoint:MQTT 服务器地址和端口
- Client ID:设备唯一标识符
- Username/Password:认证凭据
- Keep Alive:心跳间隔(默认240秒)
{
"type": "hello",
"version": 3,
"transport": "udp",
"features": {
"mcp": true
},
"audio_params": {
"format": "opus",
"sample_rate": 16000,
"channels": 1,
"frame_duration": 60
}
}
{
"type": "hello",
"transport": "udp",
"session_id": "xxx",
"audio_params": {
"format": "opus",
"sample_rate": 24000,
"channels": 1,
"frame_duration": 60
},
"udp": {
"server": "192.168.1.100",
"port": 8888,
"key": "0123456789ABCDEF0123456789ABCDEF",
"nonce": "0123456789ABCDEF0123456789ABCDEF"
}
}
字段说明:
- udp.server:UDP 服务器地址
- udp.port:UDP 服务器端口
- udp.key:AES 加密密钥(十六进制字符串)
- udp.nonce:AES 加密随机数(十六进制字符串)
Listen 消息
json { "session_id": "xxx", "type": "listen", "state": "start", "mode": "manual" }
Abort 消息
json { "session_id": "xxx", "type": "abort", "reason": "wake_word_detected" }
MCP 消息
json { "session_id": "xxx", "type": "mcp", "payload": { "jsonrpc": "2.0", "id": 1, "result": {...} } }
Goodbye 消息
json { "session_id": "xxx", "type": "goodbye" }
支持的消息类型与 WebSocket 协议一致,包括:
- STT:语音识别结果
- TTS:语音合成控制
- LLM:情感表达控制
- MCP:物联网控制
- System:系统控制
- Custom:自定义消息(可选)
设备收到 MQTT Hello 响应后,使用其中的 UDP 连接信息建立音频通道:
1. 解析 UDP 服务器地址和端口
2. 解析加密密钥和随机数
3. 初始化 AES-CTR 加密上下文
4. 建立 UDP 连接
|type 1byte|flags 1byte|payload_len 2bytes|ssrc 4bytes|timestamp 4bytes|sequence 4bytes|
|payload payload_len bytes|
字段说明:
- type:数据包类型,固定为 0x01
- flags:标志位,当前未使用
- payload_len:负载长度(网络字节序)
- ssrc:同步源标识符
- timestamp:时间戳(网络字节序)
- sequence:序列号(网络字节序)
- payload:加密的 Opus 音频数据
使用 AES-CTR 模式加密:
- 密钥:128位,由服务器提供
- 随机数:128位,由服务器提供
- 计数器:包含时间戳和序列号信息
local_sequence_ 单调递增remote_sequence_ 验证连续性stateDiagram
direction TB
[*] --> Disconnected
Disconnected --> MqttConnecting: StartMqttClient()
MqttConnecting --> MqttConnected: MQTT Connected
MqttConnecting --> Disconnected: Connect Failed
MqttConnected --> RequestingChannel: OpenAudioChannel()
RequestingChannel --> ChannelOpened: Hello Exchange Success
RequestingChannel --> MqttConnected: Hello Timeout/Failed
ChannelOpened --> UdpConnected: UDP Connect Success
UdpConnected --> AudioStreaming: Start Audio Transfer
AudioStreaming --> UdpConnected: Stop Audio Transfer
UdpConnected --> ChannelOpened: UDP Disconnect
ChannelOpened --> MqttConnected: CloseAudioChannel()
MqttConnected --> Disconnected: MQTT Disconnect
设备通过以下条件判断音频通道是否可用:cpp bool IsAudioChannelOpened() const { return udp_ != nullptr && !error_occurred_ && !IsTimeout(); }
从设置中读取的配置项:
- endpoint:MQTT 服务器地址
- client_id:客户端标识符
- username:用户名
- password:密码
- keepalive:心跳间隔(默认240秒)
- publish_topic:发布主题
基类 Protocol 提供超时检测:
- 默认超时时间:120 秒
- 基于最后接收时间计算
- 超时时自动标记为不可用
使用互斥锁保护 UDP 连接:cpp std::lock_guard<std::mutex> lock(channel_mutex_);
| 特性 | MQTT + UDP | WebSocket |
|---|---|---|
| 控制通道 | MQTT | WebSocket |
| 音频通道 | UDP (加密) | WebSocket (二进制) |
| 实时性 | 高 (UDP) | 中等 |
| 可靠性 | 中等 | 高 |
| 复杂度 | 高 | 低 |
| 加密 | AES-CTR | TLS |
| 防火墙友好度 | 低 | 高 |
MQTT + UDP 混合协议通过以下设计实现高效的音视频通信:
该协议适用于对实时性要求较高的语音交互场景,但需要在网络复杂度和传输性能之间做出权衡。