前言
上篇文章我们已经能够使用串口助手实现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
然后开始讲用法
cpp
uint8_t esp8266_buffer[128],esp8266_buff[1],esp8266_len=0,esp8266_time=0,esp8266_flag=0;
char esp8266_wait[100];
这个是变量定义,全局的
要放在串口中断
cpp
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定时器中断
cpp
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();
}
}
处理获取一次的数据
cpp
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地址和端口号
cpp
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";
三.初始化
cpp
void ESP8266_Init(void)
{
HAL_TIM_Base_Start_IT(ESP8266_TIM);
HAL_UART_Receive_IT(ESP8266_UART,(uint8_t *)esp8266_buff,1);
}
四.发送指令函数
这里对需要连接的指令进行封装,第一个是发送的指令,第二个是接收到什么指令才能继续
cpp
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就会重新发送一次指令,直到缓冲区有接受到指令的字符串文本
cpp
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
上面的弄好后,进行一次循环就可以连接上
cpp
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网络发送消息
也是对原有函数的封装,先获取要发送的长度,然后再发送
cpp
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发送来的消息
根据我实测的话,虽然串口显示第一个是+,但是打印显示是在后两个位置,所以我做一个偏移
cpp
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文件的修改
把串口和定时器编号修改对九可以了
cpp
#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,还有发送两次数据
cpp
ESP8266_Init();
ESP8266_CONNECT_TO_TCP();
ESP8266_send_TCP("hello\r\n");
ESP8266_send_TCP("yes ok\r\n");
然后编译烧录
串口助手打印的数据
cpp
AT+RST
OK
WIFI DISCONNECT
cmd=0
(5d6f877)
SPI Speed : 40MHz
SPI Mode : DOUT
SPI Flash Size & Map: 8Mbit(512KB+512KB)
jump to run user1 @ 1000
?rls髇l
ready
AT
time out
AT
AT
busy p...
OK
cmd=1
AT+CWMODE=1
time out
AT+CWMODE=1
AT+CWMODE=1
busy p...
OK
cmd=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 CONNECTED
AT+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 IP
AT+CWJAP?
time out
AT+CWJAP?
busy p...
AT+CWJAP?
time out
AT+CWJAP?
busy p...
AT+CWJAP?
time out
AT+CWJAP?
busy p...
OK
cmd=4
AT+CIPMUX=0
time out
AT+CIPMUX=0
AT+CIPMUX=0
busy p...
OK
cmd=5
AT+CIPSTART="TCP","112.125.89.8",42137
time out
AT+CIPSTART="TCP","112.125.89.8",42137
AT+CIPSTART="TCP","112.125.89.8",42137
busy p...
CONNECT
OK
cmd=6
AT+CIPSEND=7
time out
AT+CIPSEND=7
AT+CIPSEND=7
busy p...
OK
>
Recv 7 bytes
SEND OK
AT+CIPSEND=8
OK
>
Recv 8 bytes
SEND OK
+IPD,7:hello
+IPD,8:yes ok
Received data: hello
+IPD,8:yes ok

然后我们去网站上发送


在串口助手上可以看见成功接收了
十一.完整源码
esp8266.c
cpp
#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
cpp
#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