在《物联网中你需要了解的ESP8266最基本的知识!》和《每谈及物联网都难以离开的MQTT协议!》中,我们使用了模拟的方式让ESP8266通过AT指令加入云服务器和MQTT接入云服务器。但是我们实际使用时中却不能模拟的,不可能每一步都得自己调,这样的话毫无疑问会非常的麻烦。那么我们必须把这些指令和操作写在程序中,让其自动、智能地运行。
以下是我们将AT指令和MQTT协议在单片机封装的C语言程序的过程。当我们需要实现某个功能的时候,引用函数即可。
STM32源码下载地址:https://github.com/Liangyz2019/IoT-LED-STM32-
本例程采用了USART2与ESP8266相连,并使用了HAL库编写。
AT指令封装
//usart2发送和接收数组
uint8_t usart2_txbuf[256];
uint8_t usart2_rxbuf[512];
uint8_t usart2_rxone[1];
uint8_t usart2_rxcounter;
//串口2发送一个字节
static void USART2_SendOneByte(uint8_t val)
{
((UART_HandleTypeDef *)&huart2)->Instance->DR = ((uint16_t)val & (uint16_t)0x01FF);
while((((UART_HandleTypeDef *)&huart2)->Instance->SR&0X40)==0);//等待发送完成
}
//向ESP8266发送定长数据
void ESP8266_ATSendBuf(uint8_t* buf,uint16_t len)
{
memset(usart2_rxbuf,0, 256);
//每次发送前将接收串口接收总数置0,为了接收
usart2_rxcounter = 0;
//定长发送
HAL_UART_Transmit(&huart2,(uint8_t *)buf,len,0xFFFF);
}
//向ESP8266发送字符串
void ESP8266_ATSendString(char* str)
{
memset(usart2_rxbuf,0, 256);
//每次发送前将接收串口接收总数置0,为了接收
usart2_rxcounter = 0;
//发送方法1
while(*str) USART2_SendOneByte(*str++);
//发送法法2
// HAL_UART_Transmit(&huart2,(uint8_t *)str,strlen(str),0xFFFF);
}
//退出透传
void ESP8266_ExitUnvarnishedTrans(void)
{
ESP8266_ATSendString("+++");HAL_Delay(50);
ESP8266_ATSendString("+++");HAL_Delay(50);
}
//查找字符串中是否包含另一个字符串
uint8_t FindStr(char* dest,char* src,uint16_t retry_nms)
{
retry_nms/=10; //超时时间
while(strstr(dest,src)==0 && --retry_nms)//等待串口接收完毕或超时退出
{
HAL_Delay(10);
}
if(retry_nms) return 1;
return 0;
}
uint8_t ESP8266_Check(void)
{
uint8_t check_cnt=5;
while(check_cnt--)
{
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf)); //清空接收缓冲
ESP8266_ATSendString("AT\r\n"); //发送AT握手指令
if(FindStr((char*)usart2_rxbuf,"OK",200) != 0)
{
return 1;
}
}
return 0;
}
void ESP8266_Restore(void)
{
ESP8266_ExitUnvarnishedTrans(); //退出透传
HAL_Delay(500);
ESP8266_ATSendString("AT+RESTORE\r\n"); //恢复出厂
}
//开启透传模式
static uint8_t ESP8266_OpenTransmission(void)
{
//设置透传模式
uint8_t cnt=2;
while(cnt--)
{
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf));
ESP8266_ATSendString("AT+CIPMODE=1\r\n");
if(FindStr((char*)usart2_rxbuf,"OK",200)!=0)
{
return 1;
}
}
return 0;
}
uint8_t DisconnectServer(void)
{
uint8_t cnt;
ESP8266_ExitUnvarnishedTrans(); //退出透传
HAL_Delay(500);
while(cnt--)
{
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf)); //清空接收缓冲
ESP8266_ATSendString("AT+CIPCLOSE\r\n");//关闭链接
if(FindStr((char*)usart2_rxbuf,"CLOSED",200)!=0)//操作成功,和服务器成功断开
{
break;
}
}
if(cnt) return 1;
return 0;
}
ESP8266初始化
uint8_t ESP8266_Init(void)
{
//清空发送和接收数组
memset(usart2_txbuf,0,sizeof(usart2_txbuf));
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf));
ESP8266_ExitUnvarnishedTrans(); //退出透传
HAL_Delay(500);
ESP8266_ATSendString("AT+RST\r\n");
HAL_Delay(800);
if(ESP8266_Check()==0) //使用AT指令检查ESP8266是否存在
{
return 0;
}
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf)); //清空接收缓冲
ESP8266_ATSendString("ATE0\r\n"); //关闭回显
if(FindStr((char*)usart2_rxbuf,"OK",500)==0) //设置不成功
{
return 0;
}
return 1; //设置成功
}
ESP8266连接热点
uint8_t ESP8266_ConnectAP(char* ssid,char* pswd)
{
uint8_t cnt=5;
while(cnt--)
{
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf));
ESP8266_ATSendString("AT+CWMODE=1\r\n"); //设置为STATION模式
if(FindStr((char*)usart2_rxbuf,"OK",200) != 0)
{
break;
}
}
if(cnt == 0)
return 0;
cnt=2;
while(cnt--)
{
memset(usart2_txbuf,0,sizeof(usart2_txbuf));//清空发送缓冲
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf));//清空接收缓冲
sprintf((char*)usart2_txbuf,"AT+CWJAP=\"%s\",\"%s\"\r\n",ssid,pswd);//连接目标AP
ESP8266_ATSendString((char*)usart2_txbuf);
if(FindStr((char*)usart2_rxbuf,"OK",8000)!=0) //连接成功且分配到IP
{
return 1;
}
}
return 0;
}
ESP8266连接阿里云服务器
uint8_t ESP8266_ConnectServer(char* mode,char* ip,uint16_t port)
{
uint8_t cnt;
ESP8266_ExitUnvarnishedTrans(); //多次连接需退出透传
HAL_Delay(500);
//连接服务器
cnt=2;
while(cnt--)
{
memset(usart2_txbuf,0,sizeof(usart2_txbuf));//清空发送缓冲
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf));//清空接收缓冲
sprintf((char*)usart2_txbuf,"AT+CIPSTART=\"%s\",\"%s\",%d\r\n",mode,ip,port);
ESP8266_ATSendString((char*)usart2_txbuf);
if(FindStr((char*)usart2_rxbuf,"CONNECT",8000) !=0 )
{
break;
}
}
if(cnt == 0)
return 0;
//设置透传模式
if(ESP8266_OpenTransmission()==0) return 0;
//开启发送状态
cnt=2;
while(cnt--)
{
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf)); //清空接收缓冲
ESP8266_ATSendString("AT+CIPSEND\r\n");//开始处于透传发送状态
if(FindStr((char*)usart2_rxbuf,">",200)!=0)
{
return 1;
}
}
return 0;
}
MQTT协议的封装
#include "esp8266_mqtt.h"
#include "esp8266_at.h"
//连接成功服务器回应 20 02 00 00
//客户端主动断开连接 e0 00
const uint8_t parket_connetAck[] = {0x20,0x02,0x00,0x00};
const uint8_t parket_disconnet[] = {0xe0,0x00};
const uint8_t parket_heart[] = {0xc0,0x00};
const uint8_t parket_heart_reply[] = {0xc0,0x00};
const uint8_t parket_subAck[] = {0x90,0x03};
volatile uint16_t MQTT_TxLen;
extern uint8_t usart2_txbuf[256];
extern uint8_t usart2_rxbuf[512];
extern uint8_t usart2_rxone[1];
extern uint8_t usart2_rxcounter;
//MQTT发送数据
void MQTT_SendBuf(uint8_t *buf,uint16_t len)
{
ESP8266_ATSendBuf(buf,len);
}
//发送心跳包
void MQTT_SentHeart(void)
{
MQTT_SendBuf((uint8_t *)parket_heart,sizeof(parket_heart));
}
//MQTT无条件断开
void MQTT_Disconnect()
{
MQTT_SendBuf((uint8_t *)parket_disconnet,sizeof(parket_disconnet));
}
//MQTT初始化
void MQTT_Init(uint8_t *prx,uint16_t rxlen,uint8_t *ptx,uint16_t txlen)
{
memset(usart2_txbuf,0,sizeof(usart2_txbuf)); //清空发送缓冲
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf)); //清空接收缓冲
//无条件先主动断开
MQTT_Disconnect();HAL_Delay(100);
MQTT_Disconnect();HAL_Delay(100);
}
ESP8266阿里云MQTT登陆
//MQTT连接服务器的打包函数
uint8_t MQTT_Connect(char *ClientID,char *Username,char *Password)
{
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);
//固定报头
//控制报文类型
usart2_txbuf[MQTT_TxLen++] = 0x10; //MQTT Message Type CONNECT
//剩余长度(不包括固定头部)
do
{
uint8_t 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;
usart2_txbuf[MQTT_TxLen++] = encodedByte;
}while ( DataLen > 0 );
//可变报头
//协议名
usart2_txbuf[MQTT_TxLen++] = 0; // Protocol Name Length MSB
usart2_txbuf[MQTT_TxLen++] = 4; // Protocol Name Length LSB
usart2_txbuf[MQTT_TxLen++] = 'M'; // ASCII Code for M
usart2_txbuf[MQTT_TxLen++] = 'Q'; // ASCII Code for Q
usart2_txbuf[MQTT_TxLen++] = 'T'; // ASCII Code for T
usart2_txbuf[MQTT_TxLen++] = 'T'; // ASCII Code for T
//协议级别
usart2_txbuf[MQTT_TxLen++] = 4; // MQTT Protocol version = 4
//连接标志
usart2_txbuf[MQTT_TxLen++] = 0xc2; // conn flags
usart2_txbuf[MQTT_TxLen++] = 0; // Keep-alive Time Length MSB
usart2_txbuf[MQTT_TxLen++] = 60; // Keep-alive Time Length LSB 60S心跳包
usart2_txbuf[MQTT_TxLen++] = BYTE1(ClientIDLen);// Client ID length MSB
usart2_txbuf[MQTT_TxLen++] = BYTE0(ClientIDLen);// Client ID length LSB
memcpy(&usart2_txbuf[MQTT_TxLen],ClientID,ClientIDLen);
MQTT_TxLen += ClientIDLen;
if(UsernameLen > 0)
{
usart2_txbuf[MQTT_TxLen++] = BYTE1(UsernameLen); //username length MSB
usart2_txbuf[MQTT_TxLen++] = BYTE0(UsernameLen); //username length LSB
memcpy(&usart2_txbuf[MQTT_TxLen],Username,UsernameLen);
MQTT_TxLen += UsernameLen;
}
if(PasswordLen > 0)
{
usart2_txbuf[MQTT_TxLen++] = BYTE1(PasswordLen); //password length MSB
usart2_txbuf[MQTT_TxLen++] = BYTE0(PasswordLen); //password length LSB
memcpy(&usart2_txbuf[MQTT_TxLen],Password,PasswordLen);
MQTT_TxLen += PasswordLen;
}
uint8_t cnt=2;
uint8_t wait;
while(cnt--)
{
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf));
MQTT_SendBuf(usart2_txbuf,MQTT_TxLen);
wait=30;//等待3s时间
while(wait--)
{
//CONNECT
if(usart2_rxbuf[0]==parket_connetAck[0] && usart2_rxbuf[1]==parket_connetAck[1]) //连接成功
{
return 1;//连接成功
}
HAL_Delay(100);
}
}
return 0;
}
ESP8266阿里云MQTT订阅主题
//MQTT订阅/取消订阅数据打包函数
//topic 主题
//qos 消息等级
//whether 订阅/取消订阅请求包
uint8_t MQTT_SubscribeTopic(char *topic,uint8_t qos,uint8_t whether)
{
MQTT_TxLen=0;
int topiclen = strlen(topic);
int DataLen = 2 + (topiclen+2) + (whether?1:0);//可变报头的长度(2字节)加上有效载荷的长度
//固定报头
//控制报文类型
if(whether) usart2_txbuf[MQTT_TxLen++] = 0x82; //消息类型和标志订阅
else usart2_txbuf[MQTT_TxLen++] = 0xA2; //取消订阅
//剩余长度
do
{
uint8_t 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;
usart2_txbuf[MQTT_TxLen++] = encodedByte;
}while ( DataLen > 0 );
//可变报头
usart2_txbuf[MQTT_TxLen++] = 0; //消息标识符 MSB
usart2_txbuf[MQTT_TxLen++] = 0x01; //消息标识符 LSB
//有效载荷
usart2_txbuf[MQTT_TxLen++] = BYTE1(topiclen);//主题长度 MSB
usart2_txbuf[MQTT_TxLen++] = BYTE0(topiclen);//主题长度 LSB
memcpy(&usart2_txbuf[MQTT_TxLen],topic,topiclen);
MQTT_TxLen += topiclen;
if(whether)
{
usart2_txbuf[MQTT_TxLen++] = qos;//QoS级别
}
uint8_t cnt=2;
uint8_t wait;
while(cnt--)
{
memset(usart2_rxbuf,0,sizeof(usart2_rxbuf));
MQTT_SendBuf(usart2_txbuf,MQTT_TxLen);
wait=30;//等待3s时间
while(wait--)
{
if(usart2_rxbuf[0]==parket_subAck[0] && usart2_rxbuf[1]==parket_subAck[1]) //订阅成功
{
return 1;//订阅成功
}
HAL_Delay(100);
}
}
if(cnt) return 1; //订阅成功
return 0;
}
ESP8266阿里云MQTT发布主题
//MQTT发布数据打包函数
//topic 主题
//message 消息
//qos 消息等级
uint8_t MQTT_PublishData(char *topic, char *message, uint8_t qos)
{
int topicLength = strlen(topic);
int messageLength = strlen(message);
static uint16_t id=0;
int DataLen;
MQTT_TxLen=0;
//有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度
//QOS为0时没有标识符
//数据长度 主题名 报文标识符 有效载荷
if(qos) DataLen = (2+topicLength) + 2 + messageLength;
else DataLen = (2+topicLength) + messageLength;
//固定报头
//控制报文类型
usart2_txbuf[MQTT_TxLen++] = 0x30; // MQTT Message Type PUBLISH
//剩余长度
do
{
uint8_t 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;
usart2_txbuf[MQTT_TxLen++] = encodedByte;
}while ( DataLen > 0 );
usart2_txbuf[MQTT_TxLen++] = BYTE1(topicLength);//主题长度MSB
usart2_txbuf[MQTT_TxLen++] = BYTE0(topicLength);//主题长度LSB
memcpy(&usart2_txbuf[MQTT_TxLen],topic,topicLength);//拷贝主题
MQTT_TxLen += topicLength;
//报文标识符
if(qos)
{
usart2_txbuf[MQTT_TxLen++] = BYTE1(id);
usart2_txbuf[MQTT_TxLen++] = BYTE0(id);
id++;
}
memcpy(&usart2_txbuf[MQTT_TxLen],message,messageLength);
MQTT_TxLen += messageLength;
MQTT_SendBuf(usart2_txbuf,MQTT_TxLen);
return MQTT_TxLen;
}
“本站所有文章均为原创,欢迎转载,请注明文章出处:https://blog.csdn.net/kasami_/article/details/117388439。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。”
版权声明:本文为CSDN博主「mini梁翊洲MAX」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kasami_/article/details/117388439
暂无评论