STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

一、环境介绍

单片机采用:STM32F103C8T6


上网方式:采用ESP8266,也可以使用其他设备代替,只要支持TCP协议即可。比如:GSM模块、有线网卡等。


开发软件:keil5


物联网平台: 腾讯IOT物联网物联网平台。腾讯的物联网平台比起其他厂家的物联网平台更加有优势,腾讯物联网平台可以将数据推到微信小程序上,用户可以直接使用小程序绑定设备,完成与设备之间交互,现在用户基本都会使用微信,所以使用起来非常方便。


本文章配套使用的STM32设备端完整源代码下载地址:   https://download.csdn.net/download/xiaolong1126626497/18785807



STM32+ESP8266使用MQTT协议连接OneNET 中国移动物联网开发平台:https://blog.csdn.net/xiaolong1126626497/article/details/107385118


STM32+ESP8266使用MQTT协议连接阿里云物联网开发平台:https://blog.csdn.net/xiaolong1126626497/article/details/107311897



二、功能介绍

本文章接下会介绍如何在腾讯物联网平台上创建设备、配置设备、推送到微信小程序、并编写STM32设备端代码,使用ESP8266联网登录腾讯物联网平台,完成数据交互。


功能:  STM32采集环境温度、湿度、光照强度实时上传至物联网平台,在微信小程序页面上,用户可以实时查看这些数据,并且可以通过界面上的按钮控制设备端的电机、LED灯的开关,完成数据上传和远程控制。  


说明:  STM32设备端所有代码均有自己全部编写,没有使用任何厂家的SDK,MQTT协议也是参考MQTT官方文档编写;ESP8266也没有使用任何专用固件,所以代码的移植性非常高。 任何能够联网的设备都可以参考本篇文章代码连接腾讯物联网平台,达到相同的效果。

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

三、登录腾讯物联网平台创建设备

腾讯云官网:  https://cloud.tencent.com/

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

下面是手机上的截图:操作过程

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台STM32+ESP8266+MQTT协议连接腾讯物联网开发平台STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

现在设备是离线状态,是无法查看的,接下来就使用MQTT客户端模拟设备,登录测试。



四、使用MQTT客户端模拟设备--测试

4.1  下载MQTT客户端

MQTT客户端可执行文件下载地址(.exe):  https://download.csdn.net/download/xiaolong1126626497/18784012


这个MQTT客户端采用QT开发,如果需要了解它的源码,请看这里: https://blog.csdn.net/xiaolong1126626497/article/details/116779490


4.2  查看物联网平台端口号与域名(IP地址)

官方文档:  https://cloud.tencent.com/document/product/634/32546

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

通过这里得到信息:   如果是广州域的设备(其实哪里都一样,只是服务器距离的远近),就填入  <产品ID>.iotcloud.tencentdevices.com  ,端口号是 1883(这是密匙认证的端口号,如果是证书认证就是另一个)。

查看产品ID的方法:

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

得打产品ID之后,那么要连接我的设备,域名就填:  8O76VHCU7Y.iotcloud.tencentdevices.com     端口就填: 1883



由于我的测试用的MQTT客户端不支持域名输入,只支持IP地址输入,所有我这里需要先将域名转为IP地址在进行下面的测试,ESP8266内部支持域名解析的,所有可以直接输入域名即可,不需要做这一步。


在线解析域名的网址: https://site.ip138.com/8O76VHCU7Y.iotcloud.tencentdevices.com/

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

得到广州腾讯云的IP地址为:  106.55.124.154



4.3  生成MQTT登录参数

就像我们登录QQ、登录微信需要账号密码一样,设备登录物联网平台也需要类似的东西。


官方文档地址: https://cloud.tencent.com/document/product/634/32546

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

上面需要的参数,在设备调试页面,点击具体的设备进行查看:

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

