3. ESP8266模块

3.1. ESP8266 简介

ESP8266 是串口型 WIFI,速度比较低,不能用来传输图像或者视频这些大容量的数据,主要应用于数据量传输比较少的场合,比如温湿度信息,一些传感器的开关量等。

详细的ESP8266模块内容请参考:https://doc.embedfire.com/products/link/zh/latest/module/wifi/esp8266.html?highlight=esp8266

3.2. ESP8266 的 AT指令

AT指令是应用于终端设备与PC应用之间的连接与通信的指令,说白了,AT指令就是大家定义好的,能表达特殊意义的字符串,每条AT命令中只能包含一条AT指令,就比如说A发送:“吃饭了”,B就会回复:“好的”。

AT指令用法:

1、测试命令(Test Command)

仅仅发送指令 “AT” 即测试ESP8266模块是否准备好,若准备好则响应“OK”。

2、读取命令(Read Command)

在AT指令后面加上“=?”即构成测试命令。例如“AT+MODE?”,会列举当前是什么模式。

3、执行命令(Execute Command)

在AT指令后面加上“=”再接上相应的参数即可,例如“AT+MODE=NORMAL”,将当前模式设置为正常模式。对于一些没有参数的指令则不需要加参数, 比如”AT+RESET”。

3.3. AP/STA模式

AP: 即无线接入点,是无线网络的中心节点,我们常使用的路由器就是一个AP。ESP8266 可以工作在AP模式,设置为AP模式、开启热点之后就可以用手机来连接 ESP8266。

STA: 每一个连接到无线网络的终端都可以理解为一个STA。ESP8266 也可以工作在STA模式,配置为STA模式之后就可以连接路由器的WiFi。

3.4. TCP/UDP/透传

3.4.1. TCP

TCP是一种面向连接的,提供可靠交付服务和全双工通信的,基于字节流的端到端的传输层通信协议。

  1. TCP在传输数据之前必须先建立连接,数据传输结束后要释放连接。

  2. 每一条TCP连接只能有2个端点,故TCP不提供广播或多播服务。

  3. TCP提供可靠交付,通过TCP连接传输的数据,无差错、不丢失、不重复、并且按序到达。

  4. TCP是面向字节流的。虽然应用进程和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据看成仅仅是一连串的无结构的字节流。TCP并不知道所传输的字节流的含义。

3.4.2. UDP

UDP是一种无连接的,尽最大努力交付的,基于报文的端到端的传输层通信协议。

  1. UDP,在发送数据之前不需要建立连接。

  2. UDP不保证可靠交付,主机不需要位置复杂的连接状态。

  3. UDP是面向报文的。UDP对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的的边界,即应用层交给UDP多长的报文,UDP就照样发送,即一次发送一个报文。在接收端,UDP一次交付一个完整的报文。

  4. UDP没有拥塞控制,网络出现的拥塞不会使源主机的发送速率降低。

  5. UDP支持一对一、一对多、多对一和多对多的交互通信。

  6. UDP的首部开销小,只有8个字节,比TCP的20个字节的首部要短。

3.4.3. 透传

透传即透明传输,是指在传输中不管传输的内容是什么,只关心传输时的源地址和目标地址,对数据内容不做任何改变。

3.4.4. 固件

固件是写入存储器中的程序,在单片机中就是写到Flash中的程序。

3.5. 连接方式

图5 - 1为ESP8266模块引脚图,

ESP8266

以指南者开发板为例,下面是ESP8266与指南者开发板的连接方式,

ESP8266接口

ESP8266 IO

STM32 IO

URXD

PB10

UTXD

PB11

CH-PD

PB8

RST

PB9

板载有 ESP8266 的开发板不需要用杜邦线连接单独的 ESP8266 模块,直接使用跳线帽连接好相关引脚就可以使用板载的 ESP8266 了。 而对于没有板载 ESP8266 的开发板就需要按照例程的接线方式(可以参考模块资料“野火ESP8266与各开发板引脚连接说明”文件夹里边的内容),来外接一个单独的 ESP8266 模块。

对于板载有 ESP8266 的开发板,也可以使用外接 ESP8266 模块,但是要先断开板载ESP8266与串口的跳帽,断开之后再使用我们的例程。

因此,由于指南者开发板已经板载有 ESP8266 模块了,只需要使用跳线帽连接 URX<—>PB10, UTX<—>PB11 即可(出厂默认已连接,在板子右下方)。

3.6. ESP8266 AT测试

3.6.1. 实现现象

按照esp8266接口将开发板与模块连接,再用USB连接开发板与电脑,打开电脑上的野火多功能调试助手,波特率设置为 115200,打开与开发板连接的端口, 如下图所示,下载程序后上位机会打印信息,发送 “AT+换行回车” 后,如果ESP8266模块工作正常的话开发板会回复 “OK”。

串口打印的调试信息:

串口打印的调试信息

3.6.2. 例程介绍

