发帖
0 0 0

基于Ai-WB2实现使用MQTT完成订阅、发布及点灯功能

云烟
中级会员

5

主题

4

回帖

362

积分

中级会员

积分
362
Ai-WB2系列 5 0 2 小时前
[i=s] 本帖最后由 云烟 于 2026-2-3 18:07 编辑 [/i]

基于Ai-WB2实现使用MQTT完成订阅、发布及点灯功能


前言

你将学到什么

通过本教程,你将掌握以下核心技能:

  • ✅ 理解 MQTT 协议的核心概念、架构设计及 QoS 质量等级
  • ✅ 在 Ai-WB2 开发板上实现 MQTT 客户端的连接、订阅与发布
  • ✅ 使用 JSON 格式消息控制 RGB LED 的亮灭
  • ✅ 使用 MQTTX 客户端工具进行功能测试与调试

MQTT 协议基础知识

MQTT 是一种基于 发布/订阅(Publish/Subscribe) 模式的轻量级消息传输协议,由 IBM 于 1999 年推出,2014 年成为 OASIS 标准。MQTT 专为资源受限的设备和低带宽、高延迟或不稳定的网络环境设计,是物联网(IoT)领域最流行的通信协议之一。

1️⃣ 核心架构与角色

MQTT 采用 发布/订阅 模式,实现了消息生产者和消费者的解耦:

角色 说明
Publisher(发布者) 消息的发出者,负责将消息发送到指定主题
Subscriber(订阅者) 消息的接收者,订阅感兴趣的主题以接收消息
Broker(代理服务器) 消息中转站,负责接收发布者的消息并转发给订阅者
Topic(主题) 消息的分类标识,使用/ 分隔层级,如 home/bedroom/light

工作流程:发布者将消息发送到 Broker 的某个 Topic → Broker 根据订阅关系将消息转发给所有订阅该 Topic 的订阅者。

