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

0

ESP8266物联网简介

ESP8266简介

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

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

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

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

ESP-01S模块

它具有的优点为:

  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 个引脚,各个引脚的作用为:

序号 名称 功能
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] = '\0';
        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 相连:

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

#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 ,并将接收到的数据再转发给串口调试工具:

因此,在初始化 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 \r\n 作为结尾的标志,在串口调试工具中需要另起一行。

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:\Users\Hello> 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 指令文档。不过很少有商家的固件会有这么新的版本。

京ICP备2021034974号
contact me by hello@frozencandles.fun