【STM32】串口通信及相关实验和项目

目录

  • 一、串口相关知识
    • [1. 通信基础](#1. 通信基础)
      • [1.1 串行通信与并行通信](#1.1 串行通信与并行通信)
      • [1.2 全双工、半双工及单工通信](#1.2 全双工、半双工及单工通信)
      • [1.3 同步通信与异步通信](#1.3 同步通信与异步通信)
      • [1.4 通信速率](#1.4 通信速率)
    • [2. 串口通信简介](#2. 串口通信简介)
    • [3. STM32的USART简介](#3. STM32的USART简介)
    • [4. USART框图](#4. USART框图)
    • [5. UART寄存器及函数介绍](#5. UART寄存器及函数介绍)
  • 二、相关实验与项目
    • [1. 小实验1:通过串口发送/接收一个字符](#1. 小实验1:通过串口发送/接收一个字符)
    • [2. 小实验2:串口接收不定长数据(接收中断)](#2. 小实验2:串口接收不定长数据(接收中断))
    • [3. 小实验3:串口接收不定长数据(空闲中断)](#3. 小实验3:串口接收不定长数据(空闲中断))
    • [4. 模块:HC-08蓝牙](#4. 模块:HC-08蓝牙)
    • [5. 项目:蓝牙遥控插座](#5. 项目:蓝牙遥控插座)
    • [6. 模块:ESP8266](#6. 模块:ESP8266)
      • [6.1 ESP8266作为设备](#6.1 ESP8266作为设备)
        • [6.1.1 入网设置](#6.1.1 入网设置)
        • [6.1.2 使用网络助手,设立TCP服务器](#6.1.2 使用网络助手,设立TCP服务器)
        • [6.1.3 连接服务器](#6.1.3 连接服务器)
        • [6.1.4 发送数据](#6.1.4 发送数据)
        • [6.1.5 透传](#6.1.5 透传)
      • [6.2 ESP8266作为服务器](#6.2 ESP8266作为服务器)
      • [6.3 ESP8266串口通讯功能STM32代码实现](#6.3 ESP8266串口通讯功能STM32代码实现)
      • [6.4 ESP8266入网功能STM32代码实现](#6.4 ESP8266入网功能STM32代码实现)
      • [6.5 ESP8266连接TCP服务器功能STM32代码实现](#6.5 ESP8266连接TCP服务器功能STM32代码实现)
      • [6.6 ESP8266作为服务器功能STM32代码实现(网络调试助手连接ESP8266之前需要把WIFI接入ESP8266)](#6.6 ESP8266作为服务器功能STM32代码实现(网络调试助手连接ESP8266之前需要把WIFI接入ESP8266))
    • [7. 项目:Wi-Fi遥控风扇](#7. 项目:Wi-Fi遥控风扇)
    • [8. 模块:4G模块](#8. 模块:4G模块)
      • [8.1 内网穿透](#8.1 内网穿透)
      • [8.2 花生壳配置](#8.2 花生壳配置)
      • [8.3 4G模块配置](#8.3 4G模块配置)
    • [9. 项目:4G遥控灯](#9. 项目:4G遥控灯)

一、串口相关知识

1. 通信基础

1.1 串行通信与并行通信

1.2 全双工、半双工及单工通信

单工通信:数据只能沿一个方向传输

半双工通信:数据可以沿两个方向传输,但需要分时进行

全双工通信:数据可以同时进行双向传输

1.3 同步通信与异步通信

  • 同步通信

  • 异步通信

同步通信:发送和接收双方按照预定的时钟节拍进行数据的发送和接收,双方的操作严格同步。

异步通信:双方不需要严格的时钟同步,每个数据块之间通过特定的起始位和停止位进行分隔,接收方可以独立地识别每个数据块。

1.4 通信速率

通信速率是指在通信系统中单位时间内传输的信息量,是评估通信系统性能的重要指标之一。

  1. 比特率(Bit rate):

定义:比特率是指在通信线路(或系统)中单位时间(每秒)内传输的信息量,即每秒能传输的二进制位数。它用单位时间内传输的二进制代码的有效位(bit)数来表示,其单位为比特/秒(bit/s或bps)。

含义:比特率越高,表示单位时间内传送的数据量越大,信息传输的速率越快。它经常被用作连接速度、传输速度、信息传输速率和数字带宽容量的同义词。

  1. 波特率(Baud rate):

定义:在电子通信领域,波特率表示每秒传送的码元的个数,即单位时间内载波调制状态变化的次数。

它用单位时间内载波调制状态改变次数来表示,其单位为波特(Baud)。

含义:波特率描述的是单位时间内调制信号的能力,它决定了在给定时间内可以通过通信通道发送多少个离散的信号单元(码元)。在数字通信中,码元是表示数字信息的最小单位。

  1. tips

比特率 = 波特率 * log2 M ,M表示每个码元承载的信息量

二进制系统中,波特率数值上等于比特率

2. 串口通信简介

串口,也称为串行接口或串行通信接口(通常指COM接口),是一种采用串行通信方式的扩展接口。它实现了数据一位一位地顺序传送,具有通信线路简单、成本低但传送速度慢的特点。只要一对传输线,串口就可以实现双向通信。

串口通信的接口类型包括TTL、CMOS、RS-232和RS-485等,它们分别代表了不同的电平标准。

  • TTL电平
    逻辑1:5V,逻辑0:0V
  • CMOS电平
    逻辑1:供电电压的最大值,逻辑0:0V
  • RS-232电平
    逻辑1:-3V~-15V,逻辑0:+3~+15V
  • RS-485电平
    采用差分信号,逻辑1:两线间的电压差为+(0.2~6)V,逻辑0:两线间的电压差为-(0.2 ~ 6)V
  1. 起始位(Start Bit):起始位为低电平时,告诉接收方数据传输即将开始,准备接收。在通信开始时,发送端首先会发送一个起始位,它是一个逻辑0(低电平)的信号,用于同步发送和接收设备之间的时钟。接收端在检测到起始位后,会开始准备接收后续的数据位。
  2. 有效数据位(Data Bits):数据位是由一系列二进制值组成,用于传输或接收实际的数据。数据位的数量决定了可以传输的不同二进制值的数量,常见的有5位、6位、7位、8位,LSB在前,MSB在后。数据位紧随起始位之后,包含了要传输的实际信息
  3. 校验位(Parity Bit):校验位用于验证数据的完整性,以确保传输过程中没有出现错误。常见的校验位选项有None(无校验位)、Odd(奇校验位)和Even(偶校验位)。在发送数据时,校验位会根据数据位中1的个数进行计算,并加入到数据中一起传输。接收端则会根据校验位的值进行校验,以判断数据是否存在错误。
  4. 停止位(Stop Bit):停止位是一个逻辑高电平(1),用于指示数据传输的结束。当停止位出现时,接收端知道数据传输已经完成,并且可以开始处理接收到的数据。停止位位于数据位和校验位之后,它的作用是确保接收端有足够的时间来识别数据帧的结束,并为下一个数据帧的到来做好准备。

3. STM32的USART简介

Universal synchronous asynchronous receiver transmitter,通用同步异步收发器

Universal asynchronous receiver transmitter,通用异步收发器

  1. 全双工通信:USART支持全双工通信,即数据可以在两个方向上同时传输(A→B且B→A)。这使得USART能够满足许多需要双向通信的应用场景。
  2. 同步与异步传输:尽管USART的"S"代表同步,但在实际应用中,USART更常用于异步通信。然而,它也支持同步通信模式,只是这种模式通常用于兼容其他协议或特殊模式,并且两个USART设备不能通过同步模式进行直接通信。
  3. 波特率发生器:USART自带波特率发生器,最高可达4.5Mbits/s,可以根据需要配置不同的波特率。
  4. 硬件流控制:USART支持硬件流控制,通过特定的信号线(如RTS/CTS)实现数据的可靠传输。当接收端没有准备好接收数据时,可以通过RTS信号通知发送端暂停发送;当接收端准备好接收数据时,再通过CTS信号通知发送端恢复发送。

4. USART框图

5. UART寄存器及函数介绍








二、相关实验与项目

1. 小实验1:通过串口发送/接收一个字符

实验目的

使用串口1发送/接收一个字符。

代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    
    while(1)
    {
        
    }
}

uart.c

c 复制代码
#include "uart1.h"
#include "stdio.h"
#include "string.h"

//定义接收缓存区
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t uart1_rx_len = 0;
//定义串口1最新接收数量,数量旧值
uint16_t uart1_cnt = 0, uart1_cnt_Pre = 0;

UART_HandleTypeDef uart1_handle = {0};
//串口1初始化函数
void uart1_init(uint32_t baudrate)
{
    //串口1选择
    uart1_handle.Instance = USART1;
    //波特率
    uart1_handle.Init.BaudRate = baudrate;
    //字长
    uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    uart1_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    uart1_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    uart1_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&uart1_handle);
}

//串口相关硬件初始化
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1){
        GPIO_InitTypeDef gpio_initstruct;
        //开启串口1时钟
        __HAL_RCC_USART1_CLK_ENABLE();
            //使能GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_9;               //Tx引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;         //复用输出
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
        gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        gpio_initstruct.Pin = GPIO_PIN_10;                //Rx引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_INPUT;       //复用输入
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        //使能中断线
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        //中断优先级配置
        HAL_NVIC_SetPriority(USART1_IRQn, 2, 2);
        //使能中断
        __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);
    }
}

//串口1中断服务函数
void USART1_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_RXNE) != RESET){
        if(uart1_cnt >= sizeof(uart1_rx_buf)){
            //接受数量置为零
            uart1_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&uart1_handle, &receive_data, 1,1000);
        uart1_rx_buf[uart1_cnt++] = receive_data;
        //再发送出去
        HAL_UART_Transmit(&uart1_handle,&receive_data, 1,1000);
    }
}

uart.h

c 复制代码
#ifndef __USART_H__
#define __USART_H__

#include "stdio.h"
#include "sys.h"

#define UART1_RX_BUF_SIZE 128
#define UART1_TX_BUF_SIZE 64

#define UART_EOK         0
#define UART_ERROR      1
#define UART_ETIMEOUT   2
#define UART_EINVAL     3

//串口1初始化
void uart1_init(uint32_t baudrate);
//串口1接收测试
void uart1_receive_test(void);

#endif

2. 小实验2:串口接收不定长数据(接收中断)

代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    
    while(1)
    {
        uart1_receive_test();
        delay_ms(10);
    }
}

uart.c

c 复制代码
#include "uart1.h"
#include "stdio.h"
#include "string.h"

//定义接收缓存区
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t uart1_rx_len = 0;
//定义串口1最新接收数量,数量旧值
uint16_t uart1_cnt = 0, uart1_cnt_Pre = 0;

UART_HandleTypeDef uart1_handle = {0};
//串口1初始化函数
void uart1_init(uint32_t baudrate)
{
    //串口1选择
    uart1_handle.Instance = USART1;
    //波特率
    uart1_handle.Init.BaudRate = baudrate;
    //字长
    uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    uart1_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    uart1_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    uart1_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&uart1_handle);
}

//串口相关硬件初始化
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1){
        GPIO_InitTypeDef gpio_initstruct;
        //开启串口1时钟
        __HAL_RCC_USART1_CLK_ENABLE();
            //使能GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_9;               //Tx引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;         //复用输出
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
        gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        gpio_initstruct.Pin = GPIO_PIN_10;                //Rx引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_INPUT;       //复用输入
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        //使能中断线
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        //中断优先级配置
        HAL_NVIC_SetPriority(USART1_IRQn, 2, 2);
        //使能中断
        __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);
    }
}

//串口1中断服务函数
void USART1_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_RXNE) != RESET){
        if(uart1_cnt >= sizeof(uart1_rx_buf)){
            //接受数量置为零
            uart1_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&uart1_handle, &receive_data, 1,1000);
        uart1_rx_buf[uart1_cnt++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&uart1_handle,&receive_data, 1,1000);
    }
}

//重写fputc函数,重定向printf
int fputc(int ch, FILE *f)
{
    while((USART1->SR & 0x40) == 0);
    //数据放到移位寄存器
    USART1->DR = (uint8_t)ch;
    return ch;
}

//等待接收函数
uint8_t uart1_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(uart1_cnt == 0){
        return UART_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(uart1_cnt == uart1_cnt_Pre){
        uart1_cnt = 0;
        return UART_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    uart1_cnt_Pre = uart1_cnt;
    return UART_ERROR;
}

//接收区清空
void uart1_rx_clear(void)
{
    memset(uart1_rx_buf, 0, sizeof(uart1_rx_buf));
    uart1_rx_len = 0;
}

//串口1接收测试
void uart1_receive_test(void)
{
    if(uart1_wait_receive() == UART_EOK){
        printf("rev:%s\r\n",uart1_rx_buf);
        uart1_rx_clear();
    }
}

uart,h

c 复制代码
#ifndef __USART_H__
#define __USART_H__

#include "stdio.h"
#include "sys.h"

#define UART1_RX_BUF_SIZE 128
#define UART1_TX_BUF_SIZE 64

#define UART_EOK         0
#define UART_ERROR      1
#define UART_ETIMEOUT   2
#define UART_EINVAL     3

//串口1初始化
void uart1_init(uint32_t baudrate);
//串口1接收测试
void uart1_receive_test(void);

#endif

3. 小实验3:串口接收不定长数据(空闲中断)

代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    
    while(1)
    {
        
    }
}

uart.c

c 复制代码
#include "uart1.h"
#include "stdio.h"
#include "string.h"

//定义接收缓存区
uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t uart1_rx_len = 0;
//定义串口1最新接收数量,数量旧值
uint16_t uart1_cnt = 0, uart1_cnt_Pre = 0;

UART_HandleTypeDef uart1_handle = {0};
//串口1初始化函数
void uart1_init(uint32_t baudrate)
{
    //串口1选择
    uart1_handle.Instance = USART1;
    //波特率
    uart1_handle.Init.BaudRate = baudrate;
    //字长
    uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    uart1_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    uart1_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    uart1_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&uart1_handle);
}

//串口相关硬件初始化
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART1){
        GPIO_InitTypeDef gpio_initstruct;
        //开启串口1时钟
        __HAL_RCC_USART1_CLK_ENABLE();
            //使能GPIOA时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_9;               //Tx引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;         //复用输出
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
        gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        gpio_initstruct.Pin = GPIO_PIN_10;                //Rx引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_INPUT;       //复用输入
        HAL_GPIO_Init(GPIOA, &gpio_initstruct);
        
        //使能中断线
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        //中断优先级配置
        HAL_NVIC_SetPriority(USART1_IRQn, 2, 2);
        //使能中断
        __HAL_UART_ENABLE_IT(huart,UART_IT_RXNE);
        //使能空闲中断
        __HAL_UART_ENABLE_IT(huart,UART_IT_IDLE);
    }
}

//接收区清空
void uart1_rx_clear(void)
{
    memset(uart1_rx_buf, 0, sizeof(uart1_rx_buf));
    uart1_rx_len = 0;
}

//串口1中断服务函数
void USART1_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_RXNE) != RESET){
        if(uart1_rx_len >= sizeof(uart1_rx_buf)){
            //接受数量置为零
            uart1_rx_len = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&uart1_handle, &receive_data, 1,1000);
        uart1_rx_buf[uart1_rx_len++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&uart1_handle,&receive_data, 1,1000);
    }
    
    //若空闲中断
    if(__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_IDLE) != RESET){
        printf("rev:%s\r\n", uart1_rx_buf);
        //清空缓存区
        uart1_rx_clear();
        //清空空闲中断标志位
        __HAL_UART_CLEAR_IDLEFLAG(&uart1_handle);
    }
}

//重写fputc函数,重定向printf
int fputc(int ch, FILE *f)
{
    while((USART1->SR & 0x40) == 0);
    //数据放到移位寄存器
    USART1->DR = (uint8_t)ch;
    return ch;
}

uart.h

c 复制代码
#ifndef __USART_H__
#define __USART_H__

#include "stdio.h"
#include "sys.h"

#define UART1_RX_BUF_SIZE 128
#define UART1_TX_BUF_SIZE 64

#define UART_EOK         0
#define UART_ERROR      1
#define UART_ETIMEOUT   2
#define UART_EINVAL     3

//串口1初始化
void uart1_init(uint32_t baudrate);
//串口1接收测试
void uart1_receive_test(void);

#endif

4. 模块:HC-08蓝牙

实物图

接线示意图

具体配置方法和AT指令详见此链接:https://blog.csdn.net/yychuyu/article/details/136082212?fromshare=blogdetail\&sharetype=blogdetail\&sharerId=136082212\&sharerefer=PC\&sharesource=qq_62361050\&sharefrom=from_link

5. 项目:蓝牙遥控插座

项目需求

手机通过蓝牙模块远程遥控风扇。

硬件接线

项目框图

代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "bluetooth.h"
#include "plugin.h"

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //串口1初始化
    uart1_init(115200);
    //蓝牙初始化
    bt_init(115200);
    //插座初始化
    plugin_init();
    
    printf("打印测试:hello world\r\n");
    
    uint8_t i = 0;
    while(1)
    {
        //bt_send("hello,bt%d\r\n",i++);
        delay_ms(500);
    }
}

bluetooth.c

c 复制代码
#include "sys.h"
#include "bluetooth.h"
#include "string.h"
#include "stdarg.h"
#include "plugin.h"

UART_HandleTypeDef uart2_handle;                                            /* uart2句柄 */

uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];                                    /* uart2接收缓冲区 */
uint16_t uart2_rx_len = 0;                                                  /* uart2接收字符长度 */

/**
 * @brief       串口1初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void bt_init(uint32_t baudrate)
{
    /*UART2 初始化设置*/
    uart2_handle.Instance = USART2;                                         /* USART2 */
    uart2_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    uart2_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    uart2_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    uart2_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&uart2_handle);                                           /* HAL_UART_Init()会使能UART2 */
}

/**
 * @brief       UART2接收缓冲区清除
 * @param       无
 * @retval      无
 */
void uart2_rx_clear(void)
{
    memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf));                          /* 清空接收缓冲区 */
    uart2_rx_len = 0;                                                       /* 接收计数器清零 */
}

/**
 * @brief       串口1中断服务函数
 * @note        在此使用接收中断及空闲中断,实现不定长数据收发
 * @param       无
 * @retval      无
 */
void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_RXNE) != RESET){        /* 获取接收RXNE标志位是否被置位 */
        if(uart2_rx_len >= sizeof(uart2_rx_buf))                            /* 如果接收的字符数大于接收缓冲区大小, */
            uart2_rx_len = 0;                                               /* 则将接收计数器清零 */
        HAL_UART_Receive(&uart2_handle, &receive_data, 1, 1000);            /* 接收一个字符 */
        uart2_rx_buf[uart2_rx_len++] = receive_data;                        /* 将接收到的字符保存在接收缓冲区 */
    }

    if (__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_IDLE) != RESET)        /* 获取接收空闲中断标志位是否被置位 */
    {
        printf("recv: %s\r\n", uart2_rx_buf);                               /* 将接收到的数据打印出来 */
        //检测收到的字符串中是否包含要求的子串
        if(strstr((char *)uart2_rx_buf,"on") != NULL){
            //打开插座
            plugin_on();
        }else if(strstr((char *)uart2_rx_buf,"off") != NULL){
            plugin_off();
        }
        
        uart2_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);                           /* 清除UART总线空闲中断 */
    }
}

