Bootstrap

手把手教你玩转ESP8266(原理+驱动)

在嵌入式开发中,无线通信的方式有很多,其中 WIFI 是绕不开的话题。说到 WIFI 通信,就不得不提 ESP8266了。

ESP8266 是一款高性能的 WIFI 串口模块,实现透明传输。只要有一定的串口知识,不需要知道 WIFI 原理就可以上手,在业内应用广泛。

1. 源码下载

本文首发 良许嵌入式网https://www.lxlinux.net/e/ ,欢迎关注!

本文所涉及的源码及安装包如下(由于平台限制,请点击以下链接阅读原文下载):

https://www.lxlinux.net/e/stm32/esp8266-tutorial.html

  • STM32F103C8T6模板工程

  • 本文的源码

  • 串口调试助手

  • 网络调试助手

  • CH340驱动

如果不知道如何搭建 STM32 编程环境,不知道如何烧录 STM32 代码,可以阅读这篇文章:

https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html

如果你连代码都不知道怎么烧录到 STM32 的,可以参考下文,提供了 5 种代码烧录方式:

https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html

文中所使用的芯片是 STM32F103C8T6 ,配套了一个工程模板,如果你需要自己搭建一个工程模板,可以参考下文:

https://www.lxlinux.net/e/stm32/create-stm32-hal-project-template.html

2. ESP8266介绍

ESP8266 是一个非常强大的 WIFI 模块,可以利用串口与单片机进行通讯,从而编程实现控制 ESP8266。利用 ESP8266 可以访问一些 API,获取天气信息或者完成网络授时,也可以连接云平台进行开发。不过因为是串口传输,速度较慢,不能用来传输图像、视频这些大容量的数据,但是传些传感器数据还是绰绰有余的。

我们常说的 ESP8266 是指 ESP8266 WIFI 模块,它是物联网和嵌入式开发常用的模块,其中 ESP8266 是 WIFI 模块的芯片型号。

ESP8266 有 ESP-01/01S/07/07S/12E/12F/12S 等规格,还有正点原子自研的 ATK-ESP8266 (修改了固件及模组引脚)。

本文用到的是 ESP-01S,长下面这样。这种模组非常便宜,某宝几块钱一个,建议可以买几个囤着。

3. 三种调试方式

ESP8266 已经是一款很成熟的模组了,前文提到,它可以通过串口进行通讯。

既然可以进行串口通讯,那么我们就可以使用以下三种方法进行调试。

  • USB 转 TTL 工具

这玩意儿大家应该非常熟悉了,通常我们用它来打印单片机 log。

当然,配合上位机(比如串口调试助手),我们也可以使用它对一些模组进行调试,比如:ESP8266、4G模组、蓝牙,等等。

如果对这个工具使用不熟悉的小伙伴,可以阅读下文:

零基础快速上手STM32开发(手把手保姆级教程)

USB转TTL模块ESP8266
3.3V3.3V
TXDRX
RXDTX
GNDGND

使用 USB 转 TTL 工具调试 ESP8266,可以通过上面的表格进行接线。

3.3V 相接后可能无法启动 ESP8266,这是因为 USB 转 TTL 模组的 3.3V 并没有真的达到 3.3V,莫慌,直接将 ESP8266 的3.3V 引脚接入 USB 转 TTL 的 5V 引脚,如果 ESP8266 会发烫到无法触摸就拔掉,如果温度你手指还能接受,那就没问题,ESP8266 还是没那么脆弱的。

接好线之后,将 USB 端连接到电脑,打开串口调试助手就可以进行调试了。

  • USB 转 ESP8266WIFI 模块

上面的 USB 转 TTL 模组需要你动手去接线,比较麻烦,而且要是线有一根接错了,就无法正常调试了。

而这个模组专门是为了调试 ESP8266 而设计,不需要一个个引脚接线,直接把 ESP8266 往上一插就可以啦,超方便的。同样,USB 端连接电脑,打开串口调试助手就可以进行调试了。

  • 单片机调试

这种就是在项目中最常用的方式了。需要将 ESP8266 接到单片机的任意一个串口,然后再编写代码驱动 ESP8266 ,实现各种业务逻辑。

