快速索引
关于ESP8266的介绍我已经看到恶心了,相同的内容到处都是,没有几个有实际价值的适合新手刚刚入门的内容。博主经过亲身实验,在成功之后在这里记录一下。一来方便引导新手,二来方便大家交流学习,最重要的是我时间久了容易忘记内容,方便自己进行查找。目前仅仅是连接通过TCP连接访问服务器
在这里说一下,本文只是ESP8266简单的使用,没有过于详细的命令讲解关于命令可以查询相应的手册,或者网络资料非常多。我使用的是独立小模块ESP-01S,就是这种:
不是直接又控制芯片的ESP32。
这里是我购买的产品的公司的AT指令示例
能用到的代码我会尽量展示全。
先献上源码链接:https://share.weiyun.com/FdQJ0Baq 密码:bkvc6y
1.硬件需求
如果仅仅是测试那只需要电脑和TTL电平转USB的转换器就好了,然后电脑打开串口调试助手,接线完成就可以实验了。如果没有合适的串口调试助可以点这个?
几款好用的串口和网络调试助手
(恬不知耻地在这为自己宣传)
如果你想连接STM32,难么需要在单片机上留出一个USART,配置成115200波特率的8N1。部分模块需要增加一个使能管脚,如果你想也可以在为它留一个外部复位管脚(反正我是都没有,不过也有这个想法,但是资源不允许呀)。
2.操作流程
1.ESP8266 station模式的操作逻辑流程:
0.检查AT命令是不是能用
1.配置成Sation模式
2.连接到wifi
2.1可以查询一下自己的地址
3.连接到服务器
4.发送数据
5.如果是单链接模式,想更换服务器,先断开现在连接的
具体为啥要单链接:因为透传模式必须是单链接。
啥是透传模式:就是给啥转发啥,不管是数据还是命令。也就是说透传模式不会识别命令,具体怎么玩后面专门记录。
2.ESP8266 AP模式的操作逻辑流程
0.检查AT命令是不是能用
1.配置成AP模式
2.配置AP信息
2.1可以查询一下自己的地址
3.开启多连接(如果要开服务器的话,必须是多连接)
4.开启服务器和设置端口(一个命令)
5.等待接收数据
3.透传模式的进去和退出
配置透传模式需要几个命令:
建立好连接后:
- 设置成单链接模式(只能在单链接模式)
AT+CIPMUX=0 - 设置为透传模式
AT+CIPMODE=1 - 进入透传模式
AT+CIPSEND
具体时先建立连接在设置透传模式,还是先设置为透传模式在建立连接都可以。但是一定在进去透传模式命令发送前建立连接。在模块给你发完 ‘>’ 符号时就设置完成了。
然后就可以欢乐的传数据了,你发啥它转发啥。如果你在电脑上开了电脑的上的网络调试助手你可以看到模块给你发的内容:
发现什么问题没?
–为啥我的AT命令也直接发出去了?!
在透传模式下无论你给ESP8266发啥,它都会无脑转出去,即使是命令(此刻的模块已经放飞自我,不受控制了)。如果想要重新可以获得控制权就需要向它发送退出透传命令。
–EXM?都说了它已经不听命令了,咋还说要发命令!垃圾博主,说的屁话,不看了!
大哥留步!这是一条非常特殊的命令!它长得就和人家不一样!
+++
你没看错,就是三个加号。在这里多说一句,所有的AT命令后面都要加回车换行就是C语言的转义字符 \r\n ,不然不识别。但是这里一!定!不!要!加!为啥? 因为人家就是不一样。
这里就要说到一个坑。退出透传命令必须要发送前有时间间隔,发送后有时间间隔,根据我实验,最短200ms即可(有些资料上写需要1s)。这里实验效果不明显,就不展示了,各位有兴趣可以自己亲自实验。
–既然透传模式设置和退出这么麻烦,那为什么要设置透传模式?
在发送大量数据,或者我们建立好连接后就需要反反复复发数据,而不会控制硬件设备的操作时,透传就体现除优越性了。至少可以减少每次发数据的命令。
4.C语言程序源码
扯了这么多,你是不是差点忘了你来是为了啥(别以为我不知道,这也是我曾经整个CSDN找资料的目的)。程序的逻辑和代码就在这里(使用的是Keil,所以代码注释在这里显示乱码,不要管,我另外有注释)。
理一下思路先:
1.我们需要控制串口收发命令。具体发送的是ASCLL码。
2.我们需要通过串口接收数据,接收的也是SACLL码。
3.我们要能识别什么时候模块发送完了。使用定时器计时,需要定义一个全局变量 ESP_RX_STATE 作为串口接收到数据的标志
4.接收完能够处理接收的结果(这里只讲返回数据识别)
1.配置串口
先配置一下USART2,具体配置方法不做多余解释,TXD->PA2、RXD->PA3。代码如下:
//宏定义
//ÍøÂç´®¿ÚʱÖÓ
#define ESP_RCC RCC_APB1Periph_USART2
//ÍøÂç´®¿Ú·¢ËͶ˿Ú
#define ESP_TX_PIN GPIO_Pin_2 //PA2
#define ESP_TX_GPIO GPIOA
//ÍøÂç´®¿Ú½ÓÊն˿Ú
#define ESP_RX_PIN GPIO_Pin_3 //PA3
#define ESP_RX_GPIO GPIOA
#define ESP_USART USART2
//wifi串口初始化 wifi´®¿Ú³õʼ»¯
void ESP_Usart_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //¿ªÆôGPIOAʱÖÓ
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //¿ªÆô¶Ë¿Ú¸´ÓÃʱÖÓ
RCC_APB1PeriphClockCmd(ESP_RCC, ENABLE); //¿ªÆôesp´®¿ÚʱÖÓ
USART_DeInit(ESP_USART); //¸´Î»esp´®¿Ú
//发送端口 ·¢ËͶ˿Ú
GPIO_InitStructure.GPIO_Pin = ESP_TX_PIN; //PA2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //¸´ÓÃÍÆÍìÊä³ö
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ESP_TX_GPIO, &GPIO_InitStructure);
//接收端口 ½ÓÊն˿Ú
GPIO_InitStructure.GPIO_Pin = ESP_RX_PIN; //PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //¸´Óø¡¿ÕÊäÈë
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ESP_RX_GPIO, &GPIO_InitStructure);
//ÅäÖô®¿Úģʽ
USART_DeInit(ESP_USART);
USART_InitStructure.USART_BaudRate = 115200; //ÉèÖò¨ÌØÂÊΪ115200bps
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //Êý¾ÝλΪ8λ
USART_InitStructure.USART_StopBits = USART_StopBits_1; //ֹͣλ1λ
USART_InitStructure.USART_Parity = USART_Parity_No; //ÎÞУÑéλ
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //·¢ËÍ¡¢½ÓÊÕģʽ´ò¿ª
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //ÎÞÓ²¼þ¿ØÖÆÁ÷¿Ø
USART_Init(ESP_USART, &USART_InitStructure); //´«ËÍÅäÖòÎÊý
USART_ClearFlag(ESP_USART, USART_FLAG_CTS); //Çå³ý·¢ËÍÍê³É±ê־λ
USART_Cmd(ESP_USART, ENABLE); //ʹÄÜ´®¿Ú1
USART_ITConfig(ESP_USART, USART_IT_RXNE, ENABLE); //¿ªÆô½ÓÊÕÖÐ¶Ï £¨1×Ö½ÚÒ»´ÎÖжϣ©
// USART_ITConfig(ESP_USART, USART_IT_IDLE, ENABLE); //¿ªÆô¿ÕÏÐÖжϣ¨8×Ö½ÚÒ»´ÎÖжϣ©
//ÉèÖÃNVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //ÉèÖô®¿Ú2ÖжÏ
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0; //ÇÀÕ¼ÓÅÏȼ¶
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //×ÓÓÅÏȼ¶Îª1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //ʹÄÜ
NVIC_Init(&NVIC_InitStructure);
printf("## ESP_usart_config_over ##\r\n");
}
2.配置定时器
还需要一个定时器,我是用的是TIM3,配置成10ms中断,并写中断函数
//定义一个全局变量用来指示是否接收到数据
//超过10ms没有收到数据认为接收结束,则ESP_RX_STATE增加1。即定时器中断就加1
u8 ESP_RX_STATE = 0; //³õʼ»¯ÒѽÓÊÕÊý¾ÝΪ0
//在头文件中声明外部变量,这样可以在其他C文件中调用
extern u8 ESP_RX_STATE; //½ÓÊÕÍê³É״̬
//定时器TIM3中断优先级配置¶¨Ê±Æ÷TIM3ÖжÏÓÅÏȼ¶ÅäÖÃ
void TIM3_NVIC_config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //ÉèÖÃ×éÓÅÏȼ¶
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //ÉèÖö¨Ê±Æ÷3ÖжÏÏß
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //ÉèÖÃÇÀÕ¼ÓÅÏȼ¶
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //ÉèÖÃ×ÓÓÅÏȼ¶2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //ʹÄÜIRQÖжÏ
NVIC_Init(&NVIC_InitStructure);
}
//定时器TIM3初始化,10ms¶¨Ê±Æ÷TIME3³õʼ»¯,10ms
void TIM3_init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //TIM3³õʼ»¯½á¹¹Ìå
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //¿ªÆôʱÖÓ
TIM_DeInit(TIM3); //½«ÍâÉèTIM3¼Ä´æÆ÷ÖØÉèΪȱʡֵ
TIM_TimeBaseStructure.TIM_Period = (2000-1); //×Ô¶¯ÖØ×°ÔؼĴæÆ÷Öµ
TIM_TimeBaseStructure.TIM_Prescaler =(3600-1); //ʱÖÓÔ¤·ÖƵÊý
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //²ÉÑù·ÖƵ£¬Ê¹¼ÆʱÆ÷¹¤×÷ƵÂÊΪ72MHz
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//ÏòÉϼÆÊýģʽ
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //ÅäÖö¨Ê±Æ÷
TIM3_NVIC_config(); //ÅäÖÃTIM3ÖжÏÓÅÏȼ¶
TIM_ClearFlag(TIM3, TIM_FLAG_Update); //Çå³ý±ê־λ
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); //ÖжÏÅäÖÃ
// TIM_Cmd(TIM3, ENABLE); //ʹÄܶ¨Ê±Æ÷TIM3
}
//TIM3中断函数TIM3ÖжϺ¯Êý
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //²úÉúÖжÏ
{
ESP_RX_STATE ++; //超过10ms,数据接收完毕,计数+1 ³¬¹ý10ms£¬Êý¾Ý½ÓÊÕÍê±Ï£¬¼ÆÊý+1
}
TIM_ClearITPendingBit(TIM3, TIM_FLAG_Update); // Çå³ýÖжϱêÖ¾
TIM_Cmd(TIM3, DISABLE); //¹Ø±ÕTIM3
}
3.准备接收缓冲区
在准备接收数据之前得先准备一个地方用来存储数据吧。我们为了简洁高效,使用循环队列作为数据接收的缓冲区
//宏定义缓冲队列最大存储上限
#define ESP_BUF_SIZE 1024 //ESP»º³åÇø×î´óÈÝÁ¿
//循环对列结构体
typedef struct
{
unsigned char buf[ESP_BUF_SIZE];
unsigned short int length;
unsigned short int fornt; //ESP¶ÓÁÐÍ·Ö¸Õë
unsigned short int rear; //ESP¶ÓÁÐβָÕë
}ESP_BufTypeDef; //¶¨ÒåESPÑ»·¶ÓÁлº³åÇø½á¹¹Ìå
//声明一个队列缓冲区,作为全局变量
ESP_BufTypeDef ESP_RX_BUF; //Wifi´®¿Ú½ÓÊÕ»º³åÇø
//在头文件中声明外部变量,这样可以在其他C文件中调用
extern ESP_BufTypeDef ESP_RX_BUF; //Wifi´®¿Ú½ÓÊÕ»º³åÇø
//接收缓冲区初始化ESP½ÓÊÕ»º³åÇø³õʼ»¯
void ESP_Rxbuf_Init(void)
{
int i ;
memset(ESP_RX_BUF.buf,0,sizeof(ESP_RX_BUF.buf)); //使用memset()函数需要包含头文件<string.h>
// for(i=0;i< ESP_BUF_SIZE; i++)
// {
// ESP_RX_BUF.buf[i] = 0;
// }
ESP_RX_BUF.fornt = 0;
ESP_RX_BUF.length = 0;
ESP_RX_BUF.rear = 0;
ESP_RX_STATE = 0; //ÔÊÐí½ÓÊÕÊý¾Ý
}
到这里接收的数据存储位置也准备好了。
4.串口中断函数
可是定时器中断函数好了,存储位置准备好了,没有数据过来也是不行的呀。那就来写数据接收过程,即网络串口接收中断函数(USART2的中断函数):
//串口接收中断函数´®¿Ú½ÓÊÕÖжϺ¯Êý
void USART2_IRQHandler(void)
{
u8 rev_byte;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //Åж϶ȼĴæÆ÷ÊÇ·ñΪ·Ç¿Õ
{
rev_byte = USART_ReceiveData(USART2); //接收数据½ÓÊÕÊý¾Ý
if(ESP_RX_BUF.length <= ESP_BUF_SIZE) //如果数据没有溢出Èç¹ûÊý¾ÝûÓÐÒç³ö
{
ESP_RX_BUF.buf[ESP_RX_BUF.rear] = rev_byte; //将数据存入缓冲区尾部½«Êý¾Ý´æÈëBUFF»º³åÇøβ²¿
ESP_RX_BUF.length ++; //缓冲区长度增加»º³åÇø³¤¶ÈÔö¼Ó
ESP_RX_BUF.rear = (ESP_RX_BUF.rear + 1) % ESP_BUF_SIZE; //尾指针+1,防止溢出,实现循环队列·ÀÖ¹Òç³ö
}
//这里的TIM3其实可以用宏定义,这样可以方便更改使用的定时器
TIM_SetCounter(TIM3,0); //定时器清空¼ÆÊýÆ÷Çå¿Õ
TIM_Cmd(TIM3,ENABLE); //使能定时器ʹÄܶ¨Ê±Æ÷
}
USART_ClearITPendingBit(USART2, USART_IT_RXNE); //清除标志Çå³ý±êÖ¾
}
如此,我们就可以将接收到的数据保存起来了。
5.读取缓冲队列函数
但是好像还是不行,只是保存起来,却不能使用,你说难不难受。
//读取ESP队列的数据¶ÁÈ¡espÊý¾Ý½ÓÊÕ¶ÓÁеÄÊý¾Ý
u8 ESP_Read_Quene_Data(ESP_BufTypeDef* Rx_buf)
{
u8 read_data_temp;
if(Rx_buf->length == 0) //没有数据
{
read_data_temp = 0;
}
else
{
read_data_temp = Rx_buf->buf[Rx_buf->fornt]; //读出头指针所指的数据
Rx_buf->fornt = (Rx_buf->fornt + 1) % ESP_BUF_SIZE;//头指针增加
Rx_buf->length --; //数据长度减少
}
return read_data_temp; //返回读取的结果
}
读取数据是不是很简单。
6.串口发送数据函数
既然可以接收和读取了,那怎么发送数据呢?这里就要使用到串口发送数据的方法了,如果回使用或者已经写好的可跳过:
/*-----------------------------------------
*串口发送字符串函数´®¿Ú·¢ËÍ×Ö·û´®º¯Êý
* USARTx Ö¸¶¨·¢Ë͵Ĵ®¿Ú
* *DataÖ¸¶¨´ý·¢Ë͵Ä×Ö·û´®
* 发送遇到字符串结束符‘\0’结束·¢ËÍÓöµ½×Ö·û´®½áÊø·û'\0'½áÊø
-----------------------------------------*/
//这个函数讲真写的不是太好,有更好的大家可以修改
void USART_Send_String(USART_TypeDef* USARTx, u8 *Data, ...)
{//使用变长参数,具体实现原理可百度
//·ÖÅäÄÚ´æ
va_list valist;
//ΪÊäÈëµÄ²ÎÊý³õʼ»¯
va_start(valist, Data);
while(*Data != '\0')
{
USART_SendData(USARTx, *Data);
while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); //µÈ´ý´®¿Ú·¢ËÍÍêÊý¾Ý
Data++;
}
//ÇåÀíΪvalist±£ÁôµÄÄÚ´æ
va_end(valist);
}
至此,我们的准备工作就算完成了,要真正开始操刀对ESP8266下手了。
7.读取回复信号函数
根据手册,我们可以知道,我们可以给ESP8266发送命令,然后它会回给我们相应的结果。我们也可以给它发数据,但是要先发相应的命令,然后跟着发送数据。所以我们先写发送命令函数,但是好像有个问题:发送完命令紧跟着是得到命令的结果,不然我们也不知道模块有没有执行。所以不要着急开始写CMD函数,先来写回复函数:
//讲真这是非常失败的函数,但是可以实现
//宏定义返回值
#define ACK_SUCCESS 1
#define ACK_DEFEAT 0
u8 ESP_Check_Ack(char *string) //¼ì²éÏàÓ¦»Ø¸´ÐźÅ
{
char *str_temp; //被检测的字符串的临时变量
u8 data_temp;
u8 rx_buff_temp[150]={0}; //临时存放接收到的数据ÁÙʱ´æ´¢½ÓÊÕµ½µÄÊý¾Ý
u8 pt_temp = 0; //rx_buff_temp的下标rx_buff_tempµÄϱê
u8 length_temp = 0; //临时数组的当前长度
str_temp = string;
if(ESP_RX_STATE > 0) //Èç¹û½ÓÊÕÊý¾Ý½áÊø
{
//»ñÈ¡ÊÕµ½µÄ»Ø¸´ ÖÁÁÙʱÊý×飬±ãÓÚÅжÏ
do
{
if(length_temp < 150)
{
data_temp = ESP_Read_Quene_Data(&ESP_RX_BUF);//从缓冲区读取一个值到数组
rx_buff_temp[pt_temp] = data_temp;
pt_temp ++; //下标向后移动一位
length_temp ++;
}
else
break; //³¬³ö´æ´¢ÉÏÏÞ£¬Ã»ÓÐÊÕµ½¡®\0¡¯Ôò»áËÀÑ»·£¬Ìø³öµ±Ç°Ñ»·
}
while(data_temp != '\0');
ESP_RX_STATE --; //读取完一个字符串,接收区计数减一¶ÁÈ¡ÍêÒ»¸ö½ÓÊÕµÄ×Ö·û´®£¬¼ÆÊý-1
}
//ÅжÏÊÇ·ñ·ûºÏÆÚÍûÐźÅ
if(strstr((const char *)rx_buff_temp, (const char *)str_temp ) != NULL) //检测到有想要的回复,就能得到不为空的地址
return ACK_SUCCESS; //返回成功Èç¹û·µ»ØµØÖ·²»Îª¿Õ£¬·µ»ØÓÐЧ
else
return ACK_DEFEAT; //如果没有,就是失败·ñÔò£¬·µ»Øʧ°Ü
}
8.发送命令函数
有检查回复信号的函数后就可以写命令函数了:
/*--------------------------------------------------
* ·¢ËÍÖ¸ÁϣÍûµÃµ½»Ø¸´
* *cmd 发送的命令
* *ack 希望得到的回复
* waittime 等待的时长
* ·µ»ØÖµ ACK_SUCCESS£¬·¢Ëͳɹ¦
* ACK_DEFEAT£¬·¢ËÍʧ°Ü
---------------------------------------------------*/
u8 ESP_Send_Cmd(char *cmd, char *ack, u16 waittime )
{//其实这里也可以写成边长参数的,而且会极大的方便后面发送命令,不过博主是写完功能了才学会的边长参数,后面我会在我程序中修改
USART_Send_String(ESP_USART, (u8 *)cmd); //直接使用发送字符串函数
while(waittime-- )
{
if(ESP_Check_Ack(ack) == ACK_SUCCESS)
{
return ACK_SUCCESS;
}
delay_ms(10); //每次等待10ms,所以waittime为20的时候,实际等待200ms
}
return ACK_DEFEAT;
}
是不是很简单。
9.发送数据函数
下面就是发送数据函数,之所以最后写,是因为发数据之前要发送一个命令,告诉ESP8266我要发数据了:
/*----------------------------------------------------
* ·¢ËÍÊý¾Ý¸øwifiÄ£¿é£¬
* *data为要发送的数据*dataΪ·¢Ë͵ÄÊý¾Ý
----------------------------------------------------*/
void ESP_Send_Data(u8 *data)
{
char cmdbuf_temp[32]; //整合命令使用的临时数组
unsigned short len; //发送数据的长度
len = sizeof(data);
sprintf(cmdbuf_temp,"AT+CIPSEND=%d\r\n", len);//格式化的命令写入命令字符串中,就是把长度加上°Ñ¸ñʽ»¯µÄÊý¾ÝдÈëij¸ö×Ö·û´®ÖÐ
if(ESP_Send_Cmd(cmdbuf_temp, ">", 200) == ACK_SUCCESS) //检测接收数据是否包含‘>’¼ì²é½ÓÊÕÊý¾ÝÊÇ·ñΪ¡°>¡±
{
USART_Send_String(ESP_USART, data); //发送数据·¢ËÍÊý¾Ý
}
}
因为本篇写的是ESP8266连接服务器的方法,所以作为AP时候使用的接收数据解析IPD就不写了。至此,所有函数写完了。
10.连接TCP并发送数据演示
在使用的时候,先把ESP8266的端口配置初始化,缓冲区初始化。然后愉快的使用ESP_Send_Cmd函数发送命令,控制模块就好了。
这里我展示一个连接TCP并开启透传模式的过程。只是简单演示如何使用,在实际发送命令过程中返回失败还是很多的,所以要有容错,读者自己思考。
#define wifi_information "AT+CWJAP_DEF=\"wifi名称\",\"wifi密码\"\r\n"
#define tcp_information "AT+CIPSTART=\"TCP\",\"TCP地址\",端口号\r\n"
if(ESP_Send_Cmd("AT\r\n","OK",20) == ACK_DEFEAT) //测试一下AT命令
{
delay_ms(50);
ESP_Send_Cmd("+++","",0); //Í˳ö͸´«Ä£Ê½
delay_ms(50);
}
ESP_Send_Cmd("AT+CWMODE=1\r\n","OK",20);//设置成Station模式
//一定要重启!!!
ESP_Send_Cmd("AT+RST\r\n","ready",20);
delay_ms(1000); //一般1s就可以,也可以设置时间长一些
ESP_Rxbuf_Init(); //往往启动是时候模块会发版本信息,清空一下
printf("Connecting to Wifi.\r\n");
if(ESP_Send_Cmd("AT+CWJAP_DEF?\r\n","No AP",50) == ACK_SUCCESS) //返回没有连接AP·µ»ØûÓÐÁ¬½ÓAP
{
for(count_up = 0; count_up < 5; count_up ++ ) //Ñ»·Á¬½Ó5´Î
{
if(ESP_Send_Cmd(wifi_information, "WIFI GOT IP", 600) == ACK_SUCCESS) //如果连接成功Èç¹ûÁ¬½Ó³É¹¦
{
printf("Have conntected.\r\n");
break;
}else
{
printf("Rejoin Wifi.\r\n");
}
}
}
//此处可以添加如果重连5次都失败的处理方法
/*有时候遇到部分8266在链接TCP后不能进行单路链接设置*/
ESP_Send_Cmd("AT+CIPMUX=0\r\n","OK",15); //单路连接模式
ESP_Send_Cmd(tcp_information, "CONNECT", 50); //连接到TCP
//这里其实就可以使用发送数据函数ESP_Send_Data开始发数据了
//ESP_Send_Data("Hllow word!\n")
ESP_Send_Cmd("AT+CIPMODE=1\r\n","OK",20); //进去透传模式
ESP_Send_Cmd("AT+CIPSEND\r\n","OK",20); //进入透传模式
//透传模式直接使用串口发送函数即可
USART_Send_String(USART2, "Anything!");
大功告成!
博主写的已经脑袋晕了,如果有错误烦请指正,我再修改
/----------------------------------------------------------------------------------------/
2020.03.23更新
发现朋友们对我的博文很感兴趣,我在这里更新一下相关代码工程的源文件。完整工程内容驳杂,加之我的代码水平不是太好,不方便初学者看,会走火入魔的。
MDK工程资源链接
https://download.csdn.net/download/qq_40329772/12307863
我把串口2的配置放在了ESP8266源文件里面了。按照有些人的习惯会将他独立出来,方到USART的相关文件里面,条理更清晰,便于代码管理维护。
以下内容谨慎观看
/*ESP8266.h*/
#ifndef __ESP8266_H_
#define __ESP8266_H_
#include "stm32f10x.h"
#define ESP_BUF_SIZE 680 //ESP缓冲区长度
#define ESP_RX_END 0x01 //接收数据结束
#define ESP_RX_ALLOW 0x00 //允许接收数据/正在接收数据
#define ACK_SUCCESS 1
#define ACK_DEFEAT 0
/*-------------TCP地址和端口--------------*/
//时间地址
#define ESP_TIME_TCP_ADDRESS "quan.suning.com"
#define ESP_TIME_TCP_POINT "80"
#define ESP_TIME_INFO "GET http://quan.suning.com/getSysTime.do HTTP/1.1\r\nHost: quan.suning.com\r\n\r\n" //GET请求时间
//
#define ESP_WEATHER_TCP_ADDRESS "api.seniverse.com" //心知天气
#define ESP_WEATHER_TCP_POINT "80"
#define ESP_WEATHER_INFO "GET http://api.seniverse.com/v3/weather/now.json?key=写入自己的密钥&location=地区&language=en&unit=c HTTP/1.1\r\nHost: api.seniverse.com\r\n\r\n" //GET请求,密钥和地区需要手动写入,为实现自动定位
//这里是解析时间时使用的便宜地址
#define YERA_ADD_DRES 11
#define MOON_ADD_DRES 15
#define DAYS_ADD_DRES 17
#define HOURS_ADD_DRES 19
#define MINUTES_ADD_DRES 21
#define SECONDS_ADD_DRES 23
#pragma pack(1)
typedef struct
{
unsigned char buf[ESP_BUF_SIZE];
unsigned short int length;
unsigned short int fornt; //ESP队列头指针
unsigned short int rear; //ESP队列尾指针
}ESP_BufTypeDef; //定义ESP循环队列缓冲区结构体
#pragma pack()
extern ESP_BufTypeDef ESP_RX_BUF; //Wifi串口接收缓冲区
extern ESP_BufTypeDef ESP_TX_BUF; //Wifi串口发送缓冲区,实际没使用
extern u8 ESP_RX_STATE; //接收完成状态
extern int WEATHER;
extern int TEMPERATURE;
//网络串口时钟
#define ESP_RCC RCC_APB1Periph_USART2
//网络串口发送端口
#define ESP_TX_PIN GPIO_Pin_2 //PA2
#define ESP_TX_GPIO GPIOA
//网络串口接收端口
#define ESP_RX_PIN GPIO_Pin_3 //PA3
#define ESP_RX_GPIO GPIOA
#define ESP_USART USART2
//wifi模块使能端口
#define ESP_CS_RCC RCC_APB2Periph_GPIOA
#define ESP_CS_PIN GPIO_Pin_4 //PA4
#define ESP_CS_GPIO GPIOA
#define ESP_CS_H GPIO_SetBits(ESP_CS_GPIO, ESP_CS_PIN) //ESP CS pin high
#define ESP_CS_L GPIO_ResetBits(ESP_CS_GPIO, ESP_CS_PIN) //ESP CS pin low
void ESP_Usart_Config(void);
void ESP_Rxbuf_Init(void); /*初始化缓冲区函数*/
u8 ESP_Connect_TCP(const char *tcp_addr, char *point);
void ESP_Set_Link_Mode(char mode);
u8 ESP_Quit_Trans(void);
u8 ESP_Read_Quene_Data(ESP_BufTypeDef* Rx_buf);
u8 ESP_Check_Ack(char *string);
u8 ESP_Send_Cmd(char *cmd, char *ack, u16 waittime);
void ESP_Send_Data(u8 *data);
void ESP_Init(void); /*初始化模块函数,主函数调用*/
u8 ESP_Set_Stationmode(void);
u8 ESP_Set_APmode(char *ap_ssid, char *ap_pwd, char chl, char ecn);
u8 ESP_Connect_AP(const char *wifi_ssid, const char *wifi_password);
void ESP_Set_Link_Mode(char mode);
u8 ESP_Connect_TCP(const char *tcp_addr, char *point);
void ESP_Set_Trans(void);
u8 ESP_Start_Trans(void);
u8 ESP_Init_Time(void); /*初始化时间函数,主函数调用*/
u8 ESP_Fefresh_NET_Massage(const char *tcp_addr, char *point, void (*function)());
u8 ESP_Get_Wifiinfo(char *wifi_info);
void GET_Net_Time(void); /*获取时间函数,主函数调用*/
void Get_NET_Waether(void); /*获取天气函数,主函数调用*/
int Get_Day(char *d);
int Get_Week(char *w);
int Get_Moonth(char *m);
int Get_Year(char *y);
int Get_Times(char *h, char *m, char *s);
void Print_ESP_Rx_Buf(void);
#endif
/*ESP8266.C*/
#include "stm32f10x.h"
#include "esp8266.h"
#include "font.h" //字体文件,用于显示屏幕
#include <string.h>
#include <stdlib.h>
ESP_BufTypeDef ESP_RX_BUF; //Wifi串口接收缓冲区
u8 ESP_RX_STATE = 0; //初始化已接收数据为0
/*天气和温度变量*/
int WEATHER = 0;
int TEMPERATURE = 0;
/*WIFI连接使用*/
char ESP_WIFI_SSID[125] = "MN001";
char ESP_WIFI_PASSWORD[125] = "MN001789";
//wifi串口初始化
void ESP_Usart_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启端口复用时钟
RCC_APB1PeriphClockCmd(ESP_RCC, ENABLE); //开启esp串口时钟
USART_DeInit(ESP_USART); //复位esp串口
// 发送端口
GPIO_InitStructure.GPIO_Pin = ESP_TX_PIN; //PA2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ESP_TX_GPIO, &GPIO_InitStructure);
// 接收端口
GPIO_InitStructure.GPIO_Pin = ESP_RX_PIN; //PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //复用浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ESP_RX_GPIO, &GPIO_InitStructure);
//配置串口模式
USART_DeInit(ESP_USART);
USART_InitStructure.USART_BaudRate = 115200; //设置波特率为115200bps
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位为8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位1位
USART_InitStructure.USART_Parity = USART_Parity_No; //无校验位
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送、接收模式打开
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件控制流控
USART_Init(ESP_USART, &USART_InitStructure); //传送配置参数
USART_ClearFlag(ESP_USART, USART_FLAG_CTS); //清除发送完成标志位
USART_Cmd(ESP_USART, ENABLE); //使能串口1
USART_ITConfig(ESP_USART, USART_IT_RXNE, ENABLE); //开启接收中断 (1字节一次中断)
//设置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //设置串口2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
printf("## ESP_usart_config_over ##\r\n");
}
/*串口接收中断函数*/
void USART2_IRQHandler(void)
{
unsigned char rev_byte;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //判断度寄存器是否为非空
{
rev_byte = USART_ReceiveData(USART2); //接收数据
// if(ESP_RX_STATE == ESP_RX_ALLOW) //允许接收数据,即接收完的数据已经处理
{ /*这里是入队函数,可以封装出来*/
if(ESP_RX_BUF.length <= ESP_BUF_SIZE) //如果数据没有溢出
{
ESP_RX_BUF.buf[ESP_RX_BUF.rear] = rev_byte; //将数据存入BUFF缓冲区尾部
ESP_RX_BUF.length ++; //缓冲区长度增加
ESP_RX_BUF.rear = (ESP_RX_BUF.rear + 1) % ESP_BUF_SIZE; //防止溢出
}
TIM_SetCounter(TIM3,0); //计数器清空
TIM_Cmd(TIM3,ENABLE); //使能定时器
}
}
USART_ClearITPendingBit(USART2, USART_IT_RXNE); //清除标志
}
//ESP接收缓冲区初始化
void ESP_Rxbuf_Init(void)
{
memset(ESP_RX_BUF.buf,0,sizeof(ESP_RX_BUF.buf));
ESP_RX_BUF.fornt = 0;
ESP_RX_BUF.length = 0;
ESP_RX_BUF.rear = 0;
ESP_RX_STATE = 0; //允许接收数据
}
//读取esp数据接收队列的数据
u8 ESP_Read_Quene_Data(ESP_BufTypeDef* Rx_buf)
{
u8 read_data_temp;
if(Rx_buf->length == 0)
{
read_data_temp = 0;
}
else
{
read_data_temp = Rx_buf->buf[Rx_buf->fornt];
Rx_buf->fornt = (Rx_buf->fornt + 1) % ESP_BUF_SIZE;
Rx_buf->length --;
}
return read_data_temp;
}
u8 ESP_Check_Ack(char *string) //检查相应回复信号
{
char *str_temp;
u8 data_temp;
u8 rx_buff_temp[150]={0}; //临时存储接收到的数据
u8 pt_temp = 0; //rx_buff_temp的下标
u8 length_temp = 0;
str_temp = string;
if(ESP_RX_STATE > 0) //如果接收数据结束
{
//获取收到的回复 至临时数组,便于判断
do
{
if(length_temp < 150)
{
data_temp = ESP_Read_Quene_Data(&ESP_RX_BUF);
rx_buff_temp[pt_temp] = data_temp;
pt_temp ++;
length_temp ++;
// printf("## data%d :%c ##\r\n",length_temp,data_temp);
}
else
break; //超出存储上限,没有收到‘\0’则会死循环,跳出当前循环
}
while(data_temp != '\0');
ESP_RX_STATE --; //读取完一个接收的字符串,计数-1
}
//判断是否符合期望信号
if(strstr((const char *)rx_buff_temp, (const char *)str_temp ) != NULL)
return ACK_SUCCESS; //如果返回地址不为空,返回有效
else
return ACK_DEFEAT; //否则,返回失败
}
//发送指令,希望得到回复
//返回值 ACK_SUCCESS,发送成功
// ACK_DEFEAT,发送失败
u8 ESP_Send_Cmd(char *cmd, char *ack, u16 waittime )
{
USART_Send_String(ESP_USART, (u8 *)cmd);
while(waittime-- )
{
if(ESP_Check_Ack(ack) == ACK_SUCCESS)
{
return ACK_SUCCESS;
}
delay_ms(10);
}
return ACK_DEFEAT;
}
//发送数据给wifi模块,
// *data为发送的数据
// len为发送数据的长度
void ESP_Send_Data(u8 *data)
{
char cmdbuf_temp[32];
unsigned short len;
len = sizeof(data);
sprintf(cmdbuf_temp,"AT+CIPSEND=%d\r\n", len);//把格式化的数据写入某个字符串中
if(ESP_Send_Cmd(cmdbuf_temp, ">", 200) == ACK_SUCCESS) //检查接收数据是否为“>”
{
USART_Send_String(ESP_USART, data); //发送数据
}
}
void ESP_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ESP_Usart_Config(); //初始化串口
//初始化片选端口
RCC_APB2PeriphClockCmd(ESP_CS_RCC, ENABLE);
GPIO_InitStructure.GPIO_Pin = ESP_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(ESP_CS_GPIO, &GPIO_InitStructure);
ESP_CS_H; //WIFI模块使能
ESP_Rxbuf_Init(); //初始化缓冲区
}
/*-------------------------------------------------
* 设置ESP8266为STATION模式
* 返回值 ACK_SUCCESS 设置成功
* ACK_DEFEAT 设置失败
-------------------------------------------------*/
u8 ESP_Set_Stationmode(void)
{
u8 return_temp = 0;
if(ESP_Send_Cmd("AT\r\n", "OK", 15) == ACK_DEFEAT) //先检查并退出透传模式
{
ESP_Quit_Trans();
}
if(ESP_Send_Cmd("AT+CWMODE?\r\n","+CWMODE:1",20) != ACK_SUCCESS) //如果查询当前不是不是Station模式
{
printf("Set ESP mode.\r\n");
//设置工作模式为Station模式
ESP_Send_Cmd("AT+CWMODE=1\r\n","OK",20);
//重启WIFI
ESP_Send_Cmd("AT+RST\r\n","ready",20);
delay_ms(1000);
ESP_Rxbuf_Init();
}
else
{
printf("Is station mode.\r\n");
}
ESP_Send_Cmd("AT+CWAUTOCONN=1\r\n","",20); //使能上电自动连接AP
if(ESP_Send_Cmd("AT+CWMODE?\r\n","+CWMODE:1",20) == ACK_SUCCESS) //如果查询当前是Station模式
{
return_temp = ACK_SUCCESS; //设置成功
}
else
{
return_temp = ACK_DEFEAT; //设置失败
}
return return_temp;
}
/*-------------------------------------------------
*设置ESP8266为AP模式
* *ap_ssid AP名称字符串
* *ap_pwd AP密码字符串
* chl 通道号
* ecn 加密方式 0:OPEN\ 1:WEP\ 2:WPA_PASK\ 3:WPA2_PASK\ 4:WPA_WPA2_PSK
*返回值 ACK_SUCCESS 设置成功
* ACK_DEFEAT 设置失败
-------------------------------------------------*/
u8 ESP_Set_APmode(char *ap_ssid, char *ap_pwd, char chl, char ecn)
{
u8 return_temp = 0;
char ap_information[150]={0}; //ap热点临时数组
if(ESP_Send_Cmd("AT\r\n", "OK", 15) == ACK_DEFEAT) //先检查并退出透传模式
{
ESP_Quit_Trans();
}
if(ESP_Send_Cmd("AT+CWMODE?\r\n","+CWMODE:2",20) != ACK_SUCCESS) //如果查询当是不是AP模式
{
printf("Set ESP mode.\r\n");
//设置工作模式为AP模式
ESP_Send_Cmd("AT+CWMODE=2\r\n","OK",20);
//重启WIFI
ESP_Send_Cmd("AT+RST\r\n","ready",20);
delay_ms(1000);
ESP_Rxbuf_Init();
}
else
{
printf("Is AP mode.\r\n");
}
sprintf(ap_information, "AT+CWSAP_DEF=\"%s\",\"%s\",%c,%c\r\n", ap_ssid, ap_pwd, chl, ecn); //将AP热点名称和密码整合存入字符串
ESP_Send_Cmd(ap_information, "OK", 100); //发送设置命令
ESP_Send_Cmd("AT+CIPMUX=1\r\n", "OK", 20); //开启多连接,然后才能开启服务器
ESP_Send_Cmd("AT+CIPSERVER=1,80\r\n", "OK", 20); //开启服务器,端口号为80
if(ESP_Send_Cmd("AT+CWMODE?\r\n","+CWMODE:2",20) == ACK_SUCCESS) //如果查询当前是AP模式
{
return_temp = ACK_SUCCESS; //设置成功
WIFI_LOGO = SHOW;
}
else
{
return_temp = ACK_DEFEAT; //设置失败
}
Print_ESP_Rx_Buf();
return return_temp;
}
/*-------------------------------------------------
* 连接WiFi热点
* 返回值 ACK_SUCCESS 连接成功
* ACK_DEFEAT 连接失败
-------------------------------------------------*/
u8 ESP_Connect_AP(const char *wifi_ssid, const char *wifi_password)
{
char wifi_information[150]={0}; //WiFi热点临时数组
short int count_up = 0; //重连次数
u8 return_temp = 0;
//建立WiFi连接前先查询ip是否已经连接,是则不在连接wifi
if(ESP_Send_Cmd("AT+CIPSTATUS\r\n","STATUS:",20) != ACK_SUCCESS) //返回没有连接AP
{
printf("Will conntect to AP %s.\r\n", ESP_WIFI_SSID);
Show_String8(7,0,"Connecting to Wifi:");
delay_ms(700);
Show_String8(7,0,(char *)wifi_ssid);
delay_ms(700);
sprintf(wifi_information, "AT+CWJAP_DEF=\"%s\",\"%s\"\r\n", wifi_ssid, wifi_password); //将WiFi热点名称和密码整合存入字符串
//建立WIFI连接
for(count_up = 0; count_up < 5; count_up ++ ) //循环连接5次
{
if(ESP_Send_Cmd(wifi_information, "WIFI GOT IP", 600) == ACK_SUCCESS) //如果连接成功
{
printf("Have conntected.\r\n");
Show_String8(7,0,"Have Connected WiFi.");
delay_ms(700);
return_temp = ACK_SUCCESS;
WIFI_LOGO = SHOW;
break;
}
else
{
printf("Rejoin Wifi.\r\n");
Show_String8(7,0,"Rejoin Wifi.");
}
}
}
else
{
printf("Had conntected.\r\n");
return_temp = ACK_SUCCESS;
WIFI_LOGO = SHOW;
Show_String8(7,0,"Had Connected WiFi.");
delay_ms(700);
}
//Print_ESP_Rx_Buf();
if(count_up >=5) //连接失败
{
printf("Conntect failed .\r\n")
return_temp = ACK_DEFEAT;
WIFI_LOGO = HIDE;
}
return return_temp;
}
/*--------------------------------------------------
* 传入的数据应该为 +IPD,设备编号,字节长度:接收内容
* 将接收到的wifi名称和密码数组传递给数组
-------------------------------------------------*/
u8 ESP_Get_Wifiinfo(char *wifi_info)
{
char *ssid = NULL;
char *password = NULL;
int ssid_pt = 0; //WiFi名称数组下标
int pwd_pt = 0; //WiFi密码数组下标
ssid = strstr((const char *) wifi_info, ":") + 1; //得到WIFI名称的第一位地址
if(ssid == NULL)
{
return ACK_DEFEAT;
}
while(*ssid != ';') //在没有遇到密码符号前
{
ESP_WIFI_SSID[ssid_pt] = *ssid;
ssid ++;
ssid_pt ++;
}
password = ssid + 1; // 遇到密码符号后,地址+1,就是密码的第一位地址
while(*password != '\0') //没有遇到结束符号
{
ESP_WIFI_PASSWORD[pwd_pt] = *password;
password ++;
pwd_pt ++;
}
ESP_WIFI_PASSWORD[pwd_pt] = *password; //此时*password 是 \0,写入密码数组做字符串结束标识符
return ACK_SUCCESS;
}
/*-------------------------------------------------
* 设置ESP8266为连接模式
* =0:单路连接模式 =1:多路连接模式
-------------------------------------------------*/
void ESP_Set_Link_Mode(char mode)
{
char str1[12] = {0}; //查询匹配使用的字符串
char str2[14] = {0}; //设置使用的字符串
sprintf(str1, "+CIPMUX:%c", mode); //查询匹配使用的字符串
sprintf(str2, "AT+CIPMUX=%c\r\n", mode); //设置使用的字符串
if((ESP_Send_Cmd("AT+CIPMUX?\r\n",str1,13)) != ACK_SUCCESS)
{
printf("Set single link.\r\n");
ESP_Send_Cmd(str2,"OK",13);
}
}
/*-------------------------------------------------
* 连接tcp
* 返回值 ACK_SUCCESS 连接成功
* ACK_DEFEAT 连接失败
-------------------------------------------------*/
u8 ESP_Connect_TCP(const char *tcp_addr, char *point)
{
char tcp_information[64]={0};
short int count_up = 0; //重连次数
u8 return_temp = 0;
sprintf(tcp_information, "AT+CIPSTART=\"TCP\",\"%s\",%s\r\n", tcp_addr, point); //将TCP,地址和端口号传给临时变量
// printf("%s\n",tcp_information);
for(count_up = 0; count_up < 5; count_up ++ ) //循环连接5次
{
if(ESP_Send_Cmd(tcp_information, "CONNECT", 50) == ACK_SUCCESS) //如果连接成功
{
// Print_ESP_Rx_Buf();
return_temp = ACK_SUCCESS;
break;
}
}
if(count_up >=5) //连接失败
{
return_temp = ACK_DEFEAT;
}
return return_temp;
}
/*-------------------------------------------------
* 设置透传模式命令
-------------------------------------------------*/
void ESP_Set_Trans(void)
{
if((ESP_Send_Cmd("AT+CIPMODE?\r\n","+CIPMODE:1",10)) != ACK_SUCCESS) //如果当前不是透传模式
{
ESP_Send_Cmd("AT+CIPMODE=1\r\n","OK",10);
}
else
{
printf("Is passthrough.\r\n");
}
ESP_Send_Cmd("AT+CIPSEND\r\n","OK",11); //开启透传 至少大于100ms否则不能成功
printf("ESP passthrough mode set over.\r\n");
}
//wifi模块退出透传模式
//返回值 ACK_SUCCESS,退出成功
// ACK_DEFEAT,退出失败
u8 ESP_Quit_Trans(void)
{
// if( ESP_Send_Cmd("AT\r\n","OK",15) == ACK_DEFEAT) //发送AT命令
{
delay_ms(20);
ESP_Send_Cmd("+++","",0); //退出透传模式
delay_ms(50);
}
return ESP_Send_Cmd("AT\r\n","OK",15); //判断是否退出
}
/*---以上ESP8266的驱动函数基本完成 ,下面是我的获取的时间和天气并且解析,有许多不好的地方。最好能够独立一个C文件------------------------------*/
/*-------------------------------------------------
* 初始化获得时间函数
-------------------------------------------------*/
u8 ESP_Init_Time(void)
{
//获取信息
Show_image(bishitu); //界面
Show_String8(0,0,"Please wait ...");
// delay_ms(1000); //等待WIFI连接稳定
// delay_ms(1000);
ESP_Quit_Trans(); //退出透传
ESP_Set_Stationmode(); //设置为STATION工作模式
Show_String8(7,0,"Getting time...");
if(ESP_Send_Cmd("AT+CWJAP_DEF?\r\n","+CWJAP_DEF:",50) != ACK_SUCCESS) //返回没有连接AP
{
if(ESP_Connect_AP(ESP_WIFI_SSID, ESP_WIFI_PASSWORD) != ACK_SUCCESS) //开始连接网络,如果没有成功
{
OLED_clear();
Show_String8(2,0,"Connect to Wifi defeat, please confirm that Wifi exists.");
Show_String8(5,0,"And RESET.");
while(1)
{;}
}
}
ESP_Set_Link_Mode(0);
if(ESP_Connect_TCP(ESP_TIME_TCP_ADDRESS,ESP_TIME_TCP_POINT) != ACK_SUCCESS)//连接时间
{
OLED_clear();
Show_String8(0,0,"Getting time defeat,");
Show_String8(1,0,"Please RESET.");
while(1)
{;}
}
ESP_Set_Trans(); //开启透传模式
ESP_Rxbuf_Init();
GET_Net_Time(); //初始化获取网络时间
ESP_Rxbuf_Init();
ESP_Quit_Trans() ; //退出透传模式
ESP_Send_Cmd("AT+CIPCLOSE\r\v", "CLOSED", 20) ; //断开tcp连接
//ESP_Set_Link_Mode(0);
// Print_ESP_Rx_Buf();
if(ESP_Connect_TCP(ESP_WEATHER_TCP_ADDRESS,ESP_WEATHER_TCP_POINT) != ACK_SUCCESS)//连接天气
{
OLED_clear();
Show_String8(0,0,"Getting weather defeat,");
Show_String8(1,0,"Please RESET.");
while(1)
{;}
}
// Print_ESP_Rx_Buf();
ESP_Set_Trans(); //开启透传模式
ESP_Rxbuf_Init();
Get_NET_Waether(); //获得天气
OLED_clear();
Show_String8(0,0,"Init over.");
ESP_Quit_Trans() ; //退出透传模式
ESP_Send_Cmd("AT+CIPCLOSE\r\v", "CLOSED", 20); //断开tcp连接
return ACK_SUCCESS;
}
/*-------------------------------------------------
* 更新网络时间函数
* 已经连接WIFI,定期连接TCP更新网络时间
-------------------------------------------------*/
u8 ESP_Fefresh_NET_Massage(const char *tcp_addr, char *point, void (*function)())
{
char wifi_information[150]={0}; //WiFi热点临时数组
short int count_up = 0; //重连次数
u8 return_temp = 0;
//检测是否连接wifi
//if(ESP_Send_Cmd("AT+CIPSTATUS\r\n","STATUS:",50) != ACK_SUCCESS) //返回没有连接AP
if(ESP_Send_Cmd("AT+CWJAP_DEF?\r\n","No AP",50) == ACK_SUCCESS) //返回没有连接AP
{
Print_ESP_Rx_Buf();
Show_String8(7,0,"Rejoin Wifi.");
printf("Rejoin Wifi");
sprintf(wifi_information, "AT+CWJAP_DEF=\"%s\",\"%s\"\r\n", ESP_WIFI_SSID, ESP_WIFI_PASSWORD); //将WiFi热点名称和密码整合存入字符串
//建立WIFI连接
for(count_up = 0; count_up < 5; count_up ++ ) //循环连接5次
{
if(ESP_Send_Cmd(wifi_information, "OK", 500) == ACK_SUCCESS) //如果连接成功
{
printf("Have conntected.\r\n");
WIFI_LOGO = SHOW;
Show_String8(7,0," ");
break;
}
}
if(count_up >=5) //连接失败
{
Show_String8(7,0," Wifi missing.");
WIFI_LOGO =HIDE;
return ACK_DEFEAT;
}
}
// Print_ESP_Rx_Buf();
ESP_Set_Link_Mode(0);
if(ESP_Connect_TCP(tcp_addr,point) == ACK_SUCCESS)//连接时间成功
{
ESP_Set_Trans(); //开启透传模式
ESP_Rxbuf_Init();
function(); //可调用获取网络时间函数和获取网络天气函数
ESP_Rxbuf_Init();
ESP_Quit_Trans() ; //退出透传模式
ESP_Send_Cmd("AT+CIPCLOSE\r\v", "CLOSED", 20); //断开TCP连接
}
else
{
return ACK_DEFEAT; //返回失败
}
return return_temp; //更新成功
}
//获取网络时间并解析给系统
void GET_Net_Time(void)
{
char *data_pt = NULL;
char *day_string;
char *moon_string;
char *year_string;
char *hour_string;
char *minute_string;
char *second_string;
USART_Send_String(USART2, ESP_TIME_INFO); //发送GET请求
while(!(ESP_RX_STATE > 0)) //判断接收标志
{
delay_ms(200);
break; //防止卡死
}
data_pt = strstr((const char *) ESP_RX_BUF.buf,(const char *)"sysTime1"); //寻找到时间结果的地址
ESP_RX_STATE --;
if(data_pt != NULL)
{
day_string = data_pt + DAYS_ADD_DRES; //日期地址
moon_string = data_pt + MOON_ADD_DRES; //月份地址
year_string = data_pt + YERA_ADD_DRES; //年份地址
hour_string = data_pt + HOURS_ADD_DRES; //小时地址
minute_string = data_pt + MINUTES_ADD_DRES; //分钟地址
second_string = data_pt + SECONDS_ADD_DRES; //秒中地址
//将时间信息传递给全局变量
DAYS = Get_Day(day_string);
MOONS = Get_Moonth(moon_string);
YEARS = Get_Year(year_string);
TIMES = Get_Times(hour_string, minute_string, second_string);
}
else
{
printf("get net time failed!\r\n");
}
}
//获得日期函数
//输入值是日期位置的地址
//返回值是 整型的10进制两位数
int Get_Day(char *d)
{
int day_return;
day_return = atoi(d)/1000000; //取日期
return day_return;
}
//获得月份函数
//输入值是月份位置的地址
//返回值是 整型的10进制两位数
int Get_Moonth(char *m)
{
int moonth_return;
moonth_return = atoi(m)/100000000; //取月份
return moonth_return;
}
//获得年函数
//输入值是年位置的地址
//返回值是 整型的10进制四位数
int Get_Year(char *y)
{
int year_return;
char *year_temp;
char year[5] = {0};
char i;
//年的获取需要提取一次字符串,否则无法读取
year_temp = y;
for(i = 0; i<4; i++)
{
year[i] = *year_temp;
year_temp ++;
}
year_return = atoi(&year[0]);
return year_return;
}
//获得时间
//输入值是时间的位置的地址
//返回值是 整型的10进制的时间总秒数
int Get_Times(char *h, char *m, char *s)
{
int time_return;
int hour_return;
int min_return;
int sec_return;
hour_return = atoi(h)/10000; //取小时
min_return = atoi(m)/100; //取分钟
sec_return = atoi(s); //取秒数
time_return = hour_return*3600 + min_return*60 + sec_return; //转换成总秒数
return time_return;
}
void Get_NET_Waether(void)
{
char *weather_pt = NULL; //存储天气起始地址
char *temperature_pt = NULL; //存储温度起始地址
int i = 0;
char weather[3] = {0}; //存储天气代码字符串
char temperature[4] = {0};//存储温度字符串
USART_Send_String(USART2, ESP_WEATHER_INFO); //获得天气
while(!(ESP_RX_STATE > 0)) //判断接收标志
{
delay_ms(200);
break; //防止卡死
}
// Print_ESP_Rx_Buf(); //测试使用
weather_pt = strstr((const char *) ESP_RX_BUF.buf,(const char *)"\"code\":") + 8; //寻找到天气结果的地址
temperature_pt = strstr((const char *) ESP_RX_BUF.buf,(const char *)"\"temperature\":") + 15; //寻找到温度结果的地址
for(i = 0; i < 2; i++) //复制天气代码到天气代码缓冲数组
{
weather[i] = *weather_pt;
weather_pt ++;
}
WEATHER = atoi(&weather[0]); //将字符串转为整型
for(i = 0; i < 3; i++) //复制温度到温度缓冲数组
{
temperature[i] = *temperature_pt;
temperature_pt ++;
}
TEMPERATURE = atoi(&temperature[0]); //将字符串转成整型
ESP_RX_STATE --;
}
void Print_ESP_Rx_Buf(void)
{
int i;
for(i=0;i<ESP_BUF_SIZE;i++)
{
printf("%c",ESP_RX_BUF.buf[i]);
}
printf("\n");
}
/*main.c*/
#include "sys1.h"
#include "stm32f10x.h"
#include "uart.h"
#include "timer.h"
#include "oled.h"
#include "spi.h"
#include "esp8266.h"
#include "font.h"
void Head_Init(void)
{
SystemInit();
Systick_init();
Print_Usart_Config(); //日志打印串口初始化
// TIM1_init();
TIM2_init();
TIM3_init();
OLED_init(); //显示屏 端口配置和初始化
ESP_Init(); //WIFI模块端口配置
printf("## Init_over ##\r\n");
}
/*主函数*/
int main(void)
{
Head_Init();
ESP_Init_Time(); //初始化时间 和天气
OLED_clear();
Display_mode(); //显示模板框架
Show_char16(0,120,power8,1);
Display_days(YEARS, MOONS, DAYS); //初始化显示时间与日期
TIM1_init(); //获取时间完成再初始化定时器
Display_time(TIMES);
Display_temperature(TEMPERATURE, WEATHER); //初始化显示温度
// IWDG_Init(); //看门狗初始化
while(1)
{
//DISPLAYTIME是定时器中断里面的一个变量,用来定时刷新
if(DISPLAYTIME == 1) //刷新显示
{
//如果达到24小时,则进行日期推演
if(TIMES >= 86400)
{
TIMES = 0;
DAYS ++;
Times_Change(DAYS,MOONS,YEARS); //计算当前日期
Display_days(YEARS, MOONS, DAYS); //显示日期
}
Display_time(TIMES);
DISPLAYTIME = 0;
// IWDG_Feed(); //喂狗
}
if(GETNETTIME >= 1800) //半小时校准一次网络时间
{
ESP_Fefresh_NET_Massage(ESP_TIME_TCP_ADDRESS, ESP_TIME_TCP_POINT,GET_Net_Time); //更新时间
Display_temperature(TEMPERATURE, WEATHER); // 每小时刷新显示天气
GETNETTIME = 0;
}
if(GETNETTIME >= 30) //每15min校准一次天气数据
{
ESP_Fefresh_NET_Massage(ESP_WEATHER_TCP_ADDRESS, ESP_WEATHER_TCP_POINT,Get_NET_Waether); //更新天气
Display_temperature(TEMPERATURE, WEATHER); // 每小时刷新显示天气
GETNETTIME = 0;
}
}