////串口2发送函数
//void bt_send(char *send_buf, uint8_t size)
//{
//    HAL_UART_Transmit(&uart2_handle, (uint8_t*)send_buf, size, 100);
//}

//串口2发送不定长数据
void bt_send(char *format, ...)
{
    uint8_t send_buf[128] = {0};
    va_list arg;
    va_start(arg, format);
    vsprintf((char *)send_buf, format ,arg);
    va_end(arg);
    HAL_UART_Transmit(&uart2_handle, send_buf, sizeof(send_buf), 100);
}

bluetooth.h

c 复制代码
#include "sys.h"
#include "bluetooth.h"
#include "string.h"
#include "stdarg.h"
#include "plugin.h"

UART_HandleTypeDef uart2_handle;                                            /* uart2句柄 */

uint8_t uart2_rx_buf[UART2_RX_BUF_SIZE];                                    /* uart2接收缓冲区 */
uint16_t uart2_rx_len = 0;                                                  /* uart2接收字符长度 */

/**
 * @brief       串口1初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void bt_init(uint32_t baudrate)
{
    /*UART2 初始化设置*/
    uart2_handle.Instance = USART2;                                         /* USART2 */
    uart2_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    uart2_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    uart2_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    uart2_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    uart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    uart2_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&uart2_handle);                                           /* HAL_UART_Init()会使能UART2 */
}

