当前位置: 首页 > news >正文

ESP32学习笔记(基于IDF):ESP32连接MQTT服务器

目录

  • 一、MQTT协议知识点
    • 1、 什么是MQTT协议?
    • 2、MQTT基本格式
    • 3、固定包头构成
    • 4、报文类型
    • 5、CONNECT与CONNECTACK报文
    • 6、CONNECT报文格式
    • 7、连接标志
    • 8、CONNECT ACK报文格式
    • 9、订阅和发布
    • 10、订阅报文
    • 11、订阅回应报文
    • 12、发布报文
    • 13、发布回应报文
  • 二、ESP32连接MQTT实验

一、MQTT协议知识点

1、 什么是MQTT协议?

在这里插入图片描述

2、MQTT基本格式

在这里插入图片描述

3、固定包头构成

在这里插入图片描述

4、报文类型

在这里插入图片描述

5、CONNECT与CONNECTACK报文

在这里插入图片描述

6、CONNECT报文格式

在这里插入图片描述

7、连接标志

在这里插入图片描述
在这里插入图片描述

8、CONNECT ACK报文格式

在这里插入图片描述

9、订阅和发布

在这里插入图片描述

10、订阅报文

在这里插入图片描述

11、订阅回应报文

在这里插入图片描述

12、发布报文

在这里插入图片描述

13、发布回应报文

在这里插入图片描述

二、ESP32连接MQTT实验

1、下载MQTTX用于调试
https://mqttx.app/
在这里插入图片描述
2、点这个加号,新建连接
在这里插入图片描述
3、这里填一下,然后点Connect
在这里插入图片描述
4、连接成功,然后先放在这里,不管它,接下来开发一下ESP32
在这里插入图片描述
5、回到根目录,新建一个工程,叫mqtt_test
在这里插入图片描述
6、进到mqtt_test目录,然后输入以下指令编译一下

idf.py build

在这里插入图片描述
7、在VSCode打开刚刚新建的工程
在这里插入图片描述
8、按住Ctrl+Shift+P,然后点击这个添加路径
在这里插入图片描述
9、在mqtt_test.c文件中写入如下代码

在这里插入图片描述

