MQTT移植
一、移植平台: pahoMQTT,stm32f103,Keil5,要想使用官方封装好MQTT函数 MQTTClient.h ,做如下移植 二、移植过程 1.将在MQTT三个文件中,如下图 找到如下MQTT文件(下图),加入到工程中。 2.打开 MQTTClient.h 头文件,在其中定义如下程序(下图),定时器接口,网络接口,定时器的5个函数,如果已有,屏蔽已有的。
#define int_ValMax 0xffffffff//int stm32 32位 typedef struct Timer Timer;//定时器接口 对外 struct Timer { unsigned int StartVal;//定时初值 unsigned int end_time;//定时时间 };
typedef struct Network Network;//网络接口 对外 struct Network { int my_socket;//当前连接的TCP socket号 //参数要求(c->ipstack, &i, 1, timeout) 返回读到的数据长度 int (mqttread) (Network ipstack, unsigned char* R_buffer, int R_buffer_Lenth, int timeoutMS);//对服务器读 函数需要自己实现 int (mqttwrite) (Network, unsigned char*, int, int);//对服务器写 参数同上 返回写入数据长度 void (disconnect) (Network);//断开TCP连接 };
/* The Timer structure must be defined in the platform specific header,//下面5个函数需要自己实现
- and have the following functions to operate on it. /
extern void TimerInit(Timer);//定时器初始化 extern char TimerIsExpired(Timer*);//返回定时器是否超时 extern void TimerCountdownMS(Timer*, unsigned int);//设置定时时间 单位ms extern void TimerCountdown(Timer*, unsigned int);//设置定时时间 单位s extern int TimerLeftMS(Timer*);//返回剩余定时时间 单位ms
3.我们要自己实现这些接口函数,还有topic处理的回调函数的实现 3.1定时器的接口函数实现 //以下函数可以外部调用 void TimerInit(Timer* timer)//定时器初始化 { timer->StartVal = 0; timer->end_time = 0; } //#define int_ValMax 0xffffffff//int stm32 32位 volatile unsigned int MQTT_sysTime = 0;//全局变量 放定时器中 char TimerIsExpired(Timer* timer)//返回定时器是否超时 { if(MQTT_sysTime >=timer->StartVal) return((MQTT_sysTime -timer->StartVal) >timer->end_time); else return((MQTT_sysTime +int_ValMax -timer->StartVal) >timer->end_time); }
void TimerCountdownMS(Timer* timer, unsigned int time)//设置定时时间 单位ms { timer->StartVal = MQTT_sysTime; timer->end_time = time; }
void TimerCountdown(Timer* timer, unsigned int time)//设置定时时间 单位s { timer->StartVal = MQTT_sysTime; timer->end_time = (time *1000); }
int TimerLeftMS(Timer* timer)//返回剩余定时时间 单位ms { if(MQTT_sysTime >=timer->StartVal) return(timer->end_time -(MQTT_sysTime -timer->StartVal)); else return(timer->end_time -(MQTT_sysTime +int_ValMax -timer->StartVal)); }
将全局变量MQTT_sysTime放入SysTick_Handler中断函数中,SysTick中断时间1毫秒 3.2网络接口函数的实现有,如下图,由于我采用DMA进行1个完整数据帧的接收,所以对数据接收部分源程序进行了重写,源程序是通过不断的查询方式进行接收。 #define MqttNetwork_USART2 1 int myMqttread(Network* Network, unsigned char* R_buffer, int R_buffer_Lenth, int timeoutMS)//对服务器读 函数需要自己实现 { // if(Network->my_socket ==MqttNetwork_USART2) // { // HAL_UART_Receive(&huart2, R_buffer,R_buffer_Lenth,timeoutMS); // return R_buffer_Lenth; // } if(Network->my_socket ==MqttNetwork_USART2)//UART 采用DMA接收1帧完整数据 { if(ESP8266_uartRx_f == 1)//接收到数据 {ESP8266_uartRx_f = 0;return 1; } } return 0; } int myMqttwrite(Network* Network, unsigned char* W_buffer, int W_buffer_Lenth, int timeoutMS)//对服务器写 参数同上 返回写入数据长度 { if(Network->my_socket ==MqttNetwork_USART2) { HAL_UART_Transmit(&huart2,W_buffer,W_buffer_Lenth,timeoutMS); return W_buffer_Lenth; } return 0; }
UART接收部分程序,DMA初始化,如下图 HAL_UART_Receive_DMA(&huart1,UpgradeCode_array,array_size); __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); HAL_UART_Receive_DMA(&huart2,ESP8266_uartRx,array_size); __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
UART接收部分程序,UART中断对接收数据的处理,注意:用的UART2来接收的数据,UART1用做调试,如下图 if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) { Uart_R_flog = 1; HAL_UART_DMAStop(&huart1); HAL_UART_Receive_DMA(&huart1,UpgradeCode_array,array_size); uint16_t lenth = mystrlen_Inc0((char*)UpgradeCode_array,sizeof(UpgradeCode_array)); HAL_UART_Transmit(&huart2,UpgradeCode_array,lenth,50); Clear_array(UpgradeCode_array,lenth);//接收清零 } __HAL_UART_CLEAR_IDLEFLAG(&huart1);
3.3 打开 MQTTClient.c 对MQTT源程序进行重写,如下图 static int readPacket(MQTTClient* c, Timer* timer)//由于UART采用DMA接收1帧完整数据 有冲突 对此函数进行重写 { MQTTHeader header = {0}; int rc = c->ipstack->mqttread(c->ipstack, c->readbuf, 1, TimerLeftMS(timer));//读第一个字节 辨别信息类型 if (rc == 0)//没有接收到数据 goto exit;
header.byte = c->readbuf[0];//取出MQTT固定报头
rc = header.bits.type;//固定报头类型
if (c->keepAliveInterval > 0)
TimerCountdown(&c->last_received, c->keepAliveInterval); // record the fact that we have successfully received a packet
exit: return rc; }
3.4 Topic处理回调函数的实现,如下图三个函数需要Topic处理回调函数,MQTT接收到数据后将会调用相应的Topic处理回调函数
回调函数的实现,如下图 my_u8* pPayload = NULL;//接收Json数据 my_u16 Payload_Lenyh = 0;//接收Json数据长度
extern my_u16 myStrlen_Inc0(my_u8 pStr,my_u16 pStr_Lenth);//数组可以包含0 extern void Clear8266_array(uint8_t array,uint16_t array_lenth);//数组清零 void Device_Upgrade(MessageData MgData)//MQTT设备升级处理回调函数 { printf("\nDeviceUpgrade Message is Rsave\n"); printf(“TopicName:\n”); HAL_UART_Transmit(&huart1,(my_u8)(MgData->topicName->lenstring.data),MgData->topicName->lenstring.len,1000); printf("\nPayload:\n"); pPayload = (my_u8*)(MgData->message->payload); Payload_Lenyh = MgData->message->payloadlen; HAL_UART_Transmit(&huart1,pPayload,Payload_Lenyh,1000); printf("\n"); }
到此,移植过程完成,但本平台编译的时候发生MQTT枚举SUCCESS与系统相冲突,对其进行了全部替换,如下图 三,测试 unsigned char sendbuf[1024] ={0}; //unsigned char readbuf[1024] ={0}; MQTTClient client_Test; Network network; network.mqttread =myMqttread;//指向网络接口函数读 network.mqttwrite = myMqttwrite; network.my_socket = MqttNetwork_USART2; MQTTClientInit(&client_Test,&network,5000,sendbuf, sizeof(sendbuf),ESP8266_uartRx, sizeof(ESP8266_uartRx));//创建一个设备并默认初始化
MQTTSetMessageHandler(&client_Test,"/ota/device/upgrade/a19tZSI5p0d/STM32",Device_Upgrade);//绑定topic与其回调处理函数
MQTTPacket_connectData options =MQTTPacket_connectData_initializer;
options.cleansession = 1; //清理会话
options.clientID.cstring = "STM32|securemode=3,signmethod=hmacsha1|";//客户端标识符
options.username.cstring = "STM32&a19tZSI5p0d";//用户名
options.password.cstring = "C78834B21306AFE988A25B5163C15C328DE84F31";//用户密码
options.MQTTVersion = 3; //MQTT版本
options.willFlag = 0;
options.keepAliveInterval = 60;
if(MQTTConnect(&client_Test, &options) ==MQTT_SUCCESS)//登陆阿里云平台
printf("Device-Connect!\n");
else
printf("Device-DisConnect!\n");
Clear8266_array(ESP8266_uartRx,sizeof(ESP8266_uartRx));//接收清零
MQTTYield(&client_Test,10000);//定时一段时间 用于接收服务器发来的数据 在此等待10秒用于接收数据
if(pPayload != NULL)//接收到Json数据
{
测试结果:成功登陆阿里云平台,并接收到返回数据 附,MQTT移植包,包含了移植的所有文件:https://download.csdn.net/download/mymycsdn321/20664289
|