/**
 * @brief       UART2接收缓冲区清除
 * @param       无
 * @retval      无
 */
void uart2_rx_clear(void)
{
    memset(uart2_rx_buf, 0, sizeof(uart2_rx_buf));                          /* 清空接收缓冲区 */
    uart2_rx_len = 0;                                                       /* 接收计数器清零 */
}

/**
 * @brief       串口1中断服务函数
 * @note        在此使用接收中断及空闲中断,实现不定长数据收发
 * @param       无
 * @retval      无
 */
void USART2_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_RXNE) != RESET){        /* 获取接收RXNE标志位是否被置位 */
        if(uart2_rx_len >= sizeof(uart2_rx_buf))                            /* 如果接收的字符数大于接收缓冲区大小, */
            uart2_rx_len = 0;                                               /* 则将接收计数器清零 */
        HAL_UART_Receive(&uart2_handle, &receive_data, 1, 1000);            /* 接收一个字符 */
        uart2_rx_buf[uart2_rx_len++] = receive_data;                        /* 将接收到的字符保存在接收缓冲区 */
    }

    if (__HAL_UART_GET_FLAG(&uart2_handle, UART_FLAG_IDLE) != RESET)        /* 获取接收空闲中断标志位是否被置位 */
    {
        printf("recv: %s\r\n", uart2_rx_buf);                               /* 将接收到的数据打印出来 */
        //检测收到的字符串中是否包含要求的子串
        if(strstr((char *)uart2_rx_buf,"on") != NULL){
            //打开插座
            plugin_on();
        }else if(strstr((char *)uart2_rx_buf,"off") != NULL){
            plugin_off();
        }
        
        uart2_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart2_handle);                           /* 清除UART总线空闲中断 */
    }
}

////串口2发送函数
//void bt_send(char *send_buf, uint8_t size)
//{
//    HAL_UART_Transmit(&uart2_handle, (uint8_t*)send_buf, size, 100);
//}

//串口2发送不定长数据
void bt_send(char *format, ...)
{
    uint8_t send_buf[128] = {0};
    va_list arg;
    va_start(arg, format);
    vsprintf((char *)send_buf, format ,arg);
    va_end(arg);
    HAL_UART_Transmit(&uart2_handle, send_buf, sizeof(send_buf), 100);
}

6. 模块:ESP8266

实物图

接线方式同蓝牙模块,不管连接USB转TTL还是STM32单片机,都是交叉接线。具体配置要使用到AT指令,在下面有具体讲解。

6.1 ESP8266作为设备

6.1.1 入网设置
  • 设置工作模式
c 复制代码
AT+CWMODE=1 //1. 是station(设备)模式 2.是AP(路由)模式 3.是双模
OK
  • 以设备模式接入家中路由器配置
c 复制代码
AT+CWJAP="HuaweiAP-1ED0_Guest","gcc11111111" //指令
WIFI CONNECTED //结果
WIFI GOT IP //结果
  • 设置单路链接模式(透传只能使用此模式)
c 复制代码
AT+CIPMUX=0
OK
  • 查询IP地址
c 复制代码
AT+CIFSR //指令
+CIFSR:APIP,"192.168.4.1"
+CIFSR:APMAC,"4e:75:25:0d:ae:2f"
+CIFSR:STAIP,"192.168.0.148"
+CIFSR:STAMAC,"4c:75:25:0d:ae:2f"
OK
6.1.2 使用网络助手,设立TCP服务器
6.1.3 连接服务器
c 复制代码
AT+CIPSTART="TCP","192.168.1.18",8080 //指令,注意双引号逗号都要半角(英文)输入
CONNECT //结果:成功
OK //结果:成功
6.1.4 发送数据
c 复制代码
AT+CIPSEND=4 // 设置即将发送数据的长度 (这里是4个字节)
>abcd // 看到大于号后,输入消息,abcd,不要带回车
Response :SEND OK //结果:成功
//注意,这种情况下,每次发送前都要先发送AT+CIPSEND=长度 的指令,再发数据!
6.1.5 透传

上一节每次发送数据都要进行字符长度设定,如果设置成透传,就有点像蓝牙模块的玩法

c 复制代码
AT+CIPMODE=1 //开启透传模式
Response :OK
AT+CIPSEND //带回车
Response: > //这个时候随意发送接收数据咯

退出透传模式

c 复制代码
//在透传发送数据过程中,若识别到单独的⼀包数据 "+++",则退出透传发送

6.2 ESP8266作为服务器