本文就是手把手教大家编写一个 ESP8266 驱动程序。

上电:正常工作验证

模块拿到手之后,我们需要先确保这个模组是正常的,否则后面的调试、写代码都是白搭。

首先,上电后,蓝色灯微弱闪烁后熄灭,表示正常工作。

还可以更进一步,接好线之后,然后打开串口调试助手发送 AT(并且还要敲一个回车),ESP8266 回复 OK,就是正常启动了。

4. ESP8266工作模式

ESP8266 支持 STA、AP、AP+STA 三种工作模式。

  • STA 模式(Station)

一般用于远距离传输。ESP8266 通过路由器连接互联网,终端设备通过互联网实现对设备的远程控制。简单来说,此时的 ESP8266 可以当作是一个客户端,可以向服务端进行数据的下载与传输。这就类似于,手机/平板/笔记本(客户端)可以通过 WIFI 连接到路由器进行上网。

  • AP 模式(Access Point)

一般用于近距离传输。ESP8266 作为热点,提供无线接入服务、数据访问,一般的无线路由/网桥工作在 AP 模式下,最多支持 4 台设备接入。简单来说,此时的 ESP8266 可以当作是一个服务端。这就类似于,ESP8266 变身为一个路由器,然后手机/平板/笔记本可以通过 WIFI 连接到 ESP8266 进行上网。

  • AP+STA 模式

两种模式的共存模式,可以通过互联网控制可实现无缝切换,方便操作。简单来说,此时的 ESP8266 可以当作是一个路由器既可以做服务端接收也可以当客户端连接路由器,进行联网传输和控制。

5. AT指令介绍

5.1 什么是AT指令?

AT 指令(AT Commands)最早是由发明拨号调制解调器的贺氏公司为了控制拨号调制解调器而发明的控制协议。后来随着网络带宽的升级,速度很低的拨号调制解调器基本退出市场,但是 AT 指令被保留了下来。

在嵌入式开发中,经常是使用 AT 命令去控制各种通讯模块,比如 ESP8266 模块、4G 模块、GPRS 模块等等。一般就是主芯片通过硬件接口(比如串口、SPI)发送 AT 指令给通讯模块,模块接收到数据之后回应响应的数据。

5.2 常用AT指令介绍

AT 指令分为四种类型:

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

AT 指令有近百条,但常用的就十几条,理解起来也非常简单,现在举例一些常用指令,并使用这些指令一步一步的通过 TCP 连接到远程的服务器实现收发数据。

AT指令功能
AT测试是否正常启动
AT+CWMODE=1设置 STA 模式
AT+CWMODE=2设置 AP 模式
AT+CWMODE=3设置 AP+STA 模式
AT+RST重启生效
AT+CWSAP=”SSID”,”password”,1,4设置 AP 参数:账号为SSID ,密码为password,通道号为 1,加密方式为:WPA_WPA2_PSK
AT+CIPMUX=0开启单连接
AT+CIPMUX=1开启多连接
AT+CIPSERVER=1,8080开启 SERVER 模式,设置端口为 8080
AT+CIPSTART=“TCP”,"192.168.X.XXX”,8080建立 TCP 连接到”192.168.X.XXX”,8080
AT+CIPSTART=“UDP”,“192.168.X.XXX”,8080建立 UDP 连接到”192.168.X.XXX”,8080
AT+CIPCLOSE断开 TCP 连接
AT+CWQAP断开热点
AT+CIPSEND=n开始传输,n表示需要传输的字节数
AT+CIPSEND=0,n向 ID0 发送 n 字节数据包,n的值自己定
AT+CIPMODE=1开启透传模式
AT+CIPSEND开始发送数据
AT+CIPMODE=0退出透传
AT+CWJAP="SSID,“password”加入 WIFI 热点:SSID ,密码为:password
AT+CIFSR查询 ESP8266 的 IP 地址
AT+CIPSTA?查询 ESP8266 的 IP 、网关地址和子网掩码

