STM32玩转物联网实战篇:2.ESP8266 WIFI模块TCP通信示例详解

1、准备开发板

开发板功能区分布图

在这里插入图片描述

开发板俯视图

在这里插入图片描述

2、ESP8266简介

    ESP8266 WIFI模块内置TCP/IP网络协议,模块支持三种网络模式,AP、STA和AP+STA模式,AP模式:模块作为WIFI热点,等待其他设备的连接,进行局域网的通信,STA模式:模块作为客户端通过路由器连接外网,和服务器进行通信,AP+STA模式:两种模式共存,可以进行任意切换。另外,模块支持AT指令操作,使用PC端或者单片机TTL串口配置简单的指令即可实现,这也是选择这款模块的一个原因。

什么是AT指令?

在这里插入图片描述

ESP8266开发常用的AT指令

基础AT指令
命令 描述
AT 测试AT启动
AT+RST 重启模块
AT+GMR 查看版本信息
WIFI功能AT指令
命令 描述
AT+CWMODE 选择WIFI应用模式
AT+CWJAP 加入AP
AT+CWLAP 列出当前可用AP
AT+CWQAP 退出与AP的连接
AT+CWSAP 设置AP模式下的参数
AT+CWLIF 查看已接入设备的IP
TCP/IP 工具箱 AT 指令
命令 描述
AT+CIPSTATUS 获得连接状态
AT+CIPSTART 建立TCP连接或注册UDP
AT+CIPSEND 发送数据
AT+CIPCLOSE 关闭TCP或UDP
AT+CIFSR 获取本地IP地址
AT+CIPMUX 启动多连接
AT+CIPSERVER 配置为服务器
AT+CIPMODE 设置模块传输模式
AT+CIPSTO 设置服务器超时时间

3、在MDK中编写代码

    在写代码之前我们要了解的编程思维:高内聚、低耦合,简单来说就是:把一些东西放在一边,另一些东西放在另一边,然后划定边界,就是一种“分类”的思想,一个模块实现一个功能,功能之间相互联系。并且我们可以用I/O模型来管理我们的设备,在计算机系统中I/O就是输入(Input)和输出(Output)的意思,用来控制计算机的数据流动包括程序、硬件。

ESP8266基础函数
函数 描述
ESP8266_IO_Delay ESP8266模块延时
ESP8266_IO_Reset 对ESP8266进行硬件重启
ESP8266_IO_Send ESP8266模块所在的串口发送数据
ESP8266_IO_WaitRecive 对ESP8266是否接收到数据进行判断
ESP8266_IO_ClearRecive 清除ESP8266模块所在的串口的缓存
ESP8266功能函数
ESP8266_SendCmd 对ESP8266发送命令,并将ESP8266返回的数据进行检索
ESP8266_SendData ESP8266向服务器发送数据
ESP8266_Net_Mode_Choose 选择ESP8266模块的工作模式
ESP8266_JoinAP ESP8266模块连接外部WiFi
ESP8266_CIPAP 设置模块的 AP IP
ESP8266_BuildAP WF-ESP8266模块创建WiFi热点
ESP8266_Enable_MultipleId ESP8266模块启动多连接
ESP8266_Link_Server ESP8266模块连接外部服务器
ESP8266_StartOrShutServer ESP8266模块开启或关闭服务器模式
ESP8266_Inquire_ApIp 获取 F-ESP8266 的 AP IP
ESP8266_UnvarnishSend 配置ESP8266模块进入透传发送
ESP8266_Get_LinkStatus ESP8266 的连接状态,较适合单端口时使用
ESP8266_GetIPD 对ESP8266返回的数据进行检索,并将处理好的数据返回

在ESP8266.h中编写以下代码

#ifndef __ESP8266_H_
#define __ESP8266_H_

#include "sys.h"

#define ESP8266_USART   LPUART1
#define USART_DEBUG 	USART1


#ifndef ESP8266_OK
#define ESP8266_OK                                              0
#endif

#ifndef ESP8266_NOK
#define ESP8266_NOK                                             1
#endif


#define  ESP8266_RETTYPE		unsigned char 

//ESP8266数据类型定义
typedef enum
{
	STA,
  	AP,
  	STA_AP  
}ENUM_Net_ModeTypeDef;

//ESP8266网络模式定义
typedef enum{
	 enumTCP,
	 enumUDP,
} ENUM_NetPro_TypeDef;

//ESP8266多连接ID定义
typedef enum{
	Multiple_ID_0 = 0,
	Multiple_ID_1 = 1,
	Multiple_ID_2 = 2,
	Multiple_ID_3 = 3,
	Multiple_ID_4 = 4,
	Single_ID_0 = 5,
} ENUM_ID_NO_TypeDef;

//ESP8266 AP加密方式定义
typedef enum{
	OPEN = 0,
	WEP = 1,
	WPA_PSK = 2,
	WPA2_PSK = 3,
	WPA_WPA2_PSK = 4,
} ENUM_AP_PsdMode_TypeDef;


#define User_ESP8266_BulitApSsid	  "PRECHIN"	  //要建立的热点的名称
#define User_ESP8266_BulitApEcn	  	  OPEN            //要建立的热点的加密方式
#define User_ESP8266_BulitApPwd  	  "prechin"      //要建立的热点的密钥

#define User_ESP8266_TCPServer_IP	  "192.168.1.119"	  //服务器开启的IP地址
#define User_ESP8266_TCPServer_PORT	  "8080"	  //服务器开启的端口

#define User_ESP8266_TCPServer_OverTime	  "1800"	  //服务器超时时间(单位:秒)

#define TCPAGREEMENT 	enumTCP	//TCP通信协议

typedef struct _NET_DEVICE_INFO
{

	USART_INFO_STRUCT* netIOInfo;	//串口句柄
	
	char staName[20];				//缓存SSID
	char staPass[20];				//缓存PassWord
	char staIPAddress[20];	//缓存IP
	char staPort[20];				//缓存Port
	
	unsigned short err : 2; 		//错误类型
	unsigned short netWork : 1;	//网络连接状态
	unsigned short initStep : 4;	//初始化步骤
	unsigned short dataType : 4;	//设定数据返回类型--16种
	unsigned short reverse : 6;		//预留

} NET_DEVICE_INFO;

extern NET_DEVICE_INFO netDeviceInfo;

void ESP8266_IO_Delay(uint32_t time);
void ESP8266_IO_Send(uint8_t *str,uint32_t strlen );
ESP8266_RETTYPE ESP8266_IO_WaitRecive(void);
void ESP8266_IO_ClearRecive(void);
void ESP8266_IO_Reset(void);

ESP8266_RETTYPE ESP8266_SendCmd(char *cmd, char *res1,char *res2,uint32_t timeOut);
ESP8266_RETTYPE ESP8266_SendData(FunctionalState enumEnUnvarnishTx,unsigned char *data, unsigned short len,ENUM_ID_NO_TypeDef ucId);
ESP8266_RETTYPE ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode);
ESP8266_RETTYPE ESP8266_JoinAP( char * pSSID, char * pPassWord );
ESP8266_RETTYPE ESP8266_CIPAP ( char * pApIp );
ESP8266_RETTYPE ESP8266_BuildAP ( char * pSSID, char * pPassWord, ENUM_AP_PsdMode_TypeDef enunPsdMode );
ESP8266_RETTYPE ESP8266_Enable_MultipleId (FunctionalState enumEnUnvarnishTx );
ESP8266_RETTYPE ESP8266_Link_Server(ENUM_NetPro_TypeDef enumE, char * ip, char * ComNum, ENUM_ID_NO_TypeDef id);
ESP8266_RETTYPE ESP8266_StartOrShutServer ( FunctionalState enumMode, char * pPortNum, char * pTimeOver );
ESP8266_RETTYPE ESP8266_Inquire_ApIp ( char * pApIp, uint8_t ucArrayLength );
ESP8266_RETTYPE ESP8266_UnvarnishSend ( void );
ESP8266_RETTYPE ESP8266_Get_LinkStatus ( void );
unsigned char *ESP8266_GetIPD(FunctionalState enumEnUnvarnishTx,unsigned short timeOut);
ESP8266_RETTYPE NET_DEVICE_Init(void);