USB转TTL插入电脑,TX--RX RX-TX VCC-3.3V GND-GND

查询IP地址:AT+CIFSR

c 复制代码
//1 配置成AP模式
AT+CWMODE=2
Response :OK
//2 使能多链接
AT+CIPMUX=1
Response :OK
//3 建立TCPServer
AT+CIPSERVER=1 // default port = 333
Response :OK
//4 发送数据
AT+CIPSEND=0,4 // 发送4个字节在连接0通道上
>abcd //输入数据,不带回车
Response :SEND OK
//5 接收数据
+IPD, 0, n: xxxxxxxxxx //+IPD是固定字符串 0是通道,n是数据长度,xxx是数据
//断开连接
AT+CIPCLOSE=0
Response :0, CLOSED OK

使用网络调试助手建立客户端连接esp8266就可以了,默认端口333

6.3 ESP8266串口通讯功能STM32代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "esp8266.h"

int main(void)
{  
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    //esp8266初始化
    esp8266_init(115200);
    //串口打印测试
    printf("串口打印测试:Hello world\r\n");
    
    while(1)
    {
        esp8266_test();
        delay_ms(10);
    }
}

esp8266.c

c 复制代码
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"

//定义接收缓存区
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t esp8266_rx_len = 0;
//定义esp8266最新接收数量,数量旧值
uint16_t esp8266_cnt = 0, esp8266_cnt_Pre = 0;

UART_HandleTypeDef esp8266_handle = {0};

//esp8266串口初始化函数
void esp8266_uart_init(uint32_t baudrate)
{
    //串口1选择
    esp8266_handle.Instance = USART2;
    //波特率
    esp8266_handle.Init.BaudRate = baudrate;
    //字长
    esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    esp8266_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    esp8266_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    esp8266_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&esp8266_handle);
}

//esp8266中断服务函数
void USART2_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&esp8266_handle, UART_FLAG_RXNE) != RESET){
        if(esp8266_cnt >= sizeof(esp8266_rx_buf)){
            //接受数量置为零
            esp8266_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&esp8266_handle, &receive_data, 1,1000);
        esp8266_rx_buf[esp8266_cnt++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&esp8266_handle,&receive_data, 1,1000);
    }
}

//等待接收函数
uint8_t esp8266_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(esp8266_cnt == 0){
        return ESP8266_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(esp8266_cnt == esp8266_cnt_Pre){
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    esp8266_cnt_Pre = esp8266_cnt;
    return ESP8266_ERROR;
}

//接收区清空
void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf, 0, sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

//esp8266接收
void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK){
        printf("esp8266 rev:%s\r\n",esp8266_rx_buf);
        esp8266_rx_clear();
    }
}

//esp8266初始化
void esp8266_init(uint32_t baudrate)
{
    //串口初始化
    esp8266_uart_init(baudrate);
}

//esp8266发送函数
uint8_t esp8266_send_command(char *cmd, char *res)
{
    uint8_t time_out = 250;
    
    //清空接收缓存区
    esp8266_rx_clear();
    //发送函数
    HAL_UART_Transmit(&esp8266_handle,(uint8_t *)cmd, strlen(cmd),100);
    //判断超时
    while(time_out--){
        //判断是否接收到新数据
        if(esp8266_wait_receive() == ESP8266_EOK){
            //判断接受的数据是否和要求比较的数据一致
            if(strstr((char *)esp8266_rx_buf, res) != NULL){
                return ESP8266_EOK;
            }
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

//测试函数
void esp8266_test(void)
{
    if(esp8266_send_command("AT","OK") == ESP8266_EOK){
        printf("esp8266 test:%s\r\n",esp8266_rx_buf);
    }
}

esp8266.h

c 复制代码
#ifndef __ESP8266_H__
#define __ESP8266_H__

#include "stdio.h"
#include "sys.h"

#define ESP8266_RX_BUF_SIZE 128
#define ESP8266_TX_BUF_SIZE 64

#define ESP8266_EOK         0
#define ESP8266_ERROR      1
#define ESP8266_ETIMEOUT   2
#define ESP8266_EINVAL     3

//esp8266初始化
void esp8266_init(uint32_t baudrate);
//esp8266接收
void esp8266_receive_data(void);
//测试函数
void esp8266_test(void);

#endif

6.4 ESP8266入网功能STM32代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "esp8266.h"

int main(void)
{  
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    //esp8266初始化
    esp8266_init(115200);
    //串口打印测试
    printf("串口打印测试:Hello world\r\n");
    
    while(1)
    {
        //esp8266_test();
        delay_ms(10);
    }
}

esp8266.c

c 复制代码
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"

//定义接收缓存区
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t esp8266_rx_len = 0;
//定义esp8266最新接收数量,数量旧值
uint16_t esp8266_cnt = 0, esp8266_cnt_Pre = 0;

UART_HandleTypeDef esp8266_handle = {0};

//esp8266串口初始化函数
void esp8266_uart_init(uint32_t baudrate)
{
    //串口1选择
    esp8266_handle.Instance = USART2;
    //波特率
    esp8266_handle.Init.BaudRate = baudrate;
    //字长
    esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    esp8266_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    esp8266_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    esp8266_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&esp8266_handle);
}

//esp8266中断服务函数
void USART2_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&esp8266_handle, UART_FLAG_RXNE) != RESET){
        if(esp8266_cnt >= sizeof(esp8266_rx_buf)){
            //接受数量置为零
            esp8266_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&esp8266_handle, &receive_data, 1,1000);
        esp8266_rx_buf[esp8266_cnt++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&esp8266_handle,&receive_data, 1,1000);
    }
}

//等待接收函数
uint8_t esp8266_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(esp8266_cnt == 0){
        return ESP8266_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(esp8266_cnt == esp8266_cnt_Pre){
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    esp8266_cnt_Pre = esp8266_cnt;
    return ESP8266_ERROR;
}

//接收区清空
void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf, 0, sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

//esp8266接收
void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK){
        printf("esp8266 rev:%s\r\n",esp8266_rx_buf);
        esp8266_rx_clear();
    }
}

//esp8266发送函数
uint8_t esp8266_send_command(char *cmd, char *res)
{
    uint8_t time_out = 250;
    
    //清空接收缓存区
    esp8266_rx_clear();
    //发送函数
    HAL_UART_Transmit(&esp8266_handle,(uint8_t *)cmd, strlen(cmd),100);
    //判断超时
    while(time_out--){
        //判断是否接收到新数据
        if(esp8266_wait_receive() == ESP8266_EOK){
            //判断接受的数据是否和要求比较的数据一致
            if(strstr((char *)esp8266_rx_buf, res) != NULL){
                return ESP8266_EOK;
            }
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

//测试
uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\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");
        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n", "OK");
        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n", "OK");
        default:
            return ESP8266_EINVAL;
    }
}

//连接网络
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_connection_mode(uint8_t mode)
{
    char cmd[64];
    
    sprintf(cmd,"AT+CIPMUX=%d\r\n",mode);
    return esp8266_send_command(cmd,"OK");
}

//esp8266初始化
void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    //串口初始化
    esp8266_uart_init(baudrate);
    
    //配置wifi模块
    printf("1. 测试esp8266是否存在\r\n");
    while(esp8266_at_test()){
        delay_ms(500);
    }
    printf("2. 设置工作模式为STA\r\n");
    while(esp8266_set_mode(ESP8266_STA_MODE)){
        delay_ms(500);
    }
    printf("3. 设置单链路链接模式\r\n");
    while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION)){
        delay_ms(500);
    }
    printf("4. 连接wifi,SSID:%s, PWD:%s\r\n",WIFI_SSID,WIFI_PWD);
     while(esp8266_join_ap(WIFI_SSID,WIFI_PWD)){
        delay_ms(1500);
    }
    printf("ESP8266初始化完成!\r\n");
}



//测试函数
void esp8266_test(void)
{
    if(esp8266_send_command("AT","OK") == ESP8266_EOK){
        printf("esp8266 test:%s\r\n",esp8266_rx_buf);
    }
}

esp8266.h

c 复制代码
#ifndef __ESP8266_H__
#define __ESP8266_H__

#include "stdio.h"
#include "sys.h"

//定义发送接收缓存大小
#define ESP8266_RX_BUF_SIZE 128
#define ESP8266_TX_BUF_SIZE 64

//定义状态标志
#define ESP8266_EOK                 0
#define ESP8266_ERROR               1
#define ESP8266_ETIMEOUT            2
#define ESP8266_EINVAL              3

