全部例程

W6300使用MQTT协议连接OneNET

W6300

更新于 2026年10月13日

本篇总结

本例程在 W6300 上通过 MQTT 协议接入中移物联网 OneNET 平台,实现属性数据上报与 LED 主题的订阅控制。

🔑 MH2457、QSPI、UART调试、硬件TOE引擎、MQTT、DNS、DHCP

硬件平台:本例程基于 W6300-EVB-M4 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板

本示例演示如何在 W6300 上通过 MQTT(消息队列遥测传输)协议连接中移物联网 OneNET 平台,实现设备与云端的双向数据交互。设备通过 TCP 连接 OneNET MQTT 服务器,使用 token 鉴权方式登录,发布属性数据,订阅属性设置主题,实现远程数据上报与控制。

前置工具准备

开始本例程前,请确认以下工具已安装:

  • WIZ UartTool V1.0:串口调试工具:点击下载
  • OneNET 平台账号:注册并登录 中移物联网 OneNET,创建产品和设备,获取设备 ID、产品 ID 和 access_key
  • token 生成工具:OneNET 提供的 token 生成器,用于生成 MQTT 登录所需的鉴权 token:参考文档

MQTT 协议简介

MQTT 是一种基于 发布/订阅模式的轻量级通信协议,构建于 TCP/IP 协议之上。由 IBM 在 1999 年发布,专为带宽有限、网络不稳定的远程通信场景设计,是物联网领域应用最广泛的协议之一。MQTT 协议的最大优点是:能以最少的代码和有限的带宽,为远程设备提供实时可靠的消息服务。

MQTT 协议的三个核心角色:

  • 发布者:向 Broker 发布消息,指定一个主题(Topic)
  • 订阅者:向 Broker 订阅感兴趣的主题,接收匹配的消息
  • Broker:消息代理,负责接收发布者的消息并转发给订阅者
特性说明
传输层基于 TCP 协议,端口 1883(明文)/ 8883(TLS 加密)
报文类型CONNECT、CONNACK、PUBLISH、SUBSCRIBE、PINGREQ 等 14 种
QoS 等级0(至多一次)、1(至少一次)、2(只有一次)
主题层级支持 "/" 分层和 "+"、"#" 通配符

OneNET 平台简介

OneNET 是中移物联网有限公司打造的 PaaS 物联网平台,支持 MQTT、HTTP、LwM2M 等多种协议接入。本例程使用 OneNET MQTT 接入方式(mqtts.heclouds.com:1883),采用 token 鉴权。设备通过发布属性主题上报遥测数据,通过订阅属性设置主题接收云端控制指令,并向回复主题发送确认响应。

硬件连接与拓扑

请注意:

本示例需要访问互联网,请确保 W6300 的网络环境及配置能够正常访问互联网(路由器需连接外网)。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器 LAN 口,路由器需连接互联网
  3. 确保 W6300 与 MH2457 之间的 QSPI 连线正确(CSn、SCK、DATA0~DATA3)
┌───────────┐ ┌─────────────────┐ ┌──────────────────┐
│ W6300 │──网线──│ 路由器/网关 │──互联网──│ OneNET 平台 │
│ (MQTT │ │ (DHCP/DNS) │ │ mqtts.heclouds.com│
│ Client) │ │ 192.168.1.1 │ │ :1883 │
└────┬──────┘ └─────────────────┘ └──────────────────┘
│ QSPI
┌────┴──────┐
│ MH2457 │──────────→ WIZ UartTool
│ MCU │
└───────────┘
网络拓扑:W6300 通过路由器接入互联网,连接 OneNET MQTT 服务器(mqtts.heclouds.com:1883),MH2457 通过 QSPI 驱动 W6300

工程结构

工程核心文件如下:

Project/
├── main.c                    ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
    ├── user_main.c           ← 网络地址定义 + MQTT 缓冲区 + user_run() 主流程
    ├── do_mqtt.c             ← MQTT 主流程(mqtt_init / do_mqtt / json_decode / messageArrived)
    ├── do_mqtt.h
    ├── do_dns.c              ← DNS 域名解析
    ├── cJSON.c               ← JSON 解析库
    ├── wiz_platform.c/h      ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
    └── wiz_interface.c/h     ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测、软件定时器

本例程相比 DNS 章节的核心新增:MQTT 客户端初始化和 发布/订阅状态机。

步骤1:注册 MQTT 定时器

文件:USER/do_mqtt.c — 在 mqtt_init() 中通过 W6300 平台的软件定时器链表注册 MilliTimer_Handler(),为 MQTT 协议栈提供 1ms 时基。