Python源代码:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import base64
import hashlib
import hmac
import random
import string
import time
import sys
# 生成指定长度的随机字符串
def RandomConnid(length):
    return  ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length))
# 生成接入物联网通信平台需要的各参数
def IotHmac(productID, devicename, devicePsk):
     # 1. 生成 connid 为一个随机字符串,方便后台定位问题
     connid   = RandomConnid(5)
     # 2. 生成过期时间,表示签名的过期时间,从纪元1970年1月1日 00:00:00 UTC 时间至今秒数的 UTF8 字符串
     expiry   = int(time.time()) + 30*24*60 * 60
     # 3. 生成 MQTT 的 clientid 部分, 格式为 ${productid}${devicename}
     clientid = "{}{}".format(productID, devicename)
     # 4. 生成 MQTT 的 username 部分, 格式为 ${clientid};${sdkappid};${connid};${expiry}
     username = "{};12010126;{};{}".format(clientid, connid, expiry)
     # 5. 对 username 进行签名,生成token
     secret_key = devicePsk.encode('utf-8')  # convert to bytes
     data_to_sign = username.encode('utf-8')  # convert to bytes
     secret_key = base64.b64decode(secret_key)  # this is still bytes
     token = hmac.new(secret_key, data_to_sign, digestmod=hashlib.sha256).hexdigest()
     # 6. 根据物联网通信平台规则生成 password 字段
     password = "{};{}".format(token, "hmacsha256")
     return {
        "clientid" : clientid,
        "username" : username,
        "password" : password
     }
if __name__ == '__main__':
    # 参数分别填入: 产品ID,设备名称,设备密匙
    print(IotHmac("8O76VHCU7Y","SmartAgriculture","OHXqYLklNBU4xLqqoZbXMQ=="))

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

得到的登录信息如下:

clientid: 8O76VHCU7YSmartAgriculture
username: 8O76VHCU7YSmartAgriculture;12010126;J4MCD;1623766532
password: a962b484079864239148b255281d54372aa66247aa8d6259d11aa6fef650fd5b;hmacsha256

4.4 了解主题上报和订阅的格式

登录之前需要先了解如何订阅设备的主题和上报的数据流格式。

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

如果设备端想要得到APP页面的按钮状态就需要订阅属性下发和属性上报的响应,主题格式就是这样的:

格式:
$thing/down/property/8O76VHCU7Y/设备名称
示例:
$thing/down/property/8O76VHCU7Y/SmartAgriculture

如果设备端想要像APP页面上传数据,那么就需要使用属性上报--发布主题:

格式:
$thing/up/property/8O76VHCU7Y/${deviceName}
示例:
$thing/up/property/8O76VHCU7Y/SmartAgriculture

设备端向APP页面上报属性时,需要上传具体的数据,数据流的格式如下:

官方文档: https://cloud.tencent.com/document/product/1081/34916

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

比如: 我的产品里有温度、湿度、电机三个设备,我可以选择一次上传3个设备的信息,数据格式就这样写:

{"method":"report","clientToken":"123","params":{"temperature":20.23,"humidity":50,"Motor":1}}

其中:  "temperature"、"humidity"、"Motor"  是设备的标识符,根据自己的情况修改,冒号后面就是给这个设备上传的具体数据。

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

4.5 使用MQTT客户端登录设备测试

万事俱备,下面就使用MQTT客户端进行登录测试。

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

MQTT客户端操作步骤:

1. 填写相关参数

2. 点击登录

3. 订阅主题

4. 发布主题

5. 去APP页面查看信息

 

4.6 微信小程序效果

已经收到MQTT客户端上传的数据,点击按钮,MQTT客户端也会收到按钮下发的数据。

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

五、STM32设备端代码

本文章配套使用的STM32设备端完整源代码下载地址:   https://download.csdn.net/download/xiaolong1126626497/18785807

5.1 下载程序

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

5.2  连接状态

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32+ESP8266+MQTT协议连接腾讯物联网开发平台

