MQTTS连接adafruit平台示例
目录
MQTTS简介
MQTTS特点
Adafruit IO 平台简介
Adafruit IO Feed 数据流介绍
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
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定时器中断中
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初始化
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()函数]
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:接收消息处理机制
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 控制。
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 通信功能,进一步展示设备在网络数据收发场景下的应用方法,敬请期待!