以指南者开发板为例,例程中使用串口3与 ESP8266 通信,把串口3的相关配置也定义到”bsp_esp8266.h”中,移植时方便修改。 串口3的TX引脚为PB10,RX引脚为PB11,ESP8266的 CH_PD引脚为PB8,RST引脚为PB9。宏定义的代码如下。

bsp_esp8266.h(以指南者为例)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ... ...

/******************************** ESP8266 连接引脚定义 ***********************************/
#define      macESP8266_CH_PD_APBxClock_FUN              RCC_APB2PeriphClockCmd /* 重定义开启ESP8266 CH_PD 端口的时钟函数 */
#define      macESP8266_CH_PD_CLK                        RCC_APB2Periph_GPIOB   /* 定义ESP8266 CH_PD 引脚端口时钟 */
#define      macESP8266_CH_PD_PORT                       GPIOB                  /* 定义ESP8266 CH_PD 引脚端口*/
#define      macESP8266_CH_PD_PIN                        GPIO_Pin_8             /* 定义ESP8266 CH_PD 引脚*/

#define      macESP8266_RST_APBxClock_FUN                RCC_APB2PeriphClockCmd /* 重定义开启ESP8266 RST 端口的时钟函数 */
#define      macESP8266_RST_CLK                          RCC_APB2Periph_GPIOB   /* 定义ESP8266 RST 引脚端口时钟 */
#define      macESP8266_RST_PORT                         GPIOB                  /* 定义ESP8266 RST 引脚端口*/
#define      macESP8266_RST_PIN                          GPIO_Pin_9             /* 定义ESP8266 RST 引脚*/



#define      macESP8266_USART_BAUD_RATE                  115200                 /* 定义ESP8266的串口波特率为 115200*/

#define      macESP8266_USARTx                           USART3                 /* 定义ESP8266串口为串口3*/
#define      macESP8266_USART_APBxClock_FUN              RCC_APB1PeriphClockCmd /* 重定义开启ESP8266 USART 的时钟函数 */
#define      macESP8266_USART_CLK                        RCC_APB1Periph_USART3  /* 重定义ESP8266 USART3 */
#define      macESP8266_USART_GPIO_APBxClock_FUN         RCC_APB2PeriphClockCmd /* 重定义开启ESP8266 USART GPIO 的时钟函数 */
#define      macESP8266_USART_GPIO_CLK                   RCC_APB2Periph_GPIOB   /* 定义ESP8266 串口 GPIO 时钟*/
#define      macESP8266_USART_TX_PORT                    GPIOB                  /* 定义ESP8266 串口 发送 GPIO 端口*/
#define      macESP8266_USART_TX_PIN                     GPIO_Pin_10            /* 定义ESP8266 串口 发送 GPIO 引脚*/
#define      macESP8266_USART_RX_PORT                    GPIOB                  /* 定义ESP8266 串口 接收 GPIO 端口*/
#define      macESP8266_USART_RX_PIN                     GPIO_Pin_11            /* 定义ESP8266 串口 接收 GPIO 引脚*/
#define      macESP8266_USART_IRQ                        USART3_IRQn            /* 重定义ESP8266 串口 中断*/
#define      macESP8266_USART_INT_FUN                    USART3_IRQHandler      /* 重定义ESP8266 串口 中断函数*/

// ... ...

ESP8266_Init 是ESP8266的初始化函数,在该函数中初始化了ESP8266用到的GPIO引脚,以及初始化了ESP8266用到的 USART3,并且开启了串口中断,代码如下。

ESP8266_Init 函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void ESP8266_Init ( void )
{
   /* 初始化ESP8266用到的GPIO引脚 */
   ESP8266_GPIO_Config ();

   /* 初始化ESP8266用到的 USART */
   ESP8266_USART_Config ();

   /* 复位引脚拉高 */
   macESP8266_RST_HIGH_LEVEL();

   /* CH使能*/
   macESP8266_CH_ENABLE();
}

在main函数中调用了ESP8266_Init对ESP8266进行初始化,以及初始化与电脑连接的串口 USART1,然后用 USART1 打印了关于本例程的提示信息。 最后进入主循环中,程序会不断检测 USART1 和连接到 ESP8266 的 USART3 是否接收到数据, 如果 USART1 接收到电脑串口助手发送过来的数据,则将接收到的数据发送给ESP8266,ESP8266 接收到有效数据后也会通过 USART3 返回信息。 数据流向如下所示:

  • 串口助手发送AT指令:电脑串口助手 -> USART1接收 -> USART3发送 -> ESP8266

  • ESP8266返回消息:ESP8266 -> USART3接收 -> USART1发送 -> 电脑串口助手

