一、MQTT简介

1.1 实现方式

实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。

MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:

Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);

payload,可以理解为消息的内容,是指订阅者具体要使用的内容。

  • MQTT服务器的主要工作是数据分发,没有数据保存功能。
  • 可以订阅自己发布的主题,服务器就是回发测试。
  • MQTT让逻辑变得更清晰,需要什么订阅什么。
  • 走标准化流程,解放了私有协议制定、实现、调试、测试一整套复杂的流程。

1.2 ESP-MQTT

ESP-MQTT 是 MQTT 协议客户端的实现(MQTT 是轻量级的发布/订阅消息协议)。

  • 支持 MQTT over TCP、SSL with mbedtls、MQTT over Websocket、MQTT over Websocket Secure。
  • 使用 URI 轻松设置
  • 多个实例(一个应用程序中有多个客户端)
  • 支持订阅、发布、身份验证、最后遗嘱消息、保持活动 ping 和所有 3 个 QoS 级别(它应该是一个功能齐全的客户端)。

ESP-IDF 编程指南——ESP-MQTT

二、API说明

以下 MQTT 客户端接口位于 components/mqtt/esp-mqtt/include/mqtt_client.h

2.1 esp_mqtt_client_init

2.2 esp_mqtt_client_register_event

2.3 esp_mqtt_client_start

2.4 esp_mqtt_client_publish

2.5 esp_mqtt_client_subscribe

2.6 esp_mqtt_client_unsubscribe

三、MQTT客户端

3.1 主要流程

3.2 配置MQTT参数

首先,要定义一个 MQTT 客户端配置结构体,最小配置即填入 MQTT 服务器的 URL 即可。

esp_mqtt_client_config_t mqtt_cfg = {.uri = CONFIG_BROKER_URL,
};

esp_mqtt_client_config_t 结构体如下:

typedef struct {mqtt_event_callback_t event_handle; /*回调*/const char *host; /*!< MQTT 服务器域名(ipv4 as string)*/const char *uri; /*!< MQTT 服务器域名 */uint32_t port; /*!< MQTT服务器端口*/const char *client_id; /*MQTT Client的名字默认是ESP32_加上MAC后3hex*/const char *username; /*MQTT用户名*/const char *password; /*MQTT密码*/const char *lwt_topic; /*!< LWT主题,默认为空*/const char *lwt_msg; /*!< LWT信息,默认为空*/int lwt_qos; /*!< LWT消息质量*/int lwt_retain; /*!< LWT保留消息标志*/int lwt_msg_len; /*!< LWT消息长度*/int disable_clean_session; /*!< mqtt clean session,默认为真*/int keepalive; /*MQTT心跳,默认120秒 */bool disable_auto_reconnect; /*错误,断开后重连,true不连*/void *user_context; /*用户信息 */int task_prio; /*!< MQTT任务优先级,默认为5,可以在make menuconfig中修改*/int task_stack; /*!< MQTT 任务堆栈大小,默认6144 bytes,可以在make menuconfig中修改*/int buffer_size; /*!< MQTT收发缓存,默认1024 */const char *cert_pem; /*指向用于服务器验证(使用SSL)的PEM格式的证书数据的指针,默认值为空,不需要验证服务器 */const char *client_cert_pem; /*指向用于SSL相互身份验证的PEM格式的证书数据的指针,默认值为空,如果不需要相互身份验证,则不需要。如果不为空,还必须提供“客户机密钥”。*/const char *client_key_pem; /*指向用于SSL相互身份验证的PEM格式的私钥数据的指针,默认值为空,如果不需要相互身份验证,则不需要。如果不为空,还必须提供“client-cert-pem”。*/esp_mqtt_transport_t transport; /*覆盖URI传输*/
} esp_mqtt_client_config_t;

3.3 初始化MQTT客户端

然后通过 esp_mqtt_client_init() 获取一个 MQTT 客户端结构体指针,参数是 MQTT 客户端配置结构体。

esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);

3.4 注册MQTT事件

默认情况下,MQTT 客户端使用事件循环库来发布相关的 MQTT 事件(已连接,已订阅,已发布等)。

所以我们要注册一个 MQTT 事件,填入 MQTT 事件处理函数 mqtt_event_handler()

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);mqtt_event_handler_cb(event_data);
}
  • 第一个参数为MQTT客户端结构体,
  • 第二个是事件ID对应的事件类型,
  • 第三个参数即事件处理函数,
  • 第四个参数为事件处理函数的参数。

3.5 开启MQTT客户端

esp_mqtt_client_start(client);

3.6 MQTT事件处理

