单片机-STM32 WIFI模块--ESP8266 (十二)
1.WIFI模块--ESP8266
名字由来:
Wi-Fi这个术语被人们普遍误以为是指无线保真(Wireless Fidelity),并且即便是Wi-Fi联盟本身也经常在新闻稿和文件中使用“Wireless Fidelity”这个词,Wi-Fi还出现在ITAA的一个论文中。但事实上,Wi-Fi一词没有任何意义,也是没有全写的。
原理部分
无线网络在无线局域网的范畴是指“无线相容性认证”,实质上是一种商业认证,同时也是一种无线联网技术,以前通过网线连接电脑,而Wi-Fi则是通过无线电波来连网;常见的就是一个无线路由器,那么在这个无线路由器的电波覆盖的有效范围都可以采用Wi-Fi连接方式进行联网,如果无线路由器连接了一条ADSL线路或者别的上网线路,则又被称为热点。
ESP8266外观图:
ESP8266如何通信:
需要使用单片机的串口控制ESP8266
因为ESP8266目前预留的就是。
单片机---》ESP8266
ESP8266的特性:
不同的WIFI模块对比:
了解以下公司:
乐鑫信息科技
安信可
有人科技
移远科技
TCP和UDP的区别
TCP是一种面向连接,可靠的稳定的网络通信方式,连接的时候必须有应答,可以确定数据传输的顺序
UDP连接是一种面向无连接的通信,发送方发送数据,不需要给回复,发送方只负责发送数据,但是数据是否发送完成,不去负责。
2.AT指令集
AT指令集是从终端设备(Terminal Equipment,TE)或数据终端设备(Data Terminal Equipment,DTE)向终端适配器(Terminal Adapter, TA)或数据电路终端设备(Data Circuit Terminal Equipment,DCE)发送的。
AT指令集是现在已经设置好的一套指令,用于驱动外部设备,如果需要使用AT指令集控制外部设备的话,需要首先熟悉AT操作。
单片机和ESP8266连接图:
为什么使用AT指令集??
1.ESP8266内部已经集成了TCP/IP协议,而且也烧写了相关固件
2.我们的MCU部分只需要像控制OLED屏一样发送指令即可
3.ESP8266控制指令全部用的都是AT指令集
ESP8266相关的AT指令集
AT指令集的四种类型:
比如,我们要查询串口配置:
AT_UART?
设置串口:
AT+UART=<baudrate>,<databits>,<stopbits>,<pa rity>,<flow control>
AT--测试指令
ATE--回显设置
AT+RST--复位
AT+GMR--查询版本信息
AT+CWMODE—设置 Wi-Fi 模式
Station--客户端
SoftAP--服务器
SoftAP+Station--混合模式
AT+CWJAP—连接 AP
AT+CPIMUX--设置连接
AT+CIPMODE—设置传输模式
AT+CIPSTART—建⽴立 TCP 连接, UDP 传输或 SSL 连接
ESP8266透传
AT
ATE0 关闭回显
AT+CWMODE= 1 //客户端
AT+CWJAP="WLL001","123456789" //
AT+CIPMUX=0 //0--单链接
AT+CIPSTART1
="TCP","120.76.100.197",10002 //设置服务器IP端口
AT+CIPMODE=1 //透传模式
AT+CIPSEND
> //收发数据
注意每一个指令的结尾都需要换行符
[15:24:06.186]发→◇AT
OK
[15:24:27.367]发→◇AT+CWMODE=1
□
[15:24:27.372]收←◆AT+CWMODE=1
OK
[15:27:47.266]发→◇AT
□
[15:27:47.271]收←◆AT
OK
[15:27:51.816]发→◇AT+CWMODE=1
□
[15:27:51.820]收←◆AT+CWMODE=1
OK
[15:27:55.146]发→◇AT+CWJAP="WLL001","123456789"
□
[15:27:55.153]收←◆AT+CWJAP="WLL001","123456789"
[15:28:09.220]收←◆+CWJAP:3
FAIL
[15:28:43.918]发→◇AT+CWJAP="WLL001","123456789"
□
[15:28:43.925]收←◆AT+CWJAP="WLL001","123456789"
[15:28:47.907]收←◆WIFI CONNECTED
[15:28:48.731]收←◆
OK
WIFI GOT IP
[15:28:53.611]发→◇AT+CIPMUX=0
□
[15:28:53.616]收←◆AT+CIPMUX=0
OK
[15:29:16.708]发→◇AT+CIPSTART="TCP","192.168.137.1",8080
□
[15:29:16.714]收←◆AT+CIPSTART="TCP","192.168.137.1",8080
CONNECT
OK
[15:29:42.209]发→◇AT+CIPMODE=1
□
[15:29:42.219]收←◆AT+CIPMODE=1
OK
[15:30:15.994]发→◇AT+CIPSEND
>
□
[15:30:15.999]收←◆AT+CIPSEND
>
busy p...
OK
>
[15:30:27.237]发→◇hello
□
[15:30:31.699]收←◆http://www.cmsoft.cn
[15:30:47.653]发→◇+++
□
[15:31:23.607]发→◇+++□
[15:31:28.834]发→◇+++□
[15:31:28.837]收←◆+++
[15:31:55.340]发→◇AT+CIPMODE=0
□
[15:31:55.346]收←◆AT+CIPMODE=0
busy p..
ERROR
[15:33:24.341]发→◇AT+CIPSTART="TCP","192.168.137.1",8080
□
[15:33:24.349]收←◆AT+CIPSTART="TCP","192.168.137.1",8080
ALREADY CONNECTED
ERROR
[15:33:24.560]发→◇AT+CIPSTART="TCP","192.168.137.1",8080
□
[15:33:24.566]收←◆AT+CIPSTART="TCP","192.168.137.1",8080
ALREADY CONNECTED
3.单片机串口配置
1.选择一个串口(串口2 或者串口3 串口4.。。。。都可以)
2.串口的接收中断,用于接收数据
3.判断数据是否接收完成的方式:
a.串口的空闲中断,用于判断数据是否接收完成,(因为接受的数据是不定长)
b.使用定时器进行判断,比如当开始接收数据的时候,打开定时器,当定时器计数3秒还没有数据再次到来,就可以判断数据接收。
4.接收到数据之后,解析数据
查找接收到的字符串中的关键字,比如“Ok”
5.复位管教和使能管教的的配置
随便找两个管教设置为,通用推完输出即可
使能(EN)---驱动ESP8266
RST--拉高--PG14
EN--拉高--PG13
使用的是串口3
TX--PB10
RX--PB11
串口3的配置部分:
void esp8266_Init(void)
{
GPIO_InitTypeDef GPIOInitTypeDef;
USART_InitTypeDef USARTInitTypeDef;
//打开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
//配置PB10--TX PB11--RX
//PB10--复用推挽输出
GPIOInitTypeDef.GPIO_Pin=GPIO_Pin_10;
GPIOInitTypeDef.GPIO_Mode=GPIO_Mode_AF_PP;
GPIOInitTypeDef.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIOInitTypeDef);
//PB11--浮空
GPIOInitTypeDef.GPIO_Pin=GPIO_Pin_11;
GPIOInitTypeDef.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB,&GPIOInitTypeDef);
//usart3的配置
USARTInitTypeDef.USART_BaudRate=115200;
USARTInitTypeDef.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USARTInitTypeDef.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USARTInitTypeDef.USART_Parity=USART_Parity_No;
USARTInitTypeDef.USART_StopBits=USART_StopBits_1;
USARTInitTypeDef.USART_WordLength=USART_WordLength_8b;
USART_Init(USART3,&USARTInitTypeDef);
//配置中断
//设置空闲中断和接收中断
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
USART_ITConfig(USART3,USART_IT_IDLE,ENABLE);
//
NVIC_SetPriority(USART3_IRQn,0);//占先优先级:1 次级优先级:1
NVIC_EnableIRQ(USART3_IRQn);
USART_Cmd(USART3,ENABLE);
}
ESP8266的使能管脚和复位管脚的配置:
void ESP8266_IO_Config(void)
{
GPIO_InitTypeDef gpio_initsources;
//打开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
gpio_initsources.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14;
gpio_initsources.GPIO_Mode=GPIO_Mode_Out_PP;
gpio_initsources.GPIO_Speed=GPIO_Speed_2MHz;
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
GPIO_Init(GPIOG,&gpio_initsources);
}
串口3中断服务函数:
void USART3_IRQHandler(void)
{
u8 data=0;
//接收数据--接收中断
if(USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET)
{
USART_ClearITPendingBit(USART3,USART_IT_RXNE);
//用于接收ESP8266的数据
rxbuff[usart_count++]=USART_ReceiveData(USART3);
//打印ESP8266的数据到串口助手上
USART1->DR=USART_ReceiveData(USART3);
// printf("0000\r\n");
}
if(USART_GetITStatus(USART3,USART_IT_IDLE)==SET)
{
// rxbuff[rx_count++]=USART_ReceiveData(USART3);//清除数据
over_flag=1;//一旦进入空闲中断,置1,本次传输结束
rx_count=usart_count;//没有用
usart_count=0;
data=USART_ReceiveData(USART3);//清空缓存区
// printf("11111\r\n");
USART_ClearITPendingBit(USART3,USART_IT_IDLE);
}
}
数据发送部分:
//发送单个字节函数
void USART3_SendData(char data)
{
USART_SendData(USART3,data);
/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
}
void Usart3_SendStr(char *str)
{
int i=0;
while(*(str+i)!='\0')
{
USART3_SendData(*(str+i));
i++;
}
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET)
{}
}
ESP8266每次上电的时候需要复位一下:
u8 Esp8266_AT_test(void)
{
//"AT\r\n"
char count=0;
GPIO_SetBits ( GPIOG, GPIO_Pin_13 );
GPIO_SetBits ( GPIOG, GPIO_Pin_14 );
printf("\r\nAT测试.....\r\n");
delay_ms ( 2000 );
while ( count < 10 )
{
printf("\r\nAT测试次数 %d......\r\n", count);
if( esp8266_SendCmd ( "AT\r\n", "OK",NULL,500) )
{
printf("\r\nAT测试启动成功 %d......\r\n", count);
return 1;
}
//复位以下
GPIO_ResetBits ( GPIOG, GPIO_Pin_14 );
delay_ms ( 500 );
GPIO_SetBits ( GPIOG, GPIO_Pin_14 );
++ count;
}
return 0;
}
发送AT指令的函数:
1、strstr() 函数搜索一个字符串在另一个字符串中的第一次出现。
2、找到所搜索的字符串,则该函数返回第一次匹配的字符串的地址;
3、如果未找到所搜索的字符串,则返回NULL。
uint8_t esp8266_SendCmd(char *ctl_cmd,char *ret01,char *ret02,uint32_t time)
{
uint8_t tim=time;
memset(rxbuff,0,sizeof(rxbuff));
//发送数据
Usart3_SendStr(ctl_cmd);
delay_ms(time);
//接收数据
while(tim--)
{
delay_ms(100);
if(over_flag==1)//代表进入了空闲中断,本次接收数据完成
{
over_flag=0;
tim=0;
if ( ( ret01 != 0 ) && ( ret02 != 0 ) )
return ( ( bool ) strstr ( rxbuff, ret01 ) ||
( bool ) strstr ( rxbuff, ret02 ) );
else if ( ret01 != 0 )
return ( ( bool ) strstr ( rxbuff, ret01 ) );
else
return ( ( bool ) strstr ( rxbuff, ret02 ) );
}
// memset(rxbuff,0,sizeof(rxbuff));
}
//发送完成之后,等待接收数据,比较返回值的内容
return 0;
}
主函数中的配置:(联网流程)
esp8266_Init();
ESP8266_IO_Config();
printf("底层配置初始化完成\r\n");
Esp8266_AT_test();
delay_ms(3000);
printf("配置联网模式\r\n");
while(!esp8266_SendCmd("AT+CWMODE=1\r\n","OK",NULL,300))
{}
printf("配置热点\r\n");
sprintf(buff,"AT+CWJAP=\"%s\",\"%s\"\r\n",WIFI_ID,WIFI_PW);
while(!esp8266_SendCmd(buff,"OK",NULL,1000))
{}
printf("配置服务器端\r\n");
memset(buff,0,sizeof(buff));
sprintf(buff,"AT+CIPSTART=\"TCP\",\"172.20.10.4\",%d\r\n",WIFI_PORT);
while(!esp8266_SendCmd(buff,"OK",NULL,1000))
{}
printf("配置透传\r\n");
while(!esp8266_SendCmd("AT+CIPMODE=1\r\n","OK",NULL,500))
{}
printf("收发数据\r\n");
while(!esp8266_SendCmd("AT+CIPSEND\r\n",">",NULL,10000))
{}
网络部分:
服务器端的配置:
完整配置:
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdlib.h"
#include "stdbool.h"
char rxbuff[256]={0};
uint8_t rx_count=0;
uint8_t usart_count=0;
uint8_t over_flag=0;
void esp8266_Init(void)
{
GPIO_InitTypeDef GPIOInitTypeDef;
USART_InitTypeDef USARTInitTypeDef;
//打开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
//配置PB10--TX PB11--RX
//PB10--复用推挽输出
GPIOInitTypeDef.GPIO_Pin=GPIO_Pin_10;
GPIOInitTypeDef.GPIO_Mode=GPIO_Mode_AF_PP;
GPIOInitTypeDef.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIOInitTypeDef);
//PB11--浮空
GPIOInitTypeDef.GPIO_Pin=GPIO_Pin_11;
GPIOInitTypeDef.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB,&GPIOInitTypeDef);
//usart3的配置
USARTInitTypeDef.USART_BaudRate=115200;
USARTInitTypeDef.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USARTInitTypeDef.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USARTInitTypeDef.USART_Parity=USART_Parity_No;
USARTInitTypeDef.USART_StopBits=USART_StopBits_1;
USARTInitTypeDef.USART_WordLength=USART_WordLength_8b;
USART_Init(USART3,&USARTInitTypeDef);
//配置中断
//设置空闲中断和接收中断
USART_ITConfig(USART3,USART_IT_RXNE,ENABLE);
USART_ITConfig(USART3,USART_IT_IDLE,ENABLE);
//
NVIC_SetPriority(USART3_IRQn,0);//占先优先级:1 次级优先级:1
NVIC_EnableIRQ(USART3_IRQn);
USART_Cmd(USART3,ENABLE);
}
void ESP8266_IO_Config(void)
{
GPIO_InitTypeDef gpio_initsources;
//打开时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG,ENABLE);
gpio_initsources.GPIO_Pin=GPIO_Pin_13|GPIO_Pin_14;
gpio_initsources.GPIO_Mode=GPIO_Mode_Out_PP;
gpio_initsources.GPIO_Speed=GPIO_Speed_2MHz;
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
GPIO_Init(GPIOG,&gpio_initsources);
}
void USART3_IRQHandler(void)
{
u8 data=0;
//接收数据--接收中断
if(USART_GetITStatus(USART3,USART_IT_RXNE)!=RESET)
{
USART_ClearITPendingBit(USART3,USART_IT_RXNE);
//用于接收ESP8266的数据
rxbuff[usart_count++]=USART_ReceiveData(USART3);
//打印ESP8266的数据到串口助手上
USART1->DR=USART_ReceiveData(USART3);
// printf("0000\r\n");
}
if(USART_GetITStatus(USART3,USART_IT_IDLE)==SET)
{
// rxbuff[rx_count++]=USART_ReceiveData(USART3);//清除数据
over_flag=1;//一旦进入空闲中断,置1,本次传输结束
rx_count=usart_count;//没有用
usart_count=0;
data=USART_ReceiveData(USART3);//清空缓存区
// printf("11111\r\n");
USART_ClearITPendingBit(USART3,USART_IT_IDLE);
}
}
void esp8266_Analysis(void)
{
int i=0;
if(over_flag==1)
{
printf("000000\r\n");
over_flag=0;
//开始处理数据
// for(i=0;i<rx_count;i++)
printf("rx_data:%s\r\n",rxbuff);
}
memset(rxbuff,0,sizeof(rxbuff));
}
//发送单个字节函数
void USART3_SendData(char data)
{
USART_SendData(USART3,data);
/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
}
void Usart3_SendStr(char *str)
{
int i=0;
while(*(str+i)!='\0')
{
USART3_SendData(*(str+i));
i++;
}
while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET)
{}
}
//发送--AT+.....
uint8_t sendcou=0;
//AT
//回复:OK
//time--每次发送数据等待接受的时间
uint8_t esp8266_SendCmd(char *ctl_cmd,char *ret01,char *ret02,uint32_t time)
{
uint8_t tim=time;
memset(rxbuff,0,sizeof(rxbuff));
//发送数据
Usart3_SendStr(ctl_cmd);
delay_ms(time);
//接收数据
while(tim--)
{
delay_ms(100);
if(over_flag==1)//代表进入了空闲中断,本次接收数据完成
{
over_flag=0;
tim=0;
if ( ( ret01 != 0 ) && ( ret02 != 0 ) )
return ( ( bool ) strstr ( rxbuff, ret01 ) ||
( bool ) strstr ( rxbuff, ret02 ) );
else if ( ret01 != 0 )
return ( ( bool ) strstr ( rxbuff, ret01 ) );
else
return ( ( bool ) strstr ( rxbuff, ret02 ) );
}
// memset(rxbuff,0,sizeof(rxbuff));
}
//发送完成之后,等待接收数据,比较返回值的内容
return 0;
}
u8 Esp8266_AT_test(void)
{
//"AT\r\n"
char count=0;
GPIO_SetBits ( GPIOG, GPIO_Pin_13 );
GPIO_SetBits ( GPIOG, GPIO_Pin_14 );
printf("\r\nAT测试.....\r\n");
delay_ms ( 2000 );
while ( count < 10 )
{
printf("\r\nAT测试次数 %d......\r\n", count);
if( esp8266_SendCmd ( "AT\r\n", "OK",NULL,500) )
{
printf("\r\nAT测试启动成功 %d......\r\n", count);
return 1;
}
//复位以下
GPIO_ResetBits ( GPIOG, GPIO_Pin_14 );
delay_ms ( 500 );
GPIO_SetBits ( GPIOG, GPIO_Pin_14 );
++ count;
}
return 0;
}
主函数:
#include "stm32f10x.h" // 相当于51单片机中的 #include <reg51.h>
#include "my_usart1.h"
#include "my_key.h"
#include "delay.h"
#include "my_tim.h"
#include "my_dma.h"
#include "esp8266.h"
#include "string.h"
#define WIFI_ID "WLL001"
#define WIFI_PW "123456789"
#define WIFI_PORT 8087
#define LED1(x) x?(GPIOB->ODR &=~(0X01<<5)):(GPIOB->ODR |=(0X01<<5))
void Led_Config(void);
void delay_tim(u32 tim);
void led_Breath(void);
int main(void)
{
char buff[256]={0};
// 来到这里的时候,系统的时钟已经被配置成72M。
NVIC_SetPriorityGrouping(6);//0---7 占先优先级--7bit 次级优先级4--6bit
systick_Init();
Led_Config();
usart1_Config(115200);
// key_Config();
// key_Interrupt_Cfg();
// tim3_Ch2_Config();
// DMA1_Config();
// printf("底层配置初始化完成\r\n");
// LED1(1);
esp8266_Init();
ESP8266_IO_Config();//pG13 PG14
printf("底层配置初始化完成\r\n");
Esp8266_AT_test();//复位
delay_ms(3000);
/*
AT
AT+CWMODE= 1 //客户端
AT+CWJAP="WLL001","123456789" //
AT+CIPMUX=0 //0--单链接
AT+CIPSTART1
="TCP","120.76.100.197",10002 //设置服务器IP端口
AT+CIPMODE=1 //透传模式
AT+CIPSEND
> //收发数据
*/
printf("配置联网模式\r\n");
while(!esp8266_SendCmd("AT+CWMODE=1\r\n","OK",NULL,300))
{}
printf("配置热点\r\n");
sprintf(buff,"AT+CWJAP=\"%s\",\"%s\"\r\n",WIFI_ID,WIFI_PW);
while(!esp8266_SendCmd(buff,"OK",NULL,1000))
{}
printf("配置服务器端\r\n");
memset(buff,0,sizeof(buff));
sprintf(buff,"AT+CIPSTART=\"TCP\",\"192.168.137.1\",%d\r\n",WIFI_PORT);
while(!esp8266_SendCmd(buff,"OK",NULL,1000))
{}
printf("配置透传\r\n");
while(!esp8266_SendCmd("AT+CIPMODE=1\r\n","OK",NULL,500))
{}
printf("收发数据\r\n");
while(!esp8266_SendCmd("AT+CIPSEND\r\n",">",NULL,10000))
{}
while(1)
{
// esp8266_SendCmd("AT\r\n","OK",NULL,300);
esp8266_Analysis();
Usart3_SendStr("hello world");
// Get_Data();
delay_ms(1000);
LED1(1);
delay_ms(1000);
LED1(0);
delay_ms(1000);
}
}
void Led_Config(void)
{
//时钟
RCC->APB2ENR |=0X01<<3;
//配置通用推挽输出
GPIOB->CRL &=~(0X0F<<20);//清零
GPIOB->CRL |=0x01<<20;//通用推挽输出--10MHZ
}
void led_Breath(void)
{
u32 i=0;
for(i=0;i<1500;i++)
{
LED1(1);
delay_us(i);
LED1(0);
delay_us(1500-i);
}
for(i=0;i<1500;i++)
{
LED1(1);
delay_us(1500-i);
LED1(0);
delay_us(i);
}
}
void delay_tim(u32 tim)
{
while(tim--);
}