接下来我们要用串口助手发送 AT 指令,如果你还不会用串口助手,那么动动小手跟我操作:

  1. 插上 USB,选择正确的串口号,正确的串口号是插上 USB 后多出的串口号;
  2. 选择波特率,ESP8266 默认波特率是 115200 ,可以通过 AT 指令修改;
  3. 点击「打开」或者「打开串口」;
  4. 在输入框输入指令,AT 指令要以回车结尾;
  5. 点击「发送」,观察 ESP8266 的返回值;
  6. 使用完点「断开」或者「关闭串口」。

不一定要用我的串口助手,别的串口助手也可以(比如正点原子开发的串口助手,甚至你自己写一个也行),都是一样的。

如果已经插上 USB,选择了正确的串口号,但是出现串口打不开并输出「【ERROR】Cannot set COM port parameters」 的情况,可能是 CH340 驱动还没安装,或者需要卸载重新安装一下。教你解决:

  1. 先点「断开」,断开串口;
  2. 打开 CH340 驱动安装程序,我文章开头有提供;
  3. 如果你没有安装过,点击「安装」;如果你安装过,点击「卸载」再点击「安装」;
  4. 安装好后拔插一下 USB,看看串口号有没有变,有变更换一下串口号;
  5. 最后点「打开」或者「打开串口」就可以啦。

如果还不明白的,可以参考下文,有图文教程:

零基础快速上手STM32开发(手把手保姆级教程)

5.2.1 测试模块是否正常(AT)

发送 AT,ESP8266 回复 OK,就是正常启动了。

5.2.2 开启/关闭回显(ATE1/ATE0)

发送了指令 AT,模块回复了「AT OK」,即模块将发送过来的指令原封不动的先复述了一遍后接着发送有效回复,我们称这种复述为 回显

发送 ATE0,关闭回显

发送 ATE1,开启回显

5.2.3 设置AP模式及AP参数(AT+CWMODE,AT+CWSAP)

发送 AT+CWMODE?,查询模块当前处于哪一种模式;

发送 AT+CWMODE=1,设置 STA 模式;

发送 AT+CWMODE=2,设置 AP 模式;

发送 AT+CWMODE=3,设置 AP+STA 模式。

如果当前为 AP 模式,那么发送 AT+CWSAP="SSID","password",通道,加密方式 ,可以设置热点。这个过程相当于给路由器设置热点名称及密码,方便手机连接。

例如:发送 AT+CWSAP="ESP8266-liangxu","12345678",5,3 ,则可以将热点名设置为 ESP8266-liangxu ,密码为 12345678,使用通道 5 ,加密方式为 WPA2_PSK(1-WEP,2-WPA_PSK,3-WPA2_PSK,4-WPA_WPA2_PSK),这里的通道对应的就是不同的射频频率,如果同一空间内存在相同通道的 WIFI 信号,将会产生干扰,影响上网质量,因此可以设置通道来避免这种干扰,常用的通道有 1、6、11,因为这三个通道互不产生干扰。

5.2.4 设置为STA模式(AT+CWMODE=1,AT+CWJAP,AT+CIFSR)

发送 AT+CWMODE=1,设置 STA 模式;

发送 AT+CWJAP="SSID","password",连接路由器;

发送 AT+CIPSTA?,路由器分配给 ESP8266 的局域网 IP 以及网关地址和子网掩码;

发送 AT+CIFSR,查询 ESP8266 的 IP 地址信息。

5.2.5 TCP透传
5.2.5.1 准备工作

实现 TCP 透传需要搭建一个 TCP 服务器,这里我们就使用网络调试助手 + 自己的电脑就好。我的网络调试助手是正点原子的,大家不一定要用我的,只要是网络助手都可以。

首先我们需要知道自己电脑的 IP 地址,有两种方式:

方法1:打开电脑的设置:

方法2:使用命令提示符:

打开网络调试助手,跟我配置,超快的。端口号 TCP 协议一般填8080。

5.2.5.2 正式开始

接下来我们利用串口助手将 ESP8266 连接到 TCP 服务器。

发送 AT+CIPSTART="协议模式","服务端IP地址",端口号 ,建立连接,协议模式有 TCP 和 UDP ;

因为我们刚刚搭建的是 TCP 服务器,服务器 IP 地址是 192.168.1.27,所以我们发送 AT+CIPSTART="TCP","192.168.1.27",8080 。如果不出意外,过一会儿 ESP8266 就连接上 TCP 服务器了,将返回 CONNECT OK 。

