目录
[Adafruit IO 平台简介](#Adafruit IO 平台简介)
[Adafruit IO Feed 数据流介绍](#Adafruit IO Feed 数据流介绍)
[MQTT连接 Adafruit IO 收发数据流程](#MQTT连接 Adafruit IO 收发数据流程)
本篇文章我们将详细介绍如何在W55MH32芯片上面实现MQTTS协议。并通过实战例程,为大家讲解如何使用W55MH32的MQTTS协议连接Adafruit IO 物联网平台。实现设备与云端的双向数据交互。
该例程用到的其他网络协议,例如 TCP和 DNS以及 DHCP ,请参考相关章节。有关W55MH32的初始化过程,请参考Network install章节,这里将不再详述。
MQTTS简介
MQTTS (MQTT Secure / MQTT over SSL/TLS) 是标准MQTT协议的安全增强版本,通过在TCP连接之上引入SSL/TLS加密层,为物联网通信提供全方位的安全保障。它广泛应用于金融支付、智能家居、医疗健康和工业控制等对数据隐私要求极高的领域。MQTTS协议在保留MQTT轻量、高效特性的同时,解决了公网传输中的数据泄露风险,特别适用于需要通过非安全网络(如公共互联网)传输敏感数据的嵌入式系统。
MQTTS特点
- **数据加密:**MQTTS利用SSL/TLS协议对传输的所有数据包进行加密,有效防止数据在传输过程中被窃听或抓包分析,确保信息的机密性。
- **身份认证:**支持单向认证(客户端验证服务器证书)和双向认证(MTLS,服务器验证客户端证书),能够确保只有合法的设备和服务器才能建立连接。
- **数据完整性:**通过TLS的消息认证机制,确保数据在传输过程中未被恶意篡改或意外损坏,保证指令和数据的准确执行。
- **无缝兼容:**上层应用逻辑(发布/订阅、QoS等)与普通MQTT完全一致,开发者无需修改业务代码,仅需配置安全证书和端口即可实现安全升级。
Adafruit IO 平台简介
Adafruit IO 是由 Adafruit Industries 开发的免费物联网数据平台,在物联网设备与云端应用之间搭建轻量、高效、易用的连接桥梁。该平台支持 MQTT/MQTTS 协议及 RESTful API,可实现传感器数据、设备状态的实时上传与可视化展示,提供丰富的 Dashboard 组件和自动化规则引擎,以支撑创客项目、教育实验及智能硬件原型的快速开发。
Adafruit IO Feed 数据流介绍
Feed(数据流)是 Adafruit IO 平台中用于组织和传输设备数据的核心单元,它定义了数据的名称、类型、存储策略和访问权限,使设备与云端之间的数据交互更加结构化和可管理。在 Adafruit IO 平台中,Feed 是数据管理与应用开发的核心概念,通过 Feed,开发者可以方便地对传感器数据进行分类存储、实时查询和历史回溯。
MQTT连接 Adafruit IO 收发数据流程
接下来,我们在 W55MH32 上实现基于 MQTTS 协议连接 Adafruit IO 平台的功能。整个流程可以分为以下几个步骤:网络参数配置、Adafruit IO 账号信息配置、MQTTS 参数初始化、TLS 安全连接建立、MQTT 连接建立,以及发布/订阅数据。
1. 准备阶段
首先需要登录 Adafruit IO 平台并完成以下配置:
- 注册并登录 Adafruit IO 账号
- 记录当前账号的用户名
- 获取当前账号的 AIO Key
- 创建业务所需的 Feed
本例中创建的 Feed 为:
- temperature
- humidity
- led
- beep

例如:
- **用户名(Username):**你的 Adafruit IO 用户名
- **AIO Key:**你的平台访问密钥
- Feed 名称: 例如
led、temperature、beep
2. 记录参数
在设备接入前,需要记录以下 MQTT 参数:
1. mqttHostUrl2. port3. clientid4. username5. passwd6. pubtopic7. subtopic
cpp
mqttconn mqtt_params = {
.mqttHostUrl = "io.adafruit.com",
.server_ip = {0},
#ifdef MQTTS
.port = 8883,
#else
.port = 1883,
#endif
.local_port = 5000,
.clientid = "5545_adafruit_io_connection",
.username = "5545",
.passwd = "aio_wLFi94popH35ahVrN1xdKqxzTMIW",
.pubtopic = "5545/feeds/temperature",
.subtopic = "5545/feeds/led",
.pubQoS = QOS0,
};
3. 连接、订阅和发布消息
完成参数配置后,设备侧执行如下步骤:
- DHCP 获取网络参数
- DNS 解析
io.adafruit.com - 建立 TCP 连接
- 初始化 TLS
- 执行 SSL/TLS 握手
- 建立 MQTT 连接
- 订阅 LED 控制 Topic
- 周期性发布传感器数据
4. 接受消息处理
当设备接收到云端消息后,先在 messageArrived() 中保存接收到的 Topic 和 Payload,然后在 do_mqtt() 的 RECV 分支中解析消息并执行对应控制。
本例中仅以LED为展示,最终实现了以下控制逻辑:
- 收到
0:LED 点亮 - 收到
1:LED 熄灭
ⓘ请注意:
本工程中板载 LED 为低电平点亮,因此 LED ON 对应的是 GPIO_ResetBits(LED_PORT, LED_PIN),LED OFF 对应的是 GPIO_SetBits(LED_PORT, LED_PIN)。
实现过程
接下来,我们看看在W55MH32上如何实现MQTT连接Adafruit IO平台,并进行订阅、发布消息以及接收消息处理。
步骤1:注册MQTT定时中断函数MilliTimer_Handler()到1ms定时器中断中
cpp
void TIM3_IRQHandler(void)
{
static uint32_t tim3_1ms_count = 0;
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
tim3_1ms_count++;
MilliTimer_Handler();
if (tim3_1ms_count >= 1000)
{
DHCP_time_handler();
DNS_time_handler();
tim3_1ms_count = 0;
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
步骤2:mqtt初始化
cpp
void mqtt_init(uint8_t sn, uint8_t *send_buf, uint8_t *recv_buf)
{
wiz_NetInfo get_info = {0};
wizchip_getnetinfo(&get_info);
if (do_dns(send_buf, (uint8_t *)mqtt_params.mqttHostUrl, mqtt_params.server_ip))
{
while (1);
}
#ifdef MQTTS
wiz_tls_socket(&tlsContext, sn, mqtt_params.local_port);
wiz_tls_init(&tlsContext, 2000, mqtt_params.mqttHostUrl, NULL, NULL, NULL);
NewNetwork(&n, sn);
n.mqttread = tls_mqtt_read;
n.mqttwrite = tls_mqtt_write;
n.disconnect = tls_mqtt_disconnect;
connect(tlsContext.socket_fd, (uint8_t *)mqtt_params.server_ip, mqtt_params.port);
wiz_tls_connect(&tlsContext, mqtt_params.mqttHostUrl, mqtt_params.port);
mbedtls_ssl_conf_read_timeout(tlsContext.conf, 2000);
MQTTClientInit(&c, &n, 2000, send_buf, MQTT_ETHERNET_MAX_SIZE, recv_buf, MQTT_ETHERNET_MAX_SIZE);
#else
NewNetwork(&n, sn);
ConnectNetwork(&n, mqtt_params.server_ip, mqtt_params.port);
MQTTClientInit(&c, &n, 1000, send_buf, MQTT_ETHERNET_MAX_SIZE, recv_buf, MQTT_ETHERNET_MAX_SIZE);
#endif
}
步骤3:进入do_mqtt()函数]
cpp
void do_mqtt(void)
{
uint8_t ret;
switch (run_status)
{
case CONN: {
ret = MQTTConnect(&c, &data);
printf("Connect to the MQTT server: %d.%d.%d.%d:%d\r\n",
mqtt_params.server_ip[0], mqtt_params.server_ip[1],
mqtt_params.server_ip[2], mqtt_params.server_ip[3],
mqtt_params.port);
printf("Connected:%s\r\n\r\n", ret == SUCCESSS ? "success" : "failed");
if (ret != SUCCESSS)
{
run_status = ERR;
}
else
{
run_status = SUB;
}
break;
}
case SUB: {
ret = MQTTSubscribe(&c, mqtt_params.subtopic, mqtt_params.subQoS, messageArrived);
printf("Subscribing to %s\r\n", mqtt_params.subtopic);
printf("Subscribed:%s\r\n\r\n", ret == SUCCESSS ? "success" : "failed");
if (ret != SUCCESSS)
{
run_status = ERR;
}
else
{
run_status = PUB_MESSAGE;
}
break;
}
case PUB_MESSAGE: {
if (mqtt_send_flag)
{
mqtt_send_flag = 0;
pubmessage.qos = QOS0;
sprintf(pubmessage.payload,
"{\"feeds\":{\"led\":%d,\"beep\":%d,\"humidity\":%.2f,\"temperature\":%.2f}}",
GPIO_ReadOutputDataBit(LED_PORT, LED_PIN),
GPIO_ReadOutputDataBit(BEEP_PORT, BEEP_PIN),
SENx.RH, SENx.T);
pubmessage.payloadlen = strlen(pubmessage.payload);
ret = MQTTPublish(&c, (char *)&(mqtt_params.pubtopic), &pubmessage);
if (ret != SUCCESSS)
{
run_status = ERR;
}
else
{
printf("publish:%s,%s\r\n\r\n", mqtt_params.pubtopic, (char *)pubmessage.payload);
}
}
/* no break, continue to KEEPALIVE */
}
case KEEPALIVE: {
if (MQTTYield(&c, 30) != SUCCESSS)
{
run_status = ERR;
}
/* no break, continue to RECV */
}
case RECV: {
if (mqtt_recv_flag)
{
mqtt_recv_flag = 0;
if (strcmp((char *)mqtt_recv_msg, "0") == 0)
{
GPIO_ResetBits(LED_PORT, LED_PIN);
printf("LED ON\r\n");
}
else if (strcmp((char *)mqtt_recv_msg, "1") == 0)
{
GPIO_SetBits(LED_PORT, LED_PIN);
printf("LED OFF\r\n");
}
else
{
json_decode((char *)mqtt_recv_msg);
}
}
break;
}
case ERR:
printf("system ERROR!");
delay_ms(1000);
break;
default:
break;
}
}
进入该函数后,程序首先根据当前运行状态执行对应的 MQTT 操作。当状态为 CONN 时,程序会调用 MQTTConnect() 尝试连接 Adafruit IO 的 MQTT 服务器;如果连接成功,则将运行状态切换为 SUB,进入主题订阅流程;如果连接失败,则进入 ERR 错误状态,并在串口打印错误信息。当状态进入 RECV 时,程序会对接收到的下行消息进行判断:若消息内容为 0,则点亮 LED;若消息内容为 1,则熄灭 LED;若接收到的是其他格式数据,则继续交由 json_decode() 进行解析处理。
步骤4:接收消息处理机制
cpp
void messageArrived(MessageData *md)
{
static char msg[512] = {0};
sprintf(msg, "%.*s", (int)md->message->payloadlen, (char *)md->message->payload);
mqtt_recv_flag = 1;
memset(mqtt_recv_msg, 0, sizeof(mqtt_recv_msg));
memcpy(mqtt_recv_msg, msg, strlen(msg));
}
当设备收到 Adafruit IO 平台下发的订阅消息后,会先进入 messageArrived() 回调函数。该函数负责提取接收到的 Topic 和 Payload,并将消息保存到接收缓冲区,同时置位接收标志。随后,主状态机在 RECV 分支中检测该标志,对消息内容进行解析,并完成 LED 控制。
cpp
case RECV: {
if (mqtt_recv_flag)
{
mqtt_recv_flag = 0;
if (strcmp((char *)mqtt_recv_msg, "0") == 0)
{
GPIO_ResetBits(LED_PORT, LED_PIN);
}
else if (strcmp((char *)mqtt_recv_msg, "1") == 0)
{
GPIO_SetBits(LED_PORT, LED_PIN);
}
}
break;
}
运行结果
ⓘ请注意:
因为本示例需要访问互联网,请确保 W55MH32 的网络环境及配置能够正常访问互联网。
烧录例程运行后,程序首先进行 PHY 链路检测,然后通过 DHCP 获取网络参数,并完成 DNS 解析与 TLS 握手,最后连接 Adafruit IO 平台的 MQTT 服务器进行通信测试。连接成功后,设备会订阅 5545/feeds/led 主题,并周期性向 5545/feeds/temperature 主题发布温湿度及状态数据;当平台下发 LED 控制消息时,设备可正确接收并执行对应控制操作,如下图所示:

然后我们在 Adafruit IO 平台的 led Feed 页面中写入控制数据,对板载 LED 状态进行远程控制。此时 W55MH32 会接收到平台下发的订阅消息,并根据消息内容执行对应操作。当写入 0 时,设备执行 LED ON;当写入 1 时,设备执行 LED OFF,如下图所示:

从串口输出结果可以看出,设备已经成功接收到 Adafruit IO 平台下发的控制消息,并根据消息内容完成 LED 亮灭控制。同时,平台侧也能够记录对应的 Feed 数据变化情况,如下图所示:
总结
本文讲解了如何在 W55MH32 芯片上实现 MQTT 协议,并连接 Adafruit IO 平台,通过实战例程展示了从准备工作、网络配置、TLS 安全连接建立、MQTT 消息订阅、发布及接收处理的完整过程。文章详细介绍了 MQTT 协议的基本概念、发布/订阅模式以及 Adafruit IO 平台的接入方式,同时结合实际测试验证了设备与平台之间的数据交互功能。
下一篇文章将继续介绍 RT-Thread + TCP 相关示例,讲解如何在 W55MH32 平台上基于 RT-Thread 实现 TCP 通信功能,进一步展示设备在网络数据收发场景下的应用方法,敬请期待!