简介
这里主要讲的是进行MQTT移植,对于阿里云物联网的连接的开发,可以使用阿里云提供的代码,也可以使用标准的MQTT协议代码。
参考了网上很多的帖子,经过多方尝试,发现这两种方法都不可行,就采用了自己的方法,先用wireshark抓网络包,然后让esp8266模拟这种网络包。
MQTT.fx接入物联网平台
这部分可以参考阿里云物联网文档,即
阿里云物联网平台 > 最佳实践 > 设备接入 > 使用MQTT.fx接入物联网平台
通过wireshark监控计算机的端口1883,抓取到的MQTT数据帧有如下类型:
- Connect Command
- Connect Ack
- Subscribe Request
- Subscribe Ack
- Publish Message
- Publish Ack
- Ping Request
- Ping Respose
其中,本地发给阿里云数据帧1,则阿里云返回数据帧2;本地发给阿里云数据帧3,则阿里云返回数据帧4;本地发给阿里云5,则阿里云返回数据帧6(前提是QoS=1);本地发给阿里云数据帧7,则阿里云返回数据帧8。当阿里云向下传输数据帧5时,本地不做应答。
参考MQTT协议版本3,在抓取到的数据的基础上,仿写了类似的数据帧实现了相应的功能。
生成MQTT数据帧
在程序中定义了如下几个函数,完成connect、subscribe、publish和ping功能。
u8 iot_connect(void);
u8 iot_subscribe(void);
u8 iot_sendheart(void);
u8 iot_publishdata(float temp, float humi);
u16 cGetDataPUBLISH(u8 *buff,u8 qos,u8 *topic,u8 *msg);
注意事项
在mqtt.c文件中,用到了malloc.h文件,因为在给一个buf申请内存之前,要初始化MCU的存储空间。也就是keil工程要添加sram.c、sram.h、XMRAM.h和XMRAM.lib文件,在main函数中要有:
my_mem_init(SRAMIN); //初始化内部内存池
相关程序源码
啥也别说了,直接上代码。
mqtt.c
#include "mqtt.h"
#include <stdlib.h>
#include "string.h"
#include "malloc.h"
#include "usart3.h"
#include "usart.h"
#include "delay.h"
//连接成功服务器回应 20 02 00 00
//客户端主动断开连接 e0 00
const u8 mqtt_connetAck[] = {0x20,0x02,0x00,0x00};
const u8 mqtt_pubAck[] = {0x40,0x02,0x00,0x01};
const u8 mqtt_disconnet[] = {0xe0,0x00};
const u8 mqtt_heart[] = {0xc0,0x00};
const u8 mqtt_heartAck[] = {0xd0,0x00};
const u8 mqtt_subAck[] = {0x90,0x03};
uint16_t buflen=200;
unsigned char buf[200];
char ClientID[128];
u8 ClientID_len;
char Username[128];
u8 Username_len;
char Password[128];
u8 Password_len;
u8 iot_connect(void)
{
uint32_t buflen;
u16 i;
u8* buf;
buf=mymalloc(SRAMIN,200); //申请200字节内存
GetDataConnet(buf);
buflen = buf[1]+2;
// 连接两次不成功则返回0
u8 cnt=2; // 尝试发送次数
u8 wait=30; // 等待时间
while(cnt--)
{
USART3_RX_STA=0;
HAL_UART_Transmit(&UART1_Handler, buf, buflen,1000); // for DEBUG
HAL_UART_Transmit(&UART3_Handler, buf, buflen,1000);
while(wait--)
{
if(USART3_RX_STA&0x8000) // 收到应答信号,uart1转发该应答信号
{
if(USART3_RX_BUF[0]==mqtt_connetAck[0] && USART3_RX_BUF[1]==mqtt_connetAck[1]) //连接成功
{
myfree(SRAMIN,buf); //释放内存
USART3_RX_STA=0;
printf("\r\nconnect successful!\r\n");
return 1; // 连接成功
}
}
delay_ms(100);
}
}
myfree(SRAMIN,buf); //释放内存
USART3_RX_STA=0;
return 0;
}
u8 iot_subscribe(void)
{
uint32_t buflen;
u16 i;
u8* buf;
buf=mymalloc(SRAMIN,200); //申请200字节内存
GetDataSUBSCRIBE(buf,S_TOPIC_NAME,2,0);
buflen = buf[1]+2;
// 连接两次不成功则返回0
u8 cnt=2; // 尝试发送次数
u8 wait=30; // 等待时间
while(cnt--)
{
USART3_RX_STA=0;
HAL_UART_Transmit(&UART1_Handler, buf, buflen,1000); // For DEBUG
HAL_UART_Transmit(&UART3_Handler, buf, buflen,1000);
while(wait--)
{
if(USART3_RX_STA&0x8000) // 收到应答信号,uart1转发该应答信号
{
if(USART3_RX_BUF[0]==mqtt_subAck[0] && USART3_RX_BUF[1]==mqtt_subAck[1]) //连接成功
{
myfree(SRAMIN,buf); //释放内存
USART3_RX_STA=0;
printf("\r\nsubscribe successful!\r\n");
return 1; // 连接成功
}
}
delay_ms(100);
}
}
myfree(SRAMIN,buf); //释放内存
USART3_RX_STA=0;
return 0;
}
u8 iot_sendheart(void)
{
uint32_t buflen;
u16 i;
u8* buf;
buf=mymalloc(SRAMIN,20); //申请20字节内存
// GetDataPINGREQ(buf);
buf[0]=mqtt_heart[0];
buf[1]=mqtt_heart[1];
buflen = 2;
// 连接两次不成功则返回0
u8 cnt=2; // 尝试发送次数
u16 wait=300; // 等待时间
while(cnt--)
{
USART3_RX_STA=0;
HAL_UART_Transmit(&UART1_Handler, buf, buflen,1000); // For DEBUG
HAL_UART_Transmit(&UART3_Handler, buf, buflen,1000);
while(wait--)
{
if(USART3_RX_STA&0x8000) // 收到应答信号,uart1转发该应答信号
{
if(USART3_RX_BUF[0]==mqtt_heartAck[0] && USART3_RX_BUF[1]==mqtt_heartAck[1]) //连接成功
{
myfree(SRAMIN,buf); //释放内存
USART3_RX_STA=0;
printf("\r\nsendheart successful! (cnt,wait)=%d,%d\r\n", cnt,wait);
return 1; // 连接成功
}
}
delay_ms(10);
}
}
myfree(SRAMIN,buf); //释放内存
USART3_RX_STA=0;
printf("\r\nsendheart failed! (cnt,wait)=%d,%d\r\n", cnt,wait);
return 0;
}
u8 iot_publishdata(float temp, float humi)
{
uint32_t buflen;
u16 i;
u8* buf;
buf=mymalloc(SRAMIN,200); //申请200字节内存
u8* msg;
msg=mymalloc(SRAMIN,200); //申请200字节内存
sprintf((char*)msg,"{\"method\":\"thing.event.property.post\",\"id\":\"477655683\",\"params\":{\"temperature\":%.1f,\"humidity\":%.1f}}", temp, humi);
buflen = cGetDataPUBLISH(buf,1,P_TOPIC_NAME,msg);
printf("publishdata: topiclen=%d,msglen=%d\r\n",strlen(P_TOPIC_NAME),strlen(msg));
// 连接两次不成功则返回0
int cnt=2; // 尝试发送次数
int wait=300; // 等待时间
while(cnt>0)
{
USART3_RX_STA=0;
HAL_UART_Transmit(&UART1_Handler, buf, buflen,1000); // For DEBUG
HAL_UART_Transmit(&UART3_Handler, buf, buflen,1000);
while(wait>0)
{
if(USART3_RX_STA&0x8000) // 收到应答信号,uart1转发该应答信号
{
if(USART3_RX_BUF[0]==mqtt_pubAck[0] && USART3_RX_BUF[1]==mqtt_pubAck[1]) //发送成功
{
myfree(SRAMIN,msg); //释放内存
myfree(SRAMIN,buf); //释放内存
USART3_RX_STA=0;
printf("\r\npublish data successful! (cnt,wait)=%d,%d\r\n", cnt,wait);
return 1; // 连接成功
}
}
delay_ms(10);
wait--;
}
cnt--;
}
myfree(SRAMIN,msg); //释放内存
myfree(SRAMIN,buf); //释放内存
USART3_RX_STA=0;
printf("\r\npublish data failed! (cnt,wait)=%d,%d\r\n", cnt,wait);
return 0;
}
unsigned char GetDataFixedHead(unsigned char MesType,unsigned char DupFlag,unsigned char QosLevel,unsigned char Retain)
{
unsigned char dat = 0;
dat = (MesType & 0x0f) << 4;
dat |= (DupFlag & 0x01) << 3;
dat |= (QosLevel & 0x03) << 1;
dat |= (Retain & 0x01);
return dat;
}
void GetDataConnet(unsigned char *buff)//获取连接的数据包正确连接返回20 02 00 00
{
unsigned int i,len,lennum = 0;
unsigned char *msg;
buff[0] = GetDataFixedHead(MQTT_TypeCONNECT,0,0,0);
buff[2] = 0x00;
buff[3] = 0x04;
buff[4] = 'M';
buff[5] = 'Q';
buff[6] = 'T';
buff[7] = 'T';
buff[8] = 0x04;//协议级别 Protocol Level
buff[9] = 0 | (MQTT_StaCleanSession << 1) | (MQTT_StaWillFlag << 1)
| (MQTT_StaWillQoS << 3) | (MQTT_StaWillRetain << 5)
| (MQTT_StaPasswordFlag << 6) |(MQTT_StaUserNameFlag << 7);//连接标志
buff[10] = MQTT_KeepAlive >> 8;
buff[11] = MQTT_KeepAlive;
len = strlen(MQTT_ClientIdentifier);
buff[12] = len >> 8;
buff[13] = len;
msg = MQTT_ClientIdentifier;
for(i = 0;i<len;i++)
{
buff[14+i] = msg[i];
}
lennum += len;
if(MQTT_StaWillFlag)
{
len = strlen(MQTT_WillTopic);
buff[13 + lennum + 1] = len >> 8;
buff[13 + lennum + 2] = len;
lennum += 2;
msg = MQTT_WillTopic;
for(i = 0;i<len;i++)
{
buff[14+lennum+i] = msg[i];
}
lennum += len;
len = strlen(MQTT_WillMessage);
buff[12] = len >> 8;
buff[13] = len;
lennum += 2;
msg = MQTT_WillMessage;
for(i = 0;i<len;i++)
{
buff[14+lennum+i] = msg[i];
}
lennum += len;
}
if(MQTT_StaUserNameFlag)
{
len = strlen(MQTT_UserName);
buff[13 + lennum + 1] = len >> 8;
buff[13 + lennum + 2] = len;
lennum += 2;
msg = MQTT_UserName;
for(i = 0;i<len;i++)
{
buff[14+lennum+i] = msg[i];
}
lennum += len;
}
if(MQTT_StaPasswordFlag)
{
len = strlen(MQTT_Password);
buff[13 + lennum + 1] = len >> 8;
buff[13 + lennum + 2] = len;
lennum += 2;
msg = MQTT_Password;
for(i = 0;i<len;i++)
{
buff[14+lennum+i] = msg[i];
}
lennum += len;
}
buff[1] = 13 + lennum - 1;
}
void GetDataDisConnet(unsigned char *buff)//获取断开连接的数据包
{
buff[0] = 0xe0;
buff[1] = 0;
}
void GetDataPINGREQ(unsigned char *buff)//心跳请求的数据包成功返回d0 00
{
buff[0] = 0xc0;
buff[1] = 0;
}
//订阅主题的数据包
//Num:主题序号
//RequestedQoS:服务质量要求0,1或2
//成功返回90 0x 00 Num RequestedQoS
void GetDataSUBSCRIBE(unsigned char *buff,const char *dat,unsigned int Num,unsigned char RequestedQoS)
{
unsigned int i,len = 0,lennum = 0;
buff[0] = 0x82;
len = strlen(dat);
buff[2] = Num>>8;
buff[3] = Num;
buff[4] = len>>8;
buff[5] = len;
for(i = 0;i<len;i++)
{
buff[6+i] = dat[i];
}
lennum = len;
buff[6 + lennum ] = RequestedQoS;
buff[1] = lennum + 5;
}
void GetDataPUBLISH(unsigned char *buff,unsigned char dup, unsigned char qos,unsigned char retain,const char *topic ,const char *msg)//获取发布消息的数据包
{
unsigned int i,len=0,lennum=0;
buff[0] = GetDataFixedHead(MQTT_TypePUBLISH,dup,qos,retain);
len = strlen(topic);
buff[2] = len>>8;
buff[3] = len;
for(i = 0;i<len;i++)
{
buff[4+i] = topic[i];
}
lennum = len;
len = strlen(msg);
for(i = 0;i<len;i++)
{
buff[4+i+lennum] = msg[i];
}
lennum += len;
buff[1] = lennum + 2;
}
u16 cGetDataPUBLISH(u8 *buff,u8 qos,u8 *topic,u8 *msg)//获取发布消息的数据包
{
int topiclen = strlen(topic);
int msglen = strlen(msg);
u16 framelen;
u16 len=0;
static u16 id=0; // message ID
if(qos) framelen = (2+topiclen) + 2 + msglen;
else framelen = (2+topiclen) + msglen;
// qos=1 则mqtt服务器有ACK信号返回
buff[len++] = GetDataFixedHead(MQTT_TypePUBLISH,0,qos,0); // MQTT Message Type PUBLISH
// Rest length
do
{
u8 encodedByte = framelen % 128;
framelen = framelen / 128;
// if there are more data to encode, set the top bit of this byte, see MQTT-3.1.1 manual
if(framelen > 0)
encodedByte = encodedByte | 128;
buff[len++] = encodedByte;
}while(framelen > 0);
buff[len++] = topiclen / 256; // MSB
buff[len++] = topiclen % 256; // LSB
memcpy(&buff[len], topic, topiclen);
len += topiclen;
if(qos)
{
buff[len++] = id / 256;
buff[len++] = id % 256;
id++;
}
memcpy(&buff[len], msg, msglen);
len += msglen;
return len;
}
mqtt.h
#ifndef __MQTT_H
#define __MQTT_H
#include "sys.h"
#include <string.h>
#include "transport.h"
#define IOT_DOMAIN_NAME "productkey.iot-as-mqtt.cn-shanghai.aliyuncs.com"
#define IOT_PORTNUM "1883"
#define PRODUCTKEY "<productkey>"
#define PRODUCTKEY_LEN strlen(PRODUCTKEY)
#define DEVICENAME "<devicename>"
#define DEVICENAME_LEN strlen(DEVICENAME)
#define DEVICESECRE "<devicesecre>"
#define DEVICESECRE_LEN strlen(DEVICESECRE)
#define S_TOPIC_NAME "/sys/<productkey>/<devicename>/thing/service/property/set"
#define P_TOPIC_NAME "/sys/<productkey>/<devicename>/thing/event/property/post"
extern uint16_t buflen;
extern unsigned char buf[200];
u8 iot_connect(void);
u8 iot_subscribe(void);
u8 iot_sendheart(void);
u8 iot_publishdata(float temp, float humi);
u16 cGetDataPUBLISH(u8 *buff,u8 qos,u8 *topic,u8 *msg);
///////////////////////////////////////////////////////////////////////////////////
#define MQTT_TypeCONNECT 1//请求连接
#define MQTT_TypeCONNACK 2//请求应答
#define MQTT_TypePUBLISH 3//发布消息
#define MQTT_TypePUBACK 4//发布应答
#define MQTT_TypePUBREC 5//发布已接收,保证传递1
#define MQTT_TypePUBREL 6//发布释放,保证传递2
#define MQTT_TypePUBCOMP 7//发布完成,保证传递3
#define MQTT_TypeSUBSCRIBE 8//订阅请求
#define MQTT_TypeSUBACK 9//订阅应答
#define MQTT_TypeUNSUBSCRIBE 10//取消订阅
#define MQTT_TypeUNSUBACK 11//取消订阅应答
#define MQTT_TypePINGREQ 12//ping请求
#define MQTT_TypePINGRESP 13//ping响应
#define MQTT_TypeDISCONNECT 14//断开连接
#define MQTT_StaCleanSession 1 //清理会话
#define MQTT_StaWillFlag 0 //遗嘱标志
#define MQTT_StaWillQoS 0 //遗嘱QoS连接标志的第4和第3位。
#define MQTT_StaWillRetain 0 //遗嘱保留
#define MQTT_StaUserNameFlag 1 //用户名标志 User Name Flag
#define MQTT_StaPasswordFlag 1 //密码标志 Password Flag
#define MQTT_KeepAlive 60
#define MQTT_ClientIdentifier "<devicename>|securemode=3,signmethod=hmacsha1|" //客户端标识符 Client Identifier
#define MQTT_WillTopic "" //遗嘱主题 Will Topic
#define MQTT_WillMessage "" //遗嘱消息 Will Message
#define MQTT_UserName "<devicename>&<productkey>" //用户名 User Name
#define MQTT_Password "<password>" //密码 Password
unsigned char GetDataFixedHead(unsigned char MesType,unsigned char DupFlag,unsigned char QosLevel,unsigned char Retain);
void GetDataPUBLISH(unsigned char *buff,unsigned char dup, unsigned char qos,unsigned char retain,const char *topic ,const char *msg);//获取发布消息的数据包
void GetDataSUBSCRIBE(unsigned char *buff,const char *dat,unsigned int Num,unsigned char RequestedQoS);//订阅主题的数据包 Num:主题序号 RequestedQoS:服务质量要求0,1或2
void GetDataDisConnet(unsigned char *buff);//获取断开连接的数据包
void GetDataConnet(unsigned char *buff);//获取连接的数据包正确连接返回20 02 00 00
void GetDataPINGREQ(unsigned char *buff);//心跳请求的数据包成功返回d0 00
#endif