/* register MQTT MilliTimer handler */
wiz_add_timer(MilliTimer_Handler, 1);

wiz_add_timer() 把回调注册到软件定时器链表,由 1ms 系统定时中断驱动 wiz_timer_handler(),累计到 1ms 后执行 MilliTimer_Handler()。MQTT 协议栈需要毫秒级时基来处理超时、重传和 keepalive。

步骤2:配置 OneNET 连接参数

文件:USER/do_mqtt.c — 需要记录以下 MQTT 参数:mqttHostUrl、port、clientid、username、passwd、pubtopic、subtopic、subtopic_reply。在 OneNET 平台创建产品和设备后,获取设备 ID、产品 ID 和 access_key,并使用 token 生成工具生成鉴权 token。

mqttconn mqtt_params = {
    .mqttHostUrl = "mqtts.heclouds.com",
    .server_ip = {0},
    .port = 1883,
    .clientid = "W6300",
    .username = "14g5eK3W52",
    .passwd = "version=2018-10-31&res=products%2F14g5eK3W52%2Fdevices%2FW6300&et=1792723202&method=md5&sign=OMogJA2DOPS7hdwEXWjVoA%3D%3D",
    .pubtopic = "$sys/14g5eK3W52/W6300/thing/property/post",
    .pubtopic_reply = "$sys/14g5eK3W52/W6300/thing/property/post/reply",
    .subtopic = "$sys/14g5eK3W52/W6300/thing/property/set",
    .subtopic_reply = "$sys/14g5eK3W52/W6300/thing/property/set_reply",
    .pubQoS = QOS0,
    .willtopic = "/wizchip/will",
    .willQoS = QOS0,
    .willmsg = "wizchip offline!",
    .subQoS = QOS0,
};

如果你想自定义

  • clientid/username → 填写设备的 device_id(设备 ID)
  • passwd → OneNET token,格式为 version&res&et&method&sign,sign 由 access_key 通过 HMAC 生成
  • pubtopic → OneNET 物模型的属性上报主题:$sys/{pid}/{device_id}/thing/property/post
  • subtopic → OneNET 物模型的属性设置主题:$sys/{pid}/{device_id}/thing/property/set

步骤3:MQTT 初始化与连接

文件:USER/do_mqtt.c — 该函数完成 DNS 解析、TCP 连接和 MQTT 客户端初始化。OneNET 使用明文 MQTT(端口 1883),鉴权通过 token 完成。

void mqtt_init(uint8_t sn, uint8_t *send_buf, uint8_t *recv_buf)
{
    /* DNS parsing */
    if (do_dns(send_buf, (uint8_t *)mqtt_params.mqttHostUrl, mqtt_params.server_ip))
    {
        if (mqtt_params.server_ip[0] != 0)
        {
            printf("Use Default Server Internet Protocol Address:%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]);
        }
        else
        {
            printf("No default server address, unable to connect to the server\r\n");
            while (1) { }
        }
    }

    /* register MQTT MilliTimer handler */
    wiz_add_timer(MilliTimer_Handler, 1);

    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);

    willdata.qos = mqtt_params.willQoS;
    willdata.topicName.lenstring.data = mqtt_params.willtopic;
    willdata.topicName.lenstring.len = strlen(willdata.topicName.lenstring.data);
    willdata.message.lenstring.data = mqtt_params.willmsg;
    willdata.message.lenstring.len = strlen(willdata.message.lenstring.data);
    willdata.retained = 0;
    willdata.struct_version = 3;

    data.willFlag = 0;
    data.will = willdata;
    data.MQTTVersion = 4;
    data.clientID.cstring = mqtt_params.clientid;
    data.username.cstring = mqtt_params.username;
    data.password.cstring = mqtt_params.passwd;
    data.keepAliveInterval = MQTT_ONENET_KEEPALIVE_S;
    data.cleansession = 1;
}

执行逻辑:先通过 do_dns() 解析 OneNET 服务器域名 mqtts.heclouds.com 获取 IP,再建立 TCP 连接,最后初始化 MQTT 客户端并设置连接参数。

步骤4:do_mqtt() 状态机处理