#include <stdio.h>
#include "mqtt_client.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "freertos/semphr.h"#define MQTT_ADDRESS   "mqtt://broker-cn.emqx.io"
#define MQTT_CLIENTID  "mqttx_esp321021"
#define MQTT_USERNAME  "panda"
#define MQTT_PASSWORD  "xxyy77.."#define MQTT_TOPIC1    "/topic/esp32_0823"       //ESP32往这个主题推送消息
#define MQTT_TOPIC2    "/topic/mqttx_0823"       //mqttx往这个主题推送消息#define TEST_SSID "Lymow"// 要连接的Wi-Fi热点名称(SSID)
#define TEST_PASSWORD "1415926535"// 要连接的Wi-Fi密码static esp_mqtt_client_handle_t mqtt_handle = NULL;static SemaphoreHandle_t s_wifi_connect_sem = NULL;#define TAG "mqtt"void mqtt_event_callback(void* event_handle_arg,esp_event_base_t event_base, int32_t event_id, void* event_data)
{// 将事件数据转换为 MQTT 事件专用结构体(方便获取事件详情)// esp_mqtt_event_handle_t 结构体中包含:主题、消息内容、消息长度、客户端句柄等信息esp_mqtt_event_handle_t data = (esp_mqtt_event_handle_t)event_data;// 根据不同的事件 ID,执行对应的处理逻辑switch (event_id){// 事件1:MQTT 客户端成功连接到服务器case MQTT_EVENT_CONNECTED:ESP_LOGI(TAG,"mqtt connected");// 连接成功后,立即订阅指定的 MQTT 主题(例如接收服务器/手机发来的指令)// 参数:客户端句柄、要订阅的主题(MQTT_TOPIC2)、QoS 等级(1表示确保消息至少到达一次)esp_mqtt_client_subscribe_single(mqtt_handle,MQTT_TOPIC2,1);break;// 事件2:MQTT 客户端与服务器断开连接case MQTT_EVENT_DISCONNECTED:ESP_LOGI(TAG,"mqtt disconnected");break;// 事件3:MQTT 客户端发送消息后,收到服务器的“发布确认”(表示服务器已收到消息)case MQTT_EVENT_PUBLISHED:ESP_LOGI(TAG,"mqtt published ack");break;// 事件4:MQTT 客户端订阅主题后,收到服务器的“订阅确认”(表示订阅成功)case MQTT_EVENT_SUBSCRIBED:ESP_LOGI(TAG,"mqtt subcribed ack");break;// 事件5:MQTT 客户端收到服务器发送的消息(来自已订阅的主题)case MQTT_EVENT_DATA:ESP_LOGI(TAG,"topic->%s",data->topic);ESP_LOGI(TAG,"payload->%s",data->data);break;default:break;}
}/*** Wi-Fi事件处理函数* 用于响应Wi-Fi连接状态变化、IP地址获取等事件* @param event_handler_arg: 事件处理参数(未使用)* @param event_base: 事件类型(如WIFI_EVENT、IP_EVENT)* @param event_id: 具体事件ID(如连接成功、断开连接)* @param event_data: 事件相关数据(未使用)*/
void wifi_event_handle(void* event_handler_arg,esp_event_base_t event_base,int32_t event_id,void* event_data)
{if(event_base == WIFI_EVENT)// 处理Wi-Fi相关事件(WIFI_EVENT类型){switch(event_id){case WIFI_EVENT_STA_START: //事件1:STA模式启动完成(Wi-Fi已准备好连接)esp_wifi_connect();// 调用连接函数,尝试连接到指定Wi-Fibreak;case WIFI_EVENT_STA_CONNECTED:// 事件2:STA成功连接到Wi-Fi热点ESP_LOGI(TAG,"esp32 connected to ap!");break;case WIFI_EVENT_STA_DISCONNECTED:// 事件3:STA与Wi-Fi热点断开连接esp_wifi_connect(); // 自动重试连接ESP_LOGI(TAG,"esp32 connect the ap faild! retry!");break;default:break;}}else if(event_base == IP_EVENT)//处理IP相关事件(IP_EVENT类型){switch(event_id){case IP_EVENT_STA_GOT_IP://事件:STA成功获取IP地址(此时可正常联网)ESP_LOGI(TAG,"esp32 get ip address");xSemaphoreGive(s_wifi_connect_sem);break;}}
}void mqtt_start(void)
{// 1. 定义并初始化MQTT客户端配置结构体(初始化为全0,避免垃圾数据)// esp_mqtt_client_config_t 是ESP-IDF定义的结构体,用于存储MQTT连接的所有参数esp_mqtt_client_config_t   mqtt_cfg = {0};// 2. 配置MQTT服务器的URI(统一资源标识符,包含协议和地址)mqtt_cfg.broker.address.uri = MQTT_ADDRESS;// 3. 配置MQTT服务器的端口号(默认1883为非加密端口,8883为加密端口)mqtt_cfg.broker.address.port = 1883;// 4. 配置客户端ID(用于在MQTT服务器中唯一标识当前ESP32设备,避免重复)mqtt_cfg.credentials.client_id = MQTT_CLIENTID;// 5. 配置连接MQTT服务器的用户名mqtt_cfg.credentials.username = MQTT_USERNAME;// 6. 配置连接MQTT服务器的密码(与用户名配套,用于身份验证)mqtt_cfg.credentials.authentication.password = MQTT_PASSWORD;// 7. 根据上述配置,初始化MQTT客户端,并获取客户端句柄(类似"连接ID",后续操作需用它)// mqtt_handle 是全局变量,用于保存客户端句柄,方便其他函数操作MQTT连接mqtt_handle = esp_mqtt_client_init(&mqtt_cfg);// 8. 注册MQTT事件回调函数(关键步骤!用于处理连接、断开、接收消息等事件)// 参数说明:// - mqtt_handle:客户端句柄(指定为哪个客户端注册回调)// - ESP_EVENT_ANY_ID:监听所有MQTT相关事件(如连接成功、接收消息等)// - mqtt_event_callback:自定义的事件处理函数(收到事件后会自动调用)// - NULL:回调函数的参数(此处未使用)esp_mqtt_client_register_event(mqtt_handle,ESP_EVENT_ANY_ID,mqtt_event_callback,NULL);// 9. 启动MQTT客户端,正式发起与服务器的连接// 连接成功后,会触发MQTT_EVENT_CONNECTED事件,在回调函数中处理后续操作(如订阅主题)esp_mqtt_client_start(mqtt_handle);
}void app_main(void)
{// 1. 初始化NVS闪存(用于存储Wi-Fi配置等信息,必须先初始化)// ESP_ERROR_CHECK:检查函数返回值,若出错则终止程序并打印错误ESP_ERROR_CHECK(nvs_flash_init());//2. 初始化网络接口(创建网络协议栈,如TCP/IP)ESP_ERROR_CHECK(esp_netif_init());// 3. 创建默认事件循环(用于处理Wi-Fi、IP等事件的消息队列)ESP_ERROR_CHECK(esp_event_loop_create_default());// 4. 创建默认的STA模式网络接口(绑定Wi-Fi STA与TCP/IP协议栈)esp_netif_create_default_wifi_sta();// 5. 初始化Wi-Fi配置(使用默认配置,包含MAC地址、信道等基础参数)wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));// 应用Wi-Fi初始化配置s_wifi_connect_sem = xSemaphoreCreateBinary();// 6. 注册事件处理器(将事件与处理函数绑定)// 注册所有Wi-Fi事件(ESP_EVENT_ANY_ID)到wifi_event_handle函数esp_event_handler_register(WIFI_EVENT,ESP_EVENT_ANY_ID,wifi_event_handle,NULL);// 注册IP获取事件(IP_EVENT_STA_GOT_IP)到wifi_event_handle函数esp_event_handler_register(IP_EVENT,IP_EVENT_STA_GOT_IP,wifi_event_handle,NULL);// 7. 配置要连接的Wi-Fi参数(热点名称、密码、加密方式等)wifi_config_t wifi_config = {.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK,// 加密方式(WPA2-PSK,常见家用Wi-Fi加密).sta.pmf_cfg.capable = true,// 支持PMF.sta.pmf_cfg.required = false,// 不强制要求路由器支持PMF};// 清空SSID数组(避免残留垃圾数据)memset(&wifi_config.sta.ssid,0,sizeof(wifi_config.sta.ssid));// 将TEST_SSID(热点名称)复制到Wi-Fi配置中memcpy(wifi_config.sta.ssid,TEST_SSID,strlen(TEST_SSID));// 清空密码数组(避免残留垃圾数据)memset(&wifi_config.sta.password,0,sizeof(wifi_config.sta.password));// 将TEST_PASSWORD(密码)复制到Wi-Fi配置中memcpy(wifi_config.sta.password,TEST_PASSWORD,strlen(TEST_PASSWORD));// 8. 设置Wi-Fi工作模式为STA(客户端模式,连接其他热点)ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));// 应用Wi-Fi配置(将上面设置的热点信息传入Wi-Fi驱动)ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&wifi_config));// 启动Wi-Fi(开始执行连接流程)ESP_ERROR_CHECK(esp_wifi_start());xSemaphoreTake(s_wifi_connect_sem,portMAX_DELAY);mqtt_start();
}