#endif

在ESP8266.c编写以下代码


NET_DEVICE_INFO netDeviceInfo = {
																&lpuart1Info,		//ESP8266所在串口
																"M5GM",				//WIFI名称
																"18022100@MMKJ",	//WIFI密码
																"192.168.0.100",	//IP地址
																"7437",				//Port端口
																0, 0, 0, 0,	
																}; 

//ESP8266模块延时
void ESP8266_IO_Delay(uint32_t time)
{
	#if SYSTEM_SUPPORT_OS 
		rt_thread_mdelay(time);
	
	#else
		delay_ms(time);
	#endif
	
}

//ESP8266模块所在的串口发送数据
void ESP8266_IO_Send(uint8_t *str,uint32_t strlen )
{
	
	USART_SendBuf(ESP8266_USART,str,strlen);
	
}

//对ESP8266是否接收到数据进行判断
//1:接收失败 返回0:接收成功
ESP8266_RETTYPE ESP8266_IO_WaitRecive(void)
{

	if(netDeviceInfo.netIOInfo->InfBit.dataLen == 0) 						//如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
		return ESP8266_NOK;
		
	if(netDeviceInfo.netIOInfo->InfBit.dataLen == netDeviceInfo.netIOInfo->dataLenPre)	//如果上一次的值和这次相同,则说明接收完毕
	{
		netDeviceInfo.netIOInfo->rxBuf[netDeviceInfo.netIOInfo->InfBit.dataLen] = '\0';	//将数据的最后一位加上'\0'用于截断字符串,这样下一次接收就不用清除缓存了
		netDeviceInfo.netIOInfo->dataLenPre = netDeviceInfo.netIOInfo->InfBit.dataLen;	//将这一次的长度进行保存,当要检查接收长度时会用到
		
		netDeviceInfo.netIOInfo->InfBit.dataLen = 0;						//清0接收计数
			
		return ESP8266_OK;								//返回接收完成标志
	}
		
	netDeviceInfo.netIOInfo->dataLenPre = netDeviceInfo.netIOInfo->InfBit.dataLen;		//置为相同
	
	return ESP8266_NOK;								//返回接收未完成标志
}


//清除ESP8266模块所在的串口的缓存
void ESP8266_IO_ClearRecive(void)
{

	netDeviceInfo.netIOInfo->InfBit.dataLen = 0;
	
	memset(netDeviceInfo.netIOInfo->rxBuf, 0, sizeof(netDeviceInfo.netIOInfo->rxBuf));

}

//对ESP8266进行硬件重启(与AT指令重启不同,这里是控制ESP8266的RESET引脚强制重启)
void ESP8266_IO_Reset(void)
{
	
	UsartPrintf(USART_DEBUG, "Tips:	NET_DEVICE_Reset\r\n");
	
//	NET_DEVICE_RST_OFF;		//结束复位
//	ESP8266_IO_Delay(250);
//	
//	NET_DEVICE_RST_ON;		//复位
//	ESP8266_IO_Delay(500);
	
}

//对ESP8266发送命令,并将ESP8266返回的数据进行检索
// cmd:待发送的AT命令(注意要添加换行符)
// res1: 检索字段1
// res2: 检索字段2
// timeOut: 超时时间
//返回1:选择失败 0:选择成功
ESP8266_RETTYPE ESP8266_SendCmd(char *cmd, char *res1,char *res2,uint32_t timeOut)
{
	
	ESP8266_RETTYPE ucExecRes = ESP8266_NOK;  
	
	ESP8266_IO_ClearRecive();	//清除缓存
	
	UsartPrintf(USART_DEBUG,"Tips: %s\r\n",cmd);
	
	
	ESP8266_IO_Send((unsigned char *)cmd, strlen((const char *)cmd));	//写命令到网络设备
//	UsartPrintf(ESP8266_USART,"%s\r\n",cmd);
	
	while(timeOut--)												//等待
	{

		if(ESP8266_IO_WaitRecive() == ESP8266_OK)							//如果收到数据
		{
//		printf("\r\n=============start=================\r\n");
//		printf("netDeviceInfo.netIOInfo->rxBuf:%s\r\n",netDeviceInfo.netIOInfo->rxBuf);
//		printf("netDeviceInfo.netIOInfo->InfBit.dataLen:%d\r\n",netDeviceInfo.netIOInfo->InfBit.dataLen);
//		printf("\r\n===============end===============\r\n");
			if(*res1 != 0 && *res2 != 0)
			{
				if(((bool)strstr((const char *)netDeviceInfo.netIOInfo->rxBuf, res1))||
								((bool)strstr((const char *)netDeviceInfo.netIOInfo->rxBuf, res2)))
				{
						ucExecRes = ESP8266_OK;
						break;
				}
			}
			else if(*res1 != 0)
			{
				if((bool)strstr((const char *)netDeviceInfo.netIOInfo->rxBuf, res1))
				{
						ucExecRes = ESP8266_OK;
						break;
				}
			}
			else
			{
				if((bool)strstr((const char *)netDeviceInfo.netIOInfo->rxBuf, res2))	//如果检索到关键词
				{
						ucExecRes = ESP8266_OK;
						break;
				}
			}
		}
		ESP8266_IO_Delay(1);											//挂起等待
	}
	
//	ESP8266_IO_ClearRecive();								//清空缓存
	return ucExecRes;
}

//对ESP8266返回的数据进行检索,并将处理好的数据返回
// enumMode:工作模式
// timeOut: 超时时间
//返回1:选择失败 0:选择成功
unsigned char *ESP8266_GetIPD(FunctionalState enumEnUnvarnishTx,unsigned short timeOut)
{
//	unsigned char byte = 0, count = 0;
//	char sByte[5];
	char *ptrIPD;

	do
	{
		if(ESP8266_IO_WaitRecive() == ESP8266_OK)								//如果接收完成
		{
			if(enumEnUnvarnishTx == DISABLE)		//如果不是透传模式
			{
//					netDeviceInfo.netIOInfo->rxBuf[netDeviceInfo.netIOInfo->InfBit.dataLen++] = '\0';
//					printf("\r\n=============start=================\r\n");
//					printf("netDeviceInfo.netIOInfo->rxBuf:%s\r\n",netDeviceInfo.netIOInfo->rxBuf);
//					printf("netDeviceInfo.netIOInfo->InfBit.dataLen:%d\r\n",netDeviceInfo.netIOInfo->InfBit.dataLen);
//					printf("\r\n===============end===============\r\n");
				
					ptrIPD = strstr((char *)netDeviceInfo.netIOInfo->rxBuf, "IPD,");				//搜索“IPD”头
					if(ptrIPD == NULL)											//如果没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间
					{
//						UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n");
					}
					else
					{
						ptrIPD = strchr(ptrIPD, ':');							//找到':'
						if(ptrIPD != NULL)
						{
							ptrIPD++;
							return (unsigned char *)(ptrIPD);
						}
						else
							return NULL;
					}
			}
			else
			{
				return netDeviceInfo.netIOInfo->rxBuf;		//如果是透传模式直接返回
			}
		}
		ESP8266_IO_Delay(20);												//延时等待
	} while(timeOut--);

	return NULL;														//超时还未找到,返回空指针

}

