本文内容
1)使用MQTT_TCP例程,ESP32通过MQTT协议与MQTT本地服务器(EMQX)进行通信。
2)如何搭建EMQX MQTT本地服务器。
3)如何使用MQTT.fx客户端调试工具或MQTTX客户端调试工具。
开发环境
1)系统平台:windows7或windows10。
2)开发板:乐鑫开发板ESP32-DevKitC-V4(模组是ESP32WROOM32E)。
3)集成开发环境:VSCODE+ESP-IDF插件,见以下链接
图文手把手教程(史上最强):windows下ESP32集成开发环境搭建和HelloWorld显示(乐鑫官方推荐方法-使用VSCode安装ESP-IDF插件)
一、准备工作
需要下载工具并安装,如下:
1.下载MQTT.fx客户端(版本v1.7.1):http://mqttfx.jensd.de/index.php/download,官网找不到下载入口?
这里提供博客下载:https://download.csdn.net/download/felix_tao/86248840
2.下载MQTT X客户端(版本v1.8.0.win64):https://mqttx.app/zh
二、搭建EMQX本地MQTT服务器
1.下载EMQX开源版,官方网址:https://www.emqx.com/zh/try?product=broker
选择版本号v5.0.3,系统:windows,然后点击“免费下载”。
2.将下载好的 emqx-5.0.3-windows-amd64.tar.gz,进行解压,我是解压到E盘根目录。
注意:根目录下最好不要有中文。
3.打开命令行工具,进入目录运行EMQX服务。
电脑左下角,右键开始->运行->输入cmd,确定->打开命令行工具,操作如下:
1)切换到E盘:输入e:
2)进入到EMQX的bin目录:输入cd E:\emqx-5.0.3-windows-amd64\bin
3)启动EMQX服务器,输入emqx start,如需停止服务器,输入emqx stop
4)查看EMQX服务器状态,先进入bin目录:输入cd E:\emqx-5.0.3-windows-amd64\bin
5)接着输入:emqx_ctl status,可以看到EMQX服务器已启动
4.登陆EMQX服务器,验证是否可用。
1)打开浏览器,输入地址:http://127.0.0.1:18083/
2)输入用户名:admin,输入密码:public,点击Login
3)更改界面为中文。
至此,本地EMQX本地MQTT服务器已搭建成功。
三、MQTT.fx客户端简单说明,如下图所示。
四、MQTT.fx客户端与WebSocket 客户端互发消息。
1.使用MQTT.fx连接到EMQX本地服务器。
1)General设置如下图所示。
2)User Credentials设置。
以上设置完毕后,点击Apply保存,然后关闭退出。
3)进行连接,亮绿灯,表示连接成功。
2.MQTT.fx发布和订阅主题,以ESP32例程的topic主题为例。
1)MQTT.fx客户端发布主题:/topic/qos1,服务质量等级选择QoS1,消息内容如下:
{ "msg": "hello I am mqtt.fx" }
2)MQTT.fx客户端订阅主题:/topic/qos0,服务质量等级选择QoS0
3.登陆EMQX服务器管理页面,连接WebSocket 客户端到服务器,并发布和订阅主题。
1)打开WebSocket 客户端,输入IP地址、端口号,用户名和密码,即可连接。
2)WebSocket客户端订阅主题:/topic/qos1,服务质量等级选择QoS1。
3)WebSocket客户端发布主题:/topic/qos0,服务质量等级选择QoS0,消息内容如下:
{ "msg": "hello I am emqx web" }
4.MQTT.fx客户端与WebSocket 客户端互发消息。
1)MQTT.fx客户端发送,WebSocket 客户端接收。
2)WebSocket 客户端发送,MQTT.fx客户端接收。
至此, MQTT.fx客户端与WebSocket 客户端互发消息成功,服务器是EMQX。
五、ESP32连接到EMQX服务器,与MQTT.fx客户端互发消息。
1.VSCODE+ESP-IDF插件开发环境的搭建,见本文的开头的链接。
2.打开示例项目MQTT_TCP:VSCODE中->"查看"->”命令面板“->输入:Show Examples projects->选择Use current ESP-IDF(E:\ESP32-IDF\esp\esp-idf)->弹出示例ESP-IDF Examples,选择mqtt->tcp->Create project using example tcp->选择示例保存的路径。
例如:E:\ESP32-IDF\project-example,因ESP-IDF框架与示例是分离的,所以示例保存的路径可以随意,但需要注意:路径不能有中文和空格,否则报错。
创建完后,如下图所示。
关于示例的说明,请阅读官方的注释。
打开E:\ESP32-IDF\esp\esp-idf\examples\protocols->README,如下图所示。
README的内容如下:
对示例有个大概了解后,接下来需要修改代码。
3.修改示例代码。
1)修改wifi配置,这里修改为自己的wifi帐号和密码即可。
wifi_config_t wifi_config = { .sta = { .ssid = "Xiaomi_tao", .password = "123456", }, };
2)修改MQTT配置,如果是使用EMQX本地服务器,即只需要修改MQTT服务器IP即可。
注意:这里的IP是指自己电脑的IPv4地址,如果是win7系统,ESP32连不上EMQX服务器,则需要将IPv4地址修改为静态固定IP地址,我这里是win10系统,不改固定IP地址也可以连上。
esp_mqtt_client_config_t mqtt_cfg = { .host = "192.168.31.107", //MQTT服务器IP .event_handle = mqtt_event_handler, //MQTT事件 .port=1883, //端口 .username = "admin", //用户名 .password = "public", //密码 // .user_context = (void *)your_context };
3.修改app_main.c,如下:
#include #include #include #include #include "esp_wifi.h"#include "esp_system.h"#include "nvs_flash.h"#include "esp_event.h"#include "esp_netif.h"#include "esp_event_loop.h"#include "tcpip_adapter_types.h"//#include "protocol_examples_common.h"#include "freertos/FreeRTOS.h"#include "freertos/task.h"#include "freertos/semphr.h"#include "freertos/queue.h"#include "freertos/event_groups.h"#include "lwip/sockets.h"#include "lwip/dns.h"#include "lwip/netdb.h"#include "esp_log.h"#include "mqtt_client.h"static const char *TAG = "MQTT_EXAMPLE";//wifi连上事件static EventGroupHandle_t wifi_event_group;//mqtt连上事件static EventGroupHandle_t mqtt_event_group;const static int CONNECTED_BIT = BIT0;esp_mqtt_client_handle_t mqttclient;// static void log_error_if_nonzero(const char *message, int error_code)// {// if (error_code != 0) {// ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);// }// }//事件处理函数static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data){ ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id); esp_mqtt_event_handle_t event = event_data; esp_mqtt_client_handle_t client = event->client; int msg_id; switch ((esp_mqtt_event_id_t)event_id) { case MQTT_EVENT_CONNECTED: //MQTT连上事件 ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED"); xEventGroupSetBits(mqtt_event_group, CONNECTED_BIT); //发布主题 // printf("Connecting to MQTT broker OK"); // msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0); // ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); // //发送订阅 // msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0); // ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); //发送订阅 msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1); ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id); // //取消订阅 // msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1"); // ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id); break; case MQTT_EVENT_DISCONNECTED: //MQTT断开连接事件 ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); //mqtt连上事件 xEventGroupClearBits(mqtt_event_group, CONNECTED_BIT); break; case MQTT_EVENT_SUBSCRIBED: //MQTT发送订阅事件 ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id); msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0); ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id); break; case MQTT_EVENT_UNSUBSCRIBED: //MQTT取消订阅事件 ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id); break; case MQTT_EVENT_PUBLISHED: //MQTT发布事件 ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id); break; case MQTT_EVENT_DATA: //MQTT接受数据事件 ESP_LOGI(TAG, "MQTT_EVENT_DATA"); printf("TOPIC=%.*s\r\n", event->topic_len, event->topic); printf("DATA=%.*s\r\n", event->data_len, event->data); break; case MQTT_EVENT_ERROR: //MQTT错误事件 ESP_LOGI(TAG, "MQTT_EVENT_ERROR"); xEventGroupClearBits(mqtt_event_group, CONNECTED_BIT); // if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) { // log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err); // log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err); // log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno); // ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno)); //} break; default: ESP_LOGI(TAG, "Other event id:%d", event->event_id); break; }}//wifi状态机static esp_err_t wifi_event_handler(void *ctx, system_event_t *event){ switch (event->event_id) { case SYSTEM_EVENT_STA_START: esp_wifi_connect(); break; case SYSTEM_EVENT_STA_GOT_IP: xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); break; case SYSTEM_EVENT_STA_DISCONNECTED: esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); break; default: break; } return ESP_OK;}//wifi初始化static void wifi_init(void){ tcpip_adapter_init(); wifi_event_group = xEventGroupCreate(); mqtt_event_group = xEventGroupCreate(); ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_handler, NULL)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); wifi_config_t wifi_config = { .sta = { .ssid = "Xiaomi_tao", .password = "123456", }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); ESP_LOGI(TAG, "start the WIFI SSID:[%s]", wifi_config.sta.ssid); ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "Waiting for wifi"); //等待wifi连上 xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);}//mqtt初始化static void mqtt_app_start(void){ esp_mqtt_client_config_t mqtt_cfg = { .host = "192.168.31.107", //MQTT服务器IP .event_handle = mqtt_event_handler, //MQTT事件 .port=1883, //端口 .username = "admin", //用户名 .password = "public", //密码 // .user_context = (void *)your_context };#if CONFIG_BROKER_URL_FROM_STDIN char line[128]; if (strcmp(mqtt_cfg.uri, "FROM_STDIN") == 0) { int count = 0; printf("Please enter url of mqtt broker\n"); while (count < 128) { int c = fgetc(stdin); if (c == '\n') { line[count] = '\0'; break; } else if (c > 0 && c < 127) { line[count] = c; ++count; } vTaskDelay(10 / portTICK_PERIOD_MS); } mqtt_cfg.uri = line; printf("Broker url: %s\n", line); } else { ESP_LOGE(TAG, "Configuration mismatch: wrong broker url"); abort(); }#endif printf("esp_mqtt_client_start\r\n"); //通过esp_mqtt_client_init获取一个MQTT客户端结构体指针,参数是MQTT客户端配置结构体 mqttclient = esp_mqtt_client_init(&mqtt_cfg); //注册MQTT事件 esp_mqtt_client_register_event(mqttclient, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); //开启MQTT功能 esp_mqtt_client_start(mqttclient); //等mqtt连上 xEventGroupWaitBits(mqtt_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);}void app_main(void){ ESP_LOGI(TAG, "[APP] Startup.."); ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size()); ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version()); esp_log_level_set("*", ESP_LOG_INFO); esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE); esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE); esp_log_level_set("TRANSPORT_BASE", ESP_LOG_VERBOSE); esp_log_level_set("esp-tls", ESP_LOG_VERBOSE); esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE); esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE); // ESP_ERROR_CHECK(nvs_flash_init()); // ESP_ERROR_CHECK(esp_netif_init()); // ESP_ERROR_CHECK(esp_event_loop_create_default()); //ESP_ERROR_CHECK(example_connect()); nvs_flash_init(); wifi_init(); mqtt_app_start(); while (1) { printf("system is running!\r\n"); //发布主题 esp_mqtt_client_publish(mqttclient, "/topic/qos0", "Hello MQTT,I am ESP32", 0, 0, 0); vTaskDelay(1000 / portTICK_PERIOD_MS); }}
4.修改完毕后,编译下载,打开串口调试助手,可以看到连接EMQX服务器成功。
注意:EMQX服务器,每次开机后,都要重新启动,\bin目示下,命令:emqx start。
5.打开EMQX服务器网页http://127.0.0.1:18083/,可以看到ESP32与MQTT.fx都连接成功。
6.ESP32与MQTT.fx客户端互通消息。
1)ESP32主动发送消息给MQTT.fx客户端。
2)MQTT.fx客户端发送消息给ESP32。
7.ESP32与MQTTX客户端互通消息,与MQTT.fx客户端用法是一样的。
1)修改中文版本。
2)填写相关信息。
2)ESP32主动发送消息给MQTTX客户端。
3)MQTTX客户端发送消息给ESP32。
至此,本文内容全部完毕,愉快的玩耍吧!
完整的例程代码下载:https://download.csdn.net/download/felix_tao/86248836
使用例程,报错怎么办,解决办法如下:
1)打开VSCODE报错,c_cpp_properties.json无法找到E:\\ESP32-IDF...
解决办法:点击.vscode->c_cpp_properties.json,修改盘符即可,例如将E盘改为D盘。
2)编译工程报错:
[0/1] Re-running CMake...
FAILED: build.ninja
CreateProcess failed: The system cannot find the file specified.
ninja: error: rebuilding 'build.ninja': subcommand failed
解决方法:清除编译产生的所有文件,左下角->点击垃圾桶图标(ESP-IDF Full Clean)->清除后,接着重新编译即OK。
本文参考了以下博客,鸣谢!
第二十一章 ESP32开发MQTT Client ESP-IDF_开源一小步的博客-CSDN博客_esp32 mqtt
mqtt.fx | 一款超级好用的Mqtt客户端软件(下载、安装、使用详解)_Mculover666的博客-CSDN博客_mqtt.fx
验证EMQ X服务器的搭建以及MQTTX的简单使用_哈哈浩丶的博客-CSDN博客
来源地址:https://blog.csdn.net/felix_tao/article/details/125882339