static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{esp_mqtt_client_handle_t client = event->client;int msg_id;// your_context_t *context = event->context;switch (event->event_id) {case MQTT_EVENT_CONNECTED:ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");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:ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");break;case MQTT_EVENT_SUBSCRIBED: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:ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);break;case MQTT_EVENT_PUBLISHED:ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);break;case MQTT_EVENT_DATA: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:ESP_LOGI(TAG, "MQTT_EVENT_ERROR");break;default:ESP_LOGI(TAG, "Other event id:%d", event->event_id);break;}return ESP_OK;
}

四、示例代码

根据 examples\protocols\mqtt\tcp 中的例程修改
根据服务器地址修改.host = "192.168.61.67",

/* MQTT (over TCP) ExampleThis example code is in the Public Domain (or CC0 licensed, at your option.)Unless required by applicable law or agreed to in writing, thissoftware is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES ORCONDITIONS OF ANY KIND, either express or implied.
*/#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.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";static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{// 获取MQTT客户端结构体指针esp_mqtt_client_handle_t client = event->client;int msg_id;// your_context_t *context = event->context;// 通过事件ID来分别处理对应的事件switch (event->event_id) {case MQTT_EVENT_CONNECTED:    // MQTT连上事件ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");// MQTT Client发布主题函数,主题是/topic/qos1,服务质量qos1,发布的数据是data-3msg_id = esp_mqtt_client_publish(client, "/topic/qos1", "data_3", 0, 1, 0);ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);// MQTT Client订阅主题函数,主题是/topic/qos0,服务质量qos0msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);// MQTT Client订阅主题函数,主题是/topic/qos1,服务质量qos1msg_id = esp_mqtt_client_subscribe(client, "/topic/qos1", 1);ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);// MQTT Client取消订阅主题函数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");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:ESP_LOGI(TAG, "MQTT_EVENT_ERROR");break;default:ESP_LOGI(TAG, "Other event id:%d", event->event_id);break;}return ESP_OK;
}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);mqtt_event_handler_cb(event_data);
}static void mqtt_app_start(void)
{// 1、定义一个MQTT客户端配置结构体,输入MQTT的urlesp_mqtt_client_config_t mqtt_cfg = {.host = "192.168.61.67",    // MQTT服务器地址.port = 1883,               // MQTT服务器端口};
#if CONFIG_BROKER_URL_FROM_STDINchar 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 /* CONFIG_BROKER_URL_FROM_STDIN */// 2、通过esp_mqtt_client_init获取一个MQTT客户端结构体指针,参数是MQTT客户端配置结构体esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);// 3、注册MQTT事件esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);// 4、开启MQTT功能esp_mqtt_client_start(client);
}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_TCP", ESP_LOG_VERBOSE);esp_log_level_set("TRANSPORT_SSL", 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());/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.* Read "Establishing Wi-Fi or Ethernet Connection" section in* examples/protocols/README.md for more information about this function.*/ESP_ERROR_CHECK(example_connect());mqtt_app_start();
}

五、搭建本地MQTT服务器

EMQ官网下载:https://www.emqx.com/zh/downloads?product=broker

  • 下载EMQ X开源版
  • 解压后进入 emqx-windows-4.3.8\emqx\bin 目录
  • Shift+右键在此处打开 Powershell 窗口,输入命令 emqx start
  • 打开浏览器,输入 http://127.0.0.1:18083/,账号 admin,密码 public,进入管理界面
  • 工具 - Websocket,选择连接
  • 订阅主题和发布消息

六、运行测试

配置连接方式:

选择WIFI连接方式,并修改要连接路由器的SSID和密码

调试打印:

服务器查看:


• 由 Leung 写于 2021 年 9 月 8 日

• 参考:第二十一章 ESP32开发MQTT Client ESP-IDF
    ESP32学习笔记(6)MQTT应用
    ESP32开发之路(9)—ESP32连接到MQTT服务器