//ESP8266向服务器发送数据
//返回1:发送失败 0:发送成功
ESP8266_RETTYPE ESP8266_SendData(FunctionalState enumEnUnvarnishTx,unsigned char *data, unsigned short len,ENUM_ID_NO_TypeDef ucId)
{
	
	char cmdBuf[30];
	ESP8266_RETTYPE ucExecRes = ESP8266_NOK;
	
	if( enumEnUnvarnishTx )	//如果是透传模式
	{
		
		ESP8266_IO_Send(data, len);	//直接发送数据
		ucExecRes = ESP8266_OK;
	}
	else
	{
		if ( ucId < 5 )		//如果是多连接
			sprintf ( cmdBuf, "AT+CIPSEND=%d,%d\r\n", ucId, len );	

		else			//如果是单连接
			sprintf ( cmdBuf, "AT+CIPSEND=%d\r\n", len );	
		
		ucExecRes = ESP8266_SendCmd ( cmdBuf, "> ", 0, 4000 );	
		if(ucExecRes == ESP8266_OK)
		{
			
			USART_SendBuf(USART_DEBUG,data,len);		//将数据在串口1打印出来
			
			ESP8266_IO_Send(data,len);	//将数据发送出去
			
		}
	}
	
	return ucExecRes;
	
}

//选择ESP8266模块的工作模式
// enumMode:工作模式
//返回1:选择失败 0:选择成功
ESP8266_RETTYPE ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode)
{
	switch ( enumMode )
	{
		case STA:
			return ESP8266_SendCmd ( "AT+CWMODE=1\r\n", "OK", "no change", 2500 ); 
		
	  	case AP:
		  	return ESP8266_SendCmd ( "AT+CWMODE=2\r\n", "OK", "no change", 2500 ); 
		
		case STA_AP:
		  	return ESP8266_SendCmd ( "AT+CWMODE=3\r\n", "OK", "no change", 2500 ); 
		
	  	default:
		  return ESP8266_NOK;
	}		
}

//ESP8266模块连接外部WiFi
//pSSID:WiFi名称字符串
//pPassWord:WiFi密码字符串
//返回1:连接失败 0:连接成功
ESP8266_RETTYPE ESP8266_JoinAP( char * pSSID, char * pPassWord )
{
	char cmdBuf [120];

	sprintf ( cmdBuf, "AT+CWJAP=\"%s\",\"%s\"\r\n", pSSID, pPassWord );
	
	return ESP8266_SendCmd( cmdBuf, "OK", NULL, 5000 );
	
}

/*
 * 函数名:ESP8266_CIPAP
 * 描述  :设置模块的 AP IP
 * 输入  :pApIp,模块的 AP IP
 * 返回  : 1,设置失败
 *         0,设置成功
 * 调用  :被外部调用
 */
ESP8266_RETTYPE ESP8266_CIPAP ( char * pApIp )
{
	char cmdBuf [ 30 ];	
	
	sprintf ( cmdBuf, "AT+CIPAP=\"%s\"\r\n", pApIp );
	
  if ( ESP8266_SendCmd ( cmdBuf, "OK", 0, 5000 ) == ESP8266_OK)
		return ESP8266_OK;
	else 
		return ESP8266_NOK;
	
}

/*
 * 函数名:ESP8266_BuildAP
 * 描述  :WF-ESP8266模块创建WiFi热点
 * 输入  :pSSID,WiFi名称字符串
 *       :pPassWord,WiFi密码字符串
 *       :enunPsdMode,WiFi加密方式代号字符串
 * 返回  : 1,创建失败
 *         0,创建成功
 * 调用  :被外部调用
 */
ESP8266_RETTYPE ESP8266_BuildAP ( char * pSSID, char * pPassWord, ENUM_AP_PsdMode_TypeDef enunPsdMode )
{
	char cmdBuf [120];

	sprintf ( cmdBuf, "AT+CWSAP=\"%s\",\"%s\",1,%d\r\n", pSSID, pPassWord, enunPsdMode );
	
	return ESP8266_SendCmd ( cmdBuf, "OK", 0, 1000 );
	
}

//ESP8266模块启动多连接
//enumEnUnvarnishTx:配置是否多连接
//返回1:配置失败 0:配置成功
ESP8266_RETTYPE ESP8266_Enable_MultipleId (FunctionalState enumEnUnvarnishTx )
{
	char cStr [20];
	
	sprintf ( cStr, "AT+CIPMUX=%d\r\n", ( enumEnUnvarnishTx ? 1 : 0 ) );
	
	return ESP8266_SendCmd ( cStr, "OK", 0, 500 );
	
}

//ESP8266模块连接外部服务器
//enumE:网络协议
//ip:服务器IP字符串
//ComNum:服务器端口字符串
//id:模块连接服务器的ID
//返回1:连接失败 0:连接成功
ESP8266_RETTYPE ESP8266_Link_Server(ENUM_NetPro_TypeDef enumE, char * ip, char * ComNum, ENUM_ID_NO_TypeDef id)
{
	char cStr [100] = { 0 }, cmdBuf [120];

  	switch (  enumE )
  	{
		case enumTCP:
		  sprintf ( cStr, "\"%s\",\"%s\",%s\r\n", "TCP", ip, ComNum );
		  break;
		
		case enumUDP:
		  sprintf ( cStr, "\"%s\",\"%s\",%s\r\n", "UDP", ip, ComNum );
		  break;
		
		default:
			break;
  	}

  	if ( id < 5 )
    	sprintf ( cmdBuf, "AT+CIPSTART=%d,%s\r\n", id, cStr);

  	else
	  	sprintf ( cmdBuf, "AT+CIPSTART=%s\r\n", cStr );

	return ESP8266_SendCmd ( cmdBuf, "OK", "ALREAY CONNECT", 4000 );
	
}


/*
 * 函数名:ESP8266_StartOrShutServer
 * 描述  :WF-ESP8266模块开启或关闭服务器模式
 * 输入  :enumMode,开启/关闭
 *       :pPortNum,服务器端口号字符串
 *       :pTimeOver,服务器超时时间字符串,单位:秒
 * 返回  : 1,操作失败
 *         0,操作成功
 * 调用  :被外部调用
 */
ESP8266_RETTYPE ESP8266_StartOrShutServer ( FunctionalState enumMode, char * pPortNum, char * pTimeOver )
{
	char cmdBuf1 [120], cmdBuf2 [120];

	if ( enumMode )
	{
		sprintf ( cmdBuf1, "AT+CIPSERVER=%d,%s\r\n", 1, pPortNum );
		
		sprintf ( cmdBuf2, "AT+CIPSTO=%s\r\n", pTimeOver );

		return ( ESP8266_SendCmd ( cmdBuf1, "OK", 0, 500 ) &&
						 ESP8266_SendCmd ( cmdBuf2, "OK", 0, 500 ) );
	}
	
	else
	{
		sprintf ( cmdBuf1, "AT+CIPSERVER=%d,%s\r\n", 0, pPortNum );

		return ESP8266_SendCmd ( cmdBuf1, "OK", 0, 500 );
	}
	
}

/*
 * 函数名:ESP8266_Inquire_ApIp
 * 描述  :获取 F-ESP8266 的 AP IP
 * 输入  :pApIp,存放 AP IP 的数组的首地址
 *         ucArrayLength,存放 AP IP 的数组的长度
 * 返回  : 0,获取成功
 *         1,获取失败
 * 调用  :被外部调用
 */
ESP8266_RETTYPE ESP8266_Inquire_ApIp ( char * pApIp, uint8_t ucArrayLength )
{
	char uc;
	
	char * pCh;
	
	
	if(!ESP8266_SendCmd ( "AT+CIFSR\r\n", "OK", 0, 500 ))
	{
		pCh = strstr ( (char*)netDeviceInfo.netIOInfo->rxBuf, "APIP,\"" );
		
		if ( pCh )
			pCh += 6;
		
		else
			return ESP8266_NOK;
		
		for ( uc = 0; uc < ucArrayLength; uc ++ )
		{
			pApIp [ uc ] = * ( pCh + uc);
			
			if ( pApIp [ uc ] == '\"' )
			{
				pApIp [ uc ] = '\0';
				break;
			}
		}
	}
	
	return ESP8266_OK;
	
}