main.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int main(void)
{
   /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
   USART_Config();
   ESP8266_Init();

   printf("欢迎使用野火STM32开发板\n\n");
   printf("这是一个ESP8266AT指令测试实验\n\n");
   printf("请使用串口调试助手发送\"AT+换行回车\"测试ESP8266是否准备好\n\n");
   printf("更多AT指令请参考模块资料\n\n");
   printf("以下是ESP8266上电初始化打印的信息\n\n");

   while(1)
   {
         if(strUSART_Fram_Record .InfBit .FramFinishFlag == 1)  //如果接收到了串口调试助手的数据
         {
            strUSART_Fram_Record .Data_RX_BUF[strUSART_Fram_Record .InfBit .FramLength] = '\0';
            Usart_SendString(macESP8266_USARTx ,strUSART_Fram_Record .Data_RX_BUF);      //数据从串口调试助手转发到ESP8266
            strUSART_Fram_Record .InfBit .FramLength = 0;                                //接收数据长度置零
            strUSART_Fram_Record .InfBit .FramFinishFlag = 0;                            //接收标志置零
      }
         if(strEsp8266_Fram_Record .InfBit .FramFinishFlag)                             //如果接收到了ESP8266的数据
         {
            strEsp8266_Fram_Record .Data_RX_BUF[strEsp8266_Fram_Record .InfBit .FramLength] = '\0';
            Usart_SendString(DEBUG_USARTx ,strEsp8266_Fram_Record .Data_RX_BUF);        //数据从ESP8266转发到串口调试助手
            strEsp8266_Fram_Record .InfBit .FramLength = 0;                             //接收数据长度置零
            strEsp8266_Fram_Record.InfBit.FramFinishFlag = 0;                           //接收标志置零
         }
   }
}

下面是中断服务函数,DEBUG_USART_IRQHandler 是单片机与电脑连接的串口中断服务函数,而 macESP8266_USART_INT_FUN 是单片机与ESP8266连接的串口中断服务函数。 当串口接收到数据或者接收数据完毕后都会产生中断,我们通过 USART_GetITStatus 函数来判断这两个不同的中断(USART_IT_RXNE 和 USART_IT_IDLE)并进行相应的处理。

stm32f10x_it.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 单片机与电脑连接的串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
   uint8_t ucCh;

   /* 判断串口是否产生接收数据中断 */
   if ( USART_GetITStatus ( DEBUG_USARTx, USART_IT_RXNE ) != RESET )
   {
      ucCh  = USART_ReceiveData( DEBUG_USARTx );

      if ( strUSART_Fram_Record .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) )                       //预留1个字节写结束符
            strUSART_Fram_Record .Data_RX_BUF [ strUSART_Fram_Record .InfBit .FramLength ++ ]  = ucCh;

   }

   /* 判断串口是否产生空闲中断 */
   if ( USART_GetITStatus( DEBUG_USARTx, USART_IT_IDLE ) == SET )                //数据帧接收完毕
   {
      strUSART_Fram_Record .InfBit .FramFinishFlag = 1;

      ucCh = USART_ReceiveData( DEBUG_USARTx );                                  //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR)
   }
}

// 单片机与ESP8266连接的串口服务函数
void macESP8266_USART_INT_FUN ( void )
{
   uint8_t ucCh;

   /* 判断串口是否产生接收数据中断 */
   if ( USART_GetITStatus ( macESP8266_USARTx, USART_IT_RXNE ) != RESET )
   {
      ucCh  = USART_ReceiveData( macESP8266_USARTx );

      if ( strEsp8266_Fram_Record .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) )       //预留1个字节写结束符
            strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ++ ]  = ucCh;
   }

   /* 判断串口是否产生空闲中断 */
   if ( USART_GetITStatus( macESP8266_USARTx, USART_IT_IDLE ) == SET )        //数据帧接收完毕
   {
      strEsp8266_Fram_Record .InfBit .FramFinishFlag = 1;

      ucCh = USART_ReceiveData( macESP8266_USARTx );                          //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR)

   }
}

3.7. WIFI透传例程介绍及实验现象

3.7.1. 设置WIFI信息

准备一个具有WiFi功能的路由器,把调试使用的电脑连接到该路由器上,并查看该电脑的 IP 地址。

下载程序之前,程序需要知道你所在的局域网的 WIFI 名称和连接密码(即路由器WiFi的名称和密码)、以及你电脑的 IP 地址,这三个信息在 bsp_esp8266_test.h 头文件中修改。如下图中所示:

wifi配置

3.7.2. 打开配置串口调试助手和网络调试助手

给开发板上电,把编译好的程序下载到开发板,用 USB 线连接好电脑和开发板的串口接口(板子上面一般标有“USB 转串口”字样),然后打开野火多功能调试助手。 野火多功能调试助手里面已经包含了串口调试助手和网络调试助手的功能。

打开串口调试助手: 设置串口号、配置波特率为115200。

打开网络调试助手:设置协议类型为 TCP Server、IP 地址、端口号。

具体设置如下:

网络调试助手设置

注意:在网络调试助手里面设置的 IP 地址和端口要和程序里设置的一样,且程序里设置的 IP 地址应当是你电脑的 IP 地址!

3.7.3. 实验现象

