在《物聯網中你需要了解的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,百度和各類采集站皆不可信,搜索請謹慎鑒別,技術類文章一般都有時效性,本人習慣不定期對自己的博文進行修正和更新,因此請訪問出處以查看本文的最新版本,”
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286480.html
標籤:其他