ESP32学习笔记(46)——MQTT客户端相关推荐

  1. MQTT学习笔记——Yeelink MQTT服务 使用mqtt.js和paho-mqtt

    0 前言 2014年8月yeelink推出基于MQTT协议的开关类型设备控制API,相比于基于HTTP RESTful的轮训方式,通过订阅相关主题消息,可以远程控制类应用实时性更好.本文使用两种方式实 ...

  2. ESP32 单片机学习笔记 - 08 - WebSocket客户端

    前言,终于要到网络模型的最后一层,第四层,应用层,http.websocket的实践了. 文章目录 ESP32 单片机学习笔记 - 08 - WebSocket客户端 一.应用层协议 科普概念 二.编 ...

  3. 关于esp32蓝牙模块的使用——esp32学习笔记

    关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 关于esp32蓝牙模块的使用--esp32学习笔记 零.前言 一.经典蓝牙BT 二.低功耗蓝牙B ...

  4. ESP32学习笔记(30)——BLE GATT服务端自定义服务和特征

    一.简介 1.1 低功耗蓝牙(BLE)协议栈 链路层(LL) 控制设备的射频状态,有五个设备状态:待机.广播.扫描.初始化和连接. 广播 为广播数据包,而 扫描 则是监听广播. GAP通信中角色,中心 ...

  5. ESP32学习笔记(14)——HTTP服务器

    一.HTTP简介 HTTP(Hyper Text Transfer Protocol) 超文本传输协议,是一种建立在 TCP 上的无状态连接,整个基本的工作流程是客户端发送一个 HTTP 请求,说明客 ...

  6. ESP32学习笔记(1)——搭建环境、编译烧写(Windows+VS Code)

    Espressif-IDE 环境搭建参看 ESP32学习笔记(50)--搭建环境.编译烧写(Windows+Espressif-IDE) 一.搭建环境 1.1 官方资料 ESP-IDF 编程指南 1. ...

  7. ESP32学习笔记(27)——BLE GAP主机端扫描

    一.背景 1.1 低功耗蓝牙(BLE)协议栈 链路层(LL) 控制设备的射频状态,有五个设备状态:待机.广播.扫描.初始化和连接. 广播 为广播数据包,而 扫描 则是监听广播. GAP通信中角色,中心 ...

  8. ESP32学习笔记(七) 复位和时钟

    ESP32学习笔记(七) 复位和时钟 目录: ESP32学习笔记(一) 芯片型号介绍 ESP32学习笔记(二) 开发环境搭建 VSCode+platformio ESP32学习笔记(三) 硬件资源介绍 ...

  9. ESP32学习笔记(49)——RFID RC522使用

    一.简介 MF RC522 是应用于 13.56MHz 非接触式通信中高集成度读写卡系列芯片中的一员.是 NXP 公司针对"三表"应用推出的一款低电压.低成本.体积小的非接触式读写 ...

  10. ESP32学习笔记(20)——SPI(从机)接口使用

    一.SPI简介 SPI(Serial Peripheral Interface) 协议是由摩托罗拉公司提出的通讯协议,即串行外围设备接口,是一种高速全双工的通信总线.它被广泛地使用在 ADC.LCD ...

最新文章

  1. 如何在 Linux 上使用 Vundle 管理 Vim 插件
  2. python快速处理ppt_人生苦短,我用 Python 之快速遍历 PPT
  3. 新风系统风速推荐表_新风系统风速标准及与噪音的关系
  4. 一起学习C语言:C语言循环结构(一)
  5. JAVA 定义全局常量码表_【Java基础】java常量是什么?
  6. MySql:Unknown collation: ‘utf8mb4_0900_ai_ci‘
  7. win7安装mysql8.0.15教程_MySQL-mysql 8.0.15安装教程
  8. 字符匹配算法之KMP
  9. Gof 设计模式 完结
  10. 当前常见游戏服务器引擎
  11. mysql 报表工具_Navicat功能:报表创建工具
  12. 联想服务器ts系列介绍,联想服务器ThinkServerTS230.ppt
  13. mac安装win10_mac磁盘空间 mac安装win10分割多少磁盘空间合适
  14. 离散数学学习笔记----命题逻辑的推理理论
  15. 大会没看够?2021 Google 开发者大会总结看这里
  16. xp升级win7_微软正式停止支持Win7,部分用户拒绝升级系统,原因令人信服
  17. 蒙特卡罗方法采样算法
  18. 一款vista边栏Gadgets汉英翻译(翻译14种语言的边栏工具下载)
  19. JAVA—— JQuery
  20. 送给她一朵漂亮的百合花(Matlab代码实现)

热门文章

  1. 微信小程序傻瓜制作_从15款工具中精选出4款,最靠谱的微信小程序制作软件!...
  2. smartadmin官网_smartadmin api_smartadmin 下载
  3. angular框架的SmartAdmin模板 如何请求后台数据
  4. Visual Studio 2015的破解密钥
  5. 近世代数 [计算机数学专题(3)]
  6. uds 诊断协议的bootloader开发
  7. sqluldr2的介绍
  8. uefi模式安装win7出现winload.efi和oxc0000428错误的另类解决办法
  9. matlab中如何调用lm算法,lm算法的matlab实现
  10. 2016年移动广告聚合平台浅析