但这个时候,我们再在输入框发送内容的话,依然是默认为指令。如果我们想把数据直接发送给 TCP 服务器,那么就需要开启透传模式。

所谓的透传模式,可以视为 ESP8266 为透明状态,客户端发送的信息直接就传输给服务端。

我们需要先发送 AT+CIPMODE=1,开启透传模式。然后,再发送 AT+CIPSEND,才真正开始透传发送。

此时,你发送的任何数据,就直接传输到服务器了。

退出透传:先发送 +++(不要带回车),再发送 AT+CIPMODE=0

如果不退出透传,发出来的就算是 AT 指令,它也会被认为是普通字符串,直接透传给对方。

AT+CWCLOSE,断开 TCP 连接。

AT+CWQAP,断开热点。

6. 编程实战

6.1 硬件接线

本教程使用的硬件如下:

  • 单片机:STM32F103C8T6

  • WiFi模块:ESP-01S

  • 串口:USB 转 TTL

  • 烧录器:ST-LINK V2。

接线可参照下表:

ESP8266STM32USB 转 TTL
3V33.3
TXA3
RXA2
GNDG
A10TX
A9RX
GGND

烧录的时候接线如下表,如果不会烧录的话可以看我之前的文章【STM32下载程序的五种方法】。

ST-Link V2STM32
SWCLKSWCLK
SWDIOSWDIO
GNDGND
3.3V3V3

其中 USB 转 TTL 连接的是 STM32 的串口1(A9和A10),用来打印 STM32 的输出;STM32 的串口2(A2和A3),用来控制 ESP8266。

接好就像这样:

6.2 模板工程加载ESP8266模块代码

打开模板工程和 ESP8266 驱动源码。

复制好后打开工程。

跟着我的贪吃蛇点点点:)

6.3 串口通讯实现

实现向串口发送数据,并等待返回值。ESP8266 的 TX 和 RX 定义在串口2。

  1. 定义串口句柄 g_uart_handle ,并调用 HAL_UART_Init 进行初始化。
UART_HandleTypeDef g_uart_handle;

void esp8266_uart_init(uint32_t baudrate)
{
    g_uart_handle.Instance          = ESP8266_UART_INTERFACE;       /* ESP8266 UART */
    g_uart_handle.Init.BaudRate     = baudrate;                     /* 波特率 */
    g_uart_handle.Init.WordLength   = UART_WORDLENGTH_8B;           /* 数据位 */
    g_uart_handle.Init.StopBits     = UART_STOPBITS_1;              /* 停止位 */
    g_uart_handle.Init.Parity       = UART_PARITY_NONE;             /* 校验位 */
    g_uart_handle.Init.Mode         = UART_MODE_TX_RX;              /* 收发模式 */
    g_uart_handle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;          /* 无硬件流控 */
    g_uart_handle.Init.OverSampling = UART_OVERSAMPLING_16;         /* 过采样 */
    HAL_UART_Init(&g_uart_handle);                                  /* 使能ESP8266 UART */
}

其中,ESP8266_UART_INTERFACE 是宏定义,指代的就是 USART2

传入参数 baudrate 可以定义该串口的波特率。

  1. 初始化串口底层函数,调用 HAL_UART_MspInit 函数。

