最近通过借鉴学习,整理了一份可以实现mqtt连接的代码。
在esp32-idf的开发中 mqtt也是开发好的库文件,可以通过直接引用对应的头文件去使用。
大概的函数在 \mqtt\esp-mqtt\mqtt_client.c 这里定义的。 我们先去分析一下 esp-idf 都给我们提供了哪些函数。

/**
 * @brief Creates mqtt client handle based on the configuration
 *
 * @param config    mqtt configuration structure
 *
 * @return mqtt_client_handle if successfully created, NULL on error
 */
esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *config);

/**
 * @brief Sets mqtt connection URI. This API is usually used to overrides the URI
 * configured in esp_mqtt_client_init
 *
 * @param client    mqtt client handle
 * @param uri
 *
 * @return ESP_FAIL if URI parse error, ESP_OK on success
 */
esp_err_t esp_mqtt_client_set_uri(esp_mqtt_client_handle_t client, const char *uri);

/**
 * @brief Starts mqtt client with already created client handle
 *
 * @param client    mqtt client handle
 *
 * @return ESP_OK on success
 *         ESP_ERR_INVALID_ARG on wrong initialization
 *         ESP_FAIL on other error
 */
esp_err_t esp_mqtt_client_start(esp_mqtt_client_handle_t client);

/**
 * @brief This api is typically used to force reconnection upon a specific event
 *
 * @param client    mqtt client handle
 *
 * @return ESP_OK on success
 *         ESP_FAIL if client is in invalid state
 */
esp_err_t esp_mqtt_client_reconnect(esp_mqtt_client_handle_t client);

/**
 * @brief This api is typically used to force disconnection from the broker
 *
 * @param client    mqtt client handle
 *
 * @return ESP_OK on success
 */
esp_err_t esp_mqtt_client_disconnect(esp_mqtt_client_handle_t client);

/**
 * @brief Stops mqtt client tasks
 *
 *  * Notes:
 *  - Cannot be called from the mqtt event handler
 *
 * @param client    mqtt client handle
 *
 * @return ESP_OK on success
 *         ESP_FAIL if client is in invalid state
 */
esp_err_t esp_mqtt_client_stop(esp_mqtt_client_handle_t client);

/**
 * @brief Subscribe the client to defined topic with defined qos
 *
 * Notes:
 * - Client must be connected to send subscribe message
 * - This API is could be executed from a user task or
 * from a mqtt event callback i.e. internal mqtt task
 * (API is protected by internal mutex, so it might block
 * if a longer data receive operation is in progress.
 *
 * @param client    mqtt client handle
 * @param topic
 * @param qos
 *
 * @return message_id of the subscribe message on success
 *         -1 on failure
 */
int esp_mqtt_client_subscribe(esp_mqtt_client_handle_t client, const char *topic, int qos);

/**
 * @brief Unsubscribe the client from defined topic
 *
 * Notes:
 * - Client must be connected to send unsubscribe message
 * - It is thread safe, please refer to `esp_mqtt_client_subscribe` for details
 *
 * @param client    mqtt client handle
 * @param topic
 *
 * @return message_id of the subscribe message on success
 *         -1 on failure
 */
int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *topic);

/**
 * @brief Client to send a publish message to the broker
 *
 * Notes:
 * - This API might block for several seconds, either due to network timeout (10s)
 *   or if publishing payloads longer than internal buffer (due to message
 *   fragmentation)
 * - Client doesn't have to be connected to send publish message
 *   (although it would drop all qos=0 messages, qos>1 messages would be enqueued)
 * - It is thread safe, please refer to `esp_mqtt_client_subscribe` for details
 *
 * @param client    mqtt client handle
 * @param topic     topic string
 * @param data      payload string (set to NULL, sending empty payload message)
 * @param len       data length, if set to 0, length is calculated from payload string
 * @param qos       qos of publish message
 * @param retain    retain flag
 *
 * @return message_id of the publish message (for QoS 0 message_id will always be zero) on success.
 *         -1 on failure.
 */
int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic, const char *data, int len, int qos, int retain);

/**
 * @brief Destroys the client handle
 *
 * Notes:
 *  - Cannot be called from the mqtt event handler
 *
 * @param client    mqtt client handle
 *
 * @return ESP_OK
 */
esp_err_t esp_mqtt_client_destroy(esp_mqtt_client_handle_t client);

/**
 * @brief Set configuration structure, typically used when updating the config (i.e. on "before_connect" event
 *
 * @param client    mqtt client handle
 *
 * @param config    mqtt configuration structure
 *
 * @return ESP_ERR_NO_MEM if failed to allocate
 *         ESP_OK on success
 */
esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_client_config_t *config);

/**
 * @brief Registers mqtt event
 *
 * @param client            mqtt client handle
 * @param event             event type
 * @param event_handler     handler callback
 * @param event_handler_arg handlers context
 *
 * @return ESP_ERR_NO_MEM if failed to allocate
 *         ESP_OK on success
 */
