STM32与物联网01-ESP8266基本操作

ESP8266物联网简介

ESP8266简介

ESP8266 是上海乐鑫公司开发的一款具有 WiFi 功能的控制芯片,它带有完整的 TCP/IP 协议栈,因此可以用作物联网开发。

ESP8266 本身也是一个性能不错的 32 位微控制器,完全可以作为普通的 MCU 使用。然而,考虑到 ESP8266 作为 MCU 时需要一整套开发环境,且 ESP8266 的外设并不算丰富,因此这里仅将其作为一个普通外围器件使用,通过 STM32 等 MCU 控制它并接收 ESP8266 收到的网络数据。

在作为外围模块使用时,ESP8266 主要通过串口收发命令和数据,因此任意可以使用串口并设置波特率的 MCU 理论上都可以操作 ESP8266 实现物联网功能,包括但不限于 51 单片机、AVR 、STM32 和树莓派。

这里选用 ESP-01 作为 WiFi 模块,其外观为:

STM32与物联网01-ESP8266基本操作

它具有的优点为:

  1. 价格非常低廉,仅需个位数
  2. 尺寸很小,大约为 25mm x 15mm
  3. 功能完善,它本身也是一个微型开发板,具有 8 个引脚,可以实现程序下载、串口收发等功能
  4. 市面上大多数 ESP-01 模块在售卖时已经内置了串口控制程序,上电后便可以正常工作。如果没有也不要紧,只需再花个位数价格就可以再买一个 ESP8266 固件下载器,结合商家给出的资料就可以重新烧入固件

在详细介绍 ESP8266 的使用方法之前,最好先了解以下背景知识:

ESP8266 所使用的 WiFi 是工作频率在 2.4GHz 波段的局域网无线通信。有些笔记本电脑或路由器默认使用的是 5GHz 的网络频段,如果不修改将会无法与 ESP8266 连接上。

ESP8266 支持两种 WiFi 通信模式:AP 和 Sta 。AP 表示接入点(access point),可以创建一个 WiFi 热点让其余设备连接,一般作为局域网服务器使用;Sta 表示连接设备,该模式下 ESP8266 可以主动连接其它 WiFi 信号,一般作为局域网客户端使用。不过 ESP8266 支持 Sta 和 AP 两模式共存,可以在连接 WiFi 的同时被其余设备连接。

在 ESP-01 模块中,具有 8 个引脚,各个引脚的作用为:

STM32与物联网01-ESP8266基本操作

序号 名称 功能
1 GND 接地
2 GPIO 2 通用输入输出(内部已上拉)
3 GPIO 0 选择模式:低电平为下载模式,未连接或高电平为正常工作模式
4 RXD 串口 0 数据接收,也可用作普通 GPIO
5 VCC 3.3V 供电
6 RST 复位线,若通过外部置为低电平则复位
7 CH_PD 高电平使能芯片,低电平失能芯片
8 TXD 串口 0 数据发送,也可用作普通 GPIO

接下来的程序使用基于 STM32 的标准库编写,并可以比较容易地修改为 HAL 库的代码,或使用其余类似的单片机编写作用相似的代码。

串口接收不定长数据方法

在正式介绍 ESP8266 操作方法之前,首先介绍一个基本的要点:如何使用串口接收 ESP8266 可能发来的不定长数据并解析。

不定长数据的接收方法有很多,例如可以通过空字符确定结尾。这里使用串口的空闲中断实现该方法,空闲中断的的产生是由于在两次数据发送间隔,串口没有检测到数据输入而产生的,从而可以判断数据接收完毕,停止接收数据。

首先,为了保存接收数据,需要定义一个缓冲区。这里通过一个结构体的形式确定缓冲区所需成员:

#define USART_RX_BUF_SIZE 1024 typedef struct {     char Body[USART_RX_BUF_SIZE];     uint16_t Length     :15;     uint16_t FinishFlag :1; } USART_Buffer; 

注意,由于不总是在中断函数内处理接收数据,因此需要一个比特的字段用于判断数据是否接收完毕。

为了接收串口空闲中断,需要先在初始化函数内使能它:

void USART_Config(void) {     // ...     USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); } 

对应的串口中断函数的实现如下:

USART_Buffer ESP8266_Buffer; void USART3_IRQHandler(void) {     if (USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) {         if (ESP8266_Buffer.Length < (USART_RX_BUF_SIZE - 1))             ESP8266_Buffer.Body[ESP8266_Buffer.Length++] = (char)USART_ReceiveData(USART3);     }     if (USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) {         ESP8266_Buffer.FinishFlag = 1;         ESP8266_Buffer.Body[ESP8266_Buffer.Length] = '';         volatile uint16_t temp;         temp = USART3->SR;         temp = USART3->DR;         ESP8266_FrameFinish_CallBack();     } } 

在串口中断函数中,对以下两个中断类型响应:USART_IT_RXNE 表示数据接收寄存器收到内容,那么将接收到的内容作为一个字符放入缓冲区中;USART_IT_IDLE 表示数据包接收完毕,在缓冲器结尾添加上一个空字符使其变为字符串,并将结束标志位置 1 。

注意在不接收中断时,串口空闲中断会一直产生,从而干扰程序运行;清除串口空闲中断标志位需要由软件完成,具体做法是通过程序先读取 USART_SR 寄存器,再读取 USART_DR 寄存器。

在程序的最后使用一个回调函数来处理本次接收的数据包,它可以根据当前项目的使用情况自行编写或替换为相应的语句。

ESP8266简单使用

设备连接与初始化

根据上文的介绍,单片机最少需要 4 个 I/O 口与 ESP8266 相连:这里选用 USART3 作为与 ESP8266 通信的串口,则 PB10 与 ESP8266 的 RXD 相连,PB11 与 TXD 相连;PA4 与 RST 相连,PA5 与 CH_PD 相连:

STM32与物联网01-ESP8266基本操作

这里主要通过以下两个宏操作引脚:

#define ESP8266_RST(state)   GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)state) #define ESP8266_CH_PD(state) GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)state) 

本节先介绍一个最简单的、手动操作 ESP8266 的方式演示操作的整个过程:通过计算机的串口调试工具将命令发送给 STM32 ,STM32 接收后转发给 ESP8266 ,并将接收到的数据再转发给串口调试工具:

STM32与物联网01-ESP8266基本操作

因此,在初始化 ESP8266 时需要初始化相应的 GPIO 及两个 USART 外设,并将 RST 和 CH_PD 都置高电平:

void ESP8266_Init(void) {     ESP8266_GPIO_Config();     ESP8266_USART_Config();     ESP8266_RST(SET);     ESP8266_CH_PD(SET); } 

注意,初始化 STM32 连接到 ESP8266 的串口时,需要将波特率设置为 115200 ,否则数据无法被正常接收。当连接上 ESP8266 后,可以通过后续发送指令修改 ESP8266 的串口波特率。

除此之外,还有一些其它的外设如定时器、调试用串口等,其使用情况可以根据项目需要自行管理,对应的初始化过程不再介绍。

在串口 3 中断中,将接收到的 ESP8266 数据转发回串口调试工具:

static void ESP8266_FrameFinish_CallBack(void) {     printf("%s", ESP8266_Buffer.Body);     ESP8266_Buffer.FinishFlag = 0;     ESP8266_Buffer.Length = 0; } 

串口 1 的中断处理过程与以上类似,这里不再重复。

AT指令简介

既然是使用串口通信的方式操作 ESP8266 ,那么收、发数据都需要遵循一定格式。ESP8266 的固件内置了 AT 指令,可以通过串口发送 AT 指令控制 ESP8266 。

所谓 AT 指令,是一种字符串形式的数据,但开头都是 AT 两个字符,后续跟上具体的选项。AT 指令有以下 4 种主要的表现形式:

指令类型 指令格式 说明
测试指令 AT+<x>=? 用于查询设置命令或内部程序设置的参数以及其取值范围
查询指令 AT+<x>? 用于查询参数当前设置的值
设置命令 AT+<x>=<...> 用于设置用户自定义的参数值
执行指令 AT+<x> 用于执行受模块内部程序控制的变参数不可变的功能

每一个 AT 指令以换行符 CRLF rn 作为结尾的标志,在串口调试工具中需要另起一行。

AT 指令很多,但是并不是每一个都会用得到。这里仅介绍需要的 AT 指令,完整的 AT 指令可以从文档中查看。

注意,某些厂商在生产开发板时,可能会对 AT 固件做一些裁剪,去除一些用处不大的指令,因此在使用时请阅读商家提供的说明文档。

最简单的 AT 指令就是单个 AT ,用于测试 AT 固件是否能用。如果能用,ESP8266 会返回 OK :

AT AT   OK 

(博客园的代码无法高亮出哪部分属于输入,如果分辨不够清楚的可以查看原文

上面发送了一个指令 AT ,而 ESP8266 则先回复了指令内容 AT ,再回复一个 OK ,这种先复述指令内容再发送有效数据的方式称为回显。回显会在一定程度上影响数据解析,并且在设计时 STM32 在接到串口调试工具发送的消息时已经执行了一次回显操作,因此可以使用 ATE0 指令关闭回显:

ATE0 ATE0   OK AT  OK 

这样后续发送指令时只会回复有效数据了。在后续的操作中全部关闭回显,命令都是通过 STM32 收到后立即转发回来的。

可以使用 AT+GMR 查看当前固件的版本信息:

AT+GMR  AT version:0.22.0.0(Mar 20 2015 10:04:26) SDK version:1.0.0 compile time:Mar 20 2015 11:00:32  OK 

如果固件版本过旧,可能也会缺少一些命令。可以使用专用的固件烧入模块通过 USB 为 ESP8266 更新固件。

上文曾经提到 ESP8266 有两种主要的工作模式:Sta 和 AP 。可以使用 AT+CWMODE=<mode> 设置 ESP8266 的通信模式:参数 <mode> 为 1 代表 ESP8266 设置为 Sta 模式;2 代表设置为 AP 模式;参数 3 则是 Sta 模式和 AP 模式共存。

这里将其设置为 Sta 模式,主动连接路由器或笔记本提供的 WiFi :

AT+CWMODE=1   OK 

在 Sta 模式下,可以使用执行命令 AT+CWLAP 列出(List)当前环境下可用的 WiFi 接入点:

AT+CWLAP  +CWLAP:(4,"Laptop",-54,"ac:4e:aa:b2:1f:f2",1) +CWLAP:(4,"TP-LINK",-28,"51:38:39:a8:d5:e0",1) +CWLAP:(4,"Mobile",-86,"a8:79:4b:22:42:e6",11)  OK 

返回的结果中,每项数据都占一行,有 5 个元素,第一个元素 <ecn> 列出了 WiFi 所使用的加密类型,值 4 代表加密类型为 WPA_WPA2_PSK ;第二个元素 <ssid> 代表 WiFi 名,第三个元素 <rssi> 代表 WiFi 强度,绝对值越小强度越高;第四个元素 <mac> 是设备的 MAC 地址;最后一个元素 <channel> 代表频道。

注意,ESP8266 返回的数据都是 UTF-8 编码的,需要将串口调试工具的编码也设置为 UTF-8 ,否则可能出现中文乱码。

连接(Join) WiFi 可以通过以下命令执行:

AT+CWJAP="TP-LINK","abc123456"   OK 

WiFi 名和密码都要以字符串的形式放在双引号内,两者间使用逗号隔开。

连接到 WiFi 后,可以使用 AT+CIFSR 命令查看当前设备的 IP 地址:

AT+CIFSR  +CIFSR:STAIP,"192.168.137.129" +CIFSR:STAMAC,"65:e8:db:a5:9b:84"  OK 

更多的 AT 指令及其用法可以参考官方文档。接下来介绍 ESP8266 从连接 WiFi 到接收网络数据的一般过程。

WiFi连接与数据收发测试

以下测试也全部在串口调试工具中发送命令与接收数据。

首先提前设置好 WiFi 名和密码,然后让 ESP8266 主动连接 WiFi :

AT+CWMODE=1   OK AT+CWJAP="TP-LINK","abc123456"   OK 

这里将计算机和 ESP8266 都主动连接到路由器提供的 WiFi 中,两者处于同一个局域网内,这样便可以比较方便地互发数据。

连接后,需要在计算机中查看本机在局域网内的地址:(IPv4 Address)

C:UsersHello> ipconfig  Windows IP Configuration  Wireless LAN adapter WLAN:      Connection-specific DNS Suffix  . :     IPv4 Address. . . . . . . . . . . : 192.168.1.105     Subnet Mask . . . . . . . . . . . : 255.255.255.0     Default Gateway . . . . . . . . . : 192.168.1.1 

以下通过 ESP8266 主动向计算机发起连接,并发送查询当前时间的命令;计算机接到命令后,向 ESP8266 返回当前的时间。在计算机的客户端,使用 Python 编写如下套接字程序:

import socket, time server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('', 12000)) server.listen(1) while True:     connect, address = server.accept()     print(address)     message = connect.recv(1024)     print(message)     if message.decode() == 'time':         connect.send(time.ctime().encode())     connect.close() 

使用 Python 编写套接字程序的方法可以参考这篇文章。运行该程序后,在单片机端通过设置指令 AT+CIPSTART 向该局域网 IP 地址与端口号发起 TCP 连接:

AT+CIPSTART="TCP","192.168.1.105",12000  CONNECT  OK 

连接完成以后,可以通过设置指令 AT+CIPSEND 发送数据,参数 <length> 为数据的长度:

AT+CIPSEND=4   OK > time SEND OK  +IPD,24:Mon Jul 11 14:58:48 2022CLOSED 

当收到此命令后,会换行返回 > 符号,表示接下来可以继续接收待发送的数据;后续通过串口发送的数据可以不用以新行结尾,当数据长度达到 <length> 时,ESP8266 才会将数据发送出去并返回 OK 。

在收到网络数据时,ESP8266 会以 +IPD 的指令形式返回,第一个逗号后面代表数据的长度,冒号后面跟随的是实际的数据。最后的 CLOSE 代表连接中断,它和数据是是分两次接收的。通过解析数组 ESP8266_Buffer.Body 中保存的数据,单片机就可以通过网络获取当前的实时时间,并用于校正当前的 RTC 时钟等。

当然,在实际使用时不会通过串口转发这么麻烦的方式,可以在程序中直接操作串口按指定的形式收发数据,后续将会介绍相应程序的编写方法。

参考资料/延伸阅读

https://docs.espressif.com/projects/esp-at/en/release-v2.2.0.0_esp8266/Get_Started/index.html

ESP-AT 指令文档。不过很少有商家的固件会有这么新的版本。

发表评论

相关文章