注意最后一行,需要调用 __HAL_UART_ENABLE_IT 函数使能接收中断。

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (huart->Instance == USART_UX)                            /* 如果是串口1,进行串口1 MSP初始化 */
    {
        ....
        // 省略串口1相关代码
        ....
    }
    else if (huart->Instance == ESP8266_UART_INTERFACE)                 /* 如果是ESP8266 UART */
    {
        ESP8266_UART_TX_GPIO_CLK_ENABLE();                              /* 使能UART TX引脚时钟 */
        ESP8266_UART_RX_GPIO_CLK_ENABLE();                              /* 使能UART RX引脚时钟 */
        ESP8266_UART_CLK_ENABLE();                                      /* 使能UART时钟 */

        gpio_init_struct.Pin    = ESP8266_UART_TX_GPIO_PIN;             /* UART TX引脚 */
        gpio_init_struct.Mode   = GPIO_MODE_AF_PP;                          /* 复用推挽输出 */
        gpio_init_struct.Pull   = GPIO_NOPULL;                              /* 无上下拉 */
        gpio_init_struct.Speed  = GPIO_SPEED_FREQ_HIGH;                     /* 高速 */
        HAL_GPIO_Init(ESP8266_UART_TX_GPIO_PORT, &gpio_init_struct);    /* 初始化UART TX引脚 */

        gpio_init_struct.Pin    = ESP8266_UART_RX_GPIO_PIN;             /* UART RX引脚 */
        gpio_init_struct.Mode   = GPIO_MODE_INPUT;                          /* 输入 */
        gpio_init_struct.Pull   = GPIO_NOPULL;                              /* 无上下拉 */
        gpio_init_struct.Speed  = GPIO_SPEED_FREQ_HIGH;                     /* 高速 */
        HAL_GPIO_Init(ESP8266_UART_RX_GPIO_PORT, &gpio_init_struct);    /* 初始化UART RX引脚 */

        HAL_NVIC_SetPriority(ESP8266_UART_IRQn, 0, 0);                  /* 抢占优先级0,子优先级0 */
        HAL_NVIC_EnableIRQ(ESP8266_UART_IRQn);                          /* 使能UART中断通道 */

        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);                          /* 使能UART接收中断 */
    }
}
  1. 使用接收中断+超时判断,完成数据接收。

由于 ESP8266 通过串口返回的数据长度不固定,所以我们可以使用接收中断+超时判断的方法完成数据接收。具体方法可以参考下文:

STM32串口接收不定长数据(接收中断+超时判断)

在串口中断服务函数里,我们可以将接收到的字符保存在接收缓冲区里 g_uart_rx_buf ,并定义一个变量 esp8266_cnt 计算总共收到了多少个字符。

void ESP8266_UART_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_RXNE) != RESET){
        if(esp8266_cnt >= sizeof(g_uart_rx_buf))
            esp8266_cnt = 0; //防止串口被刷爆
        HAL_UART_Receive(&g_uart_handle, &receive_data, 1, 1000);//串口2接收1位数据
        g_uart_rx_buf[esp8266_cnt++] = receive_data;  
    }
    HAL_UART_IRQHandler(&g_uart_handle);
}

假如一帧的数据接收完成了,那么 esp8266_cnt 变量的值应该维持不变。

我们通过 while 死循环不停计算当前收到多少个字符,当前统计的值计算在 esp8266_cnt 变量里,定义另一个变量 esp8266_cntPre ,用于记录上一次统计接收到的数据的长度,如果本次统计数据长度跟上一次一样的话就说明数据接收完成了。代码如下:

uint8_t esp8266_wait_receive(void)
{
    if(esp8266_cnt == 0)                             //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
        return ESP8266_ERROR;

    if(esp8266_cnt == esp8266_cntPre) {                //如果上一次的值和这次相同,则说明接收完毕
        esp8266_cnt = 0;                            //清0接收计数
        return ESP8266_EOK;                            //返回接收完成标志
    }

    esp8266_cntPre = esp8266_cnt;                    //置为相同
    return ESP8266_ERROR;                            //返回接收未完成标志
}

当然,接收到的数据使用完成之后,我们就应该清空接收缓冲区,并将计数器置 0 ,方便下一次接收。

void esp8266_clear(void)
{
    memset(g_uart_rx_buf, 0, sizeof(g_uart_rx_buf));
    esp8266_cnt = 0;
}

接下来,就是最关键的一个函数了。我们使用这个函数通过串口向 ESP8266 发送一个字符串,并循环等待我们所期待得到的字符串。

在下面这个函数里,cmd 变量是我们向 ESP8266 发送的字符串,res 变量是我们期待得到的回复。

比如,我们向 ESP8266 发送 AT 这个字符串,那么 ESP8266 如果正常的话应该会回复 OK 。此时,cmd 就是 AT ,而 res 就是 OK