//定义AT指令填充字段
#define ESP8266_STA_MODE            1
#define ESP8266_AP_MODE             2
#define ESP8266_STA_AP_MODE         3

#define ESP8266_SINGLE_CONNECTION   0
#define ESP8266_MULTI_CONNECTION    1

#define WIFI_SSID                   "HUAWEI Mate 60 Pro"
#define WIFI_PWD                    "88888888"

//esp8266初始化
void esp8266_init(uint32_t baudrate);
//esp8266接收
void esp8266_receive_data(void);
//测试函数
void esp8266_test(void);

#endif

6.5 ESP8266连接TCP服务器功能STM32代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "esp8266.h"

int main(void)
{  
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    //esp8266初始化
    esp8266_init(115200);
    //串口打印测试
    printf("串口打印测试:Hello world\r\n");
    
    while(1)
    {
        esp8266_test();
        delay_ms(500);
    }
}

esp8266.c

c 复制代码
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"

//定义发送缓存区
uint8_t esp8266_tx_buf[ESP8266_TX_BUF_SIZE] = {0};
//定义接收缓存区
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t esp8266_rx_len = 0;
//定义esp8266最新接收数量,数量旧值
uint16_t esp8266_cnt = 0, esp8266_cnt_Pre = 0;

UART_HandleTypeDef esp8266_handle = {0};

//esp8266串口初始化函数
void esp8266_uart_init(uint32_t baudrate)
{
    //串口1选择
    esp8266_handle.Instance = USART2;
    //波特率
    esp8266_handle.Init.BaudRate = baudrate;
    //字长
    esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    esp8266_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    esp8266_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    esp8266_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&esp8266_handle);
}

//esp8266中断服务函数
void USART2_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&esp8266_handle, UART_FLAG_RXNE) != RESET){
        if(esp8266_cnt >= sizeof(esp8266_rx_buf)){
            //接受数量置为零
            esp8266_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&esp8266_handle, &receive_data, 1,1000);
        esp8266_rx_buf[esp8266_cnt++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&esp8266_handle,&receive_data, 1,1000);
    }
}

//等待接收函数
uint8_t esp8266_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(esp8266_cnt == 0){
        return ESP8266_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(esp8266_cnt == esp8266_cnt_Pre){
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    esp8266_cnt_Pre = esp8266_cnt;
    return ESP8266_ERROR;
}

//接收区清空
void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf, 0, sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

//esp8266发送
void esp8266_send_data(char *fmt, ...)
{
    // 定义可变参数列表变量
    va_list ap;
    // 定义长度变量,用于存储字符串长度
    uint16_t len;
    // 初始化可变参数列表,ap指向fmt后的第一个参数
    va_start(ap, fmt);
    // 使用vsprintf将格式化后的字符串输出到发送缓冲区
    // vsprintf会自动处理可变参数,根据fmt格式字符串将参数格式化后存入缓冲区
    vsprintf((char* )esp8266_tx_buf, fmt, ap);
    // 计算格式化后字符串的长度(不包括字符串结束符'\0')
    len = strlen((const char *)esp8266_tx_buf);
    // 通过UART发送数据
    HAL_UART_Transmit(&esp8266_handle, esp8266_tx_buf, len, 100);
}

//esp8266接收
void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK){
        printf("esp8266 rev:%s\r\n",esp8266_rx_buf);
        esp8266_rx_clear();
    }
}

//esp8266发送函数
uint8_t esp8266_send_command(char *cmd, char *res)
{
    uint8_t time_out = 250;
    
    //清空接收缓存区
    esp8266_rx_clear();
    //发送函数
    HAL_UART_Transmit(&esp8266_handle,(uint8_t *)cmd, strlen(cmd),100);
    //判断超时
    while(time_out--){
        //判断是否接收到新数据
        if(esp8266_wait_receive() == ESP8266_EOK){
            //判断接受的数据是否和要求比较的数据一致
            if(strstr((char *)esp8266_rx_buf, res) != NULL){
                return ESP8266_EOK;
            }
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

//测试
uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\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");
        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n", "OK");
        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n", "OK");
        default:
            return ESP8266_EINVAL;
    }
}

//连接网络
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_connection_mode(uint8_t mode)
{
    char cmd[64];
    
    sprintf(cmd,"AT+CIPMUX=%d\r\n",mode);
    return esp8266_send_command(cmd,"OK");
}

//连接服务器
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_unvarnishied(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;
    }   
}

//esp8266初始化
void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    //串口初始化
    esp8266_uart_init(baudrate);
    
    //配置wifi模块
    printf("1. 测试esp8266是否存在\r\n");
    while(esp8266_at_test()){
        delay_ms(500);
    }
    printf("2. 设置工作模式为STA...\r\n");
    while(esp8266_set_mode(ESP8266_STA_MODE)){
        delay_ms(500);
    }
    printf("3. 设置单链路链接模式...\r\n");
    while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION)){
        delay_ms(500);
    }
    printf("4. 连接wifi,SSID:%s, PWD:%s...\r\n",WIFI_SSID,WIFI_PWD);
     while(esp8266_join_ap(WIFI_SSID,WIFI_PWD)){
        delay_ms(2000);
    }
    printf("5. 连接云服务器,server_ip:%s,server_port:%s...\r\n",TCP_SERVER_IP,TCP_SERVER_PORT);
    while(esp8266_connect_tcp_server(TCP_SERVER_IP, TCP_SERVER_PORT)){
        delay_ms(500);
    }
    printf("6. 进入透传模式...\r\n");
    while(esp8266_enter_unvarnishied()){
        delay_ms(500);
    }
    printf("ESP8266已经连接服务器并进入透传模式!\r\n");
    printf("ESP8266初始化完成!\r\n");
}



//测试函数
void esp8266_test(void)
{
    esp8266_send_data("This is from esp8266\r\n");
    esp8266_receive_data();
}

esp8266.h

c 复制代码
#ifndef __ESP8266_H__
#define __ESP8266_H__

#include "stdio.h"
#include "sys.h"

//定义发送接收缓存大小
#define ESP8266_RX_BUF_SIZE 128
#define ESP8266_TX_BUF_SIZE 64

//定义状态标志
#define ESP8266_EOK                 0
#define ESP8266_ERROR               1
#define ESP8266_ETIMEOUT            2
#define ESP8266_EINVAL              3

//定义AT指令填充字段
#define ESP8266_STA_MODE            1
#define ESP8266_AP_MODE             2
#define ESP8266_STA_AP_MODE         3

#define ESP8266_SINGLE_CONNECTION   0
#define ESP8266_MULTI_CONNECTION    1

#define WIFI_SSID                   "HUAWEI Mate 60 Pro"
#define WIFI_PWD                    "88888888"

#define TCP_SERVER_IP               "10.102.14.154"
#define TCP_SERVER_PORT             "8080"

//esp8266初始化
void esp8266_init(uint32_t baudrate);
//esp8266接收
void esp8266_receive_data(void);
//测试函数
void esp8266_test(void);

#endif

6.6 ESP8266作为服务器功能STM32代码实现(网络调试助手连接ESP8266之前需要把WIFI接入ESP8266)

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "esp8266.h"

int main(void)
{  
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    //esp8266初始化
    esp8266_init(115200);
    //串口打印测试
    printf("串口打印:Hello world\r\n");
    
    while(1)
    {
        esp8266_receive_data();
        delay_ms(10);
    }
}

esp8266.c

c 复制代码
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"

//定义发送缓存区
uint8_t esp8266_tx_buf[ESP8266_TX_BUF_SIZE] = {0};
//定义接收缓存区
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t esp8266_rx_len = 0;
//定义esp8266最新接收数量,数量旧值
uint16_t esp8266_cnt = 0, esp8266_cnt_Pre = 0;

UART_HandleTypeDef esp8266_handle = {0};

//esp8266串口初始化函数
void esp8266_uart_init(uint32_t baudrate)
{
    //串口1选择
    esp8266_handle.Instance = USART2;
    //波特率
    esp8266_handle.Init.BaudRate = baudrate;
    //字长
    esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    esp8266_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    esp8266_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    esp8266_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&esp8266_handle);
}

//esp8266中断服务函数
void USART2_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&esp8266_handle, UART_FLAG_RXNE) != RESET){
        if(esp8266_cnt >= sizeof(esp8266_rx_buf)){
            //接受数量置为零
            esp8266_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&esp8266_handle, &receive_data, 1,1000);
        esp8266_rx_buf[esp8266_cnt++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&esp8266_handle,&receive_data, 1,1000);
    }
}