STM32设备上按下按键后,手机打开微信小程序可以看到实时上传的数据,速度非常快。

 

5.3  main.c文件

#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include <string.h>
#include "timer.h"
#include "bluetooth.h"
#include "esp8266.h"
#include "mqtt.h"
 
//腾讯物联网服务器的设备信息
#define MQTT_ClientID "8O76VHCU7YSmartAgriculture"
#define MQTT_UserName "8O76VHCU7YSmartAgriculture;12010126;J4MCD;1623766532"
#define MQTT_PassWord "a962b484079864239148b255281d54372aa66247aa8d6259d11aa6fef650fd5b;hmacsha256"
 
//订阅与发布的主题
#define SET_TOPIC  "$thing/down/property/8O76VHCU7Y/SmartAgriculture"  //订阅
#define POST_TOPIC "$thing/up/property/8O76VHCU7Y/SmartAgriculture"  //发布
 
char mqtt_message[200];//上报数据缓存区
 
int main()
{
   u32 time_cnt=0;
   u32 i;
   u8 key;
   LED_Init();
   BEEP_Init();
   KEY_Init();
   USART1_Init(115200);
   TIMER1_Init(72,20000); //超时时间20ms
   USART2_Init(9600);//串口-蓝牙
   TIMER2_Init(72,20000); //超时时间20ms
   USART3_Init(115200);//串口-WIFI
   TIMER3_Init(72,20000); //超时时间20ms
   USART1_Printf("正在初始化WIFI请稍等.\n");
   if(ESP8266_Init())
   {
      USART1_Printf("ESP8266硬件检测错误.\n");  
   }
   else
   {
      //加密端口
      //USART1_Printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode("OnePlus5T","1126626497","183.230.40.16",8883,1));
      
      //非加密端口
      USART1_Printf("WIFI:%d\n",ESP8266_STA_TCP_Client_Mode("CMCC-Cqvn","99pu58cb","106.55.124.154",1883,1));
  
   }
   
    //2. MQTT协议初始化  
    MQTT_Init(); 
    //3. 连接OneNet服务器        
    while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord))
    {
        USART1_Printf("服务器连接失败,正在重试...\n");
        delay_ms(500);
    }
    USART1_Printf("服务器连接成功.\n");
    
    //3. 订阅主题
    if(MQTT_SubscribeTopic(SET_TOPIC,0,1))
    {
        USART1_Printf("主题订阅失败.\n");
    }
    else
    {
        USART1_Printf("主题订阅成功.\n");
    }        
    
    while(1)
    {    
        key=KEY_Scan(0);
        if(key==2)
        {
            time_cnt=0;
            sprintf(mqtt_message,"{\"method\":\"report\",\"clientToken\":\"123\",\"params\":{\"temperature\":20.23,\"humidity\":50,\"Motor\":1}}");
            MQTT_PublishData(POST_TOPIC,mqtt_message,0);
            USART1_Printf("发送状态1\r\n");
        }
        else if(key==3)
        {
            time_cnt=0;
            sprintf(mqtt_message,"{\"method\":\"report\",\"clientToken\":\"123\",\"params\":{\"temperature\":10.23,\"humidity\":60,\"Motor\":0}}");
            MQTT_PublishData(POST_TOPIC,mqtt_message,0);
            USART1_Printf("发送状态0\r\n");
        }  
 
        if(USART3_RX_FLAG)
        {
            USART3_RX_BUFFER[USART3_RX_CNT]='\0';
            for(i=0;i<USART3_RX_CNT;i++)
            {
                USART1_Printf("%c",USART3_RX_BUFFER[i]);
            }
            USART3_RX_CNT=0;
            USART3_RX_FLAG=0;
        }
 
        //定时发送心跳包,保持连接
        delay_ms(10);
        time_cnt++;
        if(time_cnt==500)
        {
            MQTT_SentHeart();//发送心跳包
            time_cnt=0;
        }
    }
}
 