//配置ESP8266模块进入透传发送
//返回1:配置失败 0:配置成功
ESP8266_RETTYPE ESP8266_UnvarnishSend ( void )
{
	if (!ESP8266_SendCmd ( "AT+CIPMODE=1\r\n", "OK", 0, 500 ))
		return ESP8266_NOK;
	
	return 
	  	ESP8266_SendCmd( "AT+CIPSEND\r\n", "OK", ">", 500 );
	
}

//ESP8266 的连接状态,较适合单端口时使用
//返回0:获取状态失败
//返回2:获得ip
//返回3:建立连接 
//返回4:失去连接 
ESP8266_RETTYPE ESP8266_Get_LinkStatus ( void )
{
	if (ESP8266_SendCmd( "AT+CIPSTATUS\r\n", "STATUS:", NULL, 1000 ) == ESP8266_OK)
	{
		UsartPrintf(USART_DEBUG,"netDeviceInfo.netIOInfo->rxBuf:%s\r\n",netDeviceInfo.netIOInfo->rxBuf);
		if ( strstr ( (char*)netDeviceInfo.netIOInfo->rxBuf, "STATUS:2\r\n" ) )
		{
				UsartPrintf(USART_DEBUG, "ESP8266 Got IP\r\n");
			return 2;
		}
		else if ( strstr ( (char*)netDeviceInfo.netIOInfo->rxBuf, "STATUS:3\r\n" ) )
		{
			UsartPrintf(USART_DEBUG, "ESP8266 Connect OK\r\n");
			return 3;
		}
		else if ( strstr ( (char*)netDeviceInfo.netIOInfo->rxBuf, "STATUS:4\r\n" ) )
		{
			UsartPrintf(USART_DEBUG, "ESP8266 Lost Connect\r\n");
			return 4;		
		}
	}
	UsartPrintf(USART_DEBUG, "ESP8266 TimeOut\r\n");			//获取状态失败
	return ESP8266_NOK;
	
}

//网络设备连接服务器前进行的初始化
//返回1:配置失败 0:配置成功
ESP8266_RETTYPE NET_DEVICE_LinkServer_Init(void)
{
	
	unsigned char errCount = 0, errType = 0;
//	char cfgBuffer[70];
	
	switch(netDeviceInfo.initStep)
	{
		case 0:
			if(ESP8266_Net_Mode_Choose(STA) == ESP8266_OK)
				netDeviceInfo.initStep++;
			break;
		case 1:
			if(ESP8266_Enable_MultipleId(DISABLE) == ESP8266_OK)
				netDeviceInfo.initStep++;
			break;
		case 2:
			if(ESP8266_JoinAP(netDeviceInfo.staName,netDeviceInfo.staPass) == ESP8266_OK)
				netDeviceInfo.initStep++;
			break;
		case 3:
			if(ESP8266_Link_Server(enumTCP,netDeviceInfo.staIPAddress,netDeviceInfo.staPort,Single_ID_0) == ESP8266_OK)
				netDeviceInfo.initStep++;
			break;
		default:
			 netDeviceInfo.netWork = 1;	
			 errType = 3;
			break;
	}
	
	
		return errType;
	
}

//网络设备初始化
//返回1:配置失败 0:配置成功
ESP8266_RETTYPE NET_DEVICE_Init(void)
{
	
	netDeviceInfo.err = NET_DEVICE_LinkServer_Init(); 									//获取连接结果	0-成功
	
	if(netDeviceInfo.err == 1) 												//如果路由信息错误,则启动ap模式 通过手机获取ssid和password
	{
		UsartPrintf(USART_DEBUG, "Wifi info Error,Use USART2 -> 8266\r\n");
        
    return ESP8266_NOK;
	}
	else if(netDeviceInfo.err == 2) 										//如果是平台信息错误
	{
		UsartPrintf(USART_DEBUG, "PT info Error,Use APP -> 8266\r\n");
		
		return ESP8266_NOK;
	}
	else if(netDeviceInfo.err == 3)
	{
		UsartPrintf(USART_DEBUG, "Tips:	NET_DEVICE STA OK\r\n");
		
		ESP8266_IO_Delay(500); //延时提示
		
		return ESP8266_OK;
	}
	else
		return ESP8266_NOK;

}

在main.c中添加以下代码

/* USER CODE BEGIN PTD */
ESP8266_RETTYPE netStatus = ESP8266_NOK;
unsigned char netErrCount = 0;
/* USER CODE END PTD */

在main.c下的main函数加入以下代码

  /* USER CODE BEGIN 1 */
	unsigned char* dataPtr = NULL;
	uint32_t send_time = 0;
	ESP8266_RETTYPE ucExecRes = ESP8266_NOK;

  /* USER CODE END 1 */
 /* USER CODE BEGIN 2 */
	
	USART_Interupt_Enable();		//使能串口中断
	TIM_Interupt_Enable();			//使能定时器中断

  /* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		
		if(!netDeviceInfo.netWork)	如果网络未连接
		{
			if(NET_DEVICE_Init() == ESP8266_OK)
			{
				printf("连接服务器成功\r\n");
			}
		}
		if(netDeviceInfo.netWork)	//如果网络连接成功
		{
			dataPtr = ESP8266_GetIPD(DISABLE,0);	//解析服务器下发的数据
			if(dataPtr != NULL)
			{
				printf("dataPtr:%s\r\n",dataPtr);
				if(strstr((char*)dataPtr,"OpenLED"))
				{
					printf("LED点亮\r\n");
					LED_Set(LED_ON);
				}
				if(strstr((char*)dataPtr,"CloseLED"))
				{
					printf("LED熄灭\r\n");
					LED_Set(LED_OFF);
				}
			}
		}
		
		if(time2Count - send_time >= 10000)		//(1ms * 2000)相当于延时2秒钟
		{
			send_time = time2Count;				//记下当前定时器的数值
			if(netDeviceInfo.netWork)	//如果网络连接成功
			{
				ucExecRes = ESP8266_SendData(DISABLE,"hello world",strlen("hello world"),Single_ID_0);	//向服务器发送数据
				if(ucExecRes != ESP8266_OK)
				{
					printf("网络可能断开了\r\n");
					netErrCount++;	//错误次数进行累加
					if(netErrCount >= 3)	//超过三次,进行自检
					{
						netStatus = ESP8266_Get_LinkStatus();	//检查连接状态
						if(netStatus == 4)		//网络已经断开
						{
								netErrCount = 0;		//将错误清零
								netDeviceInfo.netWork = 0;	//标志网络断开
								netDeviceInfo.initStep = 0;	//将初始化步骤清零
						}
					}
				}
				else
				{
					netErrCount = 0;	//无错误,清除错误计数
				}
			}
		}	
  }
  /* USER CODE END 3 */

4、实验现象

实现的功能
1、上电自动连接WIFI
2、成功连接WIFI后自动连接TCP服务器
3、成功连接TCP服务器后自动发送数据给服务器
4、TCP服务器下发OpenLED指令会点亮LED,下发CloseLED会熄灭LED灯
5、当服务器连接断开的时候,会自动进行重新连接服务器,并重新发送数据

在这里插入图片描述

版权声明:本文为CSDN博主「AIoT-韶华分明」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42523645/article/details/120177865

1、准备开发板

开发板功能区分布图

在这里插入图片描述

开发板俯视图

在这里插入图片描述

