前言
代码中的运行流程已经在
https://cloud.tencent.com/developer/article/1602667
文章的最后做了介绍
其实咱学东西最主要的是学以致用,应用到自己的工程项目里面
这节着重说一下如何把源码中的MQTT底层包移植到用户自己的工程
这节代码只是讲解详细流程,并不可以使用
这节代码使用的底层:
https://cloud.tencent.com/developer/article/1602665
这节代码是在底层又做了一层封装
主要是加入了数据缓存发送,便于处理消息等级为1和2的消息
准备一个空的工程
1.注:请自行准备一个已经可以实现TCP连接的工程
由于用的网络芯片不一样,连接方式不一样,本人就在工程中假设连接了TCP
2.把以下文件放入自己的工程
添加一个文件夹
添加文件
添加MQTT文件中的所有文件
添加头文件路径
包含mqtt.h 编译下
错误原因
这是两个把数据发送给网络模块的函数
这两个函数需要根据自己的修改
用户需要把数据发送给网络模块的函数放到此处
就是模块作为TCP客户端,把TCP客户端发送数据给TCP服务器数据的函数放在这里
大家不要有任何的疑问,让您怎么做就怎么做就可以!!!!
在1ms定时器中放入以下函数
#include "mqtt.h"
mqtt_time_data(&mymqtt);
连接MQTT
1.用一些数组存储连接MQTT的信息
注册连接和断开函数
代码语言:javascript复制unsigned char MQTTid[50] = "123456789";//ClientID
unsigned char MQTTUserName[20] = "yang";//用户名
unsigned char MQTTPassWord[50] = "11223344";//密码
unsigned char MQTTkeepAlive = 30;//心跳包时间
unsigned char MQTTPublishTopic[30]="1111";//存储MQTT发布的主题
unsigned char MQTTWillMessage[50] = "{"data":"status","status":"offline"}";//遗嘱消息
unsigned char MQTTWillQos = 0;//消息等级
unsigned char MQTTWillRetained = 1;//是否需要服务器记录
代码语言:javascript复制/**连接上MQTT回调函数
* @brief 连接上MQTT回调函数
* @param None
* @retval None
* @warning None
* @example
**/
void MqttConnect()
{
}
/**MQTT断开连接回调
* @brief MQTT断开连接回调
* @param None
* @retval None
* @warning None
* @example
**/
void MqttDisConnect()
{
mqtt_init(&mymqtt);
}
代码语言:javascript复制 mqtt_init(&mymqtt);
mqtt_connect_reg(&mymqtt,MqttConnect);//注册连接回调函数
mqtt_disconnect_reg(&mymqtt,MqttDisConnect);//注册断开连接回调函数
注:如果不想使用遗嘱,可自行屏蔽
2.实现具体的连接
用户先用自己的模块用TCP连接上TCP服务器(MQTT服务器)
然后发送连接MQTT指令
最后判断下是否连接成功
代码语言:javascript复制int len;//获取数据长度
unsigned char *str;//打包的数据首地址
char ConnectedMqttFlag;//1:连接上MQTT 0:未连接
代码语言:javascript复制if(ConnectedMqttFlag == 0){//没有连接上MQTT
//用户先自行控制模块以TCP方式连接上MQTT服务器
//发送连接MQTT的指令
mymqtt.mqtt_connect_info.client_id = MQTTid;//client_id
mymqtt.mqtt_connect_info.keepalive = MQTTkeepAlive;//心跳包时间
mymqtt.mqtt_connect_info.username = MQTTUserName;//用户名
mymqtt.mqtt_connect_info.password = MQTTPassWord;///密码
mymqtt.mqtt_connect_info.will_topic = MQTTPublishTopic;//遗嘱发布的主题
mymqtt.mqtt_connect_info.will_message = MQTTWillMessage;//遗嘱的消息
mymqtt.mqtt_connect_info.will_qos = MQTTWillQos;//遗嘱的消息等级
mymqtt.mqtt_connect_info.will_retain = MQTTWillRetained;//是否需要服务器保留消息
mymqtt.mqtt_connect_info.clean_session = 1;//清除连接信息
len = mqtt_connect(&mymqtt,&str);//打包连接信息
//打包的数据首地址 str 数据长度:len
if(len>0){
UsartOutStr(str,len);//发送MQTT协议数据给网络模块,其实就是TCP客户端发送数据给服务器(请根据自己的替换)
}
//发送完连接指令以后,MQTT服务器会返回连接信息,请按照下面程序处理信息
//判断模块是否连接上,假设模块返回的数据存入了Usart1ReadBuff数组
if(mqtt_connect_ack(Usart1ReadBuff)==0)//连接上了MQTT
{
ConnectedMqttFlag = 1;//连接上MQTT
if(mymqtt.connectCb){//调用连接回调函数
mymqtt.connectCb();
}
}
}
3.如果断开连接,需要让模块重新连接MQTT,请在断开连接回调函数中
代码语言:javascript复制ConnectedMqttFlag=0;//断开连接以后,重新配置模块连接
接收处理通信过程中的各种数据
1.后期的通信都是利用数据缓存实现,请先添加以下程序
代码语言:javascript复制 if(ConnectedMqttFlag)//配置模块成功(模块连接了MQTT)
{
mqtt_send_function(&mymqtt);//提取发送缓存的MQTT协议
mqtt_keep_alive(&mymqtt);//处理发送心跳包
//网络模块接收到了数据
if(1)//此处请根据自己的网络模块自行修改
{
//假设网络接收到的数据:Usart1ReadBuff 数据长度:Usart1ReadCntCopy
mqtt_read_function(&mymqtt,Usart1ReadBuff,Usart1ReadCntCopy);
}
}
2.注册接收数据函数
代码语言:javascript复制/**
* @brief MQTT接收数据回调
* @param topic:主题
* @param topic_len:主题长度
* @param data:接收的数据
* @param lengh:接收的数据长度
* @retval None
* @warning None
* @example
**/
void MqttReceive(const char* topic, uint32_t topic_len,const char *data, uint32_t lengh)//接收到数据回调
{
}
代码语言:javascript复制mqtt_received_reg(&mymqtt,MqttReceive);//注册接收数据回调函数
订阅主题
1.一般可以在连接成功回调函数里面订阅主题
当然只要连接上以后,可以在程序的任何地方执行订阅函数
代码语言:javascript复制unsigned char MQTTSubscribeTopic[30]="2222";//存储MQTT订阅的主题
代码语言:javascript复制/**订阅主题成功
* @brief 订阅主题成功
* @param None
* @retval None
* @warning None
* @example
**/
void subscribedCb(int pdata)
{
}
/**订阅主题失败
* @brief 订阅主题失败
* @param None
* @retval None
* @warning None
* @example
**/
void failsubscribedCb(int pdata)
{
}
代码语言:javascript复制mqtt_subscribe(&mymqtt, MQTTSubscribeTopic,0,subscribedCb,failsubscribedCb);//订阅主题
订阅的消息等级支持0,1,2
mqtt_subscribe(&mymqtt, MQTTSubscribeTopic,0,subscribedCb,failsubscribedCb);
mqtt_subscribe(&mymqtt, MQTTSubscribeTopic,1,subscribedCb,failsubscribedCb);
mqtt_subscribe(&mymqtt, MQTTSubscribeTopic,2,subscribedCb,failsubscribedCb);
发布消息
1.列如:把接收的消息发布出去
代码语言:javascript复制/**
* @brief 发布成功
* @param None
* @retval None
* @warning None
* @example
**/
void PublishedCb()
{
}
/**
* @brief MQTT接收数据回调
* @param topic:主题
* @param topic_len:主题长度
* @param data:接收的数据
* @param lengh:接收的数据长度
* @retval None
* @warning None
* @example
**/
void MqttReceive(const char* topic, uint32_t topic_len,const char *data, uint32_t lengh)//接收到数据回调
{
mqtt_publish(&mymqtt,MQTTPublishTopic,(unsigned char*)data,topic_len, 0, 1,PublishedCb);//发布消息
}
提示:只有发布的消息等级是1/2的时候,服务器才会有应答信息
所以只有1/2的时候才会进入发布成功回调函数
深入源码
1.要想知道底层如何封装处理的,必须知道的基础知识
https://cloud.tencent.com/developer/article/1583664
2.订阅主题,发布消息,发送心跳包的数据都存储在了缓存里面
程序中默认每隔20Ms提取一次
注:特殊的地方会超过20Ms
提取数据
这个函数一开始让大家放在了1Ms定时器里面
深入源码之-订阅主题0
1.订阅主题
2.提取到数据,发给服务器
有些需要等待服务器返回的地方都会启用超时检测
超时时间归零以后,才会把后面缓存的其它数据发送给服务器
3.判断服务器的返回
深入源码之-发布消息,消息等级0
因为消息等级0不需要任何的应答,所以组合的消息直接用TCP发送出去
深入源码之-发布消息,消息等级1
深入源码之-发布消息,消息等级2
深入源码之-接收消息,消息等级1
深入源码之-接收消息,消息等级2
深入源码之-心跳包
1.心跳包内部自动处理,用户只需要调用以下函数
mqtt_keep_alive(&mymqtt);
2.处理思路
到达发送心跳包时间,把心跳包数据插入缓存,
如果超过5S服务器没有返回应答,则再发一次
如果超过5S还是没有返回应答,则执行mqtt断开连接函数
返回了心跳应答,清零所有关于心跳包的变量,重新开始计数
注:鉴于如果MQTT服务器一直处于发送其余数据的状态,
那么服务器会延迟发送过来心跳包应答
所以,只要MQTT服务器返回其它数据,就清零心跳包变量