10、输入以下指令对工程进行编译、下载、调试

idf.py flash monitor

在这里插入图片描述
11、打印这两句,说明连接MQTT服务器成功了
在这里插入图片描述
12、根据代码里所写,ESP32订阅的是/topic/mqttx_0823这个主题
在这里插入图片描述
13、回到MQTTX,向这个主题发布消息
在这里插入图片描述
14、查看ESP32终端打印,可以看到接收到了消息
在这里插入图片描述
15、现在尝试从ESP32发布消息,然后MQTTX客户端订阅下消息
在这里插入图片描述
16、MQTTX订阅主题要和ESP32发布主题一致
在这里插入图片描述
17、Qos指定为1
在这里插入图片描述
18、mqtt_test.c文件里写入如下代码

#include <stdio.h>
#include "mqtt_client.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "freertos/semphr.h"#define MQTT_ADDRESS   "mqtt://broker-cn.emqx.io"
#define MQTT_CLIENTID  "mqttx_esp321021"
#define MQTT_USERNAME  "panda"
#define MQTT_PASSWORD  "xxyy77.."#define MQTT_TOPIC1    "/topic/esp32_0823"       //ESP32往这个主题推送消息
#define MQTT_TOPIC2    "/topic/mqttx_0823"       //mqttx往这个主题推送消息#define TEST_SSID "Lymow"// 要连接的Wi-Fi热点名称(SSID)
#define TEST_PASSWORD "1415926535"// 要连接的Wi-Fi密码static esp_mqtt_client_handle_t mqtt_handle = NULL;static SemaphoreHandle_t s_wifi_connect_sem = NULL;#define TAG "mqtt"void mqtt_event_callback(void* event_handle_arg,esp_event_base_t event_base, int32_t event_id, void* event_data)
{// 将事件数据转换为 MQTT 事件专用结构体(方便获取事件详情)// esp_mqtt_event_handle_t 结构体中包含:主题、消息内容、消息长度、客户端句柄等信息esp_mqtt_event_handle_t data = (esp_mqtt_event_handle_t)event_data;// 根据不同的事件 ID,执行对应的处理逻辑switch (event_id){// 事件1:MQTT 客户端成功连接到服务器case MQTT_EVENT_CONNECTED:ESP_LOGI(TAG,"mqtt connected");// 连接成功后,立即订阅指定的 MQTT 主题(例如接收服务器/手机发来的指令)// 参数:客户端句柄、要订阅的主题(MQTT_TOPIC2)、QoS 等级(1表示确保消息至少到达一次)esp_mqtt_client_subscribe_single(mqtt_handle,MQTT_TOPIC2,1);break;// 事件2:MQTT 客户端与服务器断开连接case MQTT_EVENT_DISCONNECTED:ESP_LOGI(TAG,"mqtt disconnected");break;// 事件3:MQTT 客户端发送消息后,收到服务器的“发布确认”(表示服务器已收到消息)case MQTT_EVENT_PUBLISHED:ESP_LOGI(TAG,"mqtt published ack");break;// 事件4:MQTT 客户端订阅主题后,收到服务器的“订阅确认”(表示订阅成功)case MQTT_EVENT_SUBSCRIBED:ESP_LOGI(TAG,"mqtt subcribed ack");break;// 事件5:MQTT 客户端收到服务器发送的消息(来自已订阅的主题)case MQTT_EVENT_DATA:ESP_LOGI(TAG,"topic->%s",data->topic);ESP_LOGI(TAG,"payload->%s",data->data);break;default:break;}
}/*** Wi-Fi事件处理函数* 用于响应Wi-Fi连接状态变化、IP地址获取等事件* @param event_handler_arg: 事件处理参数(未使用)* @param event_base: 事件类型(如WIFI_EVENT、IP_EVENT)* @param event_id: 具体事件ID(如连接成功、断开连接)* @param event_data: 事件相关数据(未使用)*/
void wifi_event_handle(void* event_handler_arg,esp_event_base_t event_base,int32_t event_id,void* event_data)
{if(event_base == WIFI_EVENT)// 处理Wi-Fi相关事件(WIFI_EVENT类型){switch(event_id){case WIFI_EVENT_STA_START: //事件1:STA模式启动完成(Wi-Fi已准备好连接)esp_wifi_connect();// 调用连接函数,尝试连接到指定Wi-Fibreak;case WIFI_EVENT_STA_CONNECTED:// 事件2:STA成功连接到Wi-Fi热点ESP_LOGI(TAG,"esp32 connected to ap!");break;case WIFI_EVENT_STA_DISCONNECTED:// 事件3:STA与Wi-Fi热点断开连接esp_wifi_connect(); // 自动重试连接ESP_LOGI(TAG,"esp32 connect the ap faild! retry!");break;default:break;}}else if(event_base == IP_EVENT)//处理IP相关事件(IP_EVENT类型){switch(event_id){case IP_EVENT_STA_GOT_IP://事件:STA成功获取IP地址(此时可正常联网)ESP_LOGI(TAG,"esp32 get ip address");xSemaphoreGive(s_wifi_connect_sem);break;}}
}void mqtt_start(void)
{// 1. 定义并初始化MQTT客户端配置结构体(初始化为全0,避免垃圾数据)// esp_mqtt_client_config_t 是ESP-IDF定义的结构体,用于存储MQTT连接的所有参数esp_mqtt_client_config_t   mqtt_cfg = {0};// 2. 配置MQTT服务器的URI(统一资源标识符,包含协议和地址)mqtt_cfg.broker.address.uri = MQTT_ADDRESS;// 3. 配置MQTT服务器的端口号(默认1883为非加密端口,8883为加密端口)mqtt_cfg.broker.address.port = 1883;// 4. 配置客户端ID(用于在MQTT服务器中唯一标识当前ESP32设备,避免重复)mqtt_cfg.credentials.client_id = MQTT_CLIENTID;// 5. 配置连接MQTT服务器的用户名mqtt_cfg.credentials.username = MQTT_USERNAME;// 6. 配置连接MQTT服务器的密码(与用户名配套,用于身份验证)mqtt_cfg.credentials.authentication.password = MQTT_PASSWORD;// 7. 根据上述配置,初始化MQTT客户端,并获取客户端句柄(类似"连接ID",后续操作需用它)// mqtt_handle 是全局变量,用于保存客户端句柄,方便其他函数操作MQTT连接mqtt_handle = esp_mqtt_client_init(&mqtt_cfg);// 8. 注册MQTT事件回调函数(关键步骤!用于处理连接、断开、接收消息等事件)// 参数说明:// - mqtt_handle:客户端句柄(指定为哪个客户端注册回调)// - ESP_EVENT_ANY_ID:监听所有MQTT相关事件(如连接成功、接收消息等)// - mqtt_event_callback:自定义的事件处理函数(收到事件后会自动调用)// - NULL:回调函数的参数(此处未使用)esp_mqtt_client_register_event(mqtt_handle,ESP_EVENT_ANY_ID,mqtt_event_callback,NULL);// 9. 启动MQTT客户端,正式发起与服务器的连接// 连接成功后,会触发MQTT_EVENT_CONNECTED事件,在回调函数中处理后续操作(如订阅主题)esp_mqtt_client_start(mqtt_handle);
}void app_main(void)
{// 1. 初始化NVS闪存(用于存储Wi-Fi配置等信息,必须先初始化)// ESP_ERROR_CHECK:检查函数返回值,若出错则终止程序并打印错误ESP_ERROR_CHECK(nvs_flash_init());//2. 初始化网络接口(创建网络协议栈,如TCP/IP)ESP_ERROR_CHECK(esp_netif_init());// 3. 创建默认事件循环(用于处理Wi-Fi、IP等事件的消息队列)ESP_ERROR_CHECK(esp_event_loop_create_default());// 4. 创建默认的STA模式网络接口(绑定Wi-Fi STA与TCP/IP协议栈)esp_netif_create_default_wifi_sta();// 5. 初始化Wi-Fi配置(使用默认配置,包含MAC地址、信道等基础参数)wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));// 应用Wi-Fi初始化配置s_wifi_connect_sem = xSemaphoreCreateBinary();// 6. 注册事件处理器(将事件与处理函数绑定)// 注册所有Wi-Fi事件(ESP_EVENT_ANY_ID)到wifi_event_handle函数esp_event_handler_register(WIFI_EVENT,ESP_EVENT_ANY_ID,wifi_event_handle,NULL);// 注册IP获取事件(IP_EVENT_STA_GOT_IP)到wifi_event_handle函数esp_event_handler_register(IP_EVENT,IP_EVENT_STA_GOT_IP,wifi_event_handle,NULL);// 7. 配置要连接的Wi-Fi参数(热点名称、密码、加密方式等)wifi_config_t wifi_config = {.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK,// 加密方式(WPA2-PSK,常见家用Wi-Fi加密).sta.pmf_cfg.capable = true,// 支持PMF.sta.pmf_cfg.required = false,// 不强制要求路由器支持PMF};// 清空SSID数组(避免残留垃圾数据)memset(&wifi_config.sta.ssid,0,sizeof(wifi_config.sta.ssid));// 将TEST_SSID(热点名称)复制到Wi-Fi配置中memcpy(wifi_config.sta.ssid,TEST_SSID,strlen(TEST_SSID));// 清空密码数组(避免残留垃圾数据)memset(&wifi_config.sta.password,0,sizeof(wifi_config.sta.password));// 将TEST_PASSWORD(密码)复制到Wi-Fi配置中memcpy(wifi_config.sta.password,TEST_PASSWORD,strlen(TEST_PASSWORD));// 8. 设置Wi-Fi工作模式为STA(客户端模式,连接其他热点)ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));// 应用Wi-Fi配置(将上面设置的热点信息传入Wi-Fi驱动)ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&wifi_config));// 启动Wi-Fi(开始执行连接流程)ESP_ERROR_CHECK(esp_wifi_start());xSemaphoreTake(s_wifi_connect_sem,portMAX_DELAY);mqtt_start();int count = 0;while(1){char count_str[32];snprintf(count_str,sizeof(count_str),"{\"count\":%d}",count);esp_mqtt_client_publish(mqtt_handle,MQTT_TOPIC1,count_str,strlen(count_str),1,0);count++;vTaskDelay(pdMS_TO_TICKS(2000));}
}