uint8_t esp8266_send_command(char *cmd, char *res)
{
    uint8_t timeOut = 250;

    esp8266_clear();
    HAL_UART_Transmit(&g_uart_handle, (unsigned char *)cmd, strlen((const char *)cmd), 100);

    while(timeOut--) {
        if(esp8266_wait_receive() == ESP8266_EOK) {                        //如果收到数据
            if(strstr((const char *)g_uart_rx_buf, res) != NULL)        //如果检索到关键词
                return ESP8266_EOK;
        }
        delay_ms(10);
    }

    return ESP8266_ERROR;
}

6.4 编程实现AT指令交互

接下来,我们就可以使用 esp8266_send_command 发送 AT 指令并确定 ESP8266 回复是否正确。以代码方式实现各个 AT 指令,例子如下:

uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\r\n", "OK");
}

uint8_t esp8266_sw_reset(void)
{
    return esp8266_send_command("AT+RST\r\n", "OK");
}

uint8_t esp8266_set_mode(uint8_t mode)
{
    switch (mode) {
        case ESP8266_STA_MODE:
            return esp8266_send_command("AT+CWMODE=1\r\n", "OK");    /* Station模式 */

        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n", "OK");    /* AP模式 */

        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n", "OK");    /* AP+Station模式 */

        default:
            return ESP8266_EINVAL;
    }
}

由于 AT 指令有很多,这里只截取了其中的一部分,完整的可以参考我提供的代码。

6.5 编程实现STA模式

STA 模式实现思路如下:

  1. 进入 STA 模式(要重启生效);
  2. 设置单路连接;
  3. 连接 WIFI(注意 ESP8266 和服务端要在同一网络内);
  4. 浅查一下 ESP8266 的 IP 地址,不查也可以;
  5. 连接TCP服务器;
  6. 开启透传。
uint8_t esp8266_single_connection(void)
{
    return esp8266_send_command("AT+CIPMUX=0\r\n", "OK");
}

uint8_t esp8266_join_ap(char *ssid, char *pwd)
{
    char cmd[64];

    sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, pwd);

    return esp8266_send_command(cmd, "WIFI GOT IP");
}

uint8_t esp8266_get_ip(char *buf)
{
    char *p_start;
    char *p_end;

    if (esp8266_send_command("AT+CIFSR\r\n", "STAIP") != ESP8266_EOK)
        return ESP8266_ERROR;

    p_start = strstr((const char *)g_uart_rx_buf, "\"");
    p_end = strstr(p_start + 1, "\"");
    *p_end = '\0';
    sprintf(buf, "%s", p_start + 1);

    return ESP8266_EOK;
}

uint8_t esp8266_connect_tcp_server(char *server_ip, char *server_port)
{
    char cmd[64];

    sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%s\r\n", server_ip, server_port);

    return esp8266_send_command(cmd, "CONNECT");
}

uint8_t esp8266_enter_unvarnished(void)
{
    uint8_t ret;

    ret  = esp8266_send_command("AT+CIPMODE=1\r\n", "OK");
    ret += esp8266_send_command("AT+CIPSEND\r\n", ">");
    if (ret == ESP8266_EOK)
        return ESP8266_EOK;
    else
        return ESP8266_ERROR;
}

/**
 * @brief       ESP8266初始化
 * @param       baudrate: ESP8266 UART通讯波特率
 * @retval      ESP8266_EOK  : ESP8266初始化成功,函数执行成功
 *              ESP8266_ERROR: ESP8266初始化失败,函数执行失败
 */
uint8_t esp8266_init(uint32_t baudrate)
{
    char ip_buf[16];

    esp8266_uart_init(baudrate);                /* ESP8266 UART初始化 */

    /* 让WIFI退出透传模式 */
    while(esp8266_exit_unvarnished())
        delay_ms(500);

    printf("1.AT\r\n");
    while(esp8266_at_test())
        delay_ms(500);

    printf("2.RST\r\n");
    while(esp8266_sw_reset())
        delay_ms(500);
    while(esp8266_disconnect_tcp_server())
        delay_ms(500);

    printf("3.CWMODE\r\n");
    while(esp8266_set_mode(ESP8266_STA_MODE))
        delay_ms(500);

    printf("4.AT+CIPMUX\r\n");  //设置单路连接模式,透传只能使用此模式
    while(esp8266_multi_connection())
        delay_ms(500);

    printf("5.CWJAP\r\n");      //连接WIFI
    printf("%s\r\n",WIFI_SSID); 
    while(esp8266_join_ap(WIFI_SSID, WIFI_PWD))
        delay_ms(1000);

    printf("6.CIFSR\r\n");
    while(esp8266_get_ip(ip_buf))
        delay_ms(500);

    printf("ESP8266 IP: %s\r\n", ip_buf);

    printf("7.CIPSTART\r\n");
    while(esp8266_connect_tcp_server(TCP_SERVER_IP, TCP_SERVER_PORT))
        delay_ms(500);

    printf("8.CIPMODE\r\n");
    while(esp8266_enter_unvarnished())
        delay_ms(500);

    printf("ESP8266初始化完成\r\n");
    return ESP8266_EOK;
}