5.4 mqtt.c

#include "mqtt.h"
 
u8 *mqtt_rxbuf;
u8 *mqtt_txbuf;
u16 mqtt_rxlen;
u16 mqtt_txlen;
u8 _mqtt_txbuf[256];//发送数据缓存区
u8 _mqtt_rxbuf[256];//接收数据缓存区
 
typedef enum
{
    //名字        值           报文流动方向  描述
    M_RESERVED1 =0  ,   //  禁止  保留
    M_CONNECT       ,   //  客户端到服务端 客户端请求连接服务端
    M_CONNACK       ,   //  服务端到客户端 连接报文确认
    M_PUBLISH       ,   //  两个方向都允许 发布消息
    M_PUBACK        ,   //  两个方向都允许 QoS 1消息发布收到确认
    M_PUBREC        ,   //  两个方向都允许 发布收到(保证交付第一步)
    M_PUBREL        ,   //  两个方向都允许 发布释放(保证交付第二步)
    M_PUBCOMP       ,   //  两个方向都允许 QoS 2消息发布完成(保证交互第三步)
    M_SUBSCRIBE     ,   //  客户端到服务端 客户端订阅请求
    M_SUBACK        ,   //  服务端到客户端 订阅请求报文确认
    M_UNSUBSCRIBE   ,   //  客户端到服务端 客户端取消订阅请求
    M_UNSUBACK      ,   //  服务端到客户端 取消订阅报文确认
    M_PINGREQ       ,   //  客户端到服务端 心跳请求
    M_PINGRESP      ,   //  服务端到客户端 心跳响应
    M_DISCONNECT    ,   //  客户端到服务端 客户端断开连接
    M_RESERVED2     ,   //  禁止  保留
}_typdef_mqtt_message;
 
//连接成功服务器回应 20 02 00 00
//客户端主动断开连接 e0 00
const u8 parket_connetAck[] = {0x20,0x02,0x00,0x00};
const u8 parket_disconnet[] = {0xe0,0x00};
const u8 parket_heart[] = {0xc0,0x00};
const u8 parket_heart_reply[] = {0xc0,0x00};
const u8 parket_subAck[] = {0x90,0x03};
 
void MQTT_Init(void)
{
    //缓冲区赋值
    mqtt_rxbuf = _mqtt_rxbuf;
    mqtt_rxlen = sizeof(_mqtt_rxbuf);
    mqtt_txbuf = _mqtt_txbuf;
    mqtt_txlen = sizeof(_mqtt_txbuf);
    memset(mqtt_rxbuf,0,mqtt_rxlen);
    memset(mqtt_txbuf,0,mqtt_txlen);
    
    //无条件先主动断开
    MQTT_Disconnect();
    delay_ms(100);
    MQTT_Disconnect();
    delay_ms(100);
}
 
