• ESP32 之 ESP-IDF 教学(十六)——MQTT客户端(ESP-MQTT)


    本文章 来自原创专栏《ESP32教学专栏 (基于ESP-IDF)》,讲解如何使用 ESP-IDF 构建 ESP32 程序,发布文章并会持续为已发布文章添加新内容! 每篇文章都经过了精打细磨!

    ↓↓↓通过下方对话框进入专栏目录页↓↓↓
    CSDN 请求进入目录       _ O x

    是否进入ESP32教学导航(基于ESP-IDF)?

           确定

    一、MQTT简介

    MQTT协议(消息队列遥测传输协议),是一种基于publish/subscribe(即发布与订阅)模式的轻量通讯协议。此协议基于TCP/IP。

    MQTT是一种低开销、低带宽占用的即时通讯协议。轻量、简单、开放和易于实现的。其最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。使其在物联网、小型设备、移动应用等方面有较广泛的应用。如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。

    二、ESP-MQTT介绍

    1、简介

    ESP-MQTTMQTT协议的一个实现

    特性:

    • 支持基于 TCPSSL(使用mbedtls)、WebsocketWebsocket Secure的MQTT实现。
    • 配置方便,代码极少
    • 面向对象设计,多实例
    • 功能齐全的客户端实现。支持订阅、发布、认证、遗嘱、保持ping和3个QoS级别

    2、配置

    ① URI

    目前支持的协议:

    协议URI schemes
    TCPmqtt
    SSLmqtts
    Websocketws
    Websocket Securewss

    URI示例
    mqtt://mqtt.eclipseprojects.io:基于TCP协议的MQTT,端口号为默认1883
    mqtt://mqtt.eclipseprojects.io:1884:同上,端口号自定义为1884
    mqtt://用户名:密码@mqtt.eclipseprojects.io:1884:同上,再加上用户名和密码

    mqtts://mqtt.eclipseprojects.io:基于SSL,端口默认8883,也可以自定义端口号如上
    ws://mqtt.eclipseprojects.io:80/mqtt:基于WebSocket
    wss://mqtt.eclipseprojects.io:443/mqtt:基于Websocket Secure

    一个简单的示例:

    // 创建MQTT配置,最简单的配置只需要填写URI
    const esp_mqtt_client_config_t mqtt_cfg = {
        .uri = "mqtt://mqtt.eclipseprojects.io",
    };
    // 传递配置,创建一个MQTT客户端
    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
    
    // 注册事件
    // 注:每个mqtt客户端都携带一个自己的事件循环,esp-mqtt并没有使用默认事件循环
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, EventHandle函数, client);
    
    // 启动mqtt客户端
    esp_mqtt_client_start(client);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ② SSL

    待续

    ③ 遗嘱

    MQTT允许在客户非正常断开连接时,通过遗嘱(LWT)消息通知其他客户。

    配置在esp_mqtt_client_config_t结构体中lwt_开头的配置:

    • lwt_topic: 遗嘱topic。
    • lwt_msg: 遗嘱内容。
    • lwt_msg_len:遗嘱内容长度。
      如果遗嘱字符串不是标准C字符串(以’\0’结尾),则此项必填。
    • lwt_qos: 遗嘱的消息服务质量
    • lwt_retain: 消息保留标志。

    ④ 其余配置

    其余配置请查看官方文档中对于esp_mqtt_client_config_t的说明,或直接查看其源码中的注释。

    官方文档对于esp_mqtt_client_config_t的解释:https://docs.espressif.com/projects/esp-idf/zh_CN/v4.4.2/esp32/api-reference/protocols/mqtt.html#other-configuration-parameters


    除此之外,在idf.py menuconfig里还有几个配置项。在Component config -> ESP-MQTT Configuration中。详细信息:https://docs.espressif.com/projects/esp-idf/zh_CN/v4.4.2/esp32/api-reference/protocols/mqtt.html#change-settings-in-project-configuration-menu

    3、MQTT客户端基础API

    4、事件

    从上文的小示例的注释中写道,每个client都携带一个事件循环event loop。因此事件操作本质上就是操作这个事件循环。

    ESP-IDF为我们提供了简化的API来操作这个事件循环。

    esp_mqtt_client_register_event(客户对象, 监听的事件ID, 事件Handler函数, 自定义参数)

    Handler函数的原型与esp-event一致:

    void mqtt_event_handler(
        void *handler_args,	// 这个参数是注册事件时的“自定义参数”
        esp_event_base_t base, // 由于这个handler特用于MQTT,因此可以忽略
        int32_t event_id, // 事件ID
        void *event_data // 事件数据,请转型为esp_mqtt_event_handle_t使用
    ) {
    	// TODO:操作事件
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    事件:

    • MQTT_EVENT_BEFORE_CONNECT: 即将准备连接代理

    • MQTT_EVENT_CONNECTED: 连接成功,可以发消息了

    • MQTT_EVENT_DISCONNECTED: 由于无法读写数据,例如服务器不可用,客户端终止了连接。

    • MQTT_EVENT_SUBSCRIBED: 订阅成功。event data中包含订阅消息的ID

    • MQTT_EVENT_UNSUBSCRIBED: 退订成功。event data中包含退订消息的ID

    • MQTT_EVENT_PUBLISHED: QoS > > 0的消息发送成功(因为QoS为0的消息不支持确认)。event data中包含消息的ID

    • MQTT_EVENT_DATA: 收到消息。
      event data包含:
      ① message ID
      ② name of the topic it was published to
      ③ received data and its length
      ④ 如果Data长度超过缓冲区,则消息会被拆成若干个碎片,并产生多个MQTT_EVENT_DATA事件。并且event中会包含current_data_offsettotal_data_len请用户自行拼接碎片消息。

    • MQTT_EVENT_ERROR: MQTT客户端出错,请使用event的error_handle字段查看错误详情。

    三、代码示例

    MQTT客户端的配置:

    // 创建MQTT配置,最简单的配置只需要填写URI
    const esp_mqtt_client_config_t mqtt_cfg = {
        .uri = "mqtt://mqtt.eclipseprojects.io",
    };
    // 传递配置,创建一个MQTT客户端
    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
    
    // 注册事件
    // 注:每个mqtt客户端都携带一个自己的事件循环,esp-mqtt并没有使用默认事件循环
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, EventHandle函数, client);
    
    // 启动mqtt客户端
    esp_mqtt_client_start(client);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    事件eventloop 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);
        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:
            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);
            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");
            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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
  • 相关阅读:
    神经网络算法的具体流程,神经网络算法难不难
    GD32f103系列教程—(时钟篇)
    Altera&Xilinx公司FPGA简介
    “Docker操作案例实践“
    设计模式原则——单一职责原则(SPS)
    顺序表在线OJ题(详解+图解)
    文献学习-6-微创手术中视频检索的无监督特征解缠
    Linux软件包管理— 脚本安装程序
    卧槽,2行代码,让接口性能提升10倍
    短视频账号矩阵系统===4年技术源头打磨
  • 原文地址:https://blog.csdn.net/m0_50064262/article/details/126499263