按照上面的连接方式将模块与开发板连接之后,通过USB线将开发板与电脑连接并上电,将WIFI透传例程打开, 找到 bsp_esp8266_test.h 文件,将 WiFi名称WiFi密码 宏定义修改为自己需要连接WiFi热点的名称和密码, 打开电脑上的野火多功能调试助手 - 网络调试助手,将协议类型设置为 TCP Server,本地IP设置为你的IP地址,端口号可以自定义,也可直接用8080端口(如果后续步骤一直没有出现连接,尝试换个任意数字的端口号,程序和网络助手中填一致),然后点“立即监听”。 还要在 bsp_esp8266_test.h 文件里将TCP服务器 IP地址服务器端口 更改为自己电脑的,然后将程序编译,编译通过后下载至开发板即可。 打开串口会打印出调试信息。

本实验使用板载的ESP8266模块实现串口 WiFi 透传功能。本实验中的ESP8266工作在STA模式,作为TCP CLIENT。

本实验网络连接模型:开发板ESP8266<——–>无线路由<———>调试用的电脑。

bsp_esp8266_test.h
1
2
3
4
5
6
/********************************** 用户需要设置的参数**********************************/
#define      macUser_ESP8266_ApSsid                       "embedfire2"         //要连接的热点的名称
#define      macUser_ESP8266_ApPwd                        "wildfire"           //要连接的热点的密钥

#define      macUser_ESP8266_TcpServer_IP                 "192.168.0.210"      //要连接的服务器的 IP
#define      macUser_ESP8266_TcpServer_Port               "8080"               //要连接的服务器的端口

串口打印的调试信息:

串口打印的调试信息

打开串口调试助手,波特率设置为115200,发送LED_RED,然后网络调试助手会通过TCP接收到并且打印如下信息。

网络调试助手打印的调试信息:

串口打印的调试信息

3.7.4. 例程介绍

在编写ESP8266模块驱动时,也要考虑更改硬件环境的情况。我们把ESP8266模块引脚相关的宏定义到”bsp_esp8266.h”文件中, 在更改或移植的时候只用改宏定义就可以。

例程中是使用串口3与esp8266通信,把串口3的相关配置也定义到”bsp_esp8266.h”中,其中还定义了一些ESP8266 数据结构,完整代码请参考WIFI透传例程。

bsp_esp8266.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ... ...

/******************************** ESP8266 连接引脚定义 ***********************************/
#define      macESP8266_CH_PD_APBxClock_FUN              RCC_APB2PeriphClockCmd /* 重定义开启ESP8266 CH_PD 端口的时钟函数 */
#define      macESP8266_CH_PD_CLK                        RCC_APB2Periph_GPIOB   /* 定义ESP8266 CH_PD 引脚端口时钟 */
#define      macESP8266_CH_PD_PORT                       GPIOB                  /* 定义ESP8266 CH_PD 引脚端口*/
#define      macESP8266_CH_PD_PIN                        GPIO_Pin_8             /* 定义ESP8266 CH_PD 引脚*/

#define      macESP8266_RST_APBxClock_FUN                RCC_APB2PeriphClockCmd /* 重定义开启ESP8266 RST 端口的时钟函数 */
#define      macESP8266_RST_CLK                          RCC_APB2Periph_GPIOB   /* 定义ESP8266 RST 引脚端口时钟 */
#define      macESP8266_RST_PORT                         GPIOB                  /* 定义ESP8266 RST 引脚端口*/
#define      macESP8266_RST_PIN                          GPIO_Pin_9             /* 定义ESP8266 RST 引脚*/



#define      macESP8266_USART_BAUD_RATE                  115200                 /* 定义ESP8266的串口波特率为 115200*/

#define      macESP8266_USARTx                           USART3                 /* 定义ESP8266串口为串口3*/
#define      macESP8266_USART_APBxClock_FUN              RCC_APB1PeriphClockCmd /* 重定义开启ESP8266 USART 的时钟函数 */
#define      macESP8266_USART_CLK                        RCC_APB1Periph_USART3  /* 重定义ESP8266 USART3 */
#define      macESP8266_USART_GPIO_APBxClock_FUN         RCC_APB2PeriphClockCmd /* 重定义开启ESP8266 USART GPIO 的时钟函数 */
#define      macESP8266_USART_GPIO_CLK                   RCC_APB2Periph_GPIOB   /* 定义ESP8266 串口 GPIO 时钟*/
#define      macESP8266_USART_TX_PORT                    GPIOB                  /* 定义ESP8266 串口 发送 GPIO 端口*/
#define      macESP8266_USART_TX_PIN                     GPIO_Pin_10            /* 定义ESP8266 串口 发送 GPIO 引脚*/
#define      macESP8266_USART_RX_PORT                    GPIOB                  /* 定义ESP8266 串口 接收 GPIO 端口*/
#define      macESP8266_USART_RX_PIN                     GPIO_Pin_11            /* 定义ESP8266 串口 接收 GPIO 引脚*/
#define      macESP8266_USART_IRQ                        USART3_IRQn            /* 重定义ESP8266 串口 中断*/
#define      macESP8266_USART_INT_FUN                    USART3_IRQHandler      /* 重定义ESP8266 串口 中断函数*/

// ... ...