代码完成,编译,烧录,打开串口,串口输出,实现效果当当当:

6.6 编程实现AP模式

AP 模式实现思路很简单:

  1. 进入 AP 模式(要重启生效);
  2. 设置 AP 参数;
  3. 开启多连接或单连接。
uint8_t esp8266_set_ap(char *ssid, char *pwd)
{
    char cmd[64];

    sprintf(cmd, "AT+CWSAP=\"%s\",\"%s\",5,3\r\n", ssid, pwd);

    return esp8266_send_command(cmd, "OK");
}

uint8_t esp8266_multi_connection(void)
{
    return esp8266_send_command("AT+CIPMUX=1\r\n", "OK");
}

uint8_t esp8266_init(uint32_t baudrate)
{
    char ip_buf[16];

    esp8266_uart_init(baudrate);                /* ESP8266 UART初始化 */

    /* 让WIFI退出透传模式 */
    while(esp8266_exit_unvarnished())
        delay_ms(500);

    printf("1.AT\r\n");
    while(esp8266_at_test())
        delay_ms(500);

    printf("2.RST\r\n");
    while(esp8266_sw_reset())
        delay_ms(500);
    while(esp8266_disconnect_tcp_server())
        delay_ms(500);

    printf("3.CWMODE\r\n");
    while(esp8266_set_mode(ESP8266_AP_MODE))
        delay_ms(500);

    printf("4.CWSAP\r\n");      //设置AP
    printf("%s\r\n",WIFI_SSID); 
    while(esp8266_set_ap(WIFI_SSID, WIFI_PWD))
        delay_ms(1000);

    printf("5.AT+CIPMUX\r\n");  //设置多路连接模式
    while(esp8266_multi_connection())
        delay_ms(500);

    printf("ESP8266_Init OK\r\n");
    return ESP8266_EOK;
}

代码完成,烧录,打开串口,串口输出,实现效果当当当:

6.7 编程实现AP+STA模式

AP+STA 模式使用不多,就是前两个的结合,这里就不多介绍了。仿照 STA 模式和 AP 模式写相应的 AT 指令代码就好啦,思路如下,需要注意的是要启动多连接:

  1. 设置为 AP+STA 模式(要重启生效);
  2. AT+CWJAP=”SSID”,”password”,连接WIFI(注意 ESP8266 和服务端要在同一网络内);
  3. AT+CWSAP="SSID","password",通道,加密方式,设置AP热点;
  4. AT+CIPMUX=1,启动多连接;
  5. AT+CIPSERVER=1,8080,打开服务器端口为8080。
uint8_t esp8266_join_ap(char *ssid, char *pwd)
{
    char cmd[64];

    sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, pwd);

    return esp8266_send_command(cmd, "WIFI GOT IP");
}

uint8_t esp8266_set_ap(char *ssid, char *pwd)
{
    char cmd[64];

    sprintf(cmd, "AT+CWSAP=\"%s\",\"%s\",5,3\r\n", ssid, pwd);

    return esp8266_send_command(cmd, "OK");
}

uint8_t esp8266_multi_connection(void)
{
    return esp8266_send_command("AT+CIPMUX=1\r\n", "OK");
}

uint8_t esp8266_open_server()
{
    return esp8266_send_command("AT+CIPSERVER=1,8080\r\n", "OK");
}

uint8_t esp8266_init(uint32_t baudrate)
{
    char ip_buf[16];

    esp8266_uart_init(baudrate);                /* ESP8266 UART初始化 */

    /* 让WIFI退出透传模式 */
    while(esp8266_exit_unvarnished())
        delay_ms(500);

    /* STA+AP模式 */
    printf("1.AT\r\n");
    while(esp8266_at_test())
        delay_ms(500);

    printf("2.RST\r\n");
    while(esp8266_sw_reset())
        delay_ms(500);
    while(esp8266_disconnect_tcp_server())
        delay_ms(500);

    printf("3.CWMODE\r\n");
    while(esp8266_set_mode(ESP8266_STA_AP_MODE))
        delay_ms(500);

    printf("4.CWJAP\r\n");      //连接WIFI
    printf("%s\r\n",WIFI_SSID); 
    while(esp8266_join_ap(WIFI_SSID, WIFI_PWD))
        delay_ms(1000);

    printf("5.CWSAP\r\n");      //设置AP
    while(esp8266_set_ap("ESP8266-liangxu", "12345678"))
        delay_ms(1000);

    printf("6.AT+CIPMUX\r\n");  //设置多路连接模式
    while(esp8266_multi_connection())
        delay_ms(500);

    printf("7.CIPSERVER\r\n");  //打开服务器端口8080
    while(esp8266_open_server())
        delay_ms(500);

    printf("ESP8266_Init OK\r\n");
    return ESP8266_EOK;
}

代码完成,编译,烧录,打开串口,串口输出,实现效果当当当:

esp8266.h文件内容如下:

#ifndef __ESP8266_H__
#define __ESP8266_H__

#include <stdint.h>
#include "usart.h"

extern UART_HandleTypeDef g_uart_handle;

/* 引脚定义 */
#define ESP8266_UART_TX_GPIO_PORT           GPIOA
#define ESP8266_UART_TX_GPIO_PIN            GPIO_PIN_2
#define ESP8266_UART_TX_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PC口时钟使能 */

#define ESP8266_UART_RX_GPIO_PORT           GPIOA
#define ESP8266_UART_RX_GPIO_PIN            GPIO_PIN_3
#define ESP8266_UART_RX_GPIO_CLK_ENABLE()   do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PD口时钟使能 */

#define ESP8266_UART_INTERFACE              USART2
#define ESP8266_UART_IRQn                   USART2_IRQn
#define ESP8266_UART_IRQHandler             USART2_IRQHandler
#define ESP8266_UART_CLK_ENABLE()           do{ __HAL_RCC_USART2_CLK_ENABLE(); }while(0) /* UART2时钟使能 */

/* UART收发缓冲大小 */
#define ESP8266_UART_RX_BUF_SIZE            128
#define ESP8266_UART_TX_BUF_SIZE            64

/* 错误代码 */
#define ESP8266_EOK                         0   /* 没有错误 */
#define ESP8266_ERROR                       1   /* 通用错误 */
#define ESP8266_ETIMEOUT                    2   /* 超时错误 */
#define ESP8266_EINVAL                      3   /* 参数错误 */

/* 工作模式 */
#define ESP8266_STA_MODE                    1
#define ESP8266_AP_MODE                     2
#define ESP8266_STA_AP_MODE                 3

#define WIFI_SSID                           "HuaweiAP-1ED0"
#define WIFI_PWD                            "87654321"

#define TCP_SERVER_IP                       "192.168.1.27"
#define TCP_SERVER_PORT                     "8080"

uint8_t esp8266_init(uint32_t baudrate);
void esp8266_clear(void);
void esp8266_uart_printf(char *fmt, ...);
#endif

7. 小结

通过学习和实践,希望大家能够了解并掌握 ESP8266 的特性和使用,从而更好地应用于嵌入式开发。无论是构建智能家居系统还是开发物联网设备,ESP8266 都可以成为您的得力助手,让我们一起玩转 ESP8266,love and peace!


另外,想进大厂的同学,一定要好好学算法,这是面试必备的。这里准备了一份 BAT 大佬总结的 LeetCode 刷题宝典,很多人靠它们进了大厂。

刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

推荐阅读:

欢迎关注我的博客:良许嵌入式教程网,满满都是干货!

;