【嵌入式人工智能产品开发实战】(二十)—— 政安晨:小智AI嵌入式终端代码解读:【C】关于项目中的MQTT+UDP核心通信交互理解

政安晨的个人主页:************政安晨****************

欢迎 👍点赞✍评论⭐收藏

希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!
小智AI作为一款基于ESP32开发板的嵌入式语音助手,其通信模块采用了MQTT协议UDP协议相结合的方式,实现了低延迟、高可靠性的数据传输。本文将深入解析小智AI嵌入式终端项目中MQTT+UDP通信交互的设计思路和代码实现,帮助开发者更好地理解其技术细节。

目录

一、MQTT+UDP通信模式的设计背景

[1.1 MQTT协议的优势](#1.1 MQTT协议的优势)

[1.2 UDP协议的引入](#1.2 UDP协议的引入)

二、MQTT+UDP通信交互的整体架构

[2.1 系统架构概述](#2.1 系统架构概述)

三、核心代码解析

[3.1 MQTT信令交互](#3.1 MQTT信令交互)

[3.1.1 功能描述](#3.1.1 功能描述)

[3.1.2 代码解析](#3.1.2 代码解析)

[3.2 UDP通道管理](#3.2 UDP通道管理)

[3.2.1 功能描述](#3.2.1 功能描述)

[3.2.2 代码解析](#3.2.2 代码解析)

[3.3 音频数据加密与传输](#3.3 音频数据加密与传输)

[3.3.1 功能描述](#3.3.1 功能描述)

[3.3.2 代码解析](#3.3.2 代码解析)

四、异常处理与可靠性保障

[4.1 数据完整性校验](#4.1 数据完整性校验)

[4.2 断电保护](#4.2 断电保护)

[4.3 日志记录](#4.3 日志记录)

五、实际应用场景

[5.1 实时语音交互](#5.1 实时语音交互)

[5.2 多语言支持](#5.2 多语言支持)

六、总结


一、MQTT+UDP通信模式的设计背景

1.1 MQTT协议的优势

MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅消息传输协议,广泛应用于物联网场景。其主要特点包括:

  • 低带宽占用:适合资源受限的嵌入式设备。
  • 可靠性:支持QoS(Quality of Service)机制,确保消息可靠传递。
  • 灵活性:通过主题(Topic)实现消息的分类和分发。

然而,MQTT协议在实时性要求较高的场景下可能表现不足,例如音频流传输。

1.2 UDP协议的引入

UDP(User Datagram Protocol)是一种无连接的传输协议,具有以下特点:

  • 低延迟:无需建立连接,适合实时数据传输。
  • 简单高效:减少了协议开销,提升了传输效率。
  • 不可靠性:缺乏确认机制,可能导致数据丢失或乱序。

为了解决MQTT协议在实时性上的不足,小智AI项目在音频流传输中引入了UDP协议,结合MQTT协议完成信令交互,形成了高效的混合通信模式。

二、MQTT+UDP通信交互的整体架构

2.1 系统架构概述

小智AI的通信模块采用分层设计,主要包括以下部分:

  1. 应用层:负责调用通信接口,处理用户请求。
  2. 业务逻辑层 :由MqttProtocol类实现,负责MQTT信令交互和UDP通道管理。
  3. 通信层:通过MQTT协议完成信令交互,通过UDP协议传输音频数据。
  4. 底层驱动:基于ESP-IDF框架提供的网络接口,完成数据收发。

业务流程:

三、核心代码解析

3.1 MQTT信令交互

3.1.1 功能描述

在音频流传输之前,设备需要通过MQTT协议与服务器进行信令交互,协商UDP通道的相关参数(如服务器地址、端口、加密密钥等)。

3.1.2 代码解析

以下是关键代码片段:

cpp 复制代码
void MqttProtocol::Start() {
    StartMqttClient(false);
}

bool MqttProtocol::StartMqttClient(bool report_error) {
    mqtt_ = Board::GetInstance().CreateMqtt();
    mqtt_->OnMessage([this](const std::string& topic, const std::string& payload) {
        cJSON* root = cJSON_Parse(payload.c_str());
        if (root == nullptr) {
            ESP_LOGE(TAG, "Failed to parse json message %s", payload.c_str());
            return;
        }
        cJSON* type = cJSON_GetObjectItem(root, "type");
        if (strcmp(type->valuestring, "hello") == 0) {
            ParseServerHello(root);
        }
    });
    mqtt_->Connect(endpoint_, 8883, client_id_, username_, password_);
}

代码解析

  1. 创建MQTT客户端 :通过Board::GetInstance().CreateMqtt()创建MQTT客户端实例。
  2. 注册回调函数:在接收到消息时,解析JSON数据并根据消息类型执行相应操作。
  3. 连接服务器 :调用mqtt_->Connect方法连接到指定的MQTT服务器。

3.2 UDP通道管理

3.2.1 功能描述

在完成信令交互后,设备通过UDP协议传输音频数据。UDP通道的管理包括初始化、数据加密、解密以及错误处理。

3.2.2 代码解析

以下是关键代码片段:

cpp 复制代码
void MqttProtocol::OpenAudioChannel() {
    std::string message = "{";
    message += "\"type\":\"hello\",";
    message += "\"version\": 3,";
    message += "\"transport\":\"udp\",";
    message += "\"audio_params\":{";
    message += "\"format\":\"opus\", \"sample_rate\":16000, \"channels\":1, \"frame_duration\":" + std::to_string(OPUS_FRAME_DURATION_MS);
    message += "}}";
    SendText(message);

    EventBits_t bits = xEventGroupWaitBits(event_group_handle_, MQTT_PROTOCOL_SERVER_HELLO_EVENT, pdTRUE, pdFALSE, pdMS_TO_TICKS(10000));
    if (!(bits & MQTT_PROTOCOL_SERVER_HELLO_EVENT)) {
        ESP_LOGE(TAG, "Failed to receive server hello");
        SetError(Lang::Strings::SERVER_TIMEOUT);
        return false;
    }
}

代码解析

  1. 发送信令消息 :构造hello消息并通过SendText方法发送给服务器。
  2. 等待响应 :通过事件组等待服务器返回的hello响应。
  3. 错误处理:如果超时未收到响应,则设置错误状态。

3.3 音频数据加密与传输

3.3.1 功能描述

为了保证音频数据的安全性,小智AI项目采用了AES加密算法对数据进行加密和解密。

3.3.2 代码解析

以下是关键代码片段:

cpp 复制代码
void MqttProtocol::SendAudio(const std::vector<uint8_t>& data) {
    std::lock_guard<std::mutex> lock(channel_mutex_);
    if (udp_ == nullptr) {
        return;
    }

    std::string nonce(aes_nonce_);
    *(uint16_t*)&nonce[2] = htons(data.size());
    *(uint32_t*)&nonce[12] = htonl(++local_sequence_);

    std::string encrypted;
    encrypted.resize(aes_nonce_.size() + data.size());
    memcpy(encrypted.data(), nonce.data(), nonce.size());

    size_t nc_off = 0;
    uint8_t stream_block[16] = {0};
    mbedtls_aes_crypt_ctr(&aes_ctx_, data.size(), &nc_off, (uint8_t*)nonce.c_str(), stream_block,
        (uint8_t*)data.data(), (uint8_t*)&encrypted[nonce.size()]);
    udp_->Send(encrypted);
}

代码解析

  1. 生成随机数(Nonce):用于AES加密的初始化向量。
  2. 加密数据 :使用mbedtls_aes_crypt_ctr函数对音频数据进行加密。
  3. 发送数据:通过UDP客户端发送加密后的数据。

四、异常处理与可靠性保障

4.1 数据完整性校验

在接收音频数据时,设备会校验数据包的序列号,确保数据按顺序到达。如果发现乱序或重复的数据包,则丢弃该数据包。

cpp 复制代码
if (sequence < remote_sequence_) {
    ESP_LOGW(TAG, "Received audio packet with old sequence: %lu, expected: %lu", sequence, remote_sequence_);
    return;
}

4.2 断电保护

在升级过程中,如果设备意外断电,可能导致固件损坏。为此,小智AI采用了双分区设计,即使升级失败,设备仍可回滚到旧版本。

4.3 日志记录

为了便于排查问题,小智AI在升级过程中记录详细日志,包括错误码、时间戳等信息。

五、实际应用场景

5.1 实时语音交互

通过MQTT+UDP通信模式,小智AI能够实现实时语音交互,满足用户对低延迟的需求。

5.2 多语言支持

通过OTA升级,小智AI可以推送新的语言包,满足不同地区用户的需求。


六、总结

小智AI的MQTT+UDP通信模式以其高效、可靠的设计,为用户提供了便捷的通信方式。

其实MQTT+UDP的方式,非常好地保证了小智AI对话时打断地实时性,笔者测试多种通信方式,发现小智选择的这种通信方式对于实时响应是非常高效的。

嘻嘻。


相关推荐
不知名。。。。。。。。18 分钟前
c++------模板进阶
开发语言·c++
庸子19 分钟前
Active Directory域服务管理与高级应用技术白皮书
运维·服务器·网络·windows·ad
牧木江26 分钟前
【从C到C++的算法竞赛迁移指南】第二篇:动态数组与字符串完全攻略 —— 写给C程序员的全新世界
c语言·c++·经验分享·笔记·算法
淋过很多场雨36 分钟前
现代c++获取linux系统版本号
linux·开发语言·c++
开发者工具分享1 小时前
项目后期发现重大漏洞,如何紧急修复
网络·安全·web安全
低技术力的Ayase1 小时前
[UEC++]UE5C++各类变量相关知识及其API(更新中)
开发语言·c++·ue5
搬码临时工1 小时前
路由器端口映射的意思、使用场景、及内网ip让公网访问常见问题和解决方法
运维·服务器·网络·物联网·tcp/ip·计算机网络·智能路由器
、我是男生。1 小时前
MQTT、HTTP短轮询、HTTP长轮询、WebSocket
websocket·网络协议·http
小徐Chao努力1 小时前
【计网】SSL/TLS核心原理
网络·网络协议·ssl
YXXY3132 小时前
C/C++内存管理
c++