一、移植平台:
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个函数需要自己实现
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