目录
前言:
1 前期准备
1.1 了解mqtt通信协议
1.1.1核心组件
1.2 ESP8266固件烧录
1.3 启动EMQX服务器
1.3.1大概了解emqx的使用
2 驱动代码讲解应用
2.1 硬件接线
2.2 AT指令
2.3 驱动代码
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.
4. 主题 (Topic): 这是 MQTT 实现通信的关键机制。 主题是一个 UTF-8 字符串,充当消息的“地址”或“路由标签”。发布者发布消息时必须指定一个主题。订阅者通过订阅特定的主题(或使用通配符)来表明它希望接收哪些主题的消息。例如:home/livingroom/temperature, factory/machine42/vibration。
5. 消息 (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可以调用下面函数
/* @briefWIFI重连* @param* @return*/
void ESPWIFI_Reconnect(void)
{//断开WiFiUART_Printf(&huart2, "AT+CWQAP");HAL_Delay(1000);//连接WiFiUART_Printf(&huart2, "AT+CWJAP=\"%s\",\"%s\"\r\n",WIFI_NAME,WIFI_PASSWORD);
}
ESP8266.c
/** 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)
{//断开WiFiUART_Printf(&huart2, "AT+CWQAP");HAL_Delay(1000);//连接WiFiUART_Printf(&huart2, "AT+CWJAP=\"%s\",\"%s\"\r\n",WIFI_NAME,WIFI_PASSWORD);
}
ESP8266.h
/** 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
/** 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
/** 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
/** 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也可以收到