首先看一下ESP8266的管脚初始化,定义一个GPIO结构体,开启CH_PD引脚的时钟,GPIO_Pin 赋值为 CH_PD ,GPIO_Mode设置为推挽输出,CH_PD对应的管脚时钟设置为50MHz, 调用GPIO_Init初始化GPIO配置。对于RST不修改管脚模式和速度,配置一样。

bsp_esp8266.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void ESP8266_GPIO_Config ( void )
{
   /*定义一个GPIO_InitTypeDef类型的结构体*/
   GPIO_InitTypeDef GPIO_InitStructure;


   /* 配置 CH_PD 引脚*/
   macESP8266_CH_PD_APBxClock_FUN ( macESP8266_CH_PD_CLK, ENABLE );

   GPIO_InitStructure.GPIO_Pin = macESP8266_CH_PD_PIN;         // CH_PD

   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      // 推挽输出

   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 //GPIO时钟为50MHz

   GPIO_Init ( macESP8266_CH_PD_PORT, & GPIO_InitStructure );


   /* 配置 RST 引脚*/
   macESP8266_RST_APBxClock_FUN ( macESP8266_RST_CLK, ENABLE );

   GPIO_InitStructure.GPIO_Pin = macESP8266_RST_PIN;                 // RST

   GPIO_Init ( macESP8266_RST_PORT, & GPIO_InitStructure );
}

再来看一下ESP8266用到的串口初始化函数。定义GPIO、USART结构体,使能串口3及对应的管脚时钟,配置 TX 引脚,GPIO_Mode设置为复用推挽输出, GPIO_Speed设置为50MHz,初始化 TX 引脚配置。配置 RX 引脚,GPIO_Mode设置为浮空输入,GPIO_Speed保留 TX 引脚的配置,然后初始化 RX 引脚配置。

USART_BaudRate为串口波特率,这里设置为115200,USART_WordLength为数据格式长度,设置字长为8位数据格式,设置一个停止位,无奇偶校验,无硬件数据流控制, 串口模式设置为收发模式,初始化串口3,最后就是使能串口的中断。

bsp_esp8266.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
static void ESP8266_USART_Config ( void )
{
   GPIO_InitTypeDef GPIO_InitStructure;
   USART_InitTypeDef USART_InitStructure;


   /* config USART clock */
   macESP8266_USART_APBxClock_FUN ( macESP8266_USART_CLK, ENABLE );  // 使能串口3时钟
   macESP8266_USART_GPIO_APBxClock_FUN ( macESP8266_USART_GPIO_CLK, ENABLE ); // 使能串口3引脚时钟

   /* USART GPIO config */
   /* Configure USART Tx as alternate function push-pull */
   GPIO_InitStructure.GPIO_Pin =  macESP8266_USART_TX_PIN;     // TX 引脚
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;             // 复用推挽输出
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;           // 引脚时钟定义为50MHz
   GPIO_Init(macESP8266_USART_TX_PORT, &GPIO_InitStructure);   // 初始化 TX 引脚配置

   /* Configure USART Rx as input floating */
   GPIO_InitStructure.GPIO_Pin = macESP8266_USART_RX_PIN;      // RX 引脚
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;       // 浮空输入
   GPIO_Init(macESP8266_USART_RX_PORT, &GPIO_InitStructure);   // 初始化 RX 引脚配置

   /* USART1 mode config */
   USART_InitStructure.USART_BaudRate = macESP8266_USART_BAUD_RATE;  // 串口波特率 115200
   USART_InitStructure.USART_WordLength = USART_WordLength_8b;       // 字长为8位数据格式
   USART_InitStructure.USART_StopBits = USART_StopBits_1;            // 一个停止位
   USART_InitStructure.USART_Parity = USART_Parity_No ;              // 无奇偶校验位
   USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
   USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;   // 收发模式
   USART_Init(macESP8266_USARTx, &USART_InitStructure);              //初始化串口3


   /* 中断配置 */
   USART_ITConfig ( macESP8266_USARTx, USART_IT_RXNE, ENABLE );      //使能串口接收中断
   USART_ITConfig ( macESP8266_USARTx, USART_IT_IDLE, ENABLE );      //使能串口总线空闲中断

   /* 配置 ESP8266 USART 的 NVIC 中断 */
   ESP8266_USART_NVIC_Configuration ();

   /* 使能串口3*/
   USART_Cmd(macESP8266_USARTx, ENABLE);
}

下面是ESP8266模块的初始化函数,在这里调用ESP8266的GPIO初始化和串口初始化,拉高ESP8266复位管脚,再将CH_PD管脚置低。

bsp_esp8266.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void ESP8266_Init ( void )
{
   /* 初始化ESP8266用到的GPIO引脚 */
   ESP8266_GPIO_Config ();

   /* 初始化ESP8266用到的 USART */
   ESP8266_USART_Config ();

   /* 拉高ESP8266复位管脚 */
   macESP8266_RST_HIGH_LEVEL();

   /* 将CH_PD管脚置低 */
   macESP8266_CH_DISABLE();
}