esp_err_t esp_mqtt_client_register_event(esp_mqtt_client_handle_t client, esp_mqtt_event_id_t event, esp_event_handler_t event_handler, void* event_handler_arg);

/**
 * @brief Get outbox size
 *
 * @param client            mqtt client handle
 * @return outbox size
 */
int esp_mqtt_client_get_outbox_size(esp_mqtt_client_handle_t client);

大概就是这么些函数,初始化,配置各种各样的参数
然后还有就是 重连,断联,停止等等 和连接相关的函数。
还有一些就是 主题相关的 订阅,发布等函数
这些的配置项里面最重要 也最全面的可以说就是他的初始化结构体了
所有的用的到的参数这里面都有。
并且
其定义如下:

typedef struct {
    mqtt_event_callback_t event_handle;     /*!< handle for MQTT events as a callback in legacy mode */
    esp_event_loop_handle_t event_loop_handle; /*!< handle for MQTT event loop library */
    const char *host;                       /*!< MQTT server domain (ipv4 as string) */
    const char *uri;                        /*!< Complete MQTT broker URI */
    uint32_t port;                          /*!< MQTT server port */
    const char *client_id;                  /*!< default client id is ``ESP32_%CHIPID%`` where %CHIPID% are last 3 bytes of MAC address in hex format */
    const char *username;                   /*!< MQTT username */
    const char *password;                   /*!< MQTT password */
    const char *lwt_topic;                  /*!< LWT (Last Will and Testament) message topic (NULL by default) */
    const char *lwt_msg;                    /*!< LWT message (NULL by default) */
    int lwt_qos;                            /*!< LWT message qos */
    int lwt_retain;                         /*!< LWT retained message flag */
    int lwt_msg_len;                        /*!< LWT message length */
    int disable_clean_session;              /*!< mqtt clean session, default clean_session is true */
    int keepalive;                          /*!< mqtt keepalive, default is 120 seconds */
    bool disable_auto_reconnect;            /*!< this mqtt client will reconnect to server (when errors/disconnect). Set disable_auto_reconnect=true to disable */
    void *user_context;                     /*!< pass user context to this option, then can receive that context in ``event->user_context`` */
    int task_prio;                          /*!< MQTT task priority, default is 5, can be changed in ``make menuconfig`` */
    int task_stack;                         /*!< MQTT task stack size, default is 6144 bytes, can be changed in ``make menuconfig`` */
    int buffer_size;                        /*!< size of MQTT send/receive buffer, default is 1024 (only receive buffer size if ``out_buffer_size`` defined) */
    const char *cert_pem;                   /*!< Pointer to certificate data in PEM or DER format for server verify (with SSL), default is NULL, not required to verify the server. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in cert_len. */
    size_t cert_len;                        /*!< Length of the buffer pointed to by cert_pem. May be 0 for null-terminated pem */
    const char *client_cert_pem;            /*!< Pointer to certificate data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_key_pem` has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_cert_len. */
    size_t client_cert_len;                 /*!< Length of the buffer pointed to by client_cert_pem. May be 0 for null-terminated pem */
    const char *client_key_pem;             /*!< Pointer to private key data in PEM or DER format for SSL mutual authentication, default is NULL, not required if mutual authentication is not needed. If it is not NULL, also `client_cert_pem` has to be provided. PEM-format must have a terminating NULL-character. DER-format requires the length to be passed in client_key_len */
    size_t client_key_len;                  /*!< Length of the buffer pointed to by client_key_pem. May be 0 for null-terminated pem */
    esp_mqtt_transport_t transport;         /*!< overrides URI transport */
    int refresh_connection_after_ms;        /*!< Refresh connection after this value (in milliseconds) */
    const struct psk_key_hint* psk_hint_key;     /*!< Pointer to PSK struct defined in esp_tls.h to enable PSK authentication (as alternative to certificate verification). If not NULL and server/client certificates are NULL, PSK is enabled */
    bool          use_global_ca_store;      /*!< Use a global ca_store for all the connections in which this bool is set. */
    int reconnect_timeout_ms;               /*!< Reconnect to the broker after this value in miliseconds if auto reconnect is not disabled (defaults to 10s) */
    const char **alpn_protos;               /*!< NULL-terminated list of supported application protocols to be used for ALPN */
    const char *clientkey_password;         /*!< Client key decryption password string */
    int clientkey_password_len;             /*!< String length of the password pointed to by clientkey_password */
    esp_mqtt_protocol_ver_t protocol_ver;   /*!< MQTT protocol version used for connection, defaults to value from menuconfig*/
    int out_buffer_size;                    /*!< size of MQTT output buffer. If not defined, both output and input buffers have the same size defined as ``buffer_size`` */
    bool skip_cert_common_name_check;       /*!< Skip any validation of server certificate CN field, this reduces the security of TLS and makes the mqtt client susceptible to MITM attacks  */
    bool use_secure_element;                /*!< enable secure element for enabling SSL connection */
    void *ds_data;                          /*!< carrier of handle for digital signature parameters */
    int network_timeout_ms;                 /*!< Abort network operation if it is not completed after this value, in milliseconds (defaults to 10s) */
    bool disable_keepalive;                 /*!< Set disable_keepalive=true to turn off keep-alive mechanism, false by default (keepalive is active by default). Note: setting the config value `keepalive` to `0` doesn't disable keepalive feature, but uses a default keepalive period */
} esp_mqtt_client_config_t;