2️⃣ MQTT 协议特点

  • 轻量级:协议头最小仅 2 字节,适合资源受限的嵌入式设备
  • 低功耗:保持连接时心跳包开销极小,适合电池供电设备
  • 可靠性:支持三种 QoS(服务质量)等级,满足不同可靠性需求
  • 实时性:支持持久会话,设备断线重连后可接收离线消息
  • 灵活性:主题支持通配符订阅(+ 单层,# 多层)

3️⃣ QoS 消息质量等级

MQTT 定义了三种服务质量等级,用于控制消息传递的可靠性:

QoS 等级 名称 特点 适用场景
QoS 0 最多一次(At most once) 消息只发送一次,不确认、不重试,可能丢失 传感器数据上报、心跳包等允许丢失的数据
QoS 1 至少一次(At least once) 确保消息到达,但可能重复送达 设备状态更新、关键指令等需要确保到达的场景
QoS 2 恰好一次(Exactly once) 四次握手确保消息只到达一次,不丢失不重复 金融交易、支付指令等绝对不允许重复的场景

MQTTS(MQTT over SSL/TLS)加密传输

在物联网应用中,设备传输的数据往往涉及敏感信息(如控制指令、传感器数据),因此 安全性 至关重要。MQTT 本身不提供加密功能,需要依赖传输层安全协议。⚠️ 本教程虽然没有涉及到 SSL/TLS 的配置,但了解其基本概念有助于后续的安全设计。

1️⃣ SSL/TLS 基础

SSL(Secure Sockets Layer)TLS(Transport Layer Security) 是为网络通信提供安全及数据完整性的加密协议。TLS 是 SSL 的继任者,目前广泛使用的是 TLS 1.2 和 TLS 1.3。

TLS 提供的安全保障

  • 加密:防止数据被窃听(对称加密传输数据)
  • 身份认证:验证通信双方身份(非对称加密交换密钥)
  • 完整性:防止数据被篡改(消息认证码 MAC)

MQTT over TLS(MQTTS)协议栈

应用层:MQTT 协议
安全层:SSL/TLS(加密/解密、身份认证)
传输层:TCP 协议
网络层:IP 协议

MQTTS 默认端口为 8883(MQTT 为 1883)。

2️⃣ 单向认证(One-way Authentication)

单向认证 是指 客户端验证服务器身份,服务器不验证客户端身份。这是最常见的 TLS 配置方式。

工作原理

  1. 客户端发起 TLS 连接请求
  2. 服务器返回自己的证书(包含公钥)
  3. 客户端验证服务器证书的合法性(检查 CA 签名、有效期、域名匹配等)
  4. 验证通过后,客户端生成会话密钥,用服务器公钥加密后发送
  5. 服务器用私钥解密获取会话密钥
  6. 双方使用会话密钥进行对称加密通信

证书需求

角色 需要的证书
服务器 服务器证书(含公钥)+ 私钥
客户端 CA 根证书(用于验证服务器证书)

适用场景

  • 公共物联网平台(设备只需确认连接到正确的服务器)
  • 设备数量庞大,难以管理每个设备的证书
  • 对安全性要求不是极端严格的场景

优点:配置简单,客户端只需存储 CA 证书
缺点:服务器无法验证客户端身份,存在被未授权设备连接的风险

3️⃣ 双向认证(Mutual Authentication / mTLS)

双向认证 是指 客户端和服务器互相验证对方身份,提供最高级别的安全性。

工作原理

  1. 客户端发起 TLS 连接请求
  2. 服务器返回自己的证书
  3. 客户端验证服务器证书
  4. 服务器请求客户端证书
  5. 客户端发送自己的证书
  6. 服务器验证客户端证书(检查 CA 签名、有效期、是否吊销等)
  7. 双方互相验证通过后,交换会话密钥
  8. 使用会话密钥进行加密通信

证书需求

角色 需要的证书
服务器 服务器证书 + 私钥 + CA 证书(验证客户端)
客户端 客户端证书 + 私钥 + CA 证书(验证服务器)

适用场景

  • 金融、医疗等高安全要求的行业
  • 工业控制系统(防止未授权设备接入)
  • 需要精确控制设备接入权限的场景

优点:最高安全性,服务器可精确识别每个客户端
缺点:证书管理复杂,每个设备需要独立证书,证书分发和更新成本高

4️⃣ 证书类型与选型

类型 说明 适用场景
DV SSL 域名验证证书,仅验证域名所有权 测试环境、非核心应用
OV SSL 组织验证证书,验证组织身份 企业应用、工业物联网
EV SSL 扩展验证证书,最高级别验证 金融、关键基础设施
自签名证书 自己生成的证书,无第三方 CA 内网测试、开发环境

密钥算法选择

  • RSA:兼容性好,应用广泛,但密钥较长、计算开销大
  • ECC(椭圆曲线):密钥短、计算快、功耗低,适合资源受限的物联网设备

一、准备工作

📦 硬件与软件清单

分类 项目 数量/版本 说明
硬件 Ai-WB2-12F 开发板 1 核心控制 MCU
Type-C 数据线 1 烧录程序与调试
软件 安信可 Ai-WB2 开源仓库 最新版 Gitee
MQTTX 客户端 最新版 官网下载

⏱️ 预计完成时间

  • 代码配置与编译:约 15 分钟
  • 烧录与调试:约 10 分钟
  • 功能测试:约 10 分钟
  • 总计:约 35 分钟

📋 前置要求

  • 熟悉 GPIO 控制和 FreeRTOS
  • 对 Linux、make、Git 指令有一定的了解
  • 对回调函数、JSON、MQTT 协议有一定了解

二、克隆完整的 Ai-WB2 开源仓库

1️⃣ 克隆仓库

以下指令可直接一键克隆完整 Git 仓库和子模块,也可点击链接跳转直接下载 ZIP 包:

# Gitee
git clone --recursive https://gitee.com/Ai-Thinker-Open/Ai-Thinker-WB2.git

🔴 提示:该仓库包含子模块内容较大(约几百兆),需要耐心等待克隆完成。

2️⃣ 检查仓库完整性

克隆下来的完整文件如图,重点关注图中红框部分的子模块包含内容是否完整无缺失:
01.png

图:Ai-WB2 项目仓库目录结构,红框标注的 toolchain/riscv 子模块应包含 Darwin、Linux、MSYS 三个文件夹且文件夹内应包含有内容。


三、点灯代码配置与编译

1️⃣ 创建 MQTT 测试项目

打开刚克隆的仓库,在 applications → protocols 目录下找到 mqtt 文件夹,将其复制粘贴一份,并重命名为 mqtt_test

02.png

图:将 mqtt 文件夹复制并重命名为 mqtt_test,红色高亮显示选中位置

2️⃣ 配置 WiFi 路由器信息

mqtt_test/tcp 文件夹下找到 main 文件,修改两个宏定义:ROUTER_SSIDROUTER_PWD,填入你的 WiFi 名称和密码:

// 路由器配置信息(需要用户根据实际情况修改)
#define ROUTER_SSID "WIFI"     // 填入你的 WiFi 名称
#define ROUTER_PWD "1234567890" // 填入你的 WiFi 密码

3️⃣ 添加 RGB LED 控制代码

📌 关键配置点:引入头文件与定义引脚

#include "blog.h" 以下添加以下头文件和宏定义:

#include "cJSON.h"    // JSON 解析库
#include <bl_gpio.h>  // GPIO 控制库

// RGB LED 引脚定义(根据实际硬件连接修改)
#define GPIO_LED_RED    14
#define GPIO_LED_GREEN  17
#define GPIO_LED_BLUE   3

📌 关键配置点:LED 状态枚举与任务实现

static void log_error_if_nonzero(const char *message, int error_code) 函数后面添加以下代码:

/* ================= LED 指示灯业务逻辑 ================= */
typedef enum {
    LED_OFF = 0,      // 闲置(全灭)
    LED_BLUE,         // 蓝灯常亮
    LED_GREEN,        // 绿灯常亮
    LED_RED           // 红灯常亮
} led_indicator_state_t;

static volatile led_indicator_state_t g_led_state = LED_OFF;

// 设置 RGB LED 的亮灭状态
static void led_set_level(uint8_t r, uint8_t g, uint8_t b) {
    bl_gpio_output_set(GPIO_LED_RED, r);
    bl_gpio_output_set(GPIO_LED_GREEN, g);
    bl_gpio_output_set(GPIO_LED_BLUE, b);
}

// LED 指示灯任务(FreeRTOS 任务)
static void led_indicator_task(void *pvParameters) {
    // 初始化 GPIO 为输出模式
    bl_gpio_enable_output(GPIO_LED_RED, 0, 0);
    bl_gpio_enable_output(GPIO_LED_GREEN, 0, 0);
    bl_gpio_enable_output(GPIO_LED_BLUE, 0, 0);
    led_set_level(0, 0, 0); // 初始状态:全灭

    while (1) {
        switch (g_led_state) {
            case LED_BLUE: // 蓝灯常亮
                led_set_level(0, 0, 1);
                vTaskDelay(pdMS_TO_TICKS(500)); 
                break;
            case LED_GREEN: // 绿灯常亮
                led_set_level(0, 1, 0);
                vTaskDelay(pdMS_TO_TICKS(500)); 
                break;
            case LED_RED:   // 红灯常亮
                led_set_level(1, 0, 0);
                vTaskDelay(pdMS_TO_TICKS(500));
                break;
            case LED_OFF:   // 全灭
            default:
                led_set_level(0, 0, 0);
                vTaskDelay(pdMS_TO_TICKS(500));
                break;
        }
    }
}

📌 关键配置点:MQTT 客户端参数配置

mqtt_start(void) 函数中配置 MQTT 客户端参数:

/* 
 * 配置 MQTT 客户端参数
 * uri: MQTT 代理服务器地址,使用 mqtt:// 协议(非加密)
 * event_handle: 设置事件回调函数,用于接收连接状态和数据
 */
axk_mqtt_client_config_t mqtt_cfg = {
    .uri = "mqtt://wx.ai-thinker.com:1883",  // Broker 服务器地址与端口
    .client_id = "yunyan_WB2",                // 客户端 ID,必须唯一
    .username = "yunyan",                     // 用户名(可选)
    .event_handle = event_cb,                 // 注册事件回调函数
};

// 创建 LED 控制任务
static TaskHandle_t led_task_handle;
xTaskCreate(led_indicator_task, "led", 512, NULL, 10, &led_task_handle);

📌 关键配置点:MQTT 事件回调函数实现

修改回调函数 event_cb(axk_mqtt_event_handle_t event),实现以下三个关键事件:

/* ==================== MQTT 连接成功事件 ==================== */
case MQTT_EVENT_CONNECTED:
    blog_info("MQTT_EVENT_CONNECTED");
  
    // 订阅主题 "/topic/qos0",QoS 等级 0(最多送达一次,不保证送达)
    msg_id = axk_mqtt_client_subscribe(client, "/topic/qos0", 0);
    blog_info("sent subscribe successful, msg_id=%d", msg_id);

    // 如需取消订阅,取消以下注释
    // msg_id = axk_mqtt_client_unsubscribe(client, "/topic/qos1");
    // blog_info("sent unsubscribe successful, msg_id=%d", msg_id);
    break;
/* ==================== 订阅成功确认事件 ==================== */
case MQTT_EVENT_SUBSCRIBED:
    blog_info("MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
  
    // 订阅成功后,立即向该主题发布一条 QoS 0 的消息 "hello,MQTT!"
    msg_id = axk_mqtt_client_publish(client, "/topic/qos", "hello,MQTT!", 0, 0, 0);
    blog_info("sent publish successful, msg_id=%d", msg_id);
    break;
/* ==================== 收到 MQTT 消息数据事件 ==================== */
case MQTT_EVENT_DATA:
    blog_info("MQTT_EVENT_DATA");

    // 1. 解析 JSON 字符串为 cJSON 对象
    cJSON *root = cJSON_Parse(event->data);
    if (root == NULL) {
        printf("Error parsing JSON!\n");
        break;
    }

    // 2. 获取 LED 字段
    cJSON *LED = cJSON_GetObjectItem(root, "LED");
  
    // 3. 访问值并进行类型检查
    if (LED && LED->type == cJSON_String) {
        if (strcmp(LED->valuestring, "LED_RED") == 0) {
            g_led_state = LED_RED;
            printf("LED_RED_ON");
        }
        else if (strcmp(LED->valuestring, "LED_GREEN") == 0) {
            g_led_state = LED_GREEN;
            printf("LED_GREEN_ON");
        }
        else if (strcmp(LED->valuestring, "LED_BLUE") == 0) {
            g_led_state = LED_BLUE;
            printf("LED_BLUE_ON");
        }
        else if (strcmp(LED->valuestring, "LED_OFF") == 0) {
            g_led_state = LED_OFF;
            printf("LED_OFF");
        }
        else {
            printf("LED ERROR");
        }  
    }

    // 4. 核心:必须手动释放 cJSON 内存!
    cJSON_Delete(root);

    printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
    printf("DATA=%.*s\r\n", event->data_len, event->data);

    break;

4️⃣ 编译与烧录

保存代码后,执行以下命令进行编译与烧录:

make flash p=/dev/ttyUSB0

编译过程如下:04.png

图:编译成功提示,生成 tcp.bin 二进制文件

烧录过程如下:

05.png

图:烧录成功日志,红色标注显示 "[All Success]"

按下复位键重启开发板,打开串口助手,波特率设置为 115200,查看08.png

图:串口日志显示 MQTT 连接成功、订阅成功、发布成功的消息


四、MQTTX 客户端配置与功能测试

1️⃣ 创建 MQTT 连接

打开 MQTTX 客户端,点击"新建"连接,填写以下信息:

06.png

图:MQTTX 连接配置界面,数字标注对应以下配置项

标注 配置项 说明
名称 随便填 连接标识名称
服务器地址 mqtt://wx.ai-thinker.com 与代码中的 URI 保持一致
端口 1883 标准 MQTT 端口(非加密)
Client ID mqttx_xxxxxxxx 确保唯一不重复
用户名 可填可不填 本例为yunyan
密码 可填可不填 根据实际情况填写

⚠️ 注意:SSL/TLS 选项保持关闭状态,因为本教程使用的是非加密 MQTT 连接。

2️⃣ 添加主题订阅

点击左侧 "+ 添加订阅"按钮,填写订阅信息:
07.png

*图:添加订阅对话框

配置项 说明
Topic /topic/qos0 订阅的主题名称
QoS 0(最多一次) 消息服务质量等级

3️⃣ 发布消息测试

向主题 /topic/qos0 发布 JSON 格式消息,控制 LED 亮灭:

发布红色 LED

{
  "LED": "LED_RED"
}

发布绿色 LED

{
  "LED": "LED_GREEN"
}

发布蓝色 LED

{
  "LED": "LED_BLUE"
}

关闭 LED

{
  "LED": "LED_OFF"
}

发布消息后,可在串口助手中看到相应的打印信息,开发板 RGB 灯也会对应亮起:09.png

图:MQTTX 消息收发界面,显示订阅主题、已发布和已接收的消息
10.png

图:Ai-WB2 开发板 RGB LED 点亮效果(绿色)


五、常见错误排查

🔴 问题 1:WiFi 连接失败

现象:串口日志显示 WiFi 连接超时或认证失败。

排查步骤

  1. 检查 ROUTER_SSIDROUTER_PWD 是否正确填写
  2. 确认 WiFi 路由器工作正常,2.4GHz 频段信号良好(无法连接5G频段)
  3. 检查 WiFi 名称中是否包含特殊字符(建议使用英文和数字)

🔴 问题 2:MQTT 连接失败

现象:串口日志显示 MQTT_EVENT_DISCONNECTED 或连接超时。

排查步骤

  1. 检查 MQTT 服务器地址是否正确:mqtt://wx.ai-thinker.com:1883
  2. 确认网络连接正常,可以访问互联网
  3. 检查 Client ID 是否唯一,避免与其他设备冲突
  4. 查看防火墙设置,确保端口 1883 未被阻止

🔴 问题 3:JSON 解析失败

现象:串口日志输出 Error parsing JSON!,LED 无响应。

排查步骤

  1. 检查发布的 JSON 格式是否正确(注意双引号、大括号配对)
  2. 确认字段名称为 LED(区分大小写)
  3. 验证值是否为以下合法值:LED_REDLED_GREENLED_BLUELED_OFF

🔴 问题 4:LED 不亮

现象:MQTT 消息已接收,但 LED 无反应。

排查步骤

  1. 检查 GPIO 引脚定义是否与实际硬件连接一致(GPIO_LED_REDGPIO_LED_GREENGPIO_LED_BLUE
  2. 使用万用表测量引脚电平是否正常变化
  3. 确认 RGB LED 模块正常工作(可使用简单的 GPIO 测试代码验证)

🔴 问题 5:编译失败

现象:执行 make 命令后出现编译错误。

排查步骤

  1. 确认子模块已完整下载(检查 toolchain/riscv 目录)
  2. 检查是否安装了必要的编译工具链(GCC、Make 等)
  3. 查看编译错误日志,根据提示修改代码或安装依赖库

总结

至此,基于 Ai-WB2 使用 MQTT 协议实现订阅、发布及点灯功能的全部内容已完成!

如有问题或建议,欢迎在评论区留言讨论!


──── 0人觉得很赞 ────

使用道具 举报

您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 30585 个
  • 话题数: 44716 篇