在main函数中需要初始化串口1,用于打印调试信息,配置DWT计数器用于延时函数,初始化RGB彩灯,初始化WiFi模块使用的接口和外设。

main.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int main ( void )
{
   /* 初始化 */
   USART_Config ();                                                      //初始化串口1
   CPU_TS_TmrInit();                                                     //初始化DWT计数器,用于延时函数
   LED_GPIO_Config();                                                    //初始化RGB彩灯
   ESP8266_Init ();                                                      //初始化WiFi模块使用的接口和外设
   //        DHT11_Init ();                                                        //初始化DHT11
   //        SysTick_Init ();                                                      //配置 SysTick 为 10ms 中断一次,在中断里读取传感器数据


   printf ( "\r\n野火 WF-ESP8266 WiFi模块测试例程\r\n" );                          //打印测试例程提示信息
   printf ( "\r\n在网络调试助手或者串口调试助手上发送以下命令可以控制板载RGB灯\r\n" );    //打印测试例程提示信息
   printf ( "\r\nLED_RED\r\nLED_GREEN\r\nLED_BLUE\r\nLED_YELLOW\r\nLED_PURPLE\r\nLED_CYAN\r\nLED_WHITE\r\nLED_RGBOFF\r\n" );


   ESP8266_StaTcpClient_Unvarnish_ConfigTest();                          //对ESP8266进行配置

   printf ( "\r\n在网络调试助手或者串口调试助手  发送以下命令控制板载RGB灯:\r\n" );    //打印测试例程提示信息
   printf ( "\r\nLED_RED\r\nLED_GREEN\r\nLED_BLUE\r\nLED_YELLOW\r\nLED_PURPLE\r\nLED_CYAN\r\nLED_WHITE\r\nLED_RGBOFF\r\n" );
   printf ( "\r\n观察RGB灯的状态变化\r\n" );

   while ( 1 )
   {

      ESP8266_CheckRecvDataTest(); // ESP8266 检查一次是否接收到了数据

   }

}

另外,还要在 ESP8266_StaTcpClient_Unvarnish_ConfigTest 这个函数里面对ESP8266进行一些配置,然后才进入主循环执行 ESP8266_CheckRecvDataTest 函数。 我们来看一下 ESP8266_StaTcpClient_Unvarnish_ConfigTest 函数和 ESP8266_CheckRecvDataTest 函数都做了些什么。

首先是 ESP8266_StaTcpClient_Unvarnish_ConfigTest 函数,拉高 CH_PD 引脚,使能模块,对WF-ESP8266模块进行AT测试启动,选择WF-ESP8266模块的工作模式为STA模式, WF-ESP8266模块连接外部WiFi,WF-ESP8266模块启动多连接,WF-ESP8266模块连接外部服务器,配置WF-ESP8266模块进入透传发送。设置完毕后就可以开始透传了。

接着进入主循环,在 ESP8266_CheckRecvDataTest 函数里面,会一直检查是否接收到数据,如果接收到了串口调试助手的数据,就转发给ESP82636,如果接收到了ESP8266的数据,就转发给串口调试助手。 同时 ESP8266_CheckRecvDataTest 函数里面还会检查 ESP8266 有没有断开连接,若是断连的话会让 ESP8266 退出透传模式,并且重新连接热点和服务器。

bsp_esp8266_test.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* @brief  ESP8266 StaTcpClient Unvarnish 配置测试函数
* @param  无
* @retval 无
*/
void ESP8266_StaTcpClient_Unvarnish_ConfigTest(void)
{
   printf( "\r\n正在配置 ESP8266 ......\r\n" );
   printf( "\r\n使能 ESP8266 ......\r\n" );
   macESP8266_CH_ENABLE();
   while( ! ESP8266_AT_Test() );

   printf( "\r\n正在配置工作模式 STA ......\r\n" );
   while( ! ESP8266_Net_Mode_Choose ( STA ) );

   printf( "\r\n正在连接 WiFi ......\r\n" );
   while( ! ESP8266_JoinAP ( macUser_ESP8266_ApSsid, macUser_ESP8266_ApPwd ) );

   printf( "\r\n禁止多连接 ......\r\n" );
   while( ! ESP8266_Enable_MultipleId ( DISABLE ) );

   printf( "\r\n正在连接 Server ......\r\n" );
   while( !  ESP8266_Link_Server ( enumTCP, macUser_ESP8266_TcpServer_IP, macUser_ESP8266_TcpServer_Port, Single_ID_0 ) );

   printf( "\r\n进入透传发送模式 ......\r\n" );
   while( ! ESP8266_UnvarnishSend () );

   printf( "\r\n配置 ESP8266 完毕\r\n" );
   printf ( "\r\n开始透传......\r\n" );
}