/*
函数功能: 登录服务器
函数返回值: 0表示成功 1表示失败
*/
u8 MQTT_Connect(char *ClientID,char *Username,char *Password)
{
    u8 i,j;
    int ClientIDLen = strlen(ClientID);
    int UsernameLen = strlen(Username);
    int PasswordLen = strlen(Password);
    int DataLen;
    mqtt_txlen=0;
    //可变报头+Payload  每个字段包含两个字节的长度标识
    DataLen = 10 + (ClientIDLen+2) + (UsernameLen+2) + (PasswordLen+2);
    
    //固定报头
    //控制报文类型
    mqtt_txbuf[mqtt_txlen++] = 0x10;        //MQTT Message Type CONNECT
    //剩余长度(不包括固定头部)
    do
    {
        u8 encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 );
        
    //可变报头
    //协议名
    mqtt_txbuf[mqtt_txlen++] = 0;           // Protocol Name Length MSB    
    mqtt_txbuf[mqtt_txlen++] = 4;           // Protocol Name Length LSB    
    mqtt_txbuf[mqtt_txlen++] = 'M';         // ASCII Code for M    
    mqtt_txbuf[mqtt_txlen++] = 'Q';         // ASCII Code for Q    
    mqtt_txbuf[mqtt_txlen++] = 'T';         // ASCII Code for T    
    mqtt_txbuf[mqtt_txlen++] = 'T';         // ASCII Code for T    
    //协议级别
    mqtt_txbuf[mqtt_txlen++] = 4;               // MQTT Protocol version = 4   对于 3.1.1 版协议,协议级别字段的值是 4(0x04)   
    //连接标志
    mqtt_txbuf[mqtt_txlen++] = 0xc2;            // conn flags 
    mqtt_txbuf[mqtt_txlen++] = 0;               // Keep-alive Time Length MSB    
    mqtt_txbuf[mqtt_txlen++] = 100;         // Keep-alive Time Length LSB  100S心跳包    保活时间
    
    mqtt_txbuf[mqtt_txlen++] = BYTE1(ClientIDLen);// Client ID length MSB    
    mqtt_txbuf[mqtt_txlen++] = BYTE0(ClientIDLen);// Client ID length LSB   
    memcpy(&mqtt_txbuf[mqtt_txlen],ClientID,ClientIDLen);
    mqtt_txlen += ClientIDLen;
    
    if(UsernameLen > 0)
    {   
        mqtt_txbuf[mqtt_txlen++] = BYTE1(UsernameLen);      //username length MSB    
        mqtt_txbuf[mqtt_txlen++] = BYTE0(UsernameLen);      //username length LSB    
        memcpy(&mqtt_txbuf[mqtt_txlen],Username,UsernameLen);
        mqtt_txlen += UsernameLen;
    }
    
    if(PasswordLen > 0)
    {    
        mqtt_txbuf[mqtt_txlen++] = BYTE1(PasswordLen);      //password length MSB    
        mqtt_txbuf[mqtt_txlen++] = BYTE0(PasswordLen);      //password length LSB  
        memcpy(&mqtt_txbuf[mqtt_txlen],Password,PasswordLen);
        mqtt_txlen += PasswordLen; 
    }    
    
  
    memset(mqtt_rxbuf,0,mqtt_rxlen);
    MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
    for(j=0;j<10;j++)
    {
        delay_ms(50);
        if(USART3_RX_FLAG)
        {
            memcpy((char *)mqtt_rxbuf,USART3_RX_BUFFER,USART3_RX_CNT);
            
            //memcpy
           
             for(i=0;i<USART3_RX_CNT;i++)USART1_Printf("%#x ",USART3_RX_BUFFER[i]);
            
            USART3_RX_FLAG=0;
            USART3_RX_CNT=0;
        }
        //CONNECT
        if(mqtt_rxbuf[0]==parket_connetAck[0] && mqtt_rxbuf[1]==parket_connetAck[1]) //连接成功            
        {
            return 0;//连接成功
        }
    }
    
    return 1;
}
 
