前言
? ? ? ? 上篇文章我們已經能夠使用串口助手實現esp8266的幾種通信,接下來我們使用單片機控制實現。這篇文章會附帶教程,增加.c和,.h,把串口和定時器放到對應的編號,然后調用初始化就可以使用了。
先講解,然后末尾再放源碼,也可以先看源碼再看講解
一.串口的不定時長接收
? ? ? ? 因為esp8266發送的信息是沒有幀頭和幀位的,所以我們只能用這個方法,串口選定我們對應連接,我這里選擇串口2,串口一用于重定向,打印調試信息(具體可以看我以前串口重定向的教程https://blog.csdn.net/m0_74211379/article/details/145366555?spm=1001.2014.3001.5502)。
? ? ? ? 這里還需要使用一個1ms的定時器中斷,這個方法也可以看我之前的文章https://blog.csdn.net/m0_74211379/article/details/146238875?spm=1001.2014.3001.5502
? ? ? ? 然后開始講用法
uint8_t esp8266_buffer[128],esp8266_buff[1],esp8266_len=0,esp8266_time=0,esp8266_flag=0;
char esp8266_wait[100];
這個是變量定義,全局的
要放在串口中斷
void ESP8266_UART_Handler(void)
{esp8266_buffer[esp8266_len++]=esp8266_buff[0];esp8266_time=0;HAL_UART_Receive_IT(ESP8266_UART,(uint8_t *)esp8266_buff,1);
}void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART2) { ESP8266_UART_Handler();}
}
放在1ms定時器中斷
void ESP8266_Time(void)
{esp8266_time++;if((esp8266_time>10)&&(esp8266_len>0)){esp8266_buffer[esp8266_len]=0;ESP8266_Change();memset(esp8266_buffer, '\0', sizeof(esp8266_buffer));esp8266_len=0;esp8266_time=0; }
}void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance==TIM2){ESP8266_Time();}
}
?
處理獲取一次的數據
void ESP8266_Change()
{printf("%s\r\n",esp8266_buffer);if(contains_OK(esp8266_buffer,esp8266_wait)){esp8266_flag=1;}ESP8266_IPD(esp8266_buffer);
}
二.WiFi名稱密碼還有TCP地址和端口號
const char ESP8266_WIFI_SSID[]="lty";
const char ESP8266_WIFI_PassWord[]="2395832518";
const char ESP8266_WIFI_TCP_IP[]="112.125.89.8";
const char ESP8266_WIFI_TCP_PORT[]="45655";
三.初始化
void ESP8266_Init(void)
{HAL_TIM_Base_Start_IT(ESP8266_TIM);HAL_UART_Receive_IT(ESP8266_UART,(uint8_t *)esp8266_buff,1);
}
四.發送指令函數
這里對需要連接的指令進行封裝,第一個是發送的指令,第二個是接收到什么指令才能繼續
void ESP8266_CMD(uint8_t cmd,uint16_t leng)
{char buffer[100];switch(cmd){case 0:sprintf(buffer,"AT+RST\r\n");sprintf(esp8266_wait,"OK"); break;case 1:sprintf(buffer,"AT\r\n");sprintf(esp8266_wait,"OK");break; case 2:sprintf(buffer,"AT+CWMODE=1\r\n");sprintf(esp8266_wait,"OK");break; case 3:sprintf(buffer,"AT+CWJAP=\"%s\",\"%s\"\r\n",ESP8266_WIFI_SSID,ESP8266_WIFI_PassWord);sprintf(esp8266_wait,"WIFI GOT IP");break; case 4:sprintf(buffer,"AT+CWJAP?\r\n");sprintf(esp8266_wait,"OK");break; case 5:sprintf(buffer,"AT+CIPMUX=0\r\n");sprintf(esp8266_wait,"OK");break; case 6:sprintf(buffer,"AT+CIPSTART=\"TCP\",\"%s\",%s\r\n",ESP8266_WIFI_TCP_IP,ESP8266_WIFI_TCP_PORT);sprintf(esp8266_wait,"OK");break; case 7:sprintf(buffer,"AT+CIPSEND=%d\r\n",leng);sprintf(esp8266_wait,"");break; case 8:sprintf(buffer,"");sprintf(esp8266_wait,"SEND OK");break;}ESP8266_commom(buffer);
}
五.等待發送指令函數
這個函數用于檢測緩沖區是否有接收完成的指令,如果沒有,那么超過200ms就會重新發送一次指令,直到緩沖區有接受到指令的字符串文本
void ESP8266_commom(const char *cmd)
{HAL_UART_Transmit(ESP8266_UART,(uint8_t *)cmd,strlen(cmd),100);esp8266_flag=0;while (esp8266_flag==0){if (esp8266_time>200){HAL_UART_Transmit(ESP8266_UART,(uint8_t *)cmd,strlen(cmd),100);printf("%s time out\r\n",cmd);esp8266_time=0;}}
}int contains_OK(const char *str, const char *substr) {if (strstr(str, substr) != NULL) {return 1; // 包含 }return 0; // 不包含
}
六.連接上TCP
上面的弄好后,進行一次循環就可以連接上
void ESP8266_CONNECT_TO_TCP()
{for(uint8_t i = 0; i < 7; i++){ ESP8266_CMD(i,0);printf("cmd=%d\r\n",i);HAL_Delay(500);}
}
七.往TCP網絡發送消息
也是對原有函數的封裝,先獲取要發送的長度,然后再發送
void ESP8266_send_TCP(const char *data)
{ESP8266_CMD(7,strlen(data));HAL_UART_Transmit(ESP8266_UART,(uint8_t *)data,strlen(data),100);ESP8266_CMD(8,0);
}
八.接收TCP發送來的消息
根據我實測的話,雖然串口顯示第一個是+,但是打印顯示是在后兩個位置,所以我做一個偏移
void ESP8266_IPD(const char *data)
{char *p=data;uint8_t leng=0;char hand[256];if(strlen(data)>6)//printf("data[0]=%c,data[1]=%c,data[2]=%c,data[3]=%c,data[4]=%c\r\n",data[0],data[1],data[2],data[3],data[4]);if (data[2] == '+' && data[3] == 'I' && data[4] == 'P' && data[5] == 'D'){p+=6; while (*p != ':') {leng = leng * 10 + *p - '0';p++; }p++; strncpy(hand, p, leng); hand[leng] = '\0'; printf("Received data: %s\r\n", hand);}
}
九.H文件的修改
把串口和定時器編號修改對九可以了
#ifndef HC_TR_H
#define HC_TR_H#include "main.h"#define ESP8266_UART &huart2
#define ESP8266_TIM &htim2
void ESP8266_Change();
void ESP8266_Time(void);
void ESP8266_Init(void);
void ESP8266_UART_Handler(void);
void ESP8266_CMD(uint8_t cmd,uint16_t leng);
int contains_OK(const char *str, const char *substr);
void ESP8266_commom(const char *cmd);
void ESP8266_CONNECT_TO_TCP();
void ESP8266_send_TCP(const char *data);
void ESP8266_IPD(const char *data);#endif /* HC_TR_H */
十.功能測試及使用教程
首先我們手機開熱點,2.4G,5G的不行
根據網站合宙 TCP/UDP web工具 (luatos.com)
獲取的填入
然后在主函數添加頭文件后,直接寫
初始化加連接WIFI,還有發送兩次數據
ESP8266_Init();
ESP8266_CONNECT_TO_TCP();
ESP8266_send_TCP("hello\r\n");
ESP8266_send_TCP("yes ok\r\n");
然后編譯燒錄
串口助手打印的數據
AT+RSTOK
WIFI DISCONNECTcmd=0
(5d6f877)
SPI Speed : 40MHz
SPI Mode : DOUT
SPI Flash Size & Map: 8Mbit(512KB+512KB)
jump to run user1 @ 1000?rls髇lreadyATtime out
AT
ATbusy p...OKcmd=1
AT+CWMODE=1time out
AT+CWMODE=1
AT+CWMODE=1busy p...OKcmd=2
AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"time out
AT+CWJAP="lty2","2395832518"busy p...
AT+CWJAP="lty2","2395832518"busy p...
AT+CWJAP="lty2","2395832518"busy p...
AT+CWJAP="lty2","2395832518"busy p...
AT+CWJAP
cmd=3
WIFI CONNECTEDAT+CWJAP?time out
AT+CWJAP?busy p...
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...WIFI GOT IPAT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...AT+CWJAP?time out
AT+CWJAP?busy p...OKcmd=4
AT+CIPMUX=0time out
AT+CIPMUX=0
AT+CIPMUX=0busy p...OKcmd=5
AT+CIPSTART="TCP","112.125.89.8",42137time out
AT+CIPSTART="TCP","112.125.89.8",42137
AT+CIPSTART="TCP","112.125.89.8",42137busy p...CONNECTOKcmd=6
AT+CIPSEND=7time out
AT+CIPSEND=7
AT+CIPSEND=7busy p...OK
> Recv 7 bytesSEND OKAT+CIPSEND=8OK
> Recv 8 bytesSEND OK+IPD,7:hello+IPD,8:yes okReceived data: hello+IPD,8:yes ok
然后我們去網站上發送
在串口助手上可以看見成功接收了
十一.完整源碼
esp8266.c
#include "esp8266.h"
#include "tim.h"
#include "gpio.h"
#include "usart.h"
#include "stdio.h"
#include "string.h"// 定義ESP8266相關的緩沖區、長度、時間、標志位和等待響應字符串
uint8_t esp8266_buffer[128],esp8266_buff[1],esp8266_len=0,esp8266_time=0,esp8266_flag=0;
char esp8266_wait[100];// 定義ESP8266連接的WiFi信息和TCP服務器信息
const char ESP8266_WIFI_SSID[]="lty2";
const char ESP8266_WIFI_PassWord[]="2395832518";
const char ESP8266_WIFI_TCP_IP[]="112.125.89.8";
const char ESP8266_WIFI_TCP_PORT[]="42137";// 初始化ESP8266模塊,啟動定時器中斷和UART接收中斷
void ESP8266_Init(void)
{HAL_TIM_Base_Start_IT(ESP8266_TIM);HAL_UART_Receive_IT(ESP8266_UART,(uint8_t *)esp8266_buff,1);
}// 發送ESP8266命令,根據不同的命令編號和數據長度構造命令
void ESP8266_CMD(uint8_t cmd,uint16_t leng)
{char buffer[100];switch(cmd){case 0:sprintf(buffer,"AT+RST\r\n");sprintf(esp8266_wait,"OK"); break;case 1:sprintf(buffer,"AT\r\n");sprintf(esp8266_wait,"OK");break; case 2:sprintf(buffer,"AT+CWMODE=1\r\n");sprintf(esp8266_wait,"OK");break; case 3:sprintf(buffer,"AT+CWJAP=\"%s\",\"%s\"\r\n",ESP8266_WIFI_SSID,ESP8266_WIFI_PassWord);sprintf(esp8266_wait,"WIFI GOT IP");break; case 4:sprintf(buffer,"AT+CWJAP?\r\n");sprintf(esp8266_wait,"OK");break; case 5:sprintf(buffer,"AT+CIPMUX=0\r\n");sprintf(esp8266_wait,"OK");break; case 6:sprintf(buffer,"AT+CIPSTART=\"TCP\",\"%s\",%s\r\n",ESP8266_WIFI_TCP_IP,ESP8266_WIFI_TCP_PORT);sprintf(esp8266_wait,"OK");break; case 7:sprintf(buffer,"AT+CIPSEND=%d\r\n",leng);sprintf(esp8266_wait,"");break; case 8:sprintf(buffer,"");sprintf(esp8266_wait,"SEND OK");break;}ESP8266_commom(buffer);
}// 通用的ESP8266命令發送函數,發送命令并等待響應,超時會重新發送
void ESP8266_commom(const char *cmd)
{HAL_UART_Transmit(ESP8266_UART,(uint8_t *)cmd,strlen(cmd),100);esp8266_flag=0;while (esp8266_flag==0){if (esp8266_time>200){HAL_UART_Transmit(ESP8266_UART,(uint8_t *)cmd,strlen(cmd),100);printf("%s time out\r\n",cmd);esp8266_time=0;}}
}// 連接ESP8266到TCP服務器,依次發送多個初始化命令
void ESP8266_CONNECT_TO_TCP()
{for(uint8_t i = 0; i < 7; i++){ ESP8266_CMD(i,0);printf("cmd=%d\r\n",i);HAL_Delay(500);}
}// 通過ESP8266向TCP服務器發送數據
void ESP8266_send_TCP(const char *data)
{ESP8266_CMD(7,strlen(data));HAL_UART_Transmit(ESP8266_UART,(uint8_t *)data,strlen(data),100);ESP8266_CMD(8,0);
}// 檢查字符串中是否包含指定子字符串
int contains_OK(const char *str, const char *substr) {if (strstr(str, substr) != NULL) {return 1; // 包含指定子字符串}return 0; // 不包含指定子字符串
}// 處理ESP8266接收的數據,檢查是否有期望響應并調用IPD處理函數
void ESP8266_Change()
{printf("%s\r\n",esp8266_buffer);if(contains_OK(esp8266_buffer,esp8266_wait)){esp8266_flag=1;}ESP8266_IPD(esp8266_buffer);
}// 解析ESP8266接收到的IPD數據
void ESP8266_IPD(const char *data)
{char *p=data;uint8_t leng=0;char hand[256];if(strlen(data)>6)//printf("data[0]=%c,data[1]=%c,data[2]=%c,data[3]=%c,data[4]=%c\r\n",data[0],data[1],data[2],data[3],data[4]);if (data[2] == '+' && data[3] == 'I' && data[4] == 'P' && data[5] == 'D'){p+=6; while (*p != ':') {leng = leng * 10 + *p - '0';p++; }p++; strncpy(hand, p, leng); hand[leng] = '\0'; printf("Received data: %s\r\n", hand);}
}// 處理ESP8266 UART接收中斷,將接收到的數據存入緩沖區
void ESP8266_UART_Handler(void)
{esp8266_buffer[esp8266_len++]=esp8266_buff[0];esp8266_time=0;HAL_UART_Receive_IT(ESP8266_UART,(uint8_t *)esp8266_buff,1);
}// 處理ESP8266定時器中斷,超時后處理接收的數據并清空緩沖區
void ESP8266_Time(void)
{esp8266_time++;if((esp8266_time>10)&&(esp8266_len>0)){esp8266_buffer[esp8266_len]=0;ESP8266_Change();memset(esp8266_buffer, '\0', sizeof(esp8266_buffer));esp8266_len=0;esp8266_time=0; }
}// UART接收完成回調函數,當USART2接收完成時調用ESP8266 UART處理函數
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART2) { ESP8266_UART_Handler();}
}// 定時器周期溢出回調函數,當TIM2溢出時調用ESP8266時間處理函數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance==TIM2){ESP8266_Time();}
}
esp8266.h
#ifndef HC_TR_H
#define HC_TR_H#include "main.h"#define ESP8266_UART &huart2
#define ESP8266_TIM &htim2
void ESP8266_Change();
void ESP8266_Time(void);
void ESP8266_Init(void);
void ESP8266_UART_Handler(void);
void ESP8266_CMD(uint8_t cmd,uint16_t leng);
int contains_OK(const char *str, const char *substr);
void ESP8266_commom(const char *cmd);
void ESP8266_CONNECT_TO_TCP();
void ESP8266_send_TCP(const char *data);
void ESP8266_IPD(const char *data);#endif