超详细基于stm32hal库的esp8266WiFi模块驱动程序(可直接移植)

目录

前言:

[1 前期准备](#1 前期准备)

[1.1 了解mqtt通信协议](#1.1 了解mqtt通信协议)

1.1.1核心组件

[1.2 ESP8266固件烧录](#1.2 ESP8266固件烧录)

[1.3 启动EMQX服务器](#1.3 启动EMQX服务器)

1.3.1大概了解emqx的使用

[2 驱动代码讲解应用](#2 驱动代码讲解应用)

[2.1 硬件接线](#2.1 硬件接线)

[2.2 AT指令](#2.2 AT指令)

[2.3 驱动代码](#2.3 驱动代码)

[2.4 效果展示](#2.4 效果展示)


前言:

esp8266支持mqtt通信协议,在一些只能在局域网环境下通信来说是十分便捷的(比如智能家居环境),下面这个工程就是stm32单片机结合esp8266作为客户端与其他客户端设备互相通信。

1 前期准备

1.1 了解mqtt通信协议

MQTT (Message Queuing Telemetry Transport) 是一种轻量级、开放标准、基于发布/订阅模式的消息传输协议。它最初由 IBM 和 Eurotech 在 1999 年设计,用于连接石油管道上的远程传感器与卫星链路。如今,它已成为物联网 (IoT) 领域事实上的标准通信协议。

1.1.1核心组件
  1. 发布者 (Publisher): 产生并发送消息的客户端(设备或应用)。例如:在我们的工程里我们将单片机采集到的数据通过串口转发给esp8266,而esp充当消息的发布者。

  2. 订阅者 (Subscriber): 接收其感兴趣消息的客户端(设备或应用)。例如:手机 App 订阅温度读数以显示,或者空调控制器订阅温度读数来决定是否开启。

  3. 代理服务器 (Broker): MQTT 通信的核心枢纽。它负责:

接收来自发布者的消息。

根据消息的主题 (Topic) 将其过滤并转发给所有订阅了该主题的订阅者。

管理客户端连接(认证、授权、会话状态)。

存储 QoS 消息以确保可靠传输(根据 QoS 级别)。

处理遗嘱消息 (Last Will & Testament)。

在这个工程里我们使用开源的mqtt服务系统EMQX.

  1. 主题 (Topic): 这是 MQTT 实现通信的关键机制。 主题是一个 UTF-8 字符串,充当消息的"地址"或"路由标签"。发布者发布消息时必须指定一个主题。订阅者通过订阅特定的主题(或使用通配符)来表明它希望接收哪些主题的消息。例如:home/livingroom/temperature, factory/machine42/vibration。

  2. 消息 (Message): 包含实际传输的数据(Payload),可以是任何格式(JSON, XML, 二进制, 纯文本等)。消息总是与一个主题相关联

1.2 ESP8266固件烧录

esp8266要使用mqtt通信需要烧录含mqtt的固件网上可以找到很多教程(一些厂家出厂自带固件)

1.3 启动EMQX服务器

需要在本地部署emqx服务器过程不难

这里推荐一个教程Windows下载安装EMQX_emqx下载-CSDN博客

1.3.1大概了解emqx的使用

在首页我们可以查看到连接服务器的设备有哪些(每一个设备有自己独立端口点开可以查看)

我们可以在服务器里创建客户端位置如下

使用规则也很简单最基本的,订阅了同一个主题的设备,当其他设备发送消息的时候,己设备可以看到,同样的当己设备发布消息的时候,其他跟我订阅同一主题的设备就可以收到己设备发送的消息。也是这样实现了多个设备之间的互相通信。

2 驱动代码讲解应用

2.1 硬件接线

只需要用到esp8266的4各引脚,gnd,3.3v,tx,rx(tx rx分别与单片机的串口交叉相连)如下图

2.2 AT指令

esp8266可以使用AT查看设备的一些远行状态下面给出以下常用的AT指令

2.3 驱动代码

这里我使用stm32hal库写了一段容易移植的代码,文件构成为ESP8266.c(关于esp8266的驱动代码),ESP8266.h,comation.c(系统驱动代码),comation.h,head.h(包含要用到的头文件)只需要在主函数里初始化一下就可以使用了。

因为该模块可以商店自动连接WiFi所以我在初始化的时候没有连接WiFi,如果想要换WiFi可以调用下面函数

cpp 复制代码
/* @briefWIFI重连
 * @param
 * @return*/
void ESPWIFI_Reconnect(void)
{
	//断开WiFi
	UART_Printf(&huart2, "AT+CWQAP");
	HAL_Delay(1000);
	//连接WiFi
	UART_Printf(&huart2, "AT+CWJAP=\"%s\",\"%s\"\r\n",WIFI_NAME,WIFI_PASSWORD);
}

ESP8266.c

cpp 复制代码
/*
 * ESP8266.c
 *
 *  Created on: Aug 11, 2025
 *      Author: ccc
 */
#include "head.h"

/*
 * @brief esp初始化连接服务器
 * @param
 * @return
 * */
char esp_response[RESP_BUFFER_SIZE];//接收缓存区
uint16_t resp_index = 0;//接受标志位
uint8_t esprx_byte;
void Inint_esp8266(void)
{
	HAL_UART_Receive_IT(&huart2, &esprx_byte, 1);
	HAL_Delay(30);
	// 配置认证
	UART_Printf(&huart2, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",MQTT_CLIENT_ID,MQTT_USERNAME,MQTT_PASSWORD);
	HAL_Delay(1000);
	// 连接服务器
	UART_Printf(&huart2, "AT+MQTTCONN=0,\"%s\",1883,0\r\n",MQTT_BROKER);
	HAL_Delay(3000);
	// 订阅主题
	UART_Printf(&huart2, "AT+MQTTSUB=0,\"%s\",1\r\n",MQTT_TOPIC);
	HAL_Delay(1000);

}
/*
 * @brief esp响应函数
 * @param
 * @return
 * */
void ESP8266_ReceiveHandler(UART_HandleTypeDef *huart)
{
    //  处理接收到的字节
    // 检查缓冲区是否已满
    if(resp_index >= RESP_BUFFER_SIZE - 1)
    {
        resp_index = 0; // 重置缓冲区,防止溢出
    }

    // 存储接收到的字节
    esp_response[resp_index++] = esprx_byte;

    //  检测是否收到完整响应(以换行符结束)
    if(esprx_byte == '\n')
    {
        esp_response[resp_index] = '\0'; // 添加字符串结束符

        //  打印原始响应
        UART_Printf(&huart1, "ESP8266get: %s\n", esp_response);

        //  处理关键响应(以下消息使用串口1转发)
        if(strstr(esp_response, "OK") != NULL)
        {
            UART_Printf(&huart1, "Getsuccessful!\n");
        }
        else if(strstr(esp_response, "ERROR") != NULL)
        {
            UART_Printf(&huart1, "Getfailed!\n");
        }
        else if(strstr(esp_response, "+MQTTCONNECTED:0") != NULL)
        {
            UART_Printf(&huart1, "MQTTConnection successful!\n");
        }
        else if(strstr(esp_response, "+MQTTDISCONNECTED:0") != NULL)
        {
            UART_Printf(&huart1, "MQTTDisconnect!\n");
        }
        else if(strstr(esp_response, "MQTTSUBRECV:0") != NULL)
        {
            // 解析MQTT消息
            // 格式: +MQTTSUBRECV:0,<topic>,<length>,<message>
            char *topic_start = strchr(esp_response, ',') + 1;
            char *topic_end = strchr(topic_start, ',');
            char *length_start = topic_end + 1;
            char *length_end = strchr(length_start, ',');
            char *msg_start = length_end + 1;

            // 提取主题
            int topic_len = topic_end - topic_start;
            char topic[topic_len + 1];
            strncpy(topic, topic_start, topic_len);
            topic[topic_len] = '\0';

            // 提取消息长度
            int msg_len = atoi(length_start);

            // 提取消息内容
            char message[msg_len + 1];
            strncpy(message, msg_start, msg_len);
            message[msg_len] = '\0';

            UART_Printf(&huart1, "getMQTTmessage: [topic] %s [message] %s\n", topic, message);
        }
        memset(esp_response, 0, sizeof(esp_response));
        resp_index = 0; // 重置缓冲区索引
    }

    //  重新启动接收,等待下一个字节
    HAL_UART_Receive_IT(&huart2, &esprx_byte, 1);
}
/*
 * @brief esp发送函数
 * @param
 * @return
 * */
void Publish_Message(const char *message)
{
    // AT+MQTTPUB=0,<topic>,<message>,<qos>,<retain>
    UART_Printf(&huart2, "AT+MQTTPUB=0,\"%s\",\"%s\",0,0\r\n", MQTT_TOPIC, message);
    HAL_Delay(100);
}

/* @briefWIFI重连
 * @param
 * @return*/
void ESPWIFI_Reconnect(void)
{
	//断开WiFi
	UART_Printf(&huart2, "AT+CWQAP");
	HAL_Delay(1000);
	//连接WiFi
	UART_Printf(&huart2, "AT+CWJAP=\"%s\",\"%s\"\r\n",WIFI_NAME,WIFI_PASSWORD);
}

ESP8266.h

cpp 复制代码
/*
 * ESP8266.h
 *
 *  Created on: Aug 11, 2025
 *      Author: ccc
 */

#ifndef ESP8266_H_
#define ESP8266_H_

#include "head.h"

#define MQTT_BROKER        "192.168.124.20"  //服务器地址ip
#define MQTT_PORT          1883	//服务器端口号  1883不限制端口号
#define MQTT_CLIENT_ID     "mcus-test-client"	//客户端唯一标识
#define MQTT_USERNAME      "user"  //用户名,如不需要可留空
#define MQTT_PASSWORD      "pass"  //密码,如不需要可留空
#define MQTT_TOPIC	       "test2" //要订阅的主题
#define WIFI_NAME          "IPhone"//wifi名
#define WIFI_PASSWORD  	   "lyl975418"//wifi密码

#define RESP_BUFFER_SIZE 256
extern char esp_response[RESP_BUFFER_SIZE];
extern uint16_t resp_index;
extern uint8_t esprx_byte;



void Inint_esp8266(void);
void ESP8266_ReceiveHandler(UART_HandleTypeDef *huart);
void Publish_Message(const char *message);
void ESPWIFI_Reconnect(void);
#endif /* ESP8266_H_ */

comation.c

cpp 复制代码
/*
 * comation.c
 *
 *  Created on: Aug 11, 2025
 *      Author: ccc
 */
#include "head.h"
/*
 * @brief格式化输出到串口上
 * @param
 * @return
 * */
void UART_Printf(UART_HandleTypeDef *USARTx, char *fmt,...)
{

	unsigned char UsartPrintfBuf[296];
	va_list ap;
	unsigned char *pStr = UsartPrintfBuf;

	va_start(ap, fmt);
	vsnprintf((char *)UsartPrintfBuf, sizeof(UsartPrintfBuf), fmt, ap);							//格式化
	va_end(ap);

	while(*pStr != '\0')
	{
       HAL_UART_Transmit (USARTx ,(uint8_t *)pStr++,1,10);
	}

}
/* @brief串口中断回调函数
 * @param
 * @return
 * */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART2)
	{
		//HAL_UART_Receive_IT(&huart2, &esprx_byte, 1);
		ESP8266_ReceiveHandler(&huart2);
	}

}

comation.h

cpp 复制代码
/*
 * comation.h
 *
 *  Created on: Aug 11, 2025
 *      Author: ccc
 */

#ifndef COMATION_H_
#define COMATION_H_
#include "head.h"

void UART_Printf(UART_HandleTypeDef *USARTx, char *fmt,...);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
#endif /* COMATION_H_ */

head.h

cpp 复制代码
/*
 * head.h
 *
 *  Created on: Aug 11, 2025
 *      Author: ccc
 */

#ifndef HEAD_H_
#define HEAD_H_
/*
 *@系统头文件
 */
#include "main.h"
#include "i2c.h"
#include "icache.h"
#include "usart.h"
#include "gpio.h"
#include "stdarg.h"
#include "stdio.h"
#include "stdbool.h"
#include <string.h>
#include <stdlib.h>

/*
 * 自定义头文件
 * */
#include "ESP8266.h"
#include "comation.h"
#endif /* HEAD_H_ */

2.4 效果展示

我们使用python写的客户端发布主题时,esp8266可以收到在串口助手展示

同样的我们使用esp8266发送消息时python也可以收到