Khi chúng tôi tích hợp AI assistant lên thiết bị phần cứng (ESP32-S3), thách thức lớn nhất không phải là model AI — mà là giao thức truyền tải. Bài viết này giải thích tại sao chúng tôi chọn MQTT + UDP thay vì WebSocket thuần, và cách thiết kế MQTT Gateway.
Vấn đề khi dùng WebSocket cho IoT
WebSocket là lựa chọn tự nhiên khi đã có sẵn API qua HTTP/WebSocket. Nhưng trên thiết bị nhúng, nó bộc lộ nhiều hạn chế nghiêm trọng:
- RAM consumption cao — WebSocket maintain TLS session + HTTP upgrade headers tốn ~45KB RAM trên ESP32 (tổng RAM 320KB)
- Reconnect phức tạp — Mất mạng WiFi cần xử lý reconnect + re-auth thủ công
- Không có QoS — Không đảm bảo delivery khi mạng không ổn định
- Không phù hợp cho audio — TCP overhead (~50ms) quá cao cho voice streaming realtime
MQTT cho điều khiển thiết bị
MQTT (Message Queuing Telemetry Transport) được thiết kế riêng cho IoT từ những năm 1990s tại IBM. Giao thức pub/sub lightweight với header tối thiểu 2 bytes.
# Topic structure trong Assistant Core
TOPIC_CMD = "assistant/{device_id}/command"
TOPIC_RESP = "assistant/{device_id}/response"
TOPIC_STATUS = "assistant/{device_id}/status"
TOPIC_AUDIO = "assistant/{device_id}/audio"
# QoS levels sử dụng
QOS_STATUS = 1 # At least once — trạng thái thiết bị
QOS_CMD = 2 # Exactly once — lệnh quan trọng
QOS_AUDIO = 0 # At most once — audio (latency > reliability)Ưu điểm MQTT với IoT:
- Last Will Testament (LWT) — Broker tự động publish khi device offline, không cần heartbeat thủ công
- Retained messages — Device vừa kết nối lại nhận ngay trạng thái mới nhất
- RAM tiết kiệm — Client library ESP-MQTT chỉ tốn ~12KB RAM
- Offline buffering — Broker giữ messages khi device tạm offline (QoS 1/2)
UDP cho audio transport
Với voice streaming, mỗi millisecond độ trễ đều quan trọng. TCP (WebSocket/MQTT) retry cơ chế gây jitter không thể chấp nhận với audio realtime. UDP là lựa chọn đúng vì:
- Không có connection overhead, không retry
- Latency thấp và ổn định (~30–50ms LAN)
- Packet loss 1–2% không ảnh hưởng đáng kể với Opus codec
# Audio pipeline trên thiết bị
Microphone (16kHz, 16-bit, mono)
→ VAD (Voice Activity Detection)
→ Opus encoder (8kbps)
→ UDP packet (20ms frames = 160 bytes)
→ Gateway → ASR → LLM → TTS
→ UDP packet (Opus audio)
→ Opus decoder trên device
→ SpeakerKiến trúc MQTT Gateway
Chúng tôi xây dựng mqtt-gateway/ — một Node.js service bridge giữa WebSocket và MQTT. Điều này cho phép:
- Web dashboard kết nối devices qua WebSocket tiêu chuẩn
- Devices kết nối lightweight qua MQTT
- Gateway translate messages real-time giữa hai giao thức
- Server-side audio processing không cần expose API trực tiếp
// mqtt-gateway: bridge WebSocket ↔ MQTT
mqttClient.on('message', (topic, payload) => {
const deviceId = parseDeviceId(topic)
const wsClient = wsClients.get(deviceId)
if (wsClient?.readyState === WebSocket.OPEN) {
wsClient.send(payload) // forward to web client
}
})
wsServer.on('connection', (ws, req) => {
const deviceId = parseDeviceId(req.url)
wsClients.set(deviceId, ws)
ws.on('message', (data) => {
// forward command to device via MQTT
mqttClient.publish(
`assistant/${deviceId}/command`,
data, { qos: 2 }
)
})
})Kết quả đo lường thực tế
Sau khi chuyển từ WebSocket thuần sang MQTT + UDP:
| Chỉ số | WebSocket | MQTT + UDP |
|---|---|---|
| Latency E2E (voice) | 380ms | 165ms |
| RAM trên ESP32 | 45KB | 12KB |
| Reconnect time | 4–8s | 1–2s |
| Battery life | baseline | +40% |
Khi nào vẫn dùng WebSocket?
MQTT + UDP không phải lúc nào cũng đúng. Chúng tôi vẫn dùng WebSocket cho:
- Web chat interface (đã có browser WebSocket native)
- Admin dashboard real-time monitoring
- Trường hợp không cần deploy MQTT broker riêng
Quy tắc đơn giản: nếu device là browser → WebSocket. Nếu device là hardware với RAM hạn chế và cần voice → MQTT + UDP.