这次我要讲一下如何使用STM32进行驱动esp8266模块去连接TCP服务器进行通信。后面我打算写一篇使用MQTTfx连接阿里云的教程,然后再写一篇使用STM32进行驱动esp8266使用mqtt连接阿里云,并实现智能云APP控制单片机,单片机控制智能云APP的教程。
一:硬件准备
准备好STM32f407单片机和esp8266模块。esp8266模块如图:
拍的不太好,请见谅。
二:软件准备
准备串口调试助手软件和网络调试助手软件。串口调试助手是使用来观察运行情况,将连接情况展示出来。网络调试助手是用来以你电脑来搭一个小型服务器以供esp8266连接。(这两个软件我都放在我的资源里面去了,自己去下就可以了)
串口调试助手软件如图:
网络调试助手软件如图:
三:准备esp8266所需指令
硬件和软件准备好后,我们还需要准备esp8266所需相关命令,和具体步骤。
1:首先我们要确认esp8266模块有没有坏,使用测试指令AT来测试模块是否能正常响应
2:使用AT+CWMODE=1指令设置ESP8266,进入station模式,使其能链接电脑、手机、 路由器热点
3: 使用 AT+CWJAP_CUR="账号名","密码"指令来链接电脑、手机、 路由器热点。(指令样式为:AT+CWJAP_CUR="cx","12345678")不要用奇怪字符和中文。
4:使用 AT+CIPMUX=0指令设置ESP8266连接服务器的模式为单链接模式(同一个时刻只能链接一个服务器)
5:使用 AT+CIPMODE=1指令设置ESP8266的数据传输模式-透传模式(发送数据的时候不需要指定数据长度)
6:使用 AT+CIPSTART="服务器类型",“服务器IP”,服务器端口号 指令设置ESP8266链接服务器服务器类型: TCP/UDP/SSL(指令样式为:AT+CIPSTART="TCP",“192.168.53.12”,8080)端口号那里没有双引号。到这里你就已经连上服务器了。但需要与服务器通信还需要几步。
7:使用 AT+CIPSEND指令设置ESP8266进入数据透传模式(使用这个指令后,你在发消息就都会传个服务器)
8: 如果不需要与服务器通信,可以使用+++指令(该指令不需要发送换行)退出数据透传模式
以上除了+++指令都需要换行后发送才有效。所有指令如果执行成功,esp8266都会传送OK回来,出了+++。
还有一些其他的指令如:AT+CWQAP(断开热点),AT+CIPCLOSE(断开服务器)。
步骤图如图:
四:写代码
现在我们已经把所有要准备的东西都准备好了,只差写代码了。首先整理一下思路,我们要初始化esp8266,所以我们要写指令给esp8266,且我们还要检查esp8266是否返回了OK。要完成这些,我们首先要能向esp8266接发信息,所以我们需要一个串口来接发信息,这里我选择的是USART3串口。然后我们先来写基础的接收和发送信息函数。
发送信息函数:
我们发送信息基本都是发字符串,所以我们要写一个发送字符串函数。代码如下:
void USART_Sendstr(USART_TypeDef* USARTx,char *str)
{
char *i=str;
while((*i)!='\0')
{
USART_SendData( USARTx, *i++);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE)==RESET);
}
}
因为字符串的末尾是 \0 符,所以我们可以检测*i是否为\0来确认是否传输完毕,再通过while循环和*i++来实现指针 i 指向的位置不断向字符串末尾移动,直到字符串全部传输完毕。(USART_SendData函数是库自带的传输数据函数,只能一字节一字节的传输)
接收函数:
我们需要在接受到信息的时候再去处理,我们不可能时时刻刻都去检查信息有没有传过来,所以我需要使用串口中断函数进行接收函数。代码如图:
void USART3_IRQHandler(void)
{
uint8_t ch=0;
//判断一下,触发中断服务函数的中断类型是否为接受中断
if(USART_GetITStatus(USART3,USART_IT_RXNE))
{
ch=USART_ReceiveData(USART3);//接收数据
usart3_recev_data[usart3_recev_data_index++]=ch;//将接收到的数据放在数组里
if((usart3_recev_data_index>=USART3_RX_SIZE-2))
{
usart3_recev_flag=1;//接收信息标志位
usart3_recev_data_index=0;//接收信息数组的下标
}
//清空中断触发
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
}
}
usart3_recev_data[]数组是专门存放串口3接收的数据的,usart3_recev_flag是是否接收完数据的的标志位,接收完数据或数组满了后置一。 usart3_recev_data_index是数组的下标,它代表你接收的数据大小为几字节。USART3_RX_SIZE是我宏定义的数组大小。
写指令函数:
我们可以通过发送字符串函数将指令发给esp8266,但光发送可不行,我们还需要检查esp8266有没有返回数据,返回的数据有没有OK字符。
既然我们知道只要esp8266执行成功,那它返回的数据里就一定会有OK,那我们就可以通过strstr函数来检测数据里有没有OK字符。可以通过while(strstr(usart3_recev_data,"OK")==NULL)来一直不断检测数据,检测到再执行后面的代码。但我们也不能一直卡在这里,如果esp8266执行失败了,返回的数据没有OK怎么办?我们可以while((strstr(usart3_recev_data,"OK")==NULL)&&timeout){timeout--;delayms(1)}。这样就算esp8266执行失败了,返回的数据没有OK,我们也可以在一定时间后跳出循环。具体代码如下:
uint32_t ESP8266_send_cmd(char *cmd,char *rtdata,int timeout)
{
//清空
ESP8266_rxbuf_clear();
//串口3发送数据
USART_Sendstr(USART3,cmd);
/* 查找接收数据包中的字符串 */
while((strstr((const char*)usart3_recev_data,(const char*)rtdata)==NULL)&&timeout)//等待串口接收完毕或超时退出
{
timeout--;
delay_ms(1);
}
printf("返回%s\n",usart3_recev_data);
if(timeout)
{
return 0;//找到匹配的响应字符串, 返回0
}
else
{
printf("超时\n");
return 1;//没有找到匹配的字符串
}
}
ESP8266_rxbuf_clear函数是清空数组用的,在代码里我们通过判断timeout的值是否大于0来判断是时间耗尽退出循环还是找到匹配的字符退出循环。
配置函数:
有了上面三个函数,我们就可以正式写配置esp8266的函数了。代码如下:
void ESP8266_rxbuf_clear(void)
{
memset((void*)usart3_recev_data,0,4096);
usart3_recev_data_index=0;
usart3_recev_flag=0;
}
uint32_t ESP8266_send_cmd(char *cmd,char *rtdata,int timeout)
{
//清空
ESP8266_rxbuf_clear();
//串口3发送数据
USART_Sendstr(USART3,cmd);
/* 查找接收数据包中的字符串 */
while((strstr((const char*)usart3_recev_data,(const char*)rtdata)==NULL)&&timeout)//等待串口接收完毕或超时退出
{
timeout--;
delay_ms(1);
}
printf("返回%s\n",usart3_recev_data);
if(timeout)
{
return 0;//找到匹配的响应字符串, 返回0
}
else
{
printf("超时\n");
return 1;//没有找到匹配的字符串
}
}
uint32_t ESP8266_test(void)
{
if(ESP8266_send_cmd("AT\r\n","OK",3000)==0)
{
printf("测试正常\n");
return 0;
}
else
{
printf("测试失败\n");
return 1;
}
}
uint32_t ESP8266_set_station(void)
{
if(ESP8266_send_cmd("AT+CWMODE=1\r\n","OK",3000)==0)
{
printf("设置station成功\n");
return 0;
}
else
{
printf("设置station失败\n");
return 1;
}
}
uint32_t ESP8266_connect_wife(char *name,char *password)
{
char buf[100]={0};
sprintf(buf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",name,password);
if(ESP8266_send_cmd(buf,"OK",10000)==0)
{
printf("连接热点成功\n");
return 0;
}
else
{
printf("连接热点失败\n");
return 1;
}
}
uint32_t ESP8266_disconnect_wife(void)
{
if(ESP8266_send_cmd("AT+CWQAP\r\n","OK",3000)==0)
{
printf("断开热点成功\n");
return 0;
}
else
{
printf("断开热点失败\n");
return 1;
}
}
uint32_t ESP8266_set_connectmode(char *s)
{
char buf[100]={0};
sprintf(buf,"AT+CIPMUX=%s\r\n",s);
if(ESP8266_send_cmd(buf,"OK",3000)==0)
{
printf("设置连接模式成功\n");
return 0;
}
else
{
printf("设置连接模式失败\n");
return 1;
}
}
uint32_t ESP8266_set_transparentmode(char *s)
{
char buf[100]={0};
sprintf(buf,"AT+CIPMODE=%s\r\n",s);
if(ESP8266_send_cmd(buf,"OK",3000)==0)
{
printf("设置传输模式成功\n");
return 0;
}
else
{
printf("设置传输模式失败\n");
return 1;
}
}
uint32_t ESP8266_connect_server(char *ip,char *port)
{
char buf[256]={0};
sprintf(buf,"AT+CIPSTART=\"TCP\",\"%s\",%s\r\n",ip,port);
if(ESP8266_send_cmd(buf,"OK",10000)==0)
{
printf("连接服务器成功\n");
return 0;
}
else
{
printf("连接服务器失败\n");
return 1;
}
}
uint32_t ESP8266_disconnect_server(void)
{
if(ESP8266_send_cmd("AT+CIPCLOSE\r\n","OK",4000)==0)
{
printf("断开服务器成功\n");
return 0;
}
else
{
printf("断开服务器失败\n");
return 1;
}
}
uint32_t ESP8266_set_transparent_transmission(void)
{
if(ESP8266_send_cmd("AT+CIPSEND\r\n","OK",3000)==0)
{
printf("设置透传模式成功\n");
return 0;
}
else
{
printf("设置透传模式失败\n");
return 1;
}
}
uint32_t ESP8266_exit_transparent_transmission(void)
{ USART_Sendstr(USART3,"+++");
delay_ms(1000);
ESP8266_send_cmd("+++","+++",100);
delay_ms(2000);
return 0;
}
uint32_t esp8266_reset(void)
{
if(ESP8266_send_cmd("AT+RST\r\n","OK",10000)==0)
{
printf("复位成功\n");
return 0;
}
else
{
printf("复位失败\n");
return 1;
}
}
/* 回显打开或关闭 */
int32_t esp8266_enable_echo(uint32_t b)
{
if(b)
{if(ESP8266_send_cmd("ATE1\r\n","OK",5000)!=0)
{printf("回显打开失败\n");
}}
else
{if(ESP8266_send_cmd("ATE0\r\n","OK",5000)!=0)
{ printf("回显关闭失败\n");
}}
return 0;
}
这些配置函数都只是调用写指令函数,换一换输入的命令字符而已。
如何判断数据接收完毕:
我们可以初始化一个定时器,使它200ms触发一次定时中断,在中断函数内对 usart3_recev_data_index进行比较,如果usart3_recev_data_index>0且与上一次中断函数的它一样大,那么就说明接收了数据且已经接收完毕。如果不一样大,则可能其还在接收数据,如果=0,则代表且没接收数据。代码如下:
void TIM6_DAC_IRQHandler()
{
IWDG_ReloadCounter();
static uint32_t usart3_recev_data_preindex=0;
if(TIM_GetITStatus(TIM6,TIM_IT_Update))//判断是否是TIM6的中断
{
if(usart3_recev_data_index==0)
{
}
else if(usart3_recev_data_index==usart3_recev_data_preindex)
{
usart3_recev_flag=1;
}
usart3_recev_data_preindex=usart3_recev_data_index;
//清除中断标志位
TIM_ClearITPendingBit(TIM6,TIM_IT_Update);
}
}
main函数:
代码如下:
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
USART3_Init(115200);
USART1_PA9_10(9600);
ESP8266_exit_transparent_transmission();
delay_ms(2000);
esp8266_reset();
delay_ms(2000);
esp8266_enable_echo(0);
delay_s(2);
ESP8266_set_station();
ESP8266_connect_wife("Redmicx","123456qwe");
delay_ms(2000);
ESP8266_set_transparentmode("1");
//连接服务器
delay_s(3);
ESP8266_connect_server(192.168.184.1,"8080");
delay_ms(2000);
ESP8266_set_transparent_transmission();
TIM6_Init();
while(1)
{
if((usart3_recev_flag)
{
ESP8266_rxbuf_clear();
GPIO_ToggleBits( GPIOE, GPIO_Pin_14);
}
delay_ms(100);
}
五:结果视频
WeChat_20250223225924
六:一些注意点
如果你连不上电脑的热点,那你有可能是因为电脑的网络波段不对,调成2.4GHZ就行了。如果服务器连不上,你可以看一下电脑的ip地址,网络调试助手的地址调成你电脑的ip地址就行了。这些信息去网络和Internet里面的设置里看。发命令没反应的话,可以把esp8266拔了重插,或者看一下你发的命令有带换行吗。还有可能是因为你没退出透传模式,stm32下载程序的时候并没有完全断电,所以esp8266可能没有被重置。
还有什么不懂得话,可以在评论区问我。如果文章哪里写的不太好,不太对可以指正我一下,毕竟我才写博客没多久,欢迎指正。