本案例程序将演示怎么在拓维Niobe WiFi IoT Core开发板上编写一个连接MQTT服务器的业务程序,实现开发板联网上报数据到服务器。
简述MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。MQTT最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
MQTT协议中的方法MQTT协议中定义了一些方法(也被称为动作),来于表示对确定资源所进行操作。这个资源可以代表预先存在的数据或动态生成数据,这取决于服务器的实现。通常来说,资源指服务器上的文件或输出。主要方法有:
- Connect。等待与服务器建立连接。
- Disconnect。等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。
- Subscribe。等待完成订阅。
- UnSubscribe。等待服务器取消客户端的一个或多个topics订阅。
- Publish。MQTT客户端发送消息请求,发送完成后返回。
特点- 开放消息协议,简单易实现
- 发布订阅模式,一对多消息发布
- 基于TCP/IP网络连接,提供有序,无损,双向连接。
- 1字节固定报头,2字节心跳报文,最小化传输开销和协议交换,有效减少网络流量。
- 消息QoS支持,可靠传输保证
- “最多一次”,尽操作环境所能提供的最大努力分发消息。消息可能会丢失。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久之后会再次发送。
- “至少一次”,保证消息可以到达,但是可能会重复。
- “仅一次”,保证消息只到达一次。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。
- 异常连接断开发生时,能通知到相关各方。
MQTT结构体详解
- typedef struct
- {
- /** The eyecatcher for this structure. must be MQTC. */
- char struct_id[4];
- /** The version number of this structure. Must be 0 */
- int struct_version;
- /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1
- */
- unsigned char MQTTVersion;
- MQTTString clientID;
- unsigned short keepAliveInterval;
- unsigned char cleansession;
- unsigned char willFlag;
- MQTTPacket_willOptions will;
- MQTTString username;
- MQTTString password;
- } MQTTPacket_connectData;
复制代码 主要代码分析- static void MQTTDemoTask(void)
- {
- // 连接WIFI
- WifiConnect(SELECT_WIFI_SSID, SELECT_WIFI_PASSWORD);
- printf("Starting ...n");
- int rc, count = 0;
- MQTTClient client;
- // 初始化网络参数
- NetworkInit(&network);
- printf("NetworkConnect ...n");
- begin:
- // 连接服务器网络
- NetworkConnect(&network, "123.60.8.120", 1883);
- printf("MQTTClientInit ...n");
- // 初始化客户端
- MQTTClientInit(&client, &network, 2000, sendBuf, sizeof(sendBuf), readBuf, sizeof(readBuf));
- // 连接参数
- MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
- // 客户ID,唯一
- data.clientID.cstring= "Talkweb";
- // 不使用临终遗言
- data.willFlag = 0;
- // MQTT版本号
- data.MQTTVersion = 3;
- // 保存存活时间
- data.keepAliveInterval = 0;
- // 重新连接后是否清除以前的信息 0表示不清除,1表示重连清除
- data.cleansession = 1;
- // 服务器用户名
- data.username.cstring = "Talkweb";
- // 服务器用户密码
- data.password.cstring = "Talkweb1996";
- printf("MQTTConnect ...n");
- // 登录服务器
- rc = MQTTConnect(&client, &data);
- // 失败处理
- if (rc != 0) {
- printf("MQTTConnect: %dn", rc);
- NetworkDisconnect(&network);
- MQTTDisconnect(&client);
- osDelay(200);
- goto begin;
- }
- printf("MQTTSubscribe ...n");
- // 订阅主题,并绑定回调函数处理接收到的消息
- rc = MQTTSubscribe(&client, "pubtopic", 2, MessageArrived);
- // 订阅失败处理
- if (rc != 0) {
- printf("MQTTSubscribe: %dn", rc);
- osDelay(200);
- goto begin;
- }
- while (count++) {
- MQTTMessage message;
- // 发布的内容
- char payload[] = {"{"name": "Talkweb","url": "https://www.talkweb.com.cn/"}"};
- // 仅一次
- message.qos = 2;
- message.retained = 0;
- message.payload = payload;
- message.payloadlen = strlen(payload);
- // 发布
- if ((rc = MQTTPublish(&client, "pubtopic", &message)) != 0) {
- printf("Return code from MQTT publish is %dn", rc);
- NetworkDisconnect(&network);
- MQTTDisconnect(&client);
- goto begin;
- }
- osDelay(100);
- }
-
- }
复制代码 编译
修改对接热点的账号密码修改mqtt_main.h第32行和33行的WiFi热点SSID和密码,改成自己环境中的WiFi热点。
- // 默认WiFi名和密码
- #define SELECT_WIFI_SSID "111"
- #define SELECT_WIFI_PASSWORD "0987654321"
复制代码
修改 BUILD.gn 文件修改 applications/app/BUILD.gn 路径中的 BUILD.gn 文件,指定 network_mqttclient_example 参与编译。
- # "TW208_Module_ds1307:module_ds1307_example",
- # "TW209_Module_gps:module_gps_example",
- # "TW301_Network_wifista:network_wifista_example",
- # "TW302_Network_wifiap:network_wifiap_example",
- "TW303_Network_mqttclient:network_mqttclient_example",
- # "TW304_Network_httpclient:network_httpclient_example",
- # "TW305_Network_ntpclient:network_ntpclient_example",
复制代码 运行结果- Starting ...
- NetworkConnect ...
- MQTTClientInit ...
- MQTTConnect ...
- MQTTSubscribe ...
- wait for suback
- Message arrived on topic pubtopic: {"code":-20,"desc":"appId cannot be empty"}
- Message arrived on topic pubtopic: {"code":-20,"desc":"appId cannot be empty"}
- Message arrived on topic pubtopic: {"code":-20,"desc":"appId cannot be empty"}
- Message arrived on topic pubtopic: {"code":-20,"desc":"appId cannot be empty"}
- Message arrived on topic pubtopic: {"code":-20,"desc":"appId cannot be empty"}
- Message arrived on topic pubtopic: {"code":-20,"desc":"appId cannot be empty"}
- Message arrived on topic pubtopic: {"code":-20,"desc":"appId cannot be empty"}
- Message arrived on topic pubtopic: {"code":-20,"desc":"appId cannot be empty"}
复制代码 自己搭建MQTT服务器,用于测试。测试步骤如下:1、下载apache-apollo-1.7.1搭建本地mqtt服务器,用于接入测试。工具使用参考:
https://blog.csdn.net/yanyf2016/article/details/108057173。 2、获取本地IP地址,并把本地IP地址和端口61613替换案例中的地址。替换MQTTDemoTask函数中的NetworkConnect(&network, "123.60.8.120", 1883); 3、对应修改服务器用户名和密码,替换为apollo的默认用户名和密码:admin,password。 4、为了方便测试,可以把本地电脑,开发板连接同一个网络。 5、正常显示如下:
- NetworkConnect ...
- MQTTClientInit ...
- MQTTConnect ...
- MQTTSubscribe ...
- wait for suback
- Message arrived on topic pubtopic: {"name": "Talkweb","url": "https://www.talkweb.com.cn/"}
- Message arrived on topic pubtopic: {"name": "Talkweb","url": "https://www.talkweb.com.cn/"}
- Message arrived on topic pubtopic: {"name": "Talkweb","url": "https://www.talkweb.com.cn/"}
- Message arrived on topic pubtopic: {"name": "Talkweb","url": "https://www.talkweb.com.cn/"}
- Message arrived on topic pubtopic: {"name": "Talkweb","url": "https://www.talkweb.com.cn/"}
- Message arrived on topic pubtopic: {"name": "Talkweb","url": "https://www.talkweb.com.cn/"}
- Message arrived on topic pubtopic: {"name": "Talkweb","url": "https://www.talkweb.com.cn/"}
- Message arrived on topic pubtopic: {"name": "Talkweb","url": "https://www.talkweb.com.cn/"}
复制代码