IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> ESP32学习笔记(46)——MQTT客户端 -> 正文阅读

[嵌入式]ESP32学习笔记(46)——MQTT客户端

一、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) Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS 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-3
            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);
            // MQTT Client订阅主题函数,主题是/topic/qos0,服务质量qos0
            msg_id = esp_mqtt_client_subscribe(client, "/topic/qos0", 0);
            ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
            // MQTT Client订阅主题函数,主题是/topic/qos1,服务质量qos1
            msg_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的url
    esp_mqtt_client_config_t mqtt_cfg = {
        .host = "192.168.61.67",    // MQTT服务器地址
        .port = 1883,               // MQTT服务器端口
    };
#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 /* 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服务器

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-09-09 11:56:09  更:2021-09-09 11:57:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 0:44:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码