Bootstrap

STM32驱动esp8266wife模块连接TCP服务器(极其详细的教程)

     这次我要讲一下如何使用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可能没有被重置。

        还有什么不懂得话,可以在评论区问我。如果文章哪里写的不太好,不太对可以指正我一下,毕竟我才写博客没多久,欢迎指正。

            


     
           

  
                

             

  

             

      

;