//等待接收函数
uint8_t esp8266_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(esp8266_cnt == 0){
        return ESP8266_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(esp8266_cnt == esp8266_cnt_Pre){
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    esp8266_cnt_Pre = esp8266_cnt;
    return ESP8266_ERROR;
}

//接收区清空
void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf, 0, sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

//esp8266发送
void esp8266_send_data(char *fmt, ...)
{
    // 定义可变参数列表变量
    va_list ap;
    // 定义长度变量,用于存储字符串长度
    uint16_t len;
    // 初始化可变参数列表,ap指向fmt后的第一个参数
    va_start(ap, fmt);
    // 使用vsprintf将格式化后的字符串输出到发送缓冲区
    // vsprintf会自动处理可变参数,根据fmt格式字符串将参数格式化后存入缓冲区
    vsprintf((char* )esp8266_tx_buf, fmt, ap);
    // 计算格式化后字符串的长度(不包括字符串结束符'\0')
    len = strlen((const char *)esp8266_tx_buf);
    // 通过UART发送数据
    HAL_UART_Transmit(&esp8266_handle, esp8266_tx_buf, len, 100);
}

//esp8266接收
void esp8266_receive_data(void)
{
    if(esp8266_wait_receive() == ESP8266_EOK){
        printf("esp8266 rev:%s\r\n",esp8266_rx_buf);
        esp8266_rx_clear();
    }
}

//esp8266发送函数
uint8_t esp8266_send_command(char *cmd, char *res)
{
    uint8_t time_out = 250;
    
    //清空接收缓存区
    esp8266_rx_clear();
    //发送函数
    HAL_UART_Transmit(&esp8266_handle,(uint8_t *)cmd, strlen(cmd),100);
    //判断超时
    while(time_out--){
        //判断是否接收到新数据
        if(esp8266_wait_receive() == ESP8266_EOK){
            //判断接受的数据是否和要求比较的数据一致
            if(strstr((char *)esp8266_rx_buf, res) != NULL){
                return ESP8266_EOK;
            }
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

//测试
uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\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");
        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n", "OK");
        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n", "OK");
        default:
            return ESP8266_EINVAL;
    }
}

//连接网络
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_connection_mode(uint8_t mode)
{
    char cmd[64];
    
    sprintf(cmd,"AT+CIPMUX=%d\r\n",mode);
    return esp8266_send_command(cmd,"OK");
}

//连接服务器
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_unvarnishied(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;
    }   
}

//创建一个TCP服务器
uint8_t esp8266_build_tcp_server(void)
{
    return esp8266_send_command("AT+CIPSERVER=1\r\n","OK");
}

//esp8266初始化
void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    //串口初始化
    esp8266_uart_init(baudrate);
    
    //配置wifi模块
    printf("1. 测试esp8266是否存在\r\n");
    while(esp8266_at_test()){
        delay_ms(500);
    }
    printf("2. 设置工作模式为AP...\r\n");
    while(esp8266_set_mode(ESP8266_AP_MODE)){
        delay_ms(500);
    }
    printf("3. 设置多链路链接模式...\r\n");
    while(esp8266_connection_mode(ESP8266_MULTI_CONNECTION)){
        delay_ms(500);
    }
    printf("4. 建立TCP服务器...\r\n");
    while(esp8266_build_tcp_server()){
        delay_ms(500);
    }
//    printf("2. 设置工作模式为STA...\r\n");
//    while(esp8266_set_mode(ESP8266_STA_MODE)){
//        delay_ms(500);
//    }
//    printf("3. 设置单链路链接模式...\r\n");
//    while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION)){
//        delay_ms(500);
//    }
//    printf("4. 连接wifi,SSID:%s, PWD:%s...\r\n",WIFI_SSID,WIFI_PWD);
//     while(esp8266_join_ap(WIFI_SSID,WIFI_PWD)){
//        delay_ms(2000);
//    }
//    printf("5. 连接云服务器,server_ip:%s,server_port:%s...\r\n",TCP_SERVER_IP,TCP_SERVER_PORT);
//    while(esp8266_connect_tcp_server(TCP_SERVER_IP, TCP_SERVER_PORT)){
//        delay_ms(500);
//    }
//    printf("6. 进入透传模式...\r\n");
//    while(esp8266_enter_unvarnishied()){
//        delay_ms(500);
//    }
//    printf("ESP8266已经连接服务器并进入透传模式!\r\n");
    printf("ESP8266初始化完成!\r\n");
}



//测试函数
void esp8266_test(void)
{
    esp8266_send_data("This is from esp8266\r\n");
    esp8266_receive_data();
}

esp8266.h

c 复制代码
#ifndef __ESP8266_H__
#define __ESP8266_H__

#include "stdio.h"
#include "sys.h"

//定义发送接收缓存大小
#define ESP8266_RX_BUF_SIZE 128
#define ESP8266_TX_BUF_SIZE 64

//定义状态标志
#define ESP8266_EOK                 0
#define ESP8266_ERROR               1
#define ESP8266_ETIMEOUT            2
#define ESP8266_EINVAL              3

//定义AT指令填充字段
#define ESP8266_STA_MODE            1
#define ESP8266_AP_MODE             2
#define ESP8266_STA_AP_MODE         3

#define ESP8266_SINGLE_CONNECTION   0
#define ESP8266_MULTI_CONNECTION    1

#define WIFI_SSID                   "HUAWEI Mate 60 Pro"
#define WIFI_PWD                    "88888888"

#define TCP_SERVER_IP               "10.102.14.154"
#define TCP_SERVER_PORT             "8080"

//esp8266初始化
void esp8266_init(uint32_t baudrate);
//esp8266接收
void esp8266_receive_data(void);
//测试函数
void esp8266_test(void);

#endif

7. 项目:Wi-Fi遥控风扇

项目需求

电脑通过esp8266模块远程遥控风扇。

硬件接线

项目框图

代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "esp8266.h"
#include "fan.h"
#include "string.h"

int main(void)
{  
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    //esp8266初始化
    esp8266_init(115200);
    //风扇初始化
    fan_init();
    //串口打印测试
    printf("串口打印测试:Hello world\r\n");
    
    char recv_data[ESP8266_RX_BUF_SIZE];
    
    while(1)
    {
        esp8266_receive_data(recv_data);
        if(strstr(recv_data, "ON") != NULL){
            fan_on();
        }else if(strstr(recv_data, "OFF") != NULL){
            fan_off();
        }
        
        delay_ms(10);
    }
}

fan.c

c 复制代码
#include "fan.h"
#include "sys.h"

//初始化GBIO口函数
void fan_init(void)
{ 
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_6;               //fan对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //上拉
    gpio_initstruct.Pull = GPIO_PULLUP;             //高速
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    
    //关闭plugin
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
}

//打开fan的函数
void fan_on(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET);   //拉低fan引脚,关闭fan
}


//关闭fan的函数

void fan_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);     //拉高fan引脚,关闭fan
}

//翻转fan的函数
void fan_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);                   //翻转fan引脚电平
}

//返回fan的状态
uint8_t fan_status_get(void)
{
    return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6);
}

fan.h

c 复制代码
#ifndef __FAN_H__
#define __FAN_H__

#include "stdint.h"

#define FAN_STATUS_ON 0
#define FAN_STATUS_OFF 0

//初始化GBIO口函数
void fan_init(void);

//打开fan的函数
void fan_on(void);

//关闭fan的函数
void fan_off(void);

//翻转fan的函数
void fan_toggle(void);

//返回继电器的状态
uint8_t fan_status_get(void);

#endif 

esp8266.c

c 复制代码
#include "esp8266.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"

//定义发送缓存区
uint8_t esp8266_tx_buf[ESP8266_TX_BUF_SIZE] = {0};
//定义接收缓存区
uint8_t esp8266_rx_buf[ESP8266_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t esp8266_rx_len = 0;
//定义esp8266最新接收数量,数量旧值
uint16_t esp8266_cnt = 0, esp8266_cnt_Pre = 0;

UART_HandleTypeDef esp8266_handle = {0};

//esp8266串口初始化函数
void esp8266_uart_init(uint32_t baudrate)
{
    //串口1选择
    esp8266_handle.Instance = USART2;
    //波特率
    esp8266_handle.Init.BaudRate = baudrate;
    //字长
    esp8266_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    esp8266_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    esp8266_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    esp8266_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    esp8266_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&esp8266_handle);
}

