MQTTS连接adafruit平台示例

目录

MQTTS简介

MQTTS特点

[Adafruit IO 平台简介](#Adafruit IO 平台简介)

[Adafruit IO Feed 数据流介绍](#Adafruit IO Feed 数据流介绍)

[MQTT连接 Adafruit IO 收发数据流程](#MQTT连接 Adafruit IO 收发数据流程)

实现过程

运行结果

总结


本篇文章我们将详细介绍如何在W55MH32芯片上面实现MQTTS协议。并通过实战例程,为大家讲解如何使用W55MH32的MQTTS协议连接Adafruit IO 物联网平台。实现设备与云端的双向数据交互。

该例程用到的其他网络协议,例如 TCPDNS以及 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 平台并完成以下配置:

  1. 注册并登录 Adafruit IO 账号
  2. 记录当前账号的用户名
  3. 获取当前账号的 AIO Key
  4. 创建业务所需的 Feed

本例中创建的 Feed 为:

  • temperature
  • humidity
  • led
  • beep

例如:

  • **用户名(Username):**你的 Adafruit IO 用户名
  • **AIO Key:**你的平台访问密钥
  • Feed 名称: 例如 ledtemperaturebeep

2. 记录参数

在设备接入前,需要记录以下 MQTT 参数:

  • 1. mqttHostUrl
  • 2. port
  • 3. clientid
  • 4. username
  • 5. passwd
  • 6. pubtopic
  • 7. 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. 连接、订阅和发布消息

完成参数配置后,设备侧执行如下步骤:

  1. DHCP 获取网络参数
  2. DNS 解析 io.adafruit.com
  3. 建立 TCP 连接
  4. 初始化 TLS
  5. 执行 SSL/TLS 握手
  6. 建立 MQTT 连接
  7. 订阅 LED 控制 Topic
  8. 周期性发布传感器数据

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 通信功能,进一步展示设备在网络数据收发场景下的应用方法,敬请期待!

相关推荐
凯瑟琳.奥古斯特7 小时前
UDP检验和原理详解
网络·网络协议
m0_738120727 小时前
后渗透维权提权基础——CTF模拟红队进行权限维持(二)
前端·网络·windows·python·安全·php
落羽的落羽7 小时前
【网络】计算机网络世界的基础概念
linux·服务器·网络·c++·人工智能·计算机网络·机器学习
ACP广源盛139246256738 小时前
ASW3742@ACP# 产品规格详解
网络·人工智能·嵌入式硬件·计算机外设·电脑
TBrL7UtdTELTTdut4BAL8 小时前
F7015TV3 光猫 Telnet 命令配置 DHCP 服务器
运维·服务器·网络
llilian_168 小时前
晶体频率测试仪 破解晶振品控核心难题:晶体频率网络测试仪深度解析 晶体网络分析仪
网络·功能测试·单片机·嵌入式硬件·测试工具·51单片机
Fisher3Star8 小时前
mediasoup中Node.js与Worker进程通信机制
网络·webrtc
byzh_rc8 小时前
[AI工具从入门到入土] 命令行
网络·人工智能·python·深度学习·matplotlib
WIZnet8 小时前
W55MH32 RTThread+TCP通信测试
网络·网络协议·tcp/ip