包括服务端的 ip 端口 以及用于验证的用户名 密码 。以及各种网络配置相关的参数。
对于一个mqtt的程序而言,大概就是这么几个步骤。

  1. 定义一个MQTT客户端配置结构体,输入MQTT的url

  2. 通过esp_mqtt_client_init获取一个MQTT客户端结构体指针,参数是MQTT客户端配置结构体

  3. 注册MQTT事件

  4. 开启MQTT功能

    对于不需要发布信息的应用场景 主要的函数逻辑都是在时间中断里面去实现的,我们需要去分析接收到的数据,然后对这些数据分别的去进行响应
    踩到的一个坑:

mqtt://xxx.xxx.xxx.xxx
mqtts://xxx.xxx.xxx.xxx
这两个uri是不一样的,下面的事使用了ssl加密的uri!!!!!

分享一下我现在使用的代码。 代码源于这里 :

https://blog.csdn.net/qq_38113006/article/details/105588424
#  他的系列作品写的非常的棒!
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "mqtt_client.h"
#include "driver/gpio.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
// 事件处理函数
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) 
{
    //printf("Event dispatched from event loop base=%s, event_id=%d \n", base, event_id);
    // 获取MQTT客户端结构体指针
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;
    // 通过事件ID来分别处理对应的事件
    switch (event->event_id) 
    {
        // 建立连接成功
        case MQTT_EVENT_CONNECTED:
            printf("MQTT_client cnnnect to EMQ ok. \n");          
            esp_mqtt_client_publish(client, "domoticz/out", "I am ESP32.", 0, 1, 0);
            // 订阅主题,qos=0
            esp_mqtt_client_subscribe(client, "domoticz/out", 0);           
            break;
        // 客户端断开连接
        case MQTT_EVENT_DISCONNECTED:
            printf("MQTT_client have disconnected. \n");
            break;
        // 主题订阅成功
        case MQTT_EVENT_SUBSCRIBED:
            printf("mqtt subscribe ok. msg_id = %d \n",event->msg_id);
            break;
        // 取消订阅
        case MQTT_EVENT_UNSUBSCRIBED:
            printf("mqtt unsubscribe ok. msg_id = %d \n",event->msg_id);
            break;
        //  主题发布成功
        case MQTT_EVENT_PUBLISHED:
            printf("mqtt published ok. msg_id = %d \n",event->msg_id);
            break;
        // 已收到订阅的主题消息
        case MQTT_EVENT_DATA:
            printf("mqtt received topic: %.*s \n",event->topic_len, event->topic);
            printf("topic data: %.*s\r\n", event->data_len, event->data);
            break;
        // 客户端遇到错误
        case MQTT_EVENT_ERROR:
            printf("MQTT_EVENT_ERROR \n");
            break;
        default:
            printf("Other event id:%d \n", event->event_id);
            break;
    }
}

void mqtt_app_start(void)
{
    // 1、定义一个MQTT客户端配置结构体,输入MQTT的url
    esp_mqtt_client_config_t mqtt_cfg = {
        .uri = "mqtt://xxx.xxx.xxx.xxx",// ip地址
        .username = "ESP32",
        .port = 1883
        //.alpn_protos,
        // .use_secure_element
    };

    // 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);
}

接收到的一个数据包如下

mqtt received topic: domoticz/out 
topic data: {
        "Battery" : 255,
        "RSSI" : 12,    
        "description" : "this is description",
        "dtype" : "Light/Switch",
        "hwid" : "7",
        "id" : "00014052",
        "idx" : 2,
        "name" : "\u7a7a\u8c03",
        "nvalue" : 1,
        "stype" : "Switch",
        "svalue1" : "100",
        "switchType" : "On/Off",
        "unit" : 1
}

mqtt received topic: domoticz/out 
topic data: {
        "Battery" : 255,
        "RSSI" : 12,
        "description" : "this is description",
        "dtype" : "Light/Switch",
        "hwid" : "7",
        "id" : "00014052",
        "idx" : 2,
        "name" : "\u7a7a\u8c03",
        "nvalue" : 0,
        "stype" : "Switch",
        "svalue1" : "100",
        "switchType" : "On/Off",
        "unit" : 1
}

更多推荐