/**
* @brief  ESP8266 检查是否接收到了数据,检查连接和掉线重连
* @param  无
* @retval 无
*/
void ESP8266_CheckRecvDataTest(void)
{
   uint8_t ucStatus;
   uint16_t i;

   /* 如果接收到了串口调试助手的数据 */
   if(strUSART_Fram_Record.InfBit.FramFinishFlag == 1)
   {
      for(i = 0;i < strUSART_Fram_Record.InfBit.FramLength; i++)
      {
         USART_SendData( macESP8266_USARTx ,strUSART_Fram_Record.Data_RX_BUF[i]); //转发给ESP82636
         while(USART_GetFlagStatus(macESP8266_USARTx,USART_FLAG_TC)==RESET){}      //等待发送完成
      }
      strUSART_Fram_Record .InfBit .FramLength = 0;                                //接收数据长度置零
      strUSART_Fram_Record .InfBit .FramFinishFlag = 0;                            //接收标志置零
      Get_ESP82666_Cmd(strUSART_Fram_Record .Data_RX_BUF);                         //检查一下是不是点灯命令
   }

   /* 如果接收到了ESP8266的数据 */
   if(strEsp8266_Fram_Record.InfBit.FramFinishFlag)
   {
      for(i = 0;i < strEsp8266_Fram_Record .InfBit .FramLength; i++)
      {
         USART_SendData( DEBUG_USARTx ,strEsp8266_Fram_Record .Data_RX_BUF[i]);    //转发给串口调试助手
         while(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TC)==RESET){}
      }
      strEsp8266_Fram_Record .InfBit .FramLength = 0;                             //接收数据长度置零
      strEsp8266_Fram_Record.InfBit.FramFinishFlag = 0;                           //接收标志置零
      Get_ESP82666_Cmd(strEsp8266_Fram_Record .Data_RX_BUF);                      //检查一下是不是点灯命令
   }

   if ( ucTcpClosedFlag )                                             //检测是否失去连接
   {
      ESP8266_ExitUnvarnishSend ();                                    //退出透传模式

      do ucStatus = ESP8266_Get_LinkStatus ();                         //获取连接状态
      while ( ! ucStatus );

      if ( ucStatus == 4 )                                             //确认失去连接后重连
      {
         printf ( "\r\n正在重连热点和服务器 ......\r\n" );

         while ( ! ESP8266_JoinAP ( macUser_ESP8266_ApSsid, macUser_ESP8266_ApPwd ) );

         while ( !   ESP8266_Link_Server ( enumTCP, macUser_ESP8266_TcpServer_IP, macUser_ESP8266_TcpServer_Port, Single_ID_0 ) );

         printf ( "\r\n重连热点和服务器成功\r\n" );

      }

      while ( ! ESP8266_UnvarnishSend () );

   }
}

下面是中断服务函数,DEBUG_USART_IRQHandler 是单片机与电脑连接的串口中断服务函数,macESP8266_USART_INT_FUN 是单片机与 ESP8266 连接的串口服务函数,

stm32f10x_it.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 单片机与电脑连接的串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
   uint8_t ucCh;

   /* 判断串口是否产生接收数据中断 */
   if ( USART_GetITStatus ( DEBUG_USARTx, USART_IT_RXNE ) != RESET )
   {
      ucCh  = USART_ReceiveData( DEBUG_USARTx );

      if ( strUSART_Fram_Record .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) )            //预留1个字节写结束符
            strUSART_Fram_Record .Data_RX_BUF [ strUSART_Fram_Record .InfBit .FramLength ++ ]  = ucCh;
   }

   /* 判断串口是否产生空闲中断 */
   if ( USART_GetITStatus( DEBUG_USARTx, USART_IT_IDLE ) == SET )                         //数据帧接收完毕
   {
      strUSART_Fram_Record .InfBit .FramFinishFlag = 1;

      ucCh = USART_ReceiveData( DEBUG_USARTx );                         //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR)
   }
}

// 单片机与ESP8266连接的串口服务函数
void macESP8266_USART_INT_FUN ( void )
{
   uint8_t ucCh;

   /* 判断串口是否产生接收数据中断 */
   if ( USART_GetITStatus ( macESP8266_USARTx, USART_IT_RXNE ) != RESET )
   {
      ucCh  = USART_ReceiveData( macESP8266_USARTx );

      if ( strEsp8266_Fram_Record .InfBit .FramLength < ( RX_BUF_MAX_LEN - 1 ) )          //预留1个字节写结束符
            strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ++ ]  = ucCh;
   }

   /* 判断串口是否产生空闲中断 */
   if ( USART_GetITStatus( macESP8266_USARTx, USART_IT_IDLE ) == SET )                    //数据帧接收完毕
   {
      strEsp8266_Fram_Record .InfBit .FramFinishFlag = 1;

      ucCh = USART_ReceiveData( macESP8266_USARTx );                    //由软件序列清除中断标志位(先读USART_SR,然后读USART_DR)

      ucTcpClosedFlag = strstr ( strEsp8266_Fram_Record .Data_RX_BUF, "CLOSED\r\n" ) ? 1 : 0;
   }
}

注意

