[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 配置方式。
工作原理:
- 客户端发起 TLS 连接请求
- 服务器返回自己的证书(包含公钥)
- 客户端验证服务器证书的合法性(检查 CA 签名、有效期、域名匹配等)
- 验证通过后,客户端生成会话密钥,用服务器公钥加密后发送
- 服务器用私钥解密获取会话密钥
- 双方使用会话密钥进行对称加密通信
证书需求:
| 角色 |
需要的证书 |
| 服务器 |
服务器证书(含公钥)+ 私钥 |
| 客户端 |
CA 根证书(用于验证服务器证书) |
适用场景:
- 公共物联网平台(设备只需确认连接到正确的服务器)
- 设备数量庞大,难以管理每个设备的证书
- 对安全性要求不是极端严格的场景
优点:配置简单,客户端只需存储 CA 证书
缺点:服务器无法验证客户端身份,存在被未授权设备连接的风险
3️⃣ 双向认证(Mutual Authentication / mTLS)
双向认证 是指 客户端和服务器互相验证对方身份,提供最高级别的安全性。
工作原理:
- 客户端发起 TLS 连接请求
- 服务器返回自己的证书
- 客户端验证服务器证书
- 服务器请求客户端证书
- 客户端发送自己的证书
- 服务器验证客户端证书(检查 CA 签名、有效期、是否吊销等)
- 双方互相验证通过后,交换会话密钥
- 使用会话密钥进行加密通信
证书需求:
| 角色 |
需要的证书 |
| 服务器 |
服务器证书 + 私钥 + 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️⃣ 检查仓库完整性
克隆下来的完整文件如图,重点关注图中红框部分的子模块包含内容是否完整无缺失:

图:Ai-WB2 项目仓库目录结构,红框标注的 toolchain/riscv 子模块应包含 Darwin、Linux、MSYS 三个文件夹且文件夹内应包含有内容。
三、点灯代码配置与编译
1️⃣ 创建 MQTT 测试项目
打开刚克隆的仓库,在 applications → protocols 目录下找到 mqtt 文件夹,将其复制粘贴一份,并重命名为 mqtt_test:

图:将 mqtt 文件夹复制并重命名为 mqtt_test,红色高亮显示选中位置
2️⃣ 配置 WiFi 路由器信息
在 mqtt_test/tcp 文件夹下找到 main 文件,修改两个宏定义:ROUTER_SSID 和 ROUTER_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
编译过程如下:
图:编译成功提示,生成 tcp.bin 二进制文件
烧录过程如下:

图:烧录成功日志,红色标注显示 "[All Success]"
按下复位键重启开发板,打开串口助手,波特率设置为 115200,查看
图:串口日志显示 MQTT 连接成功、订阅成功、发布成功的消息
四、MQTTX 客户端配置与功能测试
1️⃣ 创建 MQTT 连接
打开 MQTTX 客户端,点击"新建"连接,填写以下信息:

图:MQTTX 连接配置界面,数字标注对应以下配置项
| 标注 |
配置项 |
值 |
说明 |
| ① |
名称 |
随便填 |
连接标识名称 |
| ② |
服务器地址 |
mqtt://wx.ai-thinker.com |
与代码中的 URI 保持一致 |
| ③ |
端口 |
1883 |
标准 MQTT 端口(非加密) |
| ④ |
Client ID |
mqttx_xxxxxxxx |
确保唯一不重复 |
| ⑤ |
用户名 |
可填可不填 |
本例为yunyan |
| ⑥ |
密码 |
可填可不填 |
根据实际情况填写 |
⚠️ 注意:SSL/TLS 选项保持关闭状态,因为本教程使用的是非加密 MQTT 连接。
2️⃣ 添加主题订阅
点击左侧 "+ 添加订阅"按钮,填写订阅信息:

*图:添加订阅对话框
| 配置项 |
值 |
说明 |
| 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 灯也会对应亮起:
图:MQTTX 消息收发界面,显示订阅主题、已发布和已接收的消息

图:Ai-WB2 开发板 RGB LED 点亮效果(绿色)
五、常见错误排查
🔴 问题 1:WiFi 连接失败
现象:串口日志显示 WiFi 连接超时或认证失败。
排查步骤:
- 检查
ROUTER_SSID 和 ROUTER_PWD 是否正确填写
- 确认 WiFi 路由器工作正常,2.4GHz 频段信号良好(无法连接5G频段)
- 检查 WiFi 名称中是否包含特殊字符(建议使用英文和数字)
🔴 问题 2:MQTT 连接失败
现象:串口日志显示 MQTT_EVENT_DISCONNECTED 或连接超时。
排查步骤:
- 检查 MQTT 服务器地址是否正确:
mqtt://wx.ai-thinker.com:1883
- 确认网络连接正常,可以访问互联网
- 检查 Client ID 是否唯一,避免与其他设备冲突
- 查看防火墙设置,确保端口 1883 未被阻止
🔴 问题 3:JSON 解析失败
现象:串口日志输出 Error parsing JSON!,LED 无响应。
排查步骤:
- 检查发布的 JSON 格式是否正确(注意双引号、大括号配对)
- 确认字段名称为
LED(区分大小写)
- 验证值是否为以下合法值:
LED_RED、LED_GREEN、LED_BLUE、LED_OFF
🔴 问题 4:LED 不亮
现象:MQTT 消息已接收,但 LED 无反应。
排查步骤:
- 检查 GPIO 引脚定义是否与实际硬件连接一致(
GPIO_LED_RED、GPIO_LED_GREEN、GPIO_LED_BLUE)
- 使用万用表测量引脚电平是否正常变化
- 确认 RGB LED 模块正常工作(可使用简单的 GPIO 测试代码验证)
🔴 问题 5:编译失败
现象:执行 make 命令后出现编译错误。
排查步骤:
- 确认子模块已完整下载(检查
toolchain/riscv 目录)
- 检查是否安装了必要的编译工具链(GCC、Make 等)
- 查看编译错误日志,根据提示修改代码或安装依赖库
总结
至此,基于 Ai-WB2 使用 MQTT 协议实现订阅、发布及点灯功能的全部内容已完成!
如有问题或建议,欢迎在评论区留言讨论!