//esp8266中断服务函数
void USART2_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&esp8266_handle, UART_FLAG_RXNE) != RESET){
        if(esp8266_cnt >= sizeof(esp8266_rx_buf)){
            //接受数量置为零
            esp8266_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&esp8266_handle, &receive_data, 1,1000);
        esp8266_rx_buf[esp8266_cnt++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&esp8266_handle,&receive_data, 1,1000);
    }
}

//等待接收函数
uint8_t esp8266_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(esp8266_cnt == 0){
        return ESP8266_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(esp8266_cnt == esp8266_cnt_Pre){
        esp8266_cnt = 0;
        return ESP8266_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    esp8266_cnt_Pre = esp8266_cnt;
    return ESP8266_ERROR;
}

//接收区清空
void esp8266_rx_clear(void)
{
    memset(esp8266_rx_buf, 0, sizeof(esp8266_rx_buf));
    esp8266_cnt = 0;
}

//esp8266发送
void esp8266_send_data(char *fmt, ...)
{
    // 定义可变参数列表变量
    va_list ap;
    // 定义长度变量,用于存储字符串长度
    uint16_t len;
    // 初始化可变参数列表,ap指向fmt后的第一个参数
    va_start(ap, fmt);
    // 使用vsprintf将格式化后的字符串输出到发送缓冲区
    // vsprintf会自动处理可变参数,根据fmt格式字符串将参数格式化后存入缓冲区
    vsprintf((char* )esp8266_tx_buf, fmt, ap);
    // 计算格式化后字符串的长度(不包括字符串结束符'\0')
    len = strlen((const char *)esp8266_tx_buf);
    // 通过UART发送数据
    HAL_UART_Transmit(&esp8266_handle, esp8266_tx_buf, len, 100);
}

//esp8266接收
uint16_t esp8266_receive_data(char *recv_data)
{
    if(esp8266_wait_receive() == ESP8266_EOK){
        printf("esp8266 rev:%s\r\n",esp8266_rx_buf);
        memcpy(recv_data, esp8266_rx_buf, strlen((const char *)esp8266_rx_buf));
        esp8266_rx_clear();
        return strlen((const char *)recv_data);
    }
    return 0;
}

//esp8266发送函数
uint8_t esp8266_send_command(char *cmd, char *res)
{
    uint8_t time_out = 250;
    
    //清空接收缓存区
    esp8266_rx_clear();
    //发送函数
    HAL_UART_Transmit(&esp8266_handle,(uint8_t *)cmd, strlen(cmd),100);
    //判断超时
    while(time_out--){
        //判断是否接收到新数据
        if(esp8266_wait_receive() == ESP8266_EOK){
            //判断接受的数据是否和要求比较的数据一致
            if(strstr((char *)esp8266_rx_buf, res) != NULL){
                return ESP8266_EOK;
            }
        }
        delay_ms(10);
    }
    return ESP8266_ERROR;
}

//测试
uint8_t esp8266_at_test(void)
{
    return esp8266_send_command("AT\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");
        case ESP8266_AP_MODE:
            return esp8266_send_command("AT+CWMODE=2\r\n", "OK");
        case ESP8266_STA_AP_MODE:
            return esp8266_send_command("AT+CWMODE=3\r\n", "OK");
        default:
            return ESP8266_EINVAL;
    }
}

//连接网络
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_connection_mode(uint8_t mode)
{
    char cmd[64];
    
    sprintf(cmd,"AT+CIPMUX=%d\r\n",mode);
    return esp8266_send_command(cmd,"OK");
}

//连接服务器
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_unvarnishied(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;
    }   
}

//esp8266初始化
void esp8266_init(uint32_t baudrate)
{
    printf("esp8266初始化开始...\r\n");
    //串口初始化
    esp8266_uart_init(baudrate);
    
    //配置wifi模块
    printf("1. 测试esp8266是否存在\r\n");
    while(esp8266_at_test()){
        delay_ms(500);
    }
    printf("2. 设置工作模式为STA...\r\n");
    while(esp8266_set_mode(ESP8266_STA_MODE)){
        delay_ms(500);
    }
    printf("3. 设置单链路链接模式...\r\n");
    while(esp8266_connection_mode(ESP8266_SINGLE_CONNECTION)){
        delay_ms(500);
    }
    printf("4. 连接wifi,SSID:%s, PWD:%s...\r\n",WIFI_SSID,WIFI_PWD);
     while(esp8266_join_ap(WIFI_SSID,WIFI_PWD)){
        delay_ms(2000);
    }
    printf("5. 连接云服务器,server_ip:%s,server_port:%s...\r\n",TCP_SERVER_IP,TCP_SERVER_PORT);
    while(esp8266_connect_tcp_server(TCP_SERVER_IP, TCP_SERVER_PORT)){
        delay_ms(500);
    }
    printf("6. 进入透传模式...\r\n");
    while(esp8266_enter_unvarnishied()){
        delay_ms(500);
    }
    printf("ESP8266已经连接服务器并进入透传模式!\r\n");
    printf("ESP8266初始化完成!\r\n");
}



//测试函数
void esp8266_test(void)
{
    esp8266_send_data("This is from esp8266\r\n");
    //esp8266_receive_data();
}

esp8266.h

c 复制代码
#ifndef __ESP8266_H__
#define __ESP8266_H__

#include "stdio.h"
#include "sys.h"

//定义发送接收缓存大小
#define ESP8266_RX_BUF_SIZE 128
#define ESP8266_TX_BUF_SIZE 64

//定义状态标志
#define ESP8266_EOK                 0
#define ESP8266_ERROR               1
#define ESP8266_ETIMEOUT            2
#define ESP8266_EINVAL              3

//定义AT指令填充字段
#define ESP8266_STA_MODE            1
#define ESP8266_AP_MODE             2
#define ESP8266_STA_AP_MODE         3

#define ESP8266_SINGLE_CONNECTION   0
#define ESP8266_MULTI_CONNECTION    1

#define WIFI_SSID                   "HUAWEI Mate 60 Pro"
#define WIFI_PWD                    "88888888"

#define TCP_SERVER_IP               "10.102.14.154"
#define TCP_SERVER_PORT             "8080"

//esp8266初始化
void esp8266_init(uint32_t baudrate);
//esp8266接收
uint16_t esp8266_receive_data(char *recv_data);
//测试函数
void esp8266_test(void);

#endif

8. 模块:4G模块

接线方式同esp8266,交叉接线。

8.1 内网穿透

内网穿透的原理就像在内网和外网之间搭建了一座桥梁,使得外部网络可以穿过内网的障碍,直接访问内部的设备。

8.2 花生壳配置



8.3 4G模块配置

详细配置可见EC03-DNC4G通信模块手册,这里只演示基本功能。

在测试之前,将电源、天线、SIM 卡、串口线等硬件连接好。

  1. 打开串口连接4G模块,串口出产默认波特率是115200,可以自行根据用户手册修改
  2. 进入AT指令模式,在串口助手内发送+++(不要勾选发送新行),必须在发送+++指令 3s 内发送其他任意 AT 指令,比如AT+CPIN
  3. 观察SIM卡灯是否亮起,AT+ICCID获得SIM卡信息,确认SIM卡安装完好 返回数据:
    +OK=89860116838013413419检查信号是否正常,通过AT+CSQ指令检查信号值,建议插入信号天线,返回数据:+OK=31
  4. AT+SOCK=TCPC,103.46.128.21,52541 连接socket服务器,103.46.128.21是公网IP地址,通过花生壳获得,26532是端口号,参数之间逗号隔开
  5. AT+LINKSTA查看连接状态,如果第四步没有问题,此时串口返回+OK=Connect

使用

重启后,模块自动进入透传模式,直接可以和服务器进行通信

踩坑

  1. SIM卡方向装反了
  2. 进入AT指令,认真操作哦
  3. AT+SOCK=TCPC,103.46.128.21,26532是正确连接socket服务器的方式,官方给的文档有错误引导有效使用的样子

9. 项目:4G遥控灯

项目需求

服务器通过4G模块远程遥控开关灯。

硬件接线


项目框图

代码实现

main.c

c 复制代码
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "string.h"
#include "e840.h"

int main(void)
{  
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */      
    //LED初始化
    led_init();
    //uart初始化
    uart1_init(115200);
    //e840初始化
    e840_uart_init(115200);
    //串口打印测试
    printf("串口打印测试:Hello world\r\n");
    
    char recv_data[E840_RX_BUF_SIZE];
    
    while(1)
    {
        e840_receive_data(recv_data);
        if(strstr(recv_data, "ON") != NULL){
            led1_on();
        }else if(strstr(recv_data, "OFF") != NULL){
            led1_off();
        }
        
        delay_ms(10);
    }
}