2、ESP8266简介

    ESP8266 WIFI模块内置TCP/IP网络协议,模块支持三种网络模式,AP、STA和AP+STA模式,AP模式:模块作为WIFI热点,等待其他设备的连接,进行局域网的通信,STA模式:模块作为客户端通过路由器连接外网,和服务器进行通信,AP+STA模式:两种模式共存,可以进行任意切换。另外,模块支持AT指令操作,使用PC端或者单片机TTL串口配置简单的指令即可实现,这也是选择这款模块的一个原因。

什么是AT指令?

在这里插入图片描述

ESP8266开发常用的AT指令

基础AT指令
命令 描述
AT 测试AT启动
AT+RST 重启模块
AT+GMR 查看版本信息
WIFI功能AT指令
命令 描述
AT+CWMODE 选择WIFI应用模式
AT+CWJAP 加入AP
AT+CWLAP 列出当前可用AP
AT+CWQAP 退出与AP的连接
AT+CWSAP 设置AP模式下的参数
AT+CWLIF 查看已接入设备的IP
TCP/IP 工具箱 AT 指令
命令 描述
AT+CIPSTATUS 获得连接状态
AT+CIPSTART 建立TCP连接或注册UDP
AT+CIPSEND 发送数据
AT+CIPCLOSE 关闭TCP或UDP
AT+CIFSR 获取本地IP地址
AT+CIPMUX 启动多连接
AT+CIPSERVER 配置为服务器
AT+CIPMODE 设置模块传输模式
AT+CIPSTO 设置服务器超时时间

3、在MDK中编写代码

    在写代码之前我们要了解的编程思维:高内聚、低耦合,简单来说就是:把一些东西放在一边,另一些东西放在另一边,然后划定边界,就是一种“分类”的思想,一个模块实现一个功能,功能之间相互联系。并且我们可以用I/O模型来管理我们的设备,在计算机系统中I/O就是输入(Input)和输出(Output)的意思,用来控制计算机的数据流动包括程序、硬件。

ESP8266基础函数
函数 描述
ESP8266_IO_Delay ESP8266模块延时
ESP8266_IO_Reset 对ESP8266进行硬件重启
ESP8266_IO_Send ESP8266模块所在的串口发送数据
ESP8266_IO_WaitRecive 对ESP8266是否接收到数据进行判断
ESP8266_IO_ClearRecive 清除ESP8266模块所在的串口的缓存
ESP8266功能函数
ESP8266_SendCmd 对ESP8266发送命令,并将ESP8266返回的数据进行检索
ESP8266_SendData ESP8266向服务器发送数据
ESP8266_Net_Mode_Choose 选择ESP8266模块的工作模式
ESP8266_JoinAP ESP8266模块连接外部WiFi
ESP8266_CIPAP 设置模块的 AP IP
ESP8266_BuildAP WF-ESP8266模块创建WiFi热点
ESP8266_Enable_MultipleId ESP8266模块启动多连接
ESP8266_Link_Server ESP8266模块连接外部服务器
ESP8266_StartOrShutServer ESP8266模块开启或关闭服务器模式
ESP8266_Inquire_ApIp 获取 F-ESP8266 的 AP IP
ESP8266_UnvarnishSend 配置ESP8266模块进入透传发送
ESP8266_Get_LinkStatus ESP8266 的连接状态,较适合单端口时使用
ESP8266_GetIPD 对ESP8266返回的数据进行检索,并将处理好的数据返回

在ESP8266.h中编写以下代码

#ifndef __ESP8266_H_
#define __ESP8266_H_

#include "sys.h"

#define ESP8266_USART   LPUART1
#define USART_DEBUG 	USART1


#ifndef ESP8266_OK
#define ESP8266_OK                                              0
#endif

#ifndef ESP8266_NOK
#define ESP8266_NOK                                             1
#endif


#define  ESP8266_RETTYPE		unsigned char 

//ESP8266数据类型定义
typedef enum
{
	STA,
  	AP,
  	STA_AP  
}ENUM_Net_ModeTypeDef;

//ESP8266网络模式定义
typedef enum{
	 enumTCP,
	 enumUDP,
} ENUM_NetPro_TypeDef;

//ESP8266多连接ID定义
typedef enum{
	Multiple_ID_0 = 0,
	Multiple_ID_1 = 1,
	Multiple_ID_2 = 2,
	Multiple_ID_3 = 3,
	Multiple_ID_4 = 4,
	Single_ID_0 = 5,
} ENUM_ID_NO_TypeDef;

//ESP8266 AP加密方式定义
typedef enum{
	OPEN = 0,
	WEP = 1,
	WPA_PSK = 2,
	WPA2_PSK = 3,
	WPA_WPA2_PSK = 4,
} ENUM_AP_PsdMode_TypeDef;


#define User_ESP8266_BulitApSsid	  "PRECHIN"	  //要建立的热点的名称
#define User_ESP8266_BulitApEcn	  	  OPEN            //要建立的热点的加密方式
#define User_ESP8266_BulitApPwd  	  "prechin"      //要建立的热点的密钥

#define User_ESP8266_TCPServer_IP	  "192.168.1.119"	  //服务器开启的IP地址
#define User_ESP8266_TCPServer_PORT	  "8080"	  //服务器开启的端口

#define User_ESP8266_TCPServer_OverTime	  "1800"	  //服务器超时时间(单位:秒)

#define TCPAGREEMENT 	enumTCP	//TCP通信协议

typedef struct _NET_DEVICE_INFO
{

	USART_INFO_STRUCT* netIOInfo;	//串口句柄
	
	char staName[20];				//缓存SSID
	char staPass[20];				//缓存PassWord
	char staIPAddress[20];	//缓存IP
	char staPort[20];				//缓存Port
	
	unsigned short err : 2; 		//错误类型
	unsigned short netWork : 1;	//网络连接状态
	unsigned short initStep : 4;	//初始化步骤
	unsigned short dataType : 4;	//设定数据返回类型--16种
	unsigned short reverse : 6;		//预留

} NET_DEVICE_INFO;

extern NET_DEVICE_INFO netDeviceInfo;

void ESP8266_IO_Delay(uint32_t time);
void ESP8266_IO_Send(uint8_t *str,uint32_t strlen );
ESP8266_RETTYPE ESP8266_IO_WaitRecive(void);
void ESP8266_IO_ClearRecive(void);
void ESP8266_IO_Reset(void);

ESP8266_RETTYPE ESP8266_SendCmd(char *cmd, char *res1,char *res2,uint32_t timeOut);
ESP8266_RETTYPE ESP8266_SendData(FunctionalState enumEnUnvarnishTx,unsigned char *data, unsigned short len,ENUM_ID_NO_TypeDef ucId);
ESP8266_RETTYPE ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode);
ESP8266_RETTYPE ESP8266_JoinAP( char * pSSID, char * pPassWord );
ESP8266_RETTYPE ESP8266_CIPAP ( char * pApIp );
ESP8266_RETTYPE ESP8266_BuildAP ( char * pSSID, char * pPassWord, ENUM_AP_PsdMode_TypeDef enunPsdMode );
ESP8266_RETTYPE ESP8266_Enable_MultipleId (FunctionalState enumEnUnvarnishTx );
ESP8266_RETTYPE ESP8266_Link_Server(ENUM_NetPro_TypeDef enumE, char * ip, char * ComNum, ENUM_ID_NO_TypeDef id);
ESP8266_RETTYPE ESP8266_StartOrShutServer ( FunctionalState enumMode, char * pPortNum, char * pTimeOver );
ESP8266_RETTYPE ESP8266_Inquire_ApIp ( char * pApIp, uint8_t ucArrayLength );
ESP8266_RETTYPE ESP8266_UnvarnishSend ( void );
ESP8266_RETTYPE ESP8266_Get_LinkStatus ( void );
unsigned char *ESP8266_GetIPD(FunctionalState enumEnUnvarnishTx,unsigned short timeOut);
ESP8266_RETTYPE NET_DEVICE_Init(void);

