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

发布于:2025-04-15 ⋅ 阅读:(19) ⋅ 点赞:(0)

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

欢迎 👍点赞✍评论⭐收藏

希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!

小智AI作为一款基于ESP32开发板的嵌入式语音助手,其通信模块采用了MQTT协议UDP协议相结合的方式,实现了低延迟、高可靠性的数据传输。本文将深入解析小智AI嵌入式终端项目中MQTT+UDP通信交互的设计思路和代码实现,帮助开发者更好地理解其技术细节。

目录

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

1.1 MQTT协议的优势

1.2 UDP协议的引入

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

2.1 系统架构概述

三、核心代码解析

3.1 MQTT信令交互

3.1.1 功能描述

3.1.2 代码解析

3.2 UDP通道管理

3.2.1 功能描述

3.2.2 代码解析

3.3 音频数据加密与传输

3.3.1 功能描述

3.3.2 代码解析

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

4.1 数据完整性校验

4.2 断电保护

4.3 日志记录

五、实际应用场景

5.1 实时语音交互

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 代码解析

以下是关键代码片段:

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 代码解析

以下是关键代码片段:

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 代码解析

以下是关键代码片段:

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 数据完整性校验

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

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对话时打断地实时性,笔者测试多种通信方式,发现小智选择的这种通信方式对于实时响应是非常高效的。

嘻嘻。



网站公告

今日签到

点亮在社区的每一天
去签到