ESP8266 WiFi模块必须要和电脑处于同一个局域网内,也就是说WiFi模块和电脑都要连接同一个路由器,一般不建议用手机当热点使用。网络调试助手不能随意修改本地主机地址,打开软件时显示的是什么就用什么。端口号一般可以随意设置,只要不与电脑中其他程序冲突就行

注意

说明:若无线路由可访问互联网且调试用的电脑具有独立的公网IP,本程序也支持公网访问(把操作中的IP改成电脑的公网IP)。 !!对公网访问不熟悉的用户,实验时请直接按以上的说明用局域网方式调试。

3.8. 向电脑网络助手上传 DHT11 温湿度例程

把开发板读取到的温湿度信息,通过 WIFI 传到局域网的电脑上的网络调试助手上显示。记得在板子上插好 DHT11 温湿度模块。这个程序是在 WIFI 透传的程序中修改而 来, 实际上就是将透传的数据改成 DHT11 的数据。开发板和DHT11连接的图片如下所示

DS18B20连接开发板示意图

3.8.1. 设置wifi信息

下载程序之前,程序需要知道你所在的局域网的 WIFI 名称和你电脑的 IP,这三个信息在如下代码中修改:

wifi配置

3.8.2. 配置网络与串口调试助手

给开发板上电,把编译好的程序下载到开发板,用 USB 线连接好电脑和开发板的串口接口: USB 转串口,打开串口调试助手软件: 设置串口号、 配置波特率;网络调试助 手:设置协议类型、 端口号、 IP 地址使用打开软件时默认的即可。 具体设置如下

网络调试助手设置

打开串口,开始监听,按一下开发板的复位键,这个时候串口部分会打印出 WIFI的配置信息

DHT11读取温湿度

配置成功后,两边都会打印读取到的温湿度信息:

网络调试助手读取温湿度

3.9. 手机APP控制例程

以指南者开发板为例,WIFI 芯片:ESP8266 已经集成在开发板上。

例程与手机在同一局域网内建立TCP连接后通讯,程序中根据 bsp_esp8266_test.h 中的 #define BUILTAP_TEST 宏选择控制模块为AP还是STA模式。

例程中默认注释BUILTAP_TEST宏以使用STA模式。

STA模式下,在bsp_esp8266_test.c中与之前例程一样,填写好需要连接的热点名字和密码,手机也连接同一个热点。执行程序后查看串口打印中模块连上热点后获取的IP,打开APP填写以上IP和程序中端口号即可连接。

AP模式下,在bsp_esp8266_test.c中填写一个自定义的开启热点名字与密码(使用OPEN无需密码,改为WPA2_PSK启用密码),和下面填写自身IP(STA模式时使用DHCP获取IP与这处无关)与端口号。 执行程序后手机连接模块开启的热点,打开APP填写以上IP和程序中端口号即可连接。

手机APP控制过程通过不同按钮发送不同的字符串,开发板程序在接收完一次数据后对字符串解析执行对应的流程。

见例程中bsp_esp8266_test.c中的ESP8266_CheckRecv_SendDataTest函数流程,此时流程中未开启透传模式,利用strstr先定位接收数据中自定义字符串的位置,再依次偏移获取后续的字符做判断,可以在第一个if语句中打断点, debug执行,用手机APP按一次按钮发送数据,停在断点后用watch工具查看Data_RX_BUF的内容方便理解字符串解析流程。

例程中使用的手机APP年代久远,目前没有源码文件,从上面描述与结合源码理解,本质还是建立TCP连接后发送自定义数据,因此这个过程可以用任意类似网络调试助手功能的手机APP代替,可以自己修改简化解析的字符串过程。

3.9.1. APP 界面简介

APP 可以控制开发板上面的RGB灯,蜂鸣器,开发板会将 DHT11 采集的温湿度信息发送给APP。 APP界面如下图所示:

手机APP界面截图
  1. APP 中的 IP 地址和端口号需要填的是 WIFI 模块 ESP8266 内部的 Server 信息,这在例程里已经设置好了(可以修改),根据例程里的进行填写,旁边的连接按钮默认是白色,当点击连接成功之后会变成黄褐色。

  2. APP 中设备状态栏返回的是设备的状态信息, LED 的亮灭和温湿度信息,因为可以同时 5 台手机控制,当一台手机控制板子的 LED 改变状态时。另一台手机界面的状态变化可以通过这个按钮选择自动更新还是手动更新,自动更新的时间一般为 3s 最合适。

  3. APP 中三个 LED 的图标可控制开发板中三个 LED 的亮灭,灭的时候全部显示灰色,亮的时候三个 LED 对应板子上的3个LED灯,如果板子上的是RGB灯,则对应RGB灯的红绿蓝三种颜色亮起。

  4. 温度和湿度是开发板上的 DHT11 传回来的信息,前提是开发板上插有温湿度传感器 DHT11。

  5. 蜂鸣器按钮可以控制蜂鸣器的开和关。

注意

有一些手机连接了WiFi模块的热点之后,会检测到和提示WiFi模块没有网络可用,会自动切换回数据流量,导致APP连接模块失败,解决办法就是暂时先关闭数据流量