19、其实就是在末尾加上这一小段循环发送逻辑
在这里插入图片描述

20、ESP32发布成功
在这里插入图片描述
21、MQTTX端也收到了
在这里插入图片描述

http://www.dtcms.com/a/512411.html

相关文章:

  • 网站建设敬请期待图片素材wordpress 获取菜单id
  • 做网站用什么源码最好wordpress建站优势
  • 网站图标 代码微信开发网站建设程序
  • 修改 Docker 容器中 MySQL 8.0 默认编码为 utf8mb4_unicode_ci
  • C# Dictionary 线程安全指南:多线程下操作 Dictionary<string, DateTime> 的加锁策略
  • 企业im聊天软件支持什么功能,应该怎么选?
  • 【Linux系统编程】权限的概念
  • githup网站建设网站营销站点有你想
  • 差分|递归
  • 如何通过纺织服装MES系统提升生产效率和管理水平?
  • 打通智慧高速核心系统:湖南某新建项目的收费、通信、监控一体化实践
  • 河南网站建设优化技术福建省建设干部网站
  • 智能机器人:今年双十一“减人增效”的AI智能客服机器人方案
  • 市环保局网站建设方案建站公司哪家好 知道万维科技
  • 房产中介网站建设的目的cms 美容网站 模版
  • vue开发中,如果出现了反显数据有问题(后端明明返回的有数据,但就是不反显,没有值)是什么原因
  • 【2025-系统规划与管理师】第12章:信息系统服务管理
  • C++---嵌套类型(Nested Types)封装与泛型的基石
  • Floyd判圈算法(Floyd Cycle Detection Algorithm)
  • 网站建设支付宝温州建设学校网站
  • 深圳网站制作 优选灵点网络前端开发语言有哪些
  • .NET8 通过自定义类映射appsettings.json 文件某个节点的配置
  • 25-DAPO: An Open-Source LLM Reinforcement LearningSystem at Scale
  • 6个网站建设网站设置保存登录密码怎么取消
  • 网站建设和维护的职责网站开发工程师有证书考试吗
  • 网站建设计算机人员招聘策划营销方案
  • 《i.MX6ULL LED 驱动实战:内核模块开发与 GPIO 控制》
  • Effective Java学习笔记:用静态工厂方法代替构造器(第一条)
  • TDengine 数学函数 POW 用户手册
  • AI大模型“战国策”:主流LLM平台简单介绍