/*
函数功能: MQTT订阅/取消订阅数据打包函数
函数参数:
    topic       主题   
    qos         消息等级 0:最多分发一次  1: 至少分发一次  2: 仅分发一次
    whether     订阅/取消订阅请求包 (1表示订阅,0表示取消订阅)
返回值: 0表示成功 1表示失败
*/
u8 MQTT_SubscribeTopic(char *topic,u8 qos,u8 whether)
{    
    u8 i,j;
    mqtt_txlen=0;
    int topiclen = strlen(topic);
    
    int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
    //固定报头
    //控制报文类型
    if(whether)mqtt_txbuf[mqtt_txlen++] = 0x82; //消息类型和标志订阅
    else    mqtt_txbuf[mqtt_txlen++] = 0xA2;    //取消订阅
 
    //剩余长度
    do
    {
        u8 encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 ); 
    
    //可变报头
    mqtt_txbuf[mqtt_txlen++] = 0;           //消息标识符 MSB
    mqtt_txbuf[mqtt_txlen++] = 0x0A;        //消息标识符 LSB
    //有效载荷
    mqtt_txbuf[mqtt_txlen++] = BYTE1(topiclen);//主题长度 MSB
    mqtt_txbuf[mqtt_txlen++] = BYTE0(topiclen);//主题长度 LSB   
    memcpy(&mqtt_txbuf[mqtt_txlen],topic,topiclen);
    mqtt_txlen += topiclen;
    
    if(whether)
    {
       mqtt_txbuf[mqtt_txlen++] = qos;//QoS级别
    }
    
    for(i=0;i<10;i++)
    {
        memset(mqtt_rxbuf,0,mqtt_rxlen);
        MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
        for(j=0;j<10;j++)
        {
            delay_ms(50);
            if(USART3_RX_FLAG)
            {
                memcpy((char *)mqtt_rxbuf,(char*)USART3_RX_BUFFER,USART3_RX_CNT);
                USART3_RX_FLAG=0;
                USART3_RX_CNT=0;
            }
            
            if(mqtt_rxbuf[0]==parket_subAck[0] && mqtt_rxbuf[1]==parket_subAck[1]) //订阅成功              
            {
                return 0;//订阅成功
            }
        }
    }
    return 1; //失败
}
 
//MQTT发布数据打包函数
//topic   主题 
//message 消息
//qos     消息等级 
u8 MQTT_PublishData(char *topic, char *message, u8 qos)
{  
    int topicLength = strlen(topic);    
    int messageLength = strlen(message);     
    static u16 id=0;
    int DataLen;
    mqtt_txlen=0;
    //有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
    //QOS为0时没有标识符
    //数据长度             主题名   报文标识符   有效载荷
    if(qos) DataLen = (2+topicLength) + 2 + messageLength;       
    else    DataLen = (2+topicLength) + messageLength;   
 
    //固定报头
    //控制报文类型
    mqtt_txbuf[mqtt_txlen++] = 0x30;    // MQTT Message Type PUBLISH  
 
    //剩余长度
    do
    {
        u8 encodedByte = DataLen % 128;
        DataLen = DataLen / 128;
        // if there are more data to encode, set the top bit of this byte
        if ( DataLen > 0 )
            encodedByte = encodedByte | 128;
        mqtt_txbuf[mqtt_txlen++] = encodedByte;
    }while ( DataLen > 0 ); 
    
    mqtt_txbuf[mqtt_txlen++] = BYTE1(topicLength);//主题长度MSB
    mqtt_txbuf[mqtt_txlen++] = BYTE0(topicLength);//主题长度LSB 
    memcpy(&mqtt_txbuf[mqtt_txlen],topic,topicLength);//拷贝主题
    mqtt_txlen += topicLength;
        
    //报文标识符
    if(qos)
    {
        mqtt_txbuf[mqtt_txlen++] = BYTE1(id);
        mqtt_txbuf[mqtt_txlen++] = BYTE0(id);
        id++;
    }
    memcpy(&mqtt_txbuf[mqtt_txlen],message,messageLength);
    mqtt_txlen += messageLength;
        
    MQTT_SendBuf(mqtt_txbuf,mqtt_txlen);
    return mqtt_txlen;
}
 
void MQTT_SentHeart(void)
{
    MQTT_SendBuf((u8 *)parket_heart,sizeof(parket_heart));
}
 
void MQTT_Disconnect(void)
{
    MQTT_SendBuf((u8 *)parket_disconnet,sizeof(parket_disconnet));
}
 
void MQTT_SendBuf(u8 *buf,u16 len)
{
    USARTx_DataSend(USART3,buf,len);
}   


上一篇:陪玩平台源码开发,如何提升用户的约单体验?


下一篇:深入理解设计模式!六大设计原则的分析与介绍