文件:USER/do_mqtt.c — 该函数采用状态机方式运行:连接服务器(CONN)→ 订阅主题(SUB)→ 发布消息(PUB_MESSAGE)→ 保活(KEEPALIVE)→ 接收处理(RECV)。

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");
        run_status = (ret != SUCCESSS) ? ERR : 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; break; }

        ret = MQTTSubscribe(&c, mqtt_params.pubtopic_reply, mqtt_params.subQoS, messageArrived);
        printf("Subscribing to %s\r\n", mqtt_params.pubtopic_reply);
        printf("Subscribed:%s\r\n\r\n", ret == SUCCESSS ? "success" : "failed");
        run_status = (ret != SUCCESSS) ? ERR : PUB_MESSAGE;
        break;
    }

    case PUB_MESSAGE:
    {
        pubmessage.qos = QOS0;
        pubmessage.payload = "{\"id\":\"123\",\"version\":\"1.0\",\"params\":{\"CurrentTemperature\":{\"value\":26.6}}}";
        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);
            run_status = KEEPALIVE;
        }
        break;
    }

    case KEEPALIVE:
    {
        if (MQTTYield(&c, 30) != SUCCESSS)
        {
            run_status = ERR;
        }
        wiz_user_delay_ms(100);
    }
    /* no break */

    case RECV:
    {
        if (mqtt_recv_flag)
        {
            mqtt_recv_flag = 0;
            json_decode(mqtt_recv_msg);
        }
        wiz_user_delay_ms(100);
        break;
    }

    case ERR:
        printf("system ERROR!");
        wiz_user_delay_ms(1000);
        break;

    default:
        break;
    }
}

发布消息时使用 OneNET 物模型的 JSON 数据格式:{"id":"123","version":"1.0","params":{"CurrentTemperature":{"value":26.6}}},包含 id、version 和 params 字段。

步骤5:接收消息处理机制

文件:USER/do_mqtt.c — 当设备收到 OneNET 平台下发的订阅消息后,会先进入 messageArrived() 回调函数保存消息,随后主状态机在 RECV 分支中调用 json_decode() 解析 JSON 数据并完成 LED 控制。

void messageArrived(MessageData *md)
{
    char topicname[64] = {0};
    char msg[512] = {0};

    sprintf(topicname, "%.*s", (int)md->topicName->lenstring.len,
            md->topicName->lenstring.data);
    sprintf(msg, "%.*s", (int)md->message->payloadlen,
            (char *)md->message->payload);

    printf("recv:%s,%s\r\n\r\n", topicname, msg);

    if (strcmp(topicname, mqtt_params.subtopic) == 0)
    {
        mqtt_recv_flag = 1;
        memset(mqtt_recv_msg, 0, sizeof(mqtt_recv_msg));
        memcpy(mqtt_recv_msg, msg, strlen(msg));
    }
}

json_decode() 函数使用 cJSON 库解析 OneNET 下发的 JSON 指令,提取 id、params 以及 params 中的 LEDSwitch 值,执行相应的 GPIO 操作,并向 subtopic_reply 主题发送确认响应。

void json_decode(char *msg)
{
    int ret;
    char replymsg[128] = {0};
    cJSON *id = NULL;
    cJSON *jsondata = NULL;
    cJSON *params = NULL;
    cJSON *LED = NULL;

    jsondata = cJSON_Parse(msg);
    if (jsondata == NULL)
    {
        printf("json parse fail.\r\n");
        return;
    }

    id = cJSON_GetObjectItem(jsondata, "id");
    params = cJSON_GetObjectItem(jsondata, "params");
    LED = cJSON_GetObjectItem(params, "LEDSwitch");

    if (LED->valueint)
    {
        printf("LED ON\r\n");
    }
    else
    {
        printf("LED OFF\r\n");
    }

    pubmessage.qos = QOS0;
    sprintf(replymsg, "{\"id\":\"%s\",\"code\":200,\"msg\":\"success\"}", id->valuestring);
    printf("reply:%s\r\n", replymsg);

    pubmessage.payload = replymsg;
    pubmessage.payloadlen = strlen(replymsg);
    ret = MQTTPublish(&c, mqtt_params.subtopic_reply, &pubmessage);
    if (ret != SUCCESSS)
    {
        run_status = ERR;
    }
    else
    {
        printf("publish:%s,%s\r\n\r\n", mqtt_params.subtopic_reply, (char *)pubmessage.payload);
    }

    cJSON_Delete(jsondata);
}

JSON 解析说明

json_decode() 使用 cJSON 库解析 OneNET 下发的 JSON 指令,字段路径为 params.LEDSwitch。OneNET 下发的指令格式为 {"id":"1","version":"1.0","params":{"LED":1}}。收到 1 时打印 LED ON,收到 0 时打印 LED OFF,随后向 set_reply 主题发送成功响应。

运行验证

打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1)。烧录例程运行后,程序首先进行 PHY 链路检测,然后通过 DHCP 获取网络参数,并完成 DNS 解析与 TCP 连接,最后连接 OneNET 平台的 MQTT 服务器进行通信测试。预期串口输出如下:

MH2457 W6300 QSPI example start
W6300 MQTT access Onenet example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
 W6300 network configuration : DHCP

 MAC         : 00:08:DC:12:34:56
 IP          : 192.168.1.8
 Subnet Mask : 255.255.255.0
 Gateway     : 192.168.1.1
 DNS         : 192.168.1.1
====================================================================================================

> Translated mqtts.heclouds.com to 183.230.40.96
Connect to the MQTT server: 183.230.40.96:1883
Connected:success

Subscribing to $sys/14g5eK3W52/W6300/thing/property/set
Subscribed:success

Subscribing to $sys/14g5eK3W52/W6300/thing/property/post/reply
Subscribed:success

publish:$sys/14g5eK3W52/W6300/thing/property/post,{"id":"123","version":"1.0","params":{"CurrentTemperature":{"value":26.6}}}

recv:$sys/14g5eK3W52/W6300/thing/property/post/reply,{"id":"123","code":200,"msg":"success"}

recv:$sys/14g5eK3W52/W6300/thing/property/set,{"id":"1","version":"1.0","params":{"LED":1}}

LED ON
reply:{"id":"1","code":200,"msg":"success"}
publish:$sys/14g5eK3W52/W6300/thing/property/set_reply,{"id":"1","code":200,"msg":"success"}

连接成功后,设备会订阅 set 和 post/reply 主题,并周期性向 post 主题发布属性数据。在 OneNET 平台的设备详情页可查看上报的遥测数据,也可通过平台下发属性设置指令控制 LED。

WIZ UartTool 串口输出 — MQTT 连接 OneNET 全流程(链路检测 → DHCP → DNS → MQTT 连接 → 订阅 → 发布)
WIZ UartTool 串口输出 — 接收 OneNET 属性设置指令控制 LED 开关,并回复 code:200 成功响应

常见问题

Q: OneNET 的 token 鉴权方式如何生成

OneNET token 格式为 version&res&et&method&sign。其中 res 为资源路径(products/{pid}/devices/{device_id}),et 为过期时间戳,method 为加密方法(md5/sha1/sha256),sign 由 access_key 通过 HMAC 加密生成。可使用 OneNET 官方提供的 token 生成工具生成。

Q: MQTT 连接失败如何排查

首先确认网络能正常访问互联网;检查 DNS 是否成功解析 mqtts.heclouds.com;确认 clientid、username、passwd 配置正确(passwd 为 token);检查 token 是否过期(et 字段);OneNET 平台确认设备已创建且处于启用状态。

Q: OneNET 物模型的 Topic 命名规则是什么

OneNET 物模型使用 $sys/{pid}/{device_id}/thing/... 格式。属性上报:$sys/{pid}/{device_id}/thing/property/post;属性设置(设备订阅接收指令):$sys/{pid}/{device_id}/thing/property/set;属性设置回复(设备发布):$sys/{pid}/{device_id}/thing/property/set_reply。其中 pid 为产品 ID,device_id 为设备 ID。

Q: 发布的 JSON 数据格式有什么要求

OneNET 物模型要求 JSON 数据包含 id、version、params 字段。params 中为属性键值对,属性名需与平台定义的物模型属性标识一致。例如 {"id":"123","version":"1.0","params":{"CurrentTemperature":{"value":26.6}}}。

Q: 为什么收到 1 是点亮 LED

本工程中板载 LED 为低电平点亮。收到 LED 值为 1 时调用 GPIO_ResetBits() 拉低引脚点亮 LED,收到 0 时调用 GPIO_SetBits() 拉高引脚熄灭 LED。如果你的开发板 LED 为高电平点亮,需要相应调整控制逻辑。

总结

本文讲解了如何在 W6300 芯片上通过 MQTT 协议连接中移物联网 OneNET 平台,通过实战例程展示了完整的接入流程:

  • MQTT 协议:基于发布/订阅模式的轻量级物联网通信协议,运行在 TCP 之上
  • OneNET 接入:DNS 解析 → TCP 连接 → token 鉴权登录 → 订阅/发布
  • 关键 API:MQTTConnect/MQTTSubscribe/MQTTPublish 实现连接、订阅和发布
  • 消息收发:messageArrived() 回调接收,json_decode() 使用 cJSON 解析 OneNET 下发的 JSON 指令
  • 数据格式:使用 OneNET 物模型 JSON 格式,包含 id、version、params 字段
  • W6300 平台特有:MQTT 时基通过软件定时器链表注册,DNS 时基也由软件定时器提供

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5

Andy

W5500 技术支持专家