目錄
前言:
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也可以收到