#endif

在ESP8266.c编写以下代码


NET_DEVICE_INFO netDeviceInfo = {
																&lpuart1Info,		//ESP8266所在串口
																"M5GM",				//WIFI名称
																"18022100@MMKJ",	//WIFI密码
																"192.168.0.100",	//IP地址
																"7437",				//Port端口
																0, 0, 0, 0,	
																}; 

//ESP8266模块延时
void ESP8266_IO_Delay(uint32_t time)
{
	#if SYSTEM_SUPPORT_OS 
		rt_thread_mdelay(time);
	
	#else
		delay_ms(time);
	#endif
	
}

//ESP8266模块所在的串口发送数据
void ESP8266_IO_Send(uint8_t *str,uint32_t strlen )
{
	
	USART_SendBuf(ESP8266_USART,str,strlen);
	
}

//对ESP8266是否接收到数据进行判断
//1:接收失败 返回0:接收成功
ESP8266_RETTYPE ESP8266_IO_WaitRecive(void)
{

	if(netDeviceInfo.netIOInfo->InfBit.dataLen == 0) 						//如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
		return ESP8266_NOK;
		
	if(netDeviceInfo.netIOInfo->InfBit.dataLen == netDeviceInfo.netIOInfo->dataLenPre)	//如果上一次的值和这次相同,则说明接收完毕
	{
		netDeviceInfo.netIOInfo->rxBuf[netDeviceInfo.netIOInfo->InfBit.dataLen] = '\0';	//将数据的最后一位加上'\0'用于截断字符串,这样下一次接收就不用清除缓存了
		netDeviceInfo.netIOInfo->dataLenPre = netDeviceInfo.netIOInfo->InfBit.dataLen;	//将这一次的长度进行保存,当要检查接收长度时会用到
		
		netDeviceInfo.netIOInfo->InfBit.dataLen = 0;						//清0接收计数
			
		return ESP8266_OK;								//返回接收完成标志
	}
		
	netDeviceInfo.netIOInfo->dataLenPre = netDeviceInfo.netIOInfo->InfBit.dataLen;		//置为相同
	
	return ESP8266_NOK;								//返回接收未完成标志
}


//清除ESP8266模块所在的串口的缓存
void ESP8266_IO_ClearRecive(void)
{

	netDeviceInfo.netIOInfo->InfBit.dataLen = 0;
	
	memset(netDeviceInfo.netIOInfo->rxBuf, 0, sizeof(netDeviceInfo.netIOInfo->rxBuf));

}

//对ESP8266进行硬件重启(与AT指令重启不同,这里是控制ESP8266的RESET引脚强制重启)
void ESP8266_IO_Reset(void)
{
	
	UsartPrintf(USART_DEBUG, "Tips:	NET_DEVICE_Reset\r\n");
	
//	NET_DEVICE_RST_OFF;		//结束复位
//	ESP8266_IO_Delay(250);
//	
//	NET_DEVICE_RST_ON;		//复位
//	ESP8266_IO_Delay(500);
	
}

//对ESP8266发送命令,并将ESP8266返回的数据进行检索
// cmd:待发送的AT命令(注意要添加换行符)
// res1: 检索字段1
// res2: 检索字段2
// timeOut: 超时时间
//返回1:选择失败 0:选择成功
ESP8266_RETTYPE ESP8266_SendCmd(char *cmd, char *res1,char *res2,uint32_t timeOut)
{
	
	ESP8266_RETTYPE ucExecRes = ESP8266_NOK;  
	
	ESP8266_IO_ClearRecive();	//清除缓存
	
	UsartPrintf(USART_DEBUG,"Tips: %s\r\n",cmd);
	
	
	ESP8266_IO_Send((unsigned char *)cmd, strlen((const char *)cmd));	//写命令到网络设备
//	UsartPrintf(ESP8266_USART,"%s\r\n",cmd);
	
	while(timeOut--)												//等待
	{

		if(ESP8266_IO_WaitRecive() == ESP8266_OK)							//如果收到数据
		{
//		printf("\r\n=============start=================\r\n");
//		printf("netDeviceInfo.netIOInfo->rxBuf:%s\r\n",netDeviceInfo.netIOInfo->rxBuf);
//		printf("netDeviceInfo.netIOInfo->InfBit.dataLen:%d\r\n",netDeviceInfo.netIOInfo->InfBit.dataLen);
//		printf("\r\n===============end===============\r\n");
			if(*res1 != 0 && *res2 != 0)
			{
				if(((bool)strstr((const char *)netDeviceInfo.netIOInfo->rxBuf, res1))||
								((bool)strstr((const char *)netDeviceInfo.netIOInfo->rxBuf, res2)))
				{
						ucExecRes = ESP8266_OK;
						break;
				}
			}
			else if(*res1 != 0)
			{
				if((bool)strstr((const char *)netDeviceInfo.netIOInfo->rxBuf, res1))
				{
						ucExecRes = ESP8266_OK;
						break;
				}
			}
			else
			{
				if((bool)strstr((const char *)netDeviceInfo.netIOInfo->rxBuf, res2))	//如果检索到关键词
				{
						ucExecRes = ESP8266_OK;
						break;
				}
			}
		}
		ESP8266_IO_Delay(1);											//挂起等待
	}
	
//	ESP8266_IO_ClearRecive();								//清空缓存
	return ucExecRes;
}

//对ESP8266返回的数据进行检索,并将处理好的数据返回
// enumMode:工作模式
// timeOut: 超时时间
//返回1:选择失败 0:选择成功
unsigned char *ESP8266_GetIPD(FunctionalState enumEnUnvarnishTx,unsigned short timeOut)
{
//	unsigned char byte = 0, count = 0;
//	char sByte[5];
	char *ptrIPD;

	do
	{
		if(ESP8266_IO_WaitRecive() == ESP8266_OK)								//如果接收完成
		{
			if(enumEnUnvarnishTx == DISABLE)		//如果不是透传模式
			{
//					netDeviceInfo.netIOInfo->rxBuf[netDeviceInfo.netIOInfo->InfBit.dataLen++] = '\0';
//					printf("\r\n=============start=================\r\n");
//					printf("netDeviceInfo.netIOInfo->rxBuf:%s\r\n",netDeviceInfo.netIOInfo->rxBuf);
//					printf("netDeviceInfo.netIOInfo->InfBit.dataLen:%d\r\n",netDeviceInfo.netIOInfo->InfBit.dataLen);
//					printf("\r\n===============end===============\r\n");
				
					ptrIPD = strstr((char *)netDeviceInfo.netIOInfo->rxBuf, "IPD,");				//搜索“IPD”头
					if(ptrIPD == NULL)											//如果没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间
					{
//						UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n");
					}
					else
					{
						ptrIPD = strchr(ptrIPD, ':');							//找到':'
						if(ptrIPD != NULL)
						{
							ptrIPD++;
							return (unsigned char *)(ptrIPD);
						}
						else
							return NULL;
					}
			}
			else
			{
				return netDeviceInfo.netIOInfo->rxBuf;		//如果是透传模式直接返回
			}
		}
		ESP8266_IO_Delay(20);												//延时等待
	} while(timeOut--);

	return NULL;														//超时还未找到,返回空指针

}

//ESP8266向服务器发送数据
//返回1:发送失败 0:发送成功
ESP8266_RETTYPE ESP8266_SendData(FunctionalState enumEnUnvarnishTx,unsigned char *data, unsigned short len,ENUM_ID_NO_TypeDef ucId)
{
	
	char cmdBuf[30];
	ESP8266_RETTYPE ucExecRes = ESP8266_NOK;
	
	if( enumEnUnvarnishTx )	//如果是透传模式
	{
		
		ESP8266_IO_Send(data, len);	//直接发送数据
		ucExecRes = ESP8266_OK;
	}
	else
	{
		if ( ucId < 5 )		//如果是多连接
			sprintf ( cmdBuf, "AT+CIPSEND=%d,%d\r\n", ucId, len );	

		else			//如果是单连接
			sprintf ( cmdBuf, "AT+CIPSEND=%d\r\n", len );	
		
		ucExecRes = ESP8266_SendCmd ( cmdBuf, "> ", 0, 4000 );	
		if(ucExecRes == ESP8266_OK)
		{
			
			USART_SendBuf(USART_DEBUG,data,len);		//将数据在串口1打印出来
			
			ESP8266_IO_Send(data,len);	//将数据发送出去
			
		}
	}
	
	return ucExecRes;
	
}

//选择ESP8266模块的工作模式
// enumMode:工作模式
//返回1:选择失败 0:选择成功
ESP8266_RETTYPE ESP8266_Net_Mode_Choose(ENUM_Net_ModeTypeDef enumMode)
{
	switch ( enumMode )
	{
		case STA:
			return ESP8266_SendCmd ( "AT+CWMODE=1\r\n", "OK", "no change", 2500 ); 
		
	  	case AP:
		  	return ESP8266_SendCmd ( "AT+CWMODE=2\r\n", "OK", "no change", 2500 ); 
		
		case STA_AP:
		  	return ESP8266_SendCmd ( "AT+CWMODE=3\r\n", "OK", "no change", 2500 ); 
		
	  	default:
		  return ESP8266_NOK;
	}		
}

//ESP8266模块连接外部WiFi
//pSSID:WiFi名称字符串
//pPassWord:WiFi密码字符串
//返回1:连接失败 0:连接成功
ESP8266_RETTYPE ESP8266_JoinAP( char * pSSID, char * pPassWord )
{
	char cmdBuf [120];

	sprintf ( cmdBuf, "AT+CWJAP=\"%s\",\"%s\"\r\n", pSSID, pPassWord );
	
	return ESP8266_SendCmd( cmdBuf, "OK", NULL, 5000 );
	
}

/*
 * 函数名:ESP8266_CIPAP
 * 描述  :设置模块的 AP IP
 * 输入  :pApIp,模块的 AP IP
 * 返回  : 1,设置失败
 *         0,设置成功
 * 调用  :被外部调用
 */
ESP8266_RETTYPE ESP8266_CIPAP ( char * pApIp )
{
	char cmdBuf [ 30 ];	
	
	sprintf ( cmdBuf, "AT+CIPAP=\"%s\"\r\n", pApIp );
	
  if ( ESP8266_SendCmd ( cmdBuf, "OK", 0, 5000 ) == ESP8266_OK)
		return ESP8266_OK;
	else 
		return ESP8266_NOK;
	
}

/*
 * 函数名:ESP8266_BuildAP
 * 描述  :WF-ESP8266模块创建WiFi热点
 * 输入  :pSSID,WiFi名称字符串
 *       :pPassWord,WiFi密码字符串
 *       :enunPsdMode,WiFi加密方式代号字符串
 * 返回  : 1,创建失败
 *         0,创建成功
 * 调用  :被外部调用
 */
ESP8266_RETTYPE ESP8266_BuildAP ( char * pSSID, char * pPassWord, ENUM_AP_PsdMode_TypeDef enunPsdMode )
{
	char cmdBuf [120];

	sprintf ( cmdBuf, "AT+CWSAP=\"%s\",\"%s\",1,%d\r\n", pSSID, pPassWord, enunPsdMode );
	
	return ESP8266_SendCmd ( cmdBuf, "OK", 0, 1000 );
	
}

//ESP8266模块启动多连接
//enumEnUnvarnishTx:配置是否多连接
//返回1:配置失败 0:配置成功
ESP8266_RETTYPE ESP8266_Enable_MultipleId (FunctionalState enumEnUnvarnishTx )
{
	char cStr [20];
	
	sprintf ( cStr, "AT+CIPMUX=%d\r\n", ( enumEnUnvarnishTx ? 1 : 0 ) );
	
	return ESP8266_SendCmd ( cStr, "OK", 0, 500 );
	
}

//ESP8266模块连接外部服务器
//enumE:网络协议
//ip:服务器IP字符串
//ComNum:服务器端口字符串
//id:模块连接服务器的ID
//返回1:连接失败 0:连接成功
ESP8266_RETTYPE ESP8266_Link_Server(ENUM_NetPro_TypeDef enumE, char * ip, char * ComNum, ENUM_ID_NO_TypeDef id)
{
	char cStr [100] = { 0 }, cmdBuf [120];

  	switch (  enumE )
  	{
		case enumTCP:
		  sprintf ( cStr, "\"%s\",\"%s\",%s\r\n", "TCP", ip, ComNum );
		  break;
		
		case enumUDP:
		  sprintf ( cStr, "\"%s\",\"%s\",%s\r\n", "UDP", ip, ComNum );
		  break;
		
		default:
			break;
  	}

  	if ( id < 5 )
    	sprintf ( cmdBuf, "AT+CIPSTART=%d,%s\r\n", id, cStr);

  	else
	  	sprintf ( cmdBuf, "AT+CIPSTART=%s\r\n", cStr );

	return ESP8266_SendCmd ( cmdBuf, "OK", "ALREAY CONNECT", 4000 );
	
}


/*
 * 函数名:ESP8266_StartOrShutServer
 * 描述  :WF-ESP8266模块开启或关闭服务器模式
 * 输入  :enumMode,开启/关闭
 *       :pPortNum,服务器端口号字符串
 *       :pTimeOver,服务器超时时间字符串,单位:秒
 * 返回  : 1,操作失败
 *         0,操作成功
 * 调用  :被外部调用
 */
ESP8266_RETTYPE ESP8266_StartOrShutServer ( FunctionalState enumMode, char * pPortNum, char * pTimeOver )
{
	char cmdBuf1 [120], cmdBuf2 [120];

	if ( enumMode )
	{
		sprintf ( cmdBuf1, "AT+CIPSERVER=%d,%s\r\n", 1, pPortNum );
		
		sprintf ( cmdBuf2, "AT+CIPSTO=%s\r\n", pTimeOver );

		return ( ESP8266_SendCmd ( cmdBuf1, "OK", 0, 500 ) &&
						 ESP8266_SendCmd ( cmdBuf2, "OK", 0, 500 ) );
	}
	
	else
	{
		sprintf ( cmdBuf1, "AT+CIPSERVER=%d,%s\r\n", 0, pPortNum );

		return ESP8266_SendCmd ( cmdBuf1, "OK", 0, 500 );
	}
	
}

/*
 * 函数名:ESP8266_Inquire_ApIp
 * 描述  :获取 F-ESP8266 的 AP IP
 * 输入  :pApIp,存放 AP IP 的数组的首地址
 *         ucArrayLength,存放 AP IP 的数组的长度
 * 返回  : 0,获取成功
 *         1,获取失败
 * 调用  :被外部调用
 */
ESP8266_RETTYPE ESP8266_Inquire_ApIp ( char * pApIp, uint8_t ucArrayLength )
{
	char uc;
	
	char * pCh;
	
	
	if(!ESP8266_SendCmd ( "AT+CIFSR\r\n", "OK", 0, 500 ))
	{
		pCh = strstr ( (char*)netDeviceInfo.netIOInfo->rxBuf, "APIP,\"" );
		
		if ( pCh )
			pCh += 6;
		
		else
			return ESP8266_NOK;
		
		for ( uc = 0; uc < ucArrayLength; uc ++ )
		{
			pApIp [ uc ] = * ( pCh + uc);
			
			if ( pApIp [ uc ] == '\"' )
			{
				pApIp [ uc ] = '\0';
				break;
			}
		}
	}
	
	return ESP8266_OK;
	
}

//配置ESP8266模块进入透传发送
//返回1:配置失败 0:配置成功
ESP8266_RETTYPE ESP8266_UnvarnishSend ( void )
{
	if (!ESP8266_SendCmd ( "AT+CIPMODE=1\r\n", "OK", 0, 500 ))
		return ESP8266_NOK;
	
	return 
	  	ESP8266_SendCmd( "AT+CIPSEND\r\n", "OK", ">", 500 );
	
}

//ESP8266 的连接状态,较适合单端口时使用
//返回0:获取状态失败
//返回2:获得ip
//返回3:建立连接 
//返回4:失去连接 
ESP8266_RETTYPE ESP8266_Get_LinkStatus ( void )
{
	if (ESP8266_SendCmd( "AT+CIPSTATUS\r\n", "STATUS:", NULL, 1000 ) == ESP8266_OK)
	{
		UsartPrintf(USART_DEBUG,"netDeviceInfo.netIOInfo->rxBuf:%s\r\n",netDeviceInfo.netIOInfo->rxBuf);
		if ( strstr ( (char*)netDeviceInfo.netIOInfo->rxBuf, "STATUS:2\r\n" ) )
		{
				UsartPrintf(USART_DEBUG, "ESP8266 Got IP\r\n");
			return 2;
		}
		else if ( strstr ( (char*)netDeviceInfo.netIOInfo->rxBuf, "STATUS:3\r\n" ) )
		{
			UsartPrintf(USART_DEBUG, "ESP8266 Connect OK\r\n");
			return 3;
		}
		else if ( strstr ( (char*)netDeviceInfo.netIOInfo->rxBuf, "STATUS:4\r\n" ) )
		{
			UsartPrintf(USART_DEBUG, "ESP8266 Lost Connect\r\n");
			return 4;		
		}
	}
	UsartPrintf(USART_DEBUG, "ESP8266 TimeOut\r\n");			//获取状态失败
	return ESP8266_NOK;
	
}

//网络设备连接服务器前进行的初始化
//返回1:配置失败 0:配置成功
ESP8266_RETTYPE NET_DEVICE_LinkServer_Init(void)
{
	
	unsigned char errCount = 0, errType = 0;
//	char cfgBuffer[70];
	
	switch(netDeviceInfo.initStep)
	{
		case 0:
			if(ESP8266_Net_Mode_Choose(STA) == ESP8266_OK)
				netDeviceInfo.initStep++;
			break;
		case 1:
			if(ESP8266_Enable_MultipleId(DISABLE) == ESP8266_OK)
				netDeviceInfo.initStep++;
			break;
		case 2:
			if(ESP8266_JoinAP(netDeviceInfo.staName,netDeviceInfo.staPass) == ESP8266_OK)
				netDeviceInfo.initStep++;
			break;
		case 3:
			if(ESP8266_Link_Server(enumTCP,netDeviceInfo.staIPAddress,netDeviceInfo.staPort,Single_ID_0) == ESP8266_OK)
				netDeviceInfo.initStep++;
			break;
		default:
			 netDeviceInfo.netWork = 1;	
			 errType = 3;
			break;
	}
	
	
		return errType;
	
}

//网络设备初始化
//返回1:配置失败 0:配置成功
ESP8266_RETTYPE NET_DEVICE_Init(void)
{
	
	netDeviceInfo.err = NET_DEVICE_LinkServer_Init(); 									//获取连接结果	0-成功
	
	if(netDeviceInfo.err == 1) 												//如果路由信息错误,则启动ap模式 通过手机获取ssid和password
	{
		UsartPrintf(USART_DEBUG, "Wifi info Error,Use USART2 -> 8266\r\n");
        
    return ESP8266_NOK;
	}
	else if(netDeviceInfo.err == 2) 										//如果是平台信息错误
	{
		UsartPrintf(USART_DEBUG, "PT info Error,Use APP -> 8266\r\n");
		
		return ESP8266_NOK;
	}
	else if(netDeviceInfo.err == 3)
	{
		UsartPrintf(USART_DEBUG, "Tips:	NET_DEVICE STA OK\r\n");
		
		ESP8266_IO_Delay(500); //延时提示
		
		return ESP8266_OK;
	}
	else
		return ESP8266_NOK;

}

在main.c中添加以下代码

/* USER CODE BEGIN PTD */
ESP8266_RETTYPE netStatus = ESP8266_NOK;
unsigned char netErrCount = 0;
/* USER CODE END PTD */

在main.c下的main函数加入以下代码

  /* USER CODE BEGIN 1 */
	unsigned char* dataPtr = NULL;
	uint32_t send_time = 0;
	ESP8266_RETTYPE ucExecRes = ESP8266_NOK;

  /* USER CODE END 1 */
 /* USER CODE BEGIN 2 */
	
	USART_Interupt_Enable();		//使能串口中断
	TIM_Interupt_Enable();			//使能定时器中断

  /* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		
		if(!netDeviceInfo.netWork)	如果网络未连接
		{
			if(NET_DEVICE_Init() == ESP8266_OK)
			{
				printf("连接服务器成功\r\n");
			}
		}
		if(netDeviceInfo.netWork)	//如果网络连接成功
		{
			dataPtr = ESP8266_GetIPD(DISABLE,0);	//解析服务器下发的数据
			if(dataPtr != NULL)
			{
				printf("dataPtr:%s\r\n",dataPtr);
				if(strstr((char*)dataPtr,"OpenLED"))
				{
					printf("LED点亮\r\n");
					LED_Set(LED_ON);
				}
				if(strstr((char*)dataPtr,"CloseLED"))
				{
					printf("LED熄灭\r\n");
					LED_Set(LED_OFF);
				}
			}
		}
		
		if(time2Count - send_time >= 10000)		//(1ms * 2000)相当于延时2秒钟
		{
			send_time = time2Count;				//记下当前定时器的数值
			if(netDeviceInfo.netWork)	//如果网络连接成功
			{
				ucExecRes = ESP8266_SendData(DISABLE,"hello world",strlen("hello world"),Single_ID_0);	//向服务器发送数据
				if(ucExecRes != ESP8266_OK)
				{
					printf("网络可能断开了\r\n");
					netErrCount++;	//错误次数进行累加
					if(netErrCount >= 3)	//超过三次,进行自检
					{
						netStatus = ESP8266_Get_LinkStatus();	//检查连接状态
						if(netStatus == 4)		//网络已经断开
						{
								netErrCount = 0;		//将错误清零
								netDeviceInfo.netWork = 0;	//标志网络断开
								netDeviceInfo.initStep = 0;	//将初始化步骤清零
						}
					}
				}
				else
				{
					netErrCount = 0;	//无错误,清除错误计数
				}
			}
		}	
  }
  /* USER CODE END 3 */

4、实验现象

实现的功能
1、上电自动连接WIFI
2、成功连接WIFI后自动连接TCP服务器
3、成功连接TCP服务器后自动发送数据给服务器
4、TCP服务器下发OpenLED指令会点亮LED,下发CloseLED会熄灭LED灯
5、当服务器连接断开的时候,会自动进行重新连接服务器,并重新发送数据

在这里插入图片描述

版权声明:本文为CSDN博主「AIoT-韶华分明」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42523645/article/details/120177865

生成海报
点赞 0

AIoT-韶华分明

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

W5500+STM32F103C8T6进行TCP通信(modbus)

一、W5500以太网模块介绍 Niren_W5500模块是一款基于WIZnet W5500芯片的以太网模块,是泥人电子继 Niren_W5100模块后设计的一块性能更好、性价比更高的以太网模块。模块集成硬件化TCP/IP协议:内