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
暂无评论