led.c

c 复制代码
#include "led.h"
#include "sys.h"

//初始化GBIO口函数
void led_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    //使能GPIOB时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;               //LED1,LED2对应引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;     //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;   //高速
    gpio_initstruct.Pull = GPIO_PULLUP;             //上拉
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
    
    //关闭LED
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
    
}

//点亮LED1的函数
void led1_on(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);   //拉低LED1引脚,点亮LED1
}


//熄灭LED1的函数

void led1_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);     //拉高LED1引脚,熄灭LED1
}

//翻转LED1的函数
void led1_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);                   //翻转LED1引脚电平
}

//点亮LED2的函数
void led2_on(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);   //拉低LED2引脚,点亮LED2
}


//熄灭LED2的函数

void led2_off(void)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);     //拉高LED2引脚,熄灭LED2
}

//翻转LED2的函数
void led2_toggle(void)
{
    HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);                   //翻转LED2引脚电平
}

led.h

c 复制代码
#ifndef __LED_H__
#define __LED_H__

//初始化GBIO口函数
void led_init(void);

//点亮LED1的函数
void led1_on(void);

//熄灭LED1的函数

void led1_off(void);

//翻转LED1的函数
void led1_toggle(void);

//点亮LED2的函数
void led2_on(void);

//熄灭LED2的函数

void led2_off(void);

//翻转LED2的函数
void led2_toggle(void);

#endif 

e840.c

c 复制代码
#include "e840.h"
#include "stdio.h"
#include "string.h"
#include "delay.h"
#include "stdarg.h"

//定义发送缓存区
uint8_t e840_tx_buf[E840_TX_BUF_SIZE] = {0};
//定义接收缓存区
uint8_t e840_rx_buf[E840_RX_BUF_SIZE] = {0};
//定义接收长度
uint16_t e840_rx_len = 0;
//定义e840最新接收数量,数量旧值
uint16_t e840_cnt = 0, e840_cnt_Pre = 0;

UART_HandleTypeDef e840_handle = {0};

//e840串口初始化函数
void e840_uart_init(uint32_t baudrate)
{
    //串口1选择
    e840_handle.Instance = USART2;
    //波特率
    e840_handle.Init.BaudRate = baudrate;
    //字长
    e840_handle.Init.WordLength = UART_WORDLENGTH_8B;
    //停止位
    e840_handle.Init.StopBits = UART_STOPBITS_1;
    //校验位
    e840_handle.Init.Parity = UART_PARITY_NONE;
    //流控
    e840_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    //模式
    e840_handle.Init.Mode = UART_MODE_TX_RX;
    //初始化
    HAL_UART_Init(&e840_handle);
}

//e840中断服务函数
void USART2_IRQHandler(void)
{
    //定义接收字符
    uint8_t receive_data = 0;
    //当接收寄存器不为空的时候
    if(__HAL_UART_GET_FLAG(&e840_handle, UART_FLAG_RXNE) != RESET){
        if(e840_cnt >= sizeof(e840_rx_buf)){
            //接受数量置为零
            e840_cnt = 0;
        }
        //接收一个字符
        HAL_UART_Receive(&e840_handle, &receive_data, 1,1000);
        e840_rx_buf[e840_cnt++] = receive_data;
        //再发送出去
        //HAL_UART_Transmit(&e840_handle,&receive_data, 1,1000);
    }
}

//等待接收函数
uint8_t e840_wait_receive(void)
{
    //如果接收数为零,则返回错误码
    if(e840_cnt == 0){
        return E840_ERROR;
    }
    //如果接收数量不变,则表示本段接收完成,返回完成码
    if(e840_cnt == e840_cnt_Pre){
        e840_cnt = 0;
        return E840_EOK;
    }
    //接受数量变了,则将新值赋给旧值,但是没接收完
    e840_cnt_Pre = e840_cnt;
    return E840_ERROR;
}

//接收区清空
void e840_rx_clear(void)
{
    memset(e840_rx_buf, 0, sizeof(e840_rx_buf));
    e840_cnt = 0;
}

//e840发送
void e840_send_data(char *fmt, ...)
{
    // 定义可变参数列表变量
    va_list ap;
    // 定义长度变量,用于存储字符串长度
    uint16_t len;
    // 初始化可变参数列表,ap指向fmt后的第一个参数
    va_start(ap, fmt);
    // 使用vsprintf将格式化后的字符串输出到发送缓冲区
    // vsprintf会自动处理可变参数,根据fmt格式字符串将参数格式化后存入缓冲区
    vsprintf((char* )e840_tx_buf, fmt, ap);
    // 计算格式化后字符串的长度(不包括字符串结束符'\0')
    len = strlen((const char *)e840_tx_buf);
    // 通过UART发送数据
    HAL_UART_Transmit(&e840_handle, e840_tx_buf, len, 100);
}

//e840接收
uint16_t e840_receive_data(char *recv_data)
{
    if(e840_wait_receive() == E840_EOK){
        printf("e840 rev:%s\r\n",e840_rx_buf);
        memcpy(recv_data, e840_rx_buf, strlen((const char *)e840_rx_buf));
        e840_rx_clear();
        return strlen((const char *)recv_data);
    }
    return 0;
}

//e840发送函数
uint8_t e840_send_command(char *cmd, char *res)
{
    uint8_t time_out = 250;
    
    //清空接收缓存区
    e840_rx_clear();
    //发送函数
    HAL_UART_Transmit(&e840_handle,(uint8_t *)cmd, strlen(cmd),100);
    //判断超时
    while(time_out--){
        //判断是否接收到新数据
        if(e840_wait_receive() == E840_EOK){
            //判断接受的数据是否和要求比较的数据一致
            if(strstr((char *)e840_rx_buf, res) != NULL){
                return E840_EOK;
            }
        }
        delay_ms(10);
    }
    return E840_ERROR;
}

e840.h

c 复制代码
#ifndef __E840_H__
#define __E840_H__

#include "stdio.h"
#include "sys.h"

//定义发送接收缓存大小
#define E840_RX_BUF_SIZE 128
#define E840_TX_BUF_SIZE 64

//定义状态标志
#define E840_EOK                 0
#define E840_ERROR               1
#define E840_ETIMEOUT            2
#define E840_EINVAL              3

//定义AT指令填充字段
#define E840_STA_MODE            1
#define E840_AP_MODE             2
#define E840_STA_AP_MODE         3

#define E840_SINGLE_CONNECTION   0
#define E840_MULTI_CONNECTION    1

#define WIFI_SSID                   "HUAWEI Mate 60 Pro"
#define WIFI_PWD                    "88888888"

#define TCP_SERVER_IP               "10.102.14.154"
#define TCP_SERVER_PORT             "8080"

//e840串口初始化函数
void e840_uart_init(uint32_t baudrate);
//e840接收
uint16_t e840_receive_data(char *recv_data);

#endif

串口相关内容到此结束。

相关推荐
JasmineX-1几秒前
下位机直接控制57步进电机匀速转动
stm32·单片机·嵌入式硬件
聆思科技AI芯片29 分钟前
【AI硬件开发上手教程】怎么使用AI编程实现小聆AI语音控制舵机
人工智能·嵌入式硬件·开源·ai编程·智能硬件
abc light1 小时前
ARM 裸机开发(6)——时钟树与定时器
arm开发·单片机·fpga开发
吠品11 小时前
智谱开源 ZCode 之后,真正的考试才开始
单片机·嵌入式硬件·1024程序员节
Chery114012 小时前
高速ADC与精密DAC怎么选?DP9230D与DP8532D应用解析
嵌入式硬件·物联网·射频工程
小李不想当小白15 小时前
SPI通信外设与W25Q64 (硬件SPI)(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
LCG米15 小时前
STM32驱动W5500以太网模块实测:TCP吞吐瓶颈定位、TX指针回绕陷阱与断线重连保活落地
stm32·单片机·嵌入式硬件·tcp/ip
尼喃17 小时前
662K/65Z5三脚稳压芯片规格书—PW6566B33/B30引脚功能
嵌入式硬件
创界工坊工作室17 小时前
63-基于STC89C52霍尔传感器的智能化电能表设计
stm32·单片机·嵌入式硬件·51单片机·proteus
智购科技自动售货机厂家18 小时前
让AI经营自动售货机:从Project Vend看工具权限与业务规则~YH
大数据·服务器·单片机·嵌入式硬件·深度学习