Merge branch 'feature/support_mqtt_5' into 'master'

mqtt: Support MQTT5 protocol

See merge request espressif/esp-mqtt!125
This commit is contained in:
David Čermák
2022-06-14 21:40:57 +08:00
9 changed files with 2680 additions and 182 deletions

280
include/mqtt5_client.h Normal file
View File

@ -0,0 +1,280 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MQTT5_CLIENT_H_
#define _MQTT5_CLIENT_H_
#include "mqtt_client.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct esp_mqtt_client *esp_mqtt5_client_handle_t;
/**
* MQTT5 protocol error reason code, more details refer to MQTT5 protocol document section 2.4
*/
enum mqtt5_error_reason_code {
MQTT5_UNSPECIFIED_ERROR = 0x80,
MQTT5_MALFORMED_PACKET = 0x81,
MQTT5_PROTOCOL_ERROR = 0x82,
MQTT5_IMPLEMENT_SPECIFIC_ERROR = 0x83,
MQTT5_UNSUPPORTED_PROTOCOL_VER = 0x84,
MQTT5_INVAILD_CLIENT_ID = 0x85,
MQTT5_BAD_USERNAME_OR_PWD = 0x86,
MQTT5_NOT_AUTHORIZED = 0x87,
MQTT5_SERVER_UNAVAILABLE = 0x88,
MQTT5_SERVER_BUSY = 0x89,
MQTT5_BANNED = 0x8A,
MQTT5_SERVER_SHUTTING_DOWN = 0x8B,
MQTT5_BAD_AUTH_METHOD = 0x8C,
MQTT5_KEEP_ALIVE_TIMEOUT = 0x8D,
MQTT5_SESSION_TAKEN_OVER = 0x8E,
MQTT5_TOPIC_FILTER_INVAILD = 0x8F,
MQTT5_TOPIC_NAME_INVAILD = 0x90,
MQTT5_PACKET_IDENTIFIER_IN_USE = 0x91,
MQTT5_PACKET_IDENTIFIER_NOT_FOUND = 0x92,
MQTT5_RECEIVE_MAXIMUM_EXCEEDED = 0x93,
MQTT5_TOPIC_ALIAS_INVAILD = 0x94,
MQTT5_PACKET_TOO_LARGE = 0x95,
MQTT5_MESSAGE_RATE_TOO_HIGH = 0x96,
MQTT5_QUOTA_EXCEEDED = 0x97,
MQTT5_ADMINISTRATIVE_ACTION = 0x98,
MQTT5_PAYLOAD_FORMAT_INVAILD = 0x99,
MQTT5_RETAIN_NOT_SUPPORT = 0x9A,
MQTT5_QOS_NOT_SUPPORT = 0x9B,
MQTT5_USE_ANOTHER_SERVER = 0x9C,
MQTT5_SERVER_MOVED = 0x9D,
MQTT5_SHARED_SUBSCR_NOT_SUPPORTED = 0x9E,
MQTT5_CONNECTION_RATE_EXCEEDED = 0x9F,
MQTT5_MAXIMUM_CONNECT_TIME = 0xA0,
MQTT5_SUBSCRIBE_IDENTIFIER_NOT_SUPPORT = 0xA1,
MQTT5_WILDCARD_SUBSCRIBE_NOT_SUPPORT = 0xA2,
};
/**
* MQTT5 user property handle
*/
typedef struct mqtt5_user_property_list_t *mqtt5_user_property_handle_t;
/**
* MQTT5 protocol connect properties and will properties configuration, more details refer to MQTT5 protocol document section 3.1.2.11 and 3.3.2.3
*/
typedef struct {
uint32_t session_expiry_interval; /*!< The interval time of session expiry */
uint32_t maximum_packet_size; /*!< The maximum packet size that we can receive */
uint16_t receive_maximum; /*!< The maximum pakcket count that we process concurrently */
uint16_t topic_alias_maximum; /*!< The maximum topic alias that we support */
bool request_resp_info; /*!< This value to request Server to return Response information */
bool request_problem_info; /*!< This value to indicate whether the reason string or user properties are sent in case of failures */
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
uint32_t will_delay_interval; /*!< The time interval that server delays publishing will message */
uint32_t message_expiry_interval; /*!< The time interval that message expiry */
bool payload_format_indicator; /*!< This value is to indicator will message payload format */
const char *content_type; /*!< This value is to indicator will message content type, use a MIME content type string */
const char *response_topic; /*!< Topic name for a response message */
const char *correlation_data; /*!< Binary data for receiver to match the response message */
uint16_t correlation_data_len; /*!< The length of correlation data */
mqtt5_user_property_handle_t will_user_property; /*!< The handle for will message user property, call function esp_mqtt5_client_set_user_property to set it */
} esp_mqtt5_connection_property_config_t;
/**
* MQTT5 protocol publish properties configuration, more details refer to MQTT5 protocol document section 3.3.2.3
*/
typedef struct {
bool payload_format_indicator; /*!< This value is to indicator publish message payload format */
uint32_t message_expiry_interval; /*!< The time interval that message expiry */
uint16_t topic_alias; /*!< An interger value to identify the topic instead of using topic name string */
const char *response_topic; /*!< Topic name for a response message */
const char *correlation_data; /*!< Binary data for receiver to match the response message */
uint16_t correlation_data_len; /*!< The length of correlation data */
const char *content_type; /*!< This value is to indicator publish message content type, use a MIME content type string */
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
} esp_mqtt5_publish_property_config_t;
/**
* MQTT5 protocol subscribe properties configuration, more details refer to MQTT5 protocol document section 3.8.2.1
*/
typedef struct {
uint16_t subscribe_id; /*!< A variable byte represents the identifier of the subscription */
bool no_local_flag; /*!< Subscription Option to allow that server publish message that client sent */
bool retain_as_published_flag; /*!< Subscription Option to keep the retain flag as published option */
uint8_t retain_handle; /*!< Subscription Option to handle retain option */
bool is_share_subscribe; /*!< Whether subscribe is a shared subscription */
const char *share_name; /*!< The name of shared subscription which is a part of $share/{share_name}/{topic} */
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
} esp_mqtt5_subscribe_property_config_t;
/**
* MQTT5 protocol unsubscribe properties configuration, more details refer to MQTT5 protocol document section 3.10.2.1
*/
typedef struct {
bool is_share_subscribe; /*!< Whether subscribe is a shared subscription */
const char *share_name; /*!< The name of shared subscription which is a part of $share/{share_name}/{topic} */
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
} esp_mqtt5_unsubscribe_property_config_t;
/**
* MQTT5 protocol disconnect properties configuration, more details refer to MQTT5 protocol document section 3.14.2.2
*/
typedef struct {
uint32_t session_expiry_interval; /*!< The interval time of session expiry */
uint8_t disconnect_reason; /*!< The reason that connection disconnet, refer to mqtt5_error_reason_code */
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_set_user_property to set it */
} esp_mqtt5_disconnect_property_config_t;
/**
* MQTT5 protocol for event properties
*/
typedef struct {
bool payload_format_indicator; /*!< Payload format of the message */
char *response_topic; /*!< Response topic of the message */
int response_topic_len; /*!< Response topic length of the message */
char *correlation_data; /*!< Correlation data of the message */
uint16_t correlation_data_len; /*!< Correlation data length of the message */
char *content_type; /*!< Content type of the message */
int content_type_len; /*!< Content type length of the message */
mqtt5_user_property_handle_t user_property; /*!< The handle for user property, call function esp_mqtt5_client_delete_user_property to free the memory */
} esp_mqtt5_event_property_t;
/**
* MQTT5 protocol for user property
*/
typedef struct {
const char *key; /*!< Item key name */
const char *value; /*!< Item value string */
} esp_mqtt5_user_property_item_t;
/**
* @brief Set MQTT5 client connect property configuration
*
* @param client mqtt client handle
* @param connect_property connect property
*
* @return ESP_ERR_NO_MEM if failed to allocate
* ESP_ERR_INVALID_ARG on wrong initialization
* ESP_FAIL on fail
* ESP_OK on success
*/
esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_connection_property_config_t *connect_property);
/**
* @brief Set MQTT5 client publish property configuration
*
* This API will not store the publish property, it is one-time configuration.
* Before call `esp_mqtt_client_publish` to publish data, call this API to set publish property if have
*
* @param client mqtt client handle
* @param property publish property
*
* @return ESP_ERR_INVALID_ARG on wrong initialization
* ESP_FAIL on fail
* ESP_OK on success
*/
esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_publish_property_config_t *property);
/**
* @brief Set MQTT5 client subscribe property configuration
*
* This API will not store the subscribe property, it is one-time configuration.
* Before call `esp_mqtt_client_subscribe` to subscribe topic, call this API to set subscribe property if have
*
* @param client mqtt client handle
* @param property subscribe property
*
* @return ESP_ERR_INVALID_ARG on wrong initialization
* ESP_FAIL on fail
* ESP_OK on success
*/
esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_subscribe_property_config_t *property);
/**
* @brief Set MQTT5 client unsubscribe property configuration
*
* This API will not store the unsubscribe property, it is one-time configuration.
* Before call `esp_mqtt_client_unsubscribe` to unsubscribe topic, call this API to set unsubscribe property if have
*
* @param client mqtt client handle
* @param property unsubscribe property
*
* @return ESP_ERR_INVALID_ARG on wrong initialization
* ESP_FAIL on fail
* ESP_OK on success
*/
esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_unsubscribe_property_config_t *property);
/**
* @brief Set MQTT5 client disconnect property configuration
*
* This API will not store the disconnect property, it is one-time configuration.
* Before call `esp_mqtt_client_disconnect` to disconnect connection, call this API to set disconnect property if have
*
* @param client mqtt client handle
* @param property disconnect property
*
* @return ESP_ERR_NO_MEM if failed to allocate
* ESP_ERR_INVALID_ARG on wrong initialization
* ESP_FAIL on fail
* ESP_OK on success
*/
esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_disconnect_property_config_t *property);
/**
* @brief Set MQTT5 client user property configuration
*
* This API will allocate memory for user_property, please DO NOT forget `call esp_mqtt5_client_delete_user_property`
* after you use it.
* Before publish data, subscribe topic, unsubscribe, etc, call this API to set user property if have
*
* @param user_property user_property handle
* @param item array of user property data (eg. {{"var","val"},{"other","2"}})
* @param item_num number of items in user property data
*
* @return ESP_ERR_NO_MEM if failed to allocate
* ESP_FAIL on fail
* ESP_OK on success
*/
esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_property, esp_mqtt5_user_property_item_t item[], uint8_t item_num);
/**
* @brief Get MQTT5 client user property
*
* @param user_property user_property handle
* @param item point that store user property data
* @param item_num number of items in user property data
*
* This API can use with `esp_mqtt5_client_get_user_property_count` to get list count of user property.
* And malloc number of count item array memory to store the user property data.
* Please DO NOT forget the item memory, key and value point in item memory when get user property data successfully.
*
* @return ESP_ERR_NO_MEM if failed to allocate
* ESP_FAIL on fail
* ESP_OK on success
*/
esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_property, esp_mqtt5_user_property_item_t *item, uint8_t *item_num);
/**
* @brief Get MQTT5 client user property list count
*
* @param user_property user_property handle
* @return user property list count
*/
uint8_t esp_mqtt5_client_get_user_property_count(mqtt5_user_property_handle_t user_property);
/**
* @brief Free the user property list
*
* @param user_property user_property handle
*
* This API will free the memory in user property list and free user_property itself
*/
void esp_mqtt5_client_delete_user_property(mqtt5_user_property_handle_t user_property);
#ifdef __cplusplus
}
#endif //__cplusplus
#endif

View File

@ -12,6 +12,9 @@
#include <string.h>
#include "esp_err.h"
#include "esp_event.h"
#ifdef CONFIG_MQTT_PROTOCOL_5
#include "mqtt5_client.h"
#endif
#ifdef __cplusplus
extern "C" {
@ -114,7 +117,8 @@ typedef enum esp_mqtt_transport_t {
typedef enum esp_mqtt_protocol_ver_t {
MQTT_PROTOCOL_UNDEFINED = 0,
MQTT_PROTOCOL_V_3_1,
MQTT_PROTOCOL_V_3_1_1
MQTT_PROTOCOL_V_3_1_1,
MQTT_PROTOCOL_V_5,
} esp_mqtt_protocol_ver_t;
/**
@ -161,6 +165,10 @@ typedef struct esp_mqtt_event_t {
bool retain; /*!< Retained flag of the message associated with this event */
int qos; /*!< qos of the messages associated with this event */
bool dup; /*!< dup flag of the message associated with this event */
esp_mqtt_protocol_ver_t protocol_ver; /*!< MQTT protocol version used for connection, defaults to value from menuconfig*/
#ifdef CONFIG_MQTT_PROTOCOL_5
esp_mqtt5_event_property_t *property; /*!< MQTT 5 property associated with this event */
#endif
} esp_mqtt_event_t;
typedef esp_mqtt_event_t *esp_mqtt_event_handle_t;
@ -420,7 +428,6 @@ esp_err_t esp_mqtt_client_register_event(esp_mqtt_client_handle_t client, esp_mq
* 0 on wrong initialization
*/
int esp_mqtt_client_get_outbox_size(esp_mqtt_client_handle_t client);
#ifdef __cplusplus
}
#endif //__cplusplus

View File

@ -0,0 +1,54 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MQTT5_CLIENT_PRIV_H_
#define _MQTT5_CLIENT_PRIV_H_
#include "mqtt5_client.h"
#include "mqtt_client_priv.h"
#include "mqtt5_msg.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct mqtt5_topic_alias {
char *topic;
uint16_t topic_len;
uint16_t topic_alias;
STAILQ_ENTRY(mqtt5_topic_alias) next;
} mqtt5_topic_alias_t;
STAILQ_HEAD(mqtt5_topic_alias_list_t, mqtt5_topic_alias);
typedef struct mqtt5_topic_alias_list_t *mqtt5_topic_alias_handle_t;
typedef struct mqtt5_topic_alias *mqtt5_topic_alias_item_t;
typedef struct {
esp_mqtt5_connection_property_storage_t connect_property_info;
esp_mqtt5_connection_will_property_storage_t will_property_info;
esp_mqtt5_connection_server_resp_property_t server_resp_property_info;
esp_mqtt5_disconnect_property_config_t disconnect_property_info;
const esp_mqtt5_publish_property_config_t *publish_property_info;
const esp_mqtt5_subscribe_property_config_t *subscribe_property_info;
const esp_mqtt5_unsubscribe_property_config_t *unsubscribe_property_info;
mqtt5_topic_alias_handle_t peer_topic_alias;
} mqtt5_config_storage_t;
void esp_mqtt5_flow_control(esp_mqtt5_client_handle_t client);
void esp_mqtt5_parse_pubcomp(esp_mqtt5_client_handle_t client);
void esp_mqtt5_parse_puback(esp_mqtt5_client_handle_t client);
void esp_mqtt5_parse_unsuback(esp_mqtt5_client_handle_t client);
void esp_mqtt5_parse_suback(esp_mqtt5_client_handle_t client);
esp_err_t esp_mqtt5_parse_connack(esp_mqtt5_client_handle_t client, int *connect_rsp_code);
void esp_mqtt5_client_destory(esp_mqtt5_client_handle_t client);
esp_err_t esp_mqtt5_client_publish_check(esp_mqtt5_client_handle_t client, int qos, int retain);
esp_err_t esp_mqtt5_client_subscribe_check(esp_mqtt5_client_handle_t client, int qos);
esp_err_t esp_mqtt5_create_default_config(esp_mqtt5_client_handle_t client);
esp_err_t esp_mqtt5_get_publish_data(esp_mqtt5_client_handle_t client, uint8_t *msg_buf, size_t msg_read_len, char **msg_topic, size_t *msg_topic_len, char **msg_data, size_t *msg_data_len);
#ifdef __cplusplus
}
#endif //__cplusplus
#endif

142
lib/include/mqtt5_msg.h Normal file
View File

@ -0,0 +1,142 @@
#ifndef MQTT5_MSG_H
#define MQTT5_MSG_H
#include <stdint.h>
#include <stdbool.h>
#include "sys/queue.h"
#include "mqtt_config.h"
#include "mqtt_msg.h"
#include "mqtt_client.h"
#ifdef __cplusplus
extern "C" {
#endif
enum mqtt_properties_type {
MQTT5_PROPERTY_PAYLOAD_FORMAT_INDICATOR = 0x01,
MQTT5_PROPERTY_MESSAGE_EXPIRY_INTERVAL = 0x02,
MQTT5_PROPERTY_CONTENT_TYPE = 0x03,
MQTT5_PROPERTY_RESPONSE_TOPIC = 0x08,
MQTT5_PROPERTY_CORRELATION_DATA = 0x09,
MQTT5_PROPERTY_SUBSCRIBE_IDENTIFIER = 0x0B,
MQTT5_PROPERTY_SESSION_EXPIRY_INTERVAL = 0x11,
MQTT5_PROPERTY_ASSIGNED_CLIENT_IDENTIFIER = 0x12,
MQTT5_PROPERTY_SERVER_KEEP_ALIVE = 0x13,
MQTT5_PROPERTY_AUTHENTICATION_METHOD = 0x15,
MQTT5_PROPERTY_AUTHENTICATION_DATA = 0x16,
MQTT5_PROPERTY_REQUEST_PROBLEM_INFO = 0x17,
MQTT5_PROPERTY_WILL_DELAY_INTERVAL = 0x18,
MQTT5_PROPERTY_REQUEST_RESP_INFO = 0x19,
MQTT5_PROPERTY_RESP_INFO = 0x1A,
MQTT5_PROPERTY_SERVER_REFERENCE = 0x1C,
MQTT5_PROPERTY_REASON_STRING = 0x1F,
MQTT5_PROPERTY_RECEIVE_MAXIMUM = 0x21,
MQTT5_PROPERTY_TOPIC_ALIAS_MAXIMIM = 0x22,
MQTT5_PROPERTY_TOPIC_ALIAS = 0x23,
MQTT5_PROPERTY_MAXIMUM_QOS = 0x24,
MQTT5_PROPERTY_RETAIN_AVAILABLE = 0x25,
MQTT5_PROPERTY_USER_PROPERTY = 0x26,
MQTT5_PROPERTY_MAXIMUM_PACKET_SIZE = 0x27,
MQTT5_PROPERTY_WILDCARD_SUBSCR_AVAILABLE = 0x28,
MQTT5_PROPERTY_SUBSCR_IDENTIFIER_AVAILABLE = 0x29,
MQTT5_PROPERTY_SHARED_SUBSCR_AVAILABLE = 0x2A,
};
typedef struct mqtt5_user_property {
char *key;
char *value;
STAILQ_ENTRY(mqtt5_user_property) next;
} mqtt5_user_property_t;
STAILQ_HEAD(mqtt5_user_property_list_t, mqtt5_user_property);
typedef struct mqtt5_user_property *mqtt5_user_property_item_t;
typedef struct {
uint32_t maximum_packet_size;
uint16_t receive_maximum;
uint16_t topic_alias_maximum;
uint8_t max_qos;
bool retain_available;
bool wildcard_subscribe_available;
bool subscribe_identifiers_available;
bool shared_subscribe_available;
char *response_info;
} esp_mqtt5_connection_server_resp_property_t;
typedef struct {
bool payload_format_indicator;
uint32_t message_expiry_interval;
uint16_t topic_alias;
char *response_topic;
int response_topic_len;
char *correlation_data;
uint16_t correlation_data_len;
char *content_type;
int content_type_len;
uint16_t subscribe_id;
} esp_mqtt5_publish_resp_property_t;
typedef struct {
uint32_t session_expiry_interval;
uint32_t maximum_packet_size;
uint16_t receive_maximum;
uint16_t topic_alias_maximum;
bool request_resp_info;
bool request_problem_info;
mqtt5_user_property_handle_t user_property;
} esp_mqtt5_connection_property_storage_t;
typedef struct {
uint32_t will_delay_interval;
uint32_t message_expiry_interval;
bool payload_format_indicator;
char *content_type;
char *response_topic;
char *correlation_data;
uint16_t correlation_data_len;
mqtt5_user_property_handle_t user_property;
} esp_mqtt5_connection_will_property_storage_t;
#define mqtt5_get_type mqtt_get_type
#define mqtt5_get_dup mqtt_get_dup
#define mqtt5_set_dup mqtt_set_dup
#define mqtt5_get_qos mqtt_get_qos
#define mqtt5_get_retain mqtt_get_retain
#define mqtt5_msg_init mqtt_msg_init
#define mqtt5_get_total_length mqtt_get_total_length
#define mqtt5_has_valid_msg_hdr mqtt_has_valid_msg_hdr
#define mqtt5_msg_pingreq mqtt_msg_pingreq
#define mqtt5_msg_pingresp mqtt_msg_pingresp
#define mqtt5_get_unsuback_data mqtt5_get_suback_data
#define mqtt5_get_pubcomp_data mqtt5_get_puback_data
uint16_t mqtt5_get_id(uint8_t *buffer, size_t length);
char *mqtt5_get_publish_property_payload(uint8_t *buffer, size_t buffer_length, char **msg_topic, size_t *msg_topic_len, esp_mqtt5_publish_resp_property_t *resp_property, uint16_t *property_len, size_t *payload_len, mqtt5_user_property_handle_t *user_property);
char *mqtt5_get_suback_data(uint8_t *buffer, size_t *length, mqtt5_user_property_handle_t *user_property);
char *mqtt5_get_puback_data(uint8_t *buffer, size_t *length, mqtt5_user_property_handle_t *user_property);
mqtt_message_t *mqtt5_msg_connect(mqtt_connection_t *connection, mqtt_connect_info_t *info, esp_mqtt5_connection_property_storage_t *property, esp_mqtt5_connection_will_property_storage_t *will_property);
mqtt_message_t *mqtt5_msg_publish(mqtt_connection_t *connection, const char *topic, const char *data, int data_length, int qos, int retain, uint16_t *message_id, const esp_mqtt5_publish_property_config_t *property, const char *resp_info);
esp_err_t mqtt5_msg_parse_connack_property(uint8_t *buffer, size_t buffer_len, mqtt_connect_info_t *connection_info, esp_mqtt5_connection_property_storage_t *connection_property, esp_mqtt5_connection_server_resp_property_t *resp_property, int *reason_code, uint8_t *ack_flag, mqtt5_user_property_handle_t *user_property);
int mqtt5_msg_get_reason_code(uint8_t *buffer, size_t length);
mqtt_message_t *mqtt5_msg_subscribe(mqtt_connection_t *connection, const char *topic, int qos, uint16_t *message_id, const esp_mqtt5_subscribe_property_config_t *property);
mqtt_message_t *mqtt5_msg_unsubscribe(mqtt_connection_t *connection, const char *topic, uint16_t *message_id, const esp_mqtt5_unsubscribe_property_config_t *property);
mqtt_message_t *mqtt5_msg_disconnect(mqtt_connection_t *connection, esp_mqtt5_disconnect_property_config_t *disconnect_property_info);
mqtt_message_t *mqtt5_msg_pubcomp(mqtt_connection_t *connection, uint16_t message_id);
mqtt_message_t *mqtt5_msg_pubrel(mqtt_connection_t *connection, uint16_t message_id);
mqtt_message_t *mqtt5_msg_pubrec(mqtt_connection_t *connection, uint16_t message_id);
mqtt_message_t *mqtt5_msg_puback(mqtt_connection_t *connection, uint16_t message_id);
#ifdef __cplusplus
}
#endif
#endif /* MQTT5_MSG_H */

View File

@ -0,0 +1,136 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _MQTT_CLIENT_PRIV_H_
#define _MQTT_CLIENT_PRIV_H_
#include <stdio.h>
#include <stdlib.h>
#include "esp_err.h"
#include "platform.h"
#include "esp_event.h"
#include "mqtt_client.h"
#include "mqtt_msg.h"
#ifdef MQTT_PROTOCOL_5
#include "mqtt5_client_priv.h"
#endif
#include "esp_transport.h"
#include "esp_transport_tcp.h"
#include "esp_transport_ssl.h"
#include "esp_transport_ws.h"
#include "esp_log.h"
#include "mqtt_outbox.h"
#include "freertos/event_groups.h"
#include <errno.h>
#include <string.h>
#include "mqtt_supported_features.h"
/* using uri parser */
#include "http_parser.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef MQTT_DISABLE_API_LOCKS
# define MQTT_API_LOCK(c)
# define MQTT_API_UNLOCK(c)
#else
# define MQTT_API_LOCK(c) xSemaphoreTakeRecursive(c->api_lock, portMAX_DELAY)
# define MQTT_API_UNLOCK(c) xSemaphoreGiveRecursive(c->api_lock)
#endif /* MQTT_USE_API_LOCKS */
typedef struct mqtt_state {
uint8_t *in_buffer;
uint8_t *out_buffer;
int in_buffer_length;
int out_buffer_length;
size_t message_length;
size_t in_buffer_read_len;
mqtt_message_t *outbound_message;
mqtt_connection_t mqtt_connection;
uint16_t pending_msg_id;
int pending_msg_type;
int pending_publish_qos;
int pending_msg_count;
} mqtt_state_t;
typedef struct {
mqtt_event_callback_t event_handle;
esp_event_loop_handle_t event_loop_handle;
int task_stack;
int task_prio;
char *uri;
char *host;
char *path;
char *scheme;
int port;
bool auto_reconnect;
void *user_context;
int network_timeout_ms;
int refresh_connection_after_ms;
int reconnect_timeout_ms;
char **alpn_protos;
int num_alpn_protos;
char *clientkey_password;
int clientkey_password_len;
bool use_global_ca_store;
esp_err_t ((*crt_bundle_attach)(void *conf));
const char *cacert_buf;
size_t cacert_bytes;
const char *clientcert_buf;
size_t clientcert_bytes;
const char *clientkey_buf;
size_t clientkey_bytes;
const struct psk_key_hint *psk_hint_key;
bool skip_cert_common_name_check;
bool use_secure_element;
void *ds_data;
int message_retransmit_timeout;
} mqtt_config_storage_t;
typedef enum {
MQTT_STATE_INIT = 0,
MQTT_STATE_DISCONNECTED,
MQTT_STATE_CONNECTED,
MQTT_STATE_WAIT_RECONNECT,
} mqtt_client_state_t;
struct esp_mqtt_client {
esp_transport_list_handle_t transport_list;
esp_transport_handle_t transport;
mqtt_config_storage_t *config;
mqtt_state_t mqtt_state;
mqtt_connect_info_t connect_info;
mqtt_client_state_t state;
uint64_t refresh_connection_tick;
int64_t keepalive_tick;
uint64_t reconnect_tick;
#ifdef MQTT_PROTOCOL_5
mqtt5_config_storage_t *mqtt5_config;
uint16_t send_publish_packet_count; // This is for MQTT v5.0 flow control
#endif
int wait_timeout_ms;
int auto_reconnect;
esp_mqtt_event_t event;
bool run;
bool wait_for_ping_resp;
outbox_handle_t outbox;
EventGroupHandle_t status_bits;
SemaphoreHandle_t api_lock;
TaskHandle_t task_handle;
};
bool esp_mqtt_set_if_config(char const *const new_config, char **old_config);
void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client);
#ifdef __cplusplus
}
#endif //__cplusplus
#endif

View File

@ -12,6 +12,10 @@
#define MQTT_PROTOCOL_311
#endif
#ifdef CONFIG_MQTT_PROTOCOL_5
#define MQTT_PROTOCOL_5
#endif
#define MQTT_RECON_DEFAULT_MS (10*1000)
#define MQTT_POLL_READ_TIMEOUT_MS (1000)

1040
lib/mqtt5_msg.c Normal file

File diff suppressed because it is too large Load Diff

757
mqtt5_client.c Normal file
View File

@ -0,0 +1,757 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mqtt_client_priv.h"
#include "esp_log.h"
#include <string.h>
static const char *TAG = "mqtt5_client";
static void esp_mqtt5_print_error_code(esp_mqtt5_client_handle_t client, int code);
static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, char *topic, size_t topic_len);
static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, size_t *topic_length);
static void esp_mqtt5_client_delete_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle);
static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_property_new, const mqtt5_user_property_handle_t user_property_old);
void esp_mqtt5_flow_control(esp_mqtt5_client_handle_t client)
{
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
int msg_type = mqtt5_get_type(client->mqtt_state.outbound_message->data);
if (msg_type == MQTT_MSG_TYPE_PUBLISH) {
int msg_qos = mqtt5_get_qos(client->mqtt_state.outbound_message->data);
if (msg_qos > 0) {
client->send_publish_packet_count ++;
ESP_LOGD(TAG, "Sent (%d) qos > 0 publish packet without ack", client->send_publish_packet_count);
}
}
}
}
void esp_mqtt5_parse_pubcomp(esp_mqtt5_client_handle_t client)
{
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBCOMP return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
size_t msg_data_len = client->mqtt_state.in_buffer_read_len;
client->event.data = mqtt5_get_pubcomp_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property);
client->event.data_len = msg_data_len;
client->event.total_data_len = msg_data_len;
client->event.current_data_offset = 0;
}
}
void esp_mqtt5_parse_puback(esp_mqtt5_client_handle_t client)
{
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
size_t msg_data_len = client->mqtt_state.in_buffer_read_len;
client->event.data = mqtt5_get_puback_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property);
client->event.data_len = msg_data_len;
client->event.total_data_len = msg_data_len;
client->event.current_data_offset = 0;
client->send_publish_packet_count --;
}
}
void esp_mqtt5_parse_unsuback(esp_mqtt5_client_handle_t client)
{
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
ESP_LOGI(TAG, "MQTT_MSG_TYPE_UNSUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
size_t msg_data_len = client->mqtt_state.in_buffer_read_len;
client->event.data = mqtt5_get_unsuback_data(client->mqtt_state.in_buffer, &msg_data_len, &client->event.property->user_property);
client->event.data_len = msg_data_len;
client->event.total_data_len = msg_data_len;
client->event.current_data_offset = 0;
}
}
void esp_mqtt5_parse_suback(esp_mqtt5_client_handle_t client)
{
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
ESP_LOGI(TAG, "MQTT_MSG_TYPE_SUBACK return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
}
}
esp_err_t esp_mqtt5_parse_connack(esp_mqtt5_client_handle_t client, int *connect_rsp_code)
{
size_t len = client->mqtt_state.in_buffer_read_len;
client->mqtt_state.in_buffer_read_len = 0;
uint8_t ack_flag = 0;
if (mqtt5_msg_parse_connack_property(client->mqtt_state.in_buffer, len, &client->connect_info, &client->mqtt5_config->connect_property_info, &client->mqtt5_config->server_resp_property_info, connect_rsp_code, &ack_flag, &client->event.property->user_property) != ESP_OK) {
ESP_LOGE(TAG, "Failed to parse CONNACK packet");
return ESP_FAIL;
}
if (*connect_rsp_code == MQTT_CONNECTION_ACCEPTED) {
ESP_LOGD(TAG, "Connected");
client->event.session_present = ack_flag & 0x01;
return ESP_OK;
}
esp_mqtt5_print_error_code(client, *connect_rsp_code);
return ESP_FAIL;
}
esp_err_t esp_mqtt5_get_publish_data(esp_mqtt5_client_handle_t client, uint8_t *msg_buf, size_t msg_read_len, char **msg_topic, size_t *msg_topic_len, char **msg_data, size_t *msg_data_len)
{
// get property
uint16_t property_len = 0;
esp_mqtt5_publish_resp_property_t property = {0};
*msg_data = mqtt5_get_publish_property_payload(msg_buf, msg_read_len, msg_topic, msg_topic_len, &property, &property_len, msg_data_len, &client->event.property->user_property);
if (*msg_data_len == 0 || *msg_data == NULL) {
ESP_LOGE(TAG, "%s: mqtt5_get_publish_property_payload() failed", __func__);
return ESP_FAIL;
}
if (property.topic_alias > client->mqtt5_config->connect_property_info.topic_alias_maximum) {
ESP_LOGE(TAG, "%s: Broker response topic alias %d is over the max topic alias %d", __func__, property.topic_alias, client->mqtt5_config->connect_property_info.topic_alias_maximum);
return ESP_FAIL;
}
if (property.topic_alias) {
if (*msg_topic_len == 0) {
ESP_LOGI(TAG, "Publish topic is empty, use topic alias");
*msg_topic = esp_mqtt5_client_get_topic_alias(client->mqtt5_config->peer_topic_alias, property.topic_alias, msg_topic_len);
if (!*msg_topic) {
ESP_LOGE(TAG, "%s: esp_mqtt5_client_get_topic_alias() failed", __func__);
return ESP_FAIL;
}
} else {
if (esp_mqtt5_client_update_topic_alias(client->mqtt5_config->peer_topic_alias, property.topic_alias, *msg_topic, *msg_topic_len) != ESP_OK) {
ESP_LOGE(TAG, "%s: esp_mqtt5_client_update_topic_alias() failed", __func__);
return ESP_FAIL;
}
}
}
client->event.property->payload_format_indicator = property.payload_format_indicator;
client->event.property->response_topic = property.response_topic;
client->event.property->response_topic_len = property.response_topic_len;
client->event.property->correlation_data = property.correlation_data;
client->event.property->correlation_data_len = property.correlation_data_len;
client->event.property->content_type = property.content_type;
client->event.property->content_type_len = property.content_type_len;
return ESP_OK;
}
esp_err_t esp_mqtt5_create_default_config(esp_mqtt5_client_handle_t client)
{
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
client->event.property = calloc(1, sizeof(esp_mqtt5_event_property_t));
ESP_MEM_CHECK(TAG, client->event.property, return ESP_FAIL)
client->mqtt5_config = calloc(1, sizeof(mqtt5_config_storage_t));
ESP_MEM_CHECK(TAG, client->mqtt5_config, return ESP_FAIL)
client->mqtt5_config->server_resp_property_info.max_qos = 2;
client->mqtt5_config->server_resp_property_info.retain_available = true;
client->mqtt5_config->server_resp_property_info.wildcard_subscribe_available = true;
client->mqtt5_config->server_resp_property_info.subscribe_identifiers_available = true;
client->mqtt5_config->server_resp_property_info.shared_subscribe_available = true;
client->mqtt5_config->server_resp_property_info.receive_maximum = 65535;
}
return ESP_OK;
}
static void esp_mqtt5_print_error_code(esp_mqtt5_client_handle_t client, int code)
{
switch (code) {
case MQTT5_UNSPECIFIED_ERROR:
ESP_LOGW(TAG, "Unspecified error");
break;
case MQTT5_MALFORMED_PACKET:
ESP_LOGW(TAG, "Malformed Packet");
break;
case MQTT5_PROTOCOL_ERROR:
ESP_LOGW(TAG, "Protocol Error");
break;
case MQTT5_IMPLEMENT_SPECIFIC_ERROR:
ESP_LOGW(TAG, "Implementation specific error");
break;
case MQTT5_UNSUPPORTED_PROTOCOL_VER:
ESP_LOGW(TAG, "Unsupported Protocol Version");
break;
case MQTT5_INVAILD_CLIENT_ID:
ESP_LOGW(TAG, "Client Identifier not valid");
break;
case MQTT5_BAD_USERNAME_OR_PWD:
ESP_LOGW(TAG, "Bad User Name or Password");
break;
case MQTT5_NOT_AUTHORIZED:
ESP_LOGW(TAG, "Not authorized");
break;
case MQTT5_SERVER_UNAVAILABLE:
ESP_LOGW(TAG, "Server unavailable");
break;
case MQTT5_SERVER_BUSY:
ESP_LOGW(TAG, "Server busy");
break;
case MQTT5_BANNED:
ESP_LOGW(TAG, "Banned");
break;
case MQTT5_SERVER_SHUTTING_DOWN:
ESP_LOGW(TAG, "Server shutting down");
break;
case MQTT5_BAD_AUTH_METHOD:
ESP_LOGW(TAG, "Bad authentication method");
break;
case MQTT5_KEEP_ALIVE_TIMEOUT:
ESP_LOGW(TAG, "Keep Alive timeout");
break;
case MQTT5_SESSION_TAKEN_OVER:
ESP_LOGW(TAG, "Session taken over");
break;
case MQTT5_TOPIC_FILTER_INVAILD:
ESP_LOGW(TAG, "Topic Filter invalid");
break;
case MQTT5_TOPIC_NAME_INVAILD:
ESP_LOGW(TAG, "Topic Name invalid");
break;
case MQTT5_PACKET_IDENTIFIER_IN_USE:
ESP_LOGW(TAG, "Packet Identifier in use");
break;
case MQTT5_PACKET_IDENTIFIER_NOT_FOUND:
ESP_LOGW(TAG, "Packet Identifier not found");
break;
case MQTT5_RECEIVE_MAXIMUM_EXCEEDED:
ESP_LOGW(TAG, "Receive Maximum exceeded");
break;
case MQTT5_TOPIC_ALIAS_INVAILD:
ESP_LOGW(TAG, "Topic Alias invalid");
break;
case MQTT5_PACKET_TOO_LARGE:
ESP_LOGW(TAG, "Packet too large");
break;
case MQTT5_MESSAGE_RATE_TOO_HIGH:
ESP_LOGW(TAG, "Message rate too high");
break;
case MQTT5_QUOTA_EXCEEDED:
ESP_LOGW(TAG, "Quota exceeded");
break;
case MQTT5_ADMINISTRATIVE_ACTION:
ESP_LOGW(TAG, "Administrative action");
break;
case MQTT5_PAYLOAD_FORMAT_INVAILD:
ESP_LOGW(TAG, "Payload format invalid");
break;
case MQTT5_RETAIN_NOT_SUPPORT:
ESP_LOGW(TAG, "Retain not supported");
break;
case MQTT5_QOS_NOT_SUPPORT:
ESP_LOGW(TAG, "QoS not supported");
break;
case MQTT5_USE_ANOTHER_SERVER:
ESP_LOGW(TAG, "Use another server");
break;
case MQTT5_SERVER_MOVED:
ESP_LOGW(TAG, "Server moved");
break;
case MQTT5_SHARED_SUBSCR_NOT_SUPPORTED:
ESP_LOGW(TAG, "Shared Subscriptions not supported");
break;
case MQTT5_CONNECTION_RATE_EXCEEDED:
ESP_LOGW(TAG, "Connection rate exceeded");
break;
case MQTT5_MAXIMUM_CONNECT_TIME:
ESP_LOGW(TAG, "Maximum connect time");
break;
case MQTT5_SUBSCRIBE_IDENTIFIER_NOT_SUPPORT:
ESP_LOGW(TAG, "Subscription Identifiers not supported");
break;
case MQTT5_WILDCARD_SUBSCRIBE_NOT_SUPPORT:
ESP_LOGW(TAG, "Wildcard Subscriptions not supported");
break;
default:
ESP_LOGW(TAG, "Connection refused, Unknow reason");
break;
}
}
esp_err_t esp_mqtt5_client_subscribe_check(esp_mqtt5_client_handle_t client, int qos)
{
/* Check Server support QoS level */
if (client->mqtt5_config->server_resp_property_info.max_qos < qos) {
ESP_LOGE(TAG, "Server only support max QoS level %d", client->mqtt5_config->server_resp_property_info.max_qos);
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t esp_mqtt5_client_publish_check(esp_mqtt5_client_handle_t client, int qos, int retain)
{
/* Check Server support QoS level */
if (client->mqtt5_config->server_resp_property_info.max_qos < qos) {
ESP_LOGE(TAG, "Server only support max QoS level %d", client->mqtt5_config->server_resp_property_info.max_qos);
return ESP_FAIL;
}
/* Check Server support RETAIN */
if (!client->mqtt5_config->server_resp_property_info.retain_available && retain) {
ESP_LOGE(TAG, "Server not support retain");
return ESP_FAIL;
}
/* Flow control to check PUBLISH(No PUBACK or PUBCOMP received) packet sent count(Only record QoS1 and QoS2)*/
if (client->send_publish_packet_count >= client->mqtt5_config->server_resp_property_info.receive_maximum) {
ESP_LOGE(TAG, "Client send more than %d QoS1 and QoS2 PUBLISH packet without no ack", client->mqtt5_config->server_resp_property_info.receive_maximum);
return ESP_FAIL;
}
return ESP_OK;
}
void esp_mqtt5_client_destory(esp_mqtt5_client_handle_t client)
{
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
if (client->mqtt5_config) {
free(client->mqtt5_config->will_property_info.content_type);
free(client->mqtt5_config->will_property_info.response_topic);
free(client->mqtt5_config->will_property_info.correlation_data);
free(client->mqtt5_config->server_resp_property_info.response_info);
esp_mqtt5_client_delete_topic_alias(client->mqtt5_config->peer_topic_alias);
esp_mqtt5_client_delete_user_property(client->mqtt5_config->connect_property_info.user_property);
esp_mqtt5_client_delete_user_property(client->mqtt5_config->will_property_info.user_property);
esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property);
free(client->mqtt5_config);
}
free(client->event.property);
}
}
static void esp_mqtt5_client_delete_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle)
{
if (topic_alias_handle) {
mqtt5_topic_alias_item_t item, tmp;
STAILQ_FOREACH_SAFE(item, topic_alias_handle, next, tmp) {
STAILQ_REMOVE(topic_alias_handle, item, mqtt5_topic_alias, next);
free(item->topic);
free(item);
}
free(topic_alias_handle);
}
}
static esp_err_t esp_mqtt5_client_update_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, char *topic, size_t topic_len)
{
mqtt5_topic_alias_item_t item;
bool found = false;
STAILQ_FOREACH(item, topic_alias_handle, next) {
if (item->topic_alias == topic_alias) {
found = true;
break;
}
}
if (found) {
if ((item->topic_len != topic_len) || strncmp(topic, item->topic, topic_len)) {
free(item->topic);
item->topic = calloc(1, topic_len);
ESP_MEM_CHECK(TAG, item->topic, return ESP_FAIL);
memcpy(item->topic, topic, topic_len);
item->topic_len = topic_len;
}
} else {
item = calloc(1, sizeof(mqtt5_topic_alias_t));
ESP_MEM_CHECK(TAG, item, return ESP_FAIL);
item->topic_alias = topic_alias;
item->topic_len = topic_len;
item->topic = calloc(1, topic_len);
ESP_MEM_CHECK(TAG, item->topic, {
free(item);
return ESP_FAIL;
});
memcpy(item->topic, topic, topic_len);
STAILQ_INSERT_TAIL(topic_alias_handle, item, next);
}
return ESP_OK;
}
static char *esp_mqtt5_client_get_topic_alias(mqtt5_topic_alias_handle_t topic_alias_handle, uint16_t topic_alias, size_t *topic_length)
{
mqtt5_topic_alias_item_t item;
STAILQ_FOREACH(item, topic_alias_handle, next) {
if (item->topic_alias == topic_alias) {
*topic_length = item->topic_len;
return item->topic;
}
}
*topic_length = 0;
return NULL;
}
static esp_err_t esp_mqtt5_user_property_copy(mqtt5_user_property_handle_t user_property_new, const mqtt5_user_property_handle_t user_property_old)
{
if (!user_property_new || !user_property_old) {
ESP_LOGE(TAG, "Input is NULL");
return ESP_FAIL;
}
mqtt5_user_property_item_t old_item, new_item;
STAILQ_FOREACH(old_item, user_property_old, next) {
new_item = calloc(1, sizeof(mqtt5_user_property_t));
ESP_MEM_CHECK(TAG, new_item, return ESP_FAIL);
new_item->key = strdup(old_item->key);
ESP_MEM_CHECK(TAG, new_item->key, {
free(new_item);
return ESP_FAIL;
});
new_item->value = strdup(old_item->value);
ESP_MEM_CHECK(TAG, new_item->value, {
free(new_item->key);
free(new_item);
return ESP_FAIL;
});
STAILQ_INSERT_TAIL(user_property_new, new_item, next);
}
return ESP_OK;
}
esp_err_t esp_mqtt5_client_set_publish_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_publish_property_config_t *property)
{
if (!client) {
ESP_LOGE(TAG, "Client was not initialized");
return ESP_ERR_INVALID_ARG;
}
MQTT_API_LOCK(client);
/* Check protocol version */
if(client->connect_info.protocol_ver != MQTT_PROTOCOL_V_5) {
ESP_LOGE(TAG, "MQTT protocol version is not v5");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
/* Check topic alias less than server maximum topic alias */
if (property->topic_alias > client->mqtt5_config->server_resp_property_info.topic_alias_maximum) {
ESP_LOGE(TAG, "Topic alias %d is bigger than server support %d", property->topic_alias, client->mqtt5_config->server_resp_property_info.topic_alias_maximum);
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
client->mqtt5_config->publish_property_info = property;
MQTT_API_UNLOCK(client);
return ESP_OK;
}
esp_err_t esp_mqtt5_client_set_subscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_subscribe_property_config_t *property)
{
if (!client) {
ESP_LOGE(TAG, "Client was not initialized");
return ESP_ERR_INVALID_ARG;
}
if (property->retain_handle > 2) {
ESP_LOGE(TAG, "retain_handle only support 0, 1, 2");
return -1;
}
MQTT_API_LOCK(client);
/* Check protocol version */
if (client->connect_info.protocol_ver != MQTT_PROTOCOL_V_5) {
ESP_LOGE(TAG, "MQTT protocol version is not v5");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
if (property->is_share_subscribe) {
if (property->no_local_flag) {
// MQTT-3.8.3-4 not allow that No Local bit to 1 on a Shared Subscription
ESP_LOGE(TAG, "Protocol error that no local flag set on shared subscription");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
if (!client->mqtt5_config->server_resp_property_info.shared_subscribe_available) {
ESP_LOGE(TAG, "MQTT broker not support shared subscribe");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
if (!property->share_name || !strlen(property->share_name)) {
ESP_LOGE(TAG, "Share name can't be empty for shared subscribe");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
}
client->mqtt5_config->subscribe_property_info = property;
MQTT_API_UNLOCK(client);
return ESP_OK;
}
esp_err_t esp_mqtt5_client_set_unsubscribe_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_unsubscribe_property_config_t *property)
{
if (!client) {
ESP_LOGE(TAG, "Client was not initialized");
return ESP_ERR_INVALID_ARG;
}
MQTT_API_LOCK(client);
/* Check protocol version */
if (client->connect_info.protocol_ver != MQTT_PROTOCOL_V_5) {
ESP_LOGE(TAG, "MQTT protocol version is not v5");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
if (property->is_share_subscribe) {
if (!client->mqtt5_config->server_resp_property_info.shared_subscribe_available) {
ESP_LOGE(TAG, "MQTT broker not support shared subscribe");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
if (!property->share_name || !strlen(property->share_name)) {
ESP_LOGE(TAG, "Share name can't be empty for shared subscribe");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
}
client->mqtt5_config->unsubscribe_property_info = property;
MQTT_API_UNLOCK(client);
return ESP_OK;
}
esp_err_t esp_mqtt5_client_set_disconnect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_disconnect_property_config_t *property)
{
if (!client) {
ESP_LOGE(TAG, "Client was not initialized");
return ESP_ERR_INVALID_ARG;
}
MQTT_API_LOCK(client);
/* Check protocol version */
if (client->connect_info.protocol_ver != MQTT_PROTOCOL_V_5) {
ESP_LOGE(TAG, "MQTT protocol version is not v5");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
if (property) {
if (property->session_expiry_interval) {
client->mqtt5_config->disconnect_property_info.session_expiry_interval = property->session_expiry_interval;
}
if (property->disconnect_reason) {
client->mqtt5_config->disconnect_property_info.disconnect_reason = property->disconnect_reason;
}
if (property->user_property) {
esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property);
client->mqtt5_config->disconnect_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
ESP_MEM_CHECK(TAG, client->mqtt5_config->disconnect_property_info.user_property, {
MQTT_API_UNLOCK(client);
return ESP_ERR_NO_MEM;
});
STAILQ_INIT(client->mqtt5_config->disconnect_property_info.user_property);
if (esp_mqtt5_user_property_copy(client->mqtt5_config->disconnect_property_info.user_property, property->user_property) != ESP_OK) {
ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail");
free(client->mqtt5_config->disconnect_property_info.user_property);
client->mqtt5_config->disconnect_property_info.user_property = NULL;
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
}
}
MQTT_API_UNLOCK(client);
return ESP_OK;
}
esp_err_t esp_mqtt5_client_set_connect_property(esp_mqtt5_client_handle_t client, const esp_mqtt5_connection_property_config_t *connect_property)
{
if (!client) {
ESP_LOGE(TAG, "Client was not initialized");
return ESP_ERR_INVALID_ARG;
}
MQTT_API_LOCK(client);
/* Check protocol version */
if (client->connect_info.protocol_ver != MQTT_PROTOCOL_V_5) {
ESP_LOGE(TAG, "MQTT protocol version is not v5");
MQTT_API_UNLOCK(client);
return ESP_FAIL;
}
if (connect_property) {
if (connect_property->session_expiry_interval) {
client->mqtt5_config->connect_property_info.session_expiry_interval = connect_property->session_expiry_interval;
}
if (connect_property->maximum_packet_size) {
if (connect_property->maximum_packet_size > client->mqtt_state.in_buffer_length) {
ESP_LOGW(TAG, "Connect maximum_packet_size property is over buffer_size(%d), Please first change it", client->mqtt_state.in_buffer_length);
MQTT_API_UNLOCK(client);
return ESP_FAIL;
} else {
client->mqtt5_config->connect_property_info.maximum_packet_size = connect_property->maximum_packet_size;
}
} else {
client->mqtt5_config->connect_property_info.maximum_packet_size = client->mqtt_state.in_buffer_length;
}
if (connect_property->receive_maximum) {
client->mqtt5_config->connect_property_info.receive_maximum = connect_property->receive_maximum;
}
if (connect_property->topic_alias_maximum) {
client->mqtt5_config->connect_property_info.topic_alias_maximum = connect_property->topic_alias_maximum;
if (!client->mqtt5_config->peer_topic_alias) {
client->mqtt5_config->peer_topic_alias = calloc(1, sizeof(struct mqtt5_topic_alias_list_t));
ESP_MEM_CHECK(TAG, client->mqtt5_config->peer_topic_alias, goto _mqtt_set_config_failed);
STAILQ_INIT(client->mqtt5_config->peer_topic_alias);
}
}
if (connect_property->request_resp_info) {
client->mqtt5_config->connect_property_info.request_resp_info = connect_property->request_resp_info;
}
if (connect_property->request_problem_info) {
client->mqtt5_config->connect_property_info.request_problem_info = connect_property->request_problem_info;
}
if (connect_property->user_property) {
esp_mqtt5_client_delete_user_property(client->mqtt5_config->connect_property_info.user_property);
client->mqtt5_config->connect_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
ESP_MEM_CHECK(TAG, client->mqtt5_config->connect_property_info.user_property, goto _mqtt_set_config_failed);
STAILQ_INIT(client->mqtt5_config->connect_property_info.user_property);
if (esp_mqtt5_user_property_copy(client->mqtt5_config->connect_property_info.user_property, connect_property->user_property) != ESP_OK) {
ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail");
goto _mqtt_set_config_failed;
}
}
if (connect_property->payload_format_indicator) {
client->mqtt5_config->will_property_info.payload_format_indicator = connect_property->payload_format_indicator;
}
if (connect_property->will_delay_interval) {
client->mqtt5_config->will_property_info.will_delay_interval = connect_property->will_delay_interval;
}
if (connect_property->message_expiry_interval) {
client->mqtt5_config->will_property_info.message_expiry_interval = connect_property->message_expiry_interval;
}
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(connect_property->content_type, &client->mqtt5_config->will_property_info.content_type), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(connect_property->response_topic, &client->mqtt5_config->will_property_info.response_topic), goto _mqtt_set_config_failed);
if (connect_property->correlation_data && connect_property->correlation_data_len) {
free(client->mqtt5_config->will_property_info.correlation_data);
client->mqtt5_config->will_property_info.correlation_data = malloc(connect_property->correlation_data_len);
ESP_MEM_CHECK(TAG, client->mqtt5_config->will_property_info.correlation_data, goto _mqtt_set_config_failed);
memcpy(client->mqtt5_config->will_property_info.correlation_data, connect_property->correlation_data, connect_property->correlation_data_len);
client->mqtt5_config->will_property_info.correlation_data_len = connect_property->correlation_data_len;
}
if (connect_property->will_user_property) {
esp_mqtt5_client_delete_user_property(client->mqtt5_config->will_property_info.user_property);
client->mqtt5_config->will_property_info.user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
ESP_MEM_CHECK(TAG, client->mqtt5_config->will_property_info.user_property, goto _mqtt_set_config_failed);
STAILQ_INIT(client->mqtt5_config->will_property_info.user_property);
if (esp_mqtt5_user_property_copy(client->mqtt5_config->will_property_info.user_property, connect_property->will_user_property) != ESP_OK) {
ESP_LOGE(TAG, "esp_mqtt5_user_property_copy fail");
goto _mqtt_set_config_failed;
}
}
}
MQTT_API_UNLOCK(client);
return ESP_OK;
_mqtt_set_config_failed:
esp_mqtt_destroy_config(client);
MQTT_API_UNLOCK(client);
return ESP_ERR_NO_MEM;
}
esp_err_t esp_mqtt5_client_set_user_property(mqtt5_user_property_handle_t *user_property, esp_mqtt5_user_property_item_t item[], uint8_t item_num)
{
if (!item_num || !item) {
ESP_LOGE(TAG, "Input value is NULL");
return ESP_FAIL;
}
if (!*user_property) {
*user_property = calloc(1, sizeof(struct mqtt5_user_property_list_t));
ESP_MEM_CHECK(TAG, *user_property, return ESP_ERR_NO_MEM);
STAILQ_INIT(*user_property);
}
for (int i = 0; i < item_num; i ++) {
if (item[i].key && item[i].value) {
mqtt5_user_property_item_t user_property_item = calloc(1, sizeof(mqtt5_user_property_t));
ESP_MEM_CHECK(TAG, user_property_item, goto err);
size_t key_len = strlen(item[i].key);
size_t value_len = strlen(item[i].value);
user_property_item->key = calloc(1, key_len + 1);
ESP_MEM_CHECK(TAG, user_property_item->key, {
free(user_property_item);
goto err;
});
memcpy(user_property_item->key, item[i].key, key_len);
user_property_item->key[key_len] = '\0';
user_property_item->value = calloc(1, value_len + 1);
ESP_MEM_CHECK(TAG, user_property_item->value, {
free(user_property_item->key);
free(user_property_item);
goto err;
});
memcpy(user_property_item->value, item[i].value, value_len);
user_property_item->value[value_len] = '\0';
STAILQ_INSERT_TAIL(*user_property, user_property_item, next);
}
}
return ESP_OK;
err:
esp_mqtt5_client_delete_user_property(*user_property);
*user_property = NULL;
return ESP_ERR_NO_MEM;
}
esp_err_t esp_mqtt5_client_get_user_property(mqtt5_user_property_handle_t user_property, esp_mqtt5_user_property_item_t *item, uint8_t *item_num)
{
int i = 0, j = 0;
if (user_property && item && *item_num) {
mqtt5_user_property_item_t user_property_item;
uint8_t num = *item_num;
STAILQ_FOREACH(user_property_item, user_property, next) {
if (i < num) {
size_t item_key_len = strlen(user_property_item->key);
size_t item_value_len = strlen(user_property_item->value);
char *key = calloc(1, item_key_len + 1);
ESP_MEM_CHECK(TAG, key, goto err);
memcpy(key, user_property_item->key, item_key_len);
key[item_key_len] = '\0';
char *value = calloc(1, item_value_len + 1);
ESP_MEM_CHECK(TAG, value, {
free(key);
goto err;
});
memcpy(value, user_property_item->value, item_value_len);
value[item_value_len] = '\0';
item[i].key = key;
item[i].value = value;
i ++;
} else {
break;
}
}
*item_num = i;
return ESP_OK;
} else {
ESP_LOGE(TAG, "Input value is NULL or item_num is 0");
return ESP_FAIL;
}
err:
for (j = 0; j < i; j ++) {
if (item[j].key) {
free((char *)item[j].key);
}
if (item[j].value) {
free((char *)item[j].value);
}
}
return ESP_ERR_NO_MEM;
}
uint8_t esp_mqtt5_client_get_user_property_count(mqtt5_user_property_handle_t user_property)
{
uint8_t count = 0;
if (user_property) {
mqtt5_user_property_item_t item;
STAILQ_FOREACH(item, user_property, next) {
count ++;
}
}
return count;
}
void esp_mqtt5_client_delete_user_property(mqtt5_user_property_handle_t user_property)
{
if (user_property) {
mqtt5_user_property_item_t item, tmp;
STAILQ_FOREACH_SAFE(item, user_property, next, tmp) {
STAILQ_REMOVE(user_property, item, mqtt5_user_property, next);
free(item->key);
free(item->value);
free(item);
}
}
free(user_property);
}

View File

@ -1,33 +1,4 @@
#include <stdio.h>
#include <stdlib.h>
#include "esp_err.h"
#include "platform.h"
#include "esp_event.h"
#include "mqtt_client.h"
#include "mqtt_msg.h"
#include "esp_transport.h"
#include "esp_transport_tcp.h"
#include "esp_transport_ssl.h"
#include "esp_transport_ws.h"
#include "esp_log.h"
#include "mqtt_outbox.h"
#include "freertos/event_groups.h"
#include <errno.h>
#include <string.h>
#include "mqtt_supported_features.h"
/* using uri parser */
#include "http_parser.h"
#ifdef MQTT_DISABLE_API_LOCKS
# define MQTT_API_LOCK(c)
# define MQTT_API_UNLOCK(c)
#else
# define MQTT_API_LOCK(c) xSemaphoreTakeRecursive(c->api_lock, portMAX_DELAY)
# define MQTT_API_UNLOCK(c) xSemaphoreGiveRecursive(c->api_lock)
#endif /* MQTT_USE_API_LOCKS */
#include "mqtt_client_priv.h"
_Static_assert(sizeof(uint64_t) == sizeof(outbox_tick_t), "mqtt-client tick type size different from outbox tick type");
#ifdef ESP_EVENT_ANY_ID
@ -49,90 +20,12 @@ ESP_EVENT_DEFINE_BASE(MQTT_EVENTS);
#define MQTT_OVER_WS_SCHEME "ws"
#define MQTT_OVER_WSS_SCHEME "wss"
typedef struct mqtt_state {
uint8_t *in_buffer;
uint8_t *out_buffer;
int in_buffer_length;
int out_buffer_length;
size_t message_length;
size_t in_buffer_read_len;
mqtt_message_t *outbound_message;
mqtt_connection_t mqtt_connection;
uint16_t pending_msg_id;
int pending_msg_type;
int pending_publish_qos;
int pending_msg_count;
} mqtt_state_t;
typedef struct {
mqtt_event_callback_t event_handle;
esp_event_loop_handle_t event_loop_handle;
int task_stack;
int task_prio;
char *uri;
char *host;
char *path;
char *scheme;
int port;
bool auto_reconnect;
void *user_context;
int network_timeout_ms;
int refresh_connection_after_ms;
int reconnect_timeout_ms;
char **alpn_protos;
int num_alpn_protos;
char *clientkey_password;
int clientkey_password_len;
bool use_global_ca_store;
esp_err_t ((*crt_bundle_attach)(void *conf));
const char *cacert_buf;
size_t cacert_bytes;
const char *clientcert_buf;
size_t clientcert_bytes;
const char *clientkey_buf;
size_t clientkey_bytes;
const struct psk_key_hint *psk_hint_key;
bool skip_cert_common_name_check;
bool use_secure_element;
void *ds_data;
int message_retransmit_timeout;
} mqtt_config_storage_t;
typedef enum {
MQTT_STATE_INIT = 0,
MQTT_STATE_DISCONNECTED,
MQTT_STATE_CONNECTED,
MQTT_STATE_WAIT_RECONNECT,
} mqtt_client_state_t;
struct esp_mqtt_client {
esp_transport_list_handle_t transport_list;
esp_transport_handle_t transport;
mqtt_config_storage_t *config;
mqtt_state_t mqtt_state;
mqtt_connect_info_t connect_info;
mqtt_client_state_t state;
uint64_t refresh_connection_tick;
int64_t keepalive_tick;
uint64_t reconnect_tick;
int wait_timeout_ms;
int auto_reconnect;
esp_mqtt_event_t event;
bool run;
bool wait_for_ping_resp;
outbox_handle_t outbox;
EventGroupHandle_t status_bits;
SemaphoreHandle_t api_lock;
TaskHandle_t task_handle;
};
const static int STOPPED_BIT = (1 << 0);
const static int RECONNECT_BIT = (1 << 1);
const static int DISCONNECT_BIT = (1 << 2);
static esp_err_t esp_mqtt_dispatch_event(esp_mqtt_client_handle_t client);
static esp_err_t esp_mqtt_dispatch_event_with_msgid(esp_mqtt_client_handle_t client);
static void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client);
static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_ms);
static void esp_mqtt_abort_connection(esp_mqtt_client_handle_t client);
static esp_err_t esp_mqtt_client_ping(esp_mqtt_client_handle_t client);
@ -362,7 +255,7 @@ static esp_err_t esp_mqtt_check_cfg_conflict(const mqtt_config_storage_t *cfg, c
return ret;
}
static bool set_if_config(char const *const new_config, char **old_config)
bool esp_mqtt_set_if_config(char const *const new_config, char **old_config)
{
if (new_config) {
free(*old_config);
@ -482,14 +375,14 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl
}
err = ESP_ERR_NO_MEM;
ESP_MEM_CHECK(TAG, set_if_config(config->host, &client->config->host), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, set_if_config(config->path, &client->config->path), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, set_if_config(config->username, &client->connect_info.username), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, set_if_config(config->password, &client->connect_info.password), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->host, &client->config->host), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->path, &client->config->path), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->username, &client->connect_info.username), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->password, &client->connect_info.password), goto _mqtt_set_config_failed);
if (!config->set_null_client_id) {
if (config->client_id) {
ESP_MEM_CHECK(TAG, set_if_config(config->client_id, &client->connect_info.client_id), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->client_id, &client->connect_info.client_id), goto _mqtt_set_config_failed);
} else if (client->connect_info.client_id == NULL) {
client->connect_info.client_id = platform_create_id_string();
}
@ -497,8 +390,8 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl
ESP_LOGD(TAG, "MQTT client_id=%s", client->connect_info.client_id);
}
ESP_MEM_CHECK(TAG, set_if_config(config->uri, &client->config->uri), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, set_if_config(config->lwt_topic, &client->connect_info.will_topic), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->uri, &client->config->uri), goto _mqtt_set_config_failed);
ESP_MEM_CHECK(TAG, esp_mqtt_set_if_config(config->lwt_topic, &client->connect_info.will_topic), goto _mqtt_set_config_failed);
if (config->lwt_msg_len && config->lwt_msg) {
free(client->connect_info.will_message);
@ -546,6 +439,11 @@ esp_err_t esp_mqtt_set_config(esp_mqtt_client_handle_t client, const esp_mqtt_cl
client->connect_info.protocol_ver = MQTT_PROTOCOL_V_3_1_1;
#else
client->connect_info.protocol_ver = MQTT_PROTOCOL_V_3_1;
#endif
} else if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifndef MQTT_PROTOCOL_5
ESP_LOGE(TAG, "Please first enable MQTT_PROTOCOL_5 feature in menuconfig");
goto _mqtt_set_config_failed;
#endif
}
@ -664,7 +562,7 @@ _mqtt_set_config_failed:
return err;
}
static void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client)
void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client)
{
if (client->config == NULL) {
return;
@ -683,6 +581,9 @@ static void esp_mqtt_destroy_config(esp_mqtt_client_handle_t client)
free(client->connect_info.client_id);
free(client->connect_info.username);
free(client->connect_info.password);
#ifdef MQTT_PROTOCOL_5
esp_mqtt5_client_destory(client);
#endif
memset(&client->connect_info, 0, sizeof(mqtt_connect_info_t));
#ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP
if (client->config->event_loop_handle) {
@ -746,18 +647,32 @@ static inline esp_err_t esp_mqtt_write(esp_mqtt_client_handle_t client)
static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_ms)
{
int read_len, connect_rsp_code;
int read_len, connect_rsp_code = 0;
client->wait_for_ping_resp = false;
client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection,
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->mqtt_state.outbound_message = mqtt5_msg_connect(&client->mqtt_state.mqtt_connection,
&client->connect_info, &client->mqtt5_config->connect_property_info, &client->mqtt5_config->will_property_info);
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_connect(&client->mqtt_state.mqtt_connection,
&client->connect_info);
}
if (client->mqtt_state.outbound_message->length == 0) {
ESP_LOGE(TAG, "Connect message cannot be created");
return ESP_FAIL;
}
client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data);
client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data,
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->mqtt_state.pending_msg_id = mqtt5_get_id(client->mqtt_state.outbound_message->data,
client->mqtt_state.outbound_message->length);
#endif
} else {
client->mqtt_state.pending_msg_id = mqtt_get_id(client->mqtt_state.outbound_message->data,
client->mqtt_state.outbound_message->length);
}
ESP_LOGD(TAG, "Sending MQTT CONNECT message, type: %d, id: %04X",
client->mqtt_state.pending_msg_type,
client->mqtt_state.pending_msg_id);
@ -784,28 +699,36 @@ static esp_err_t esp_mqtt_connect(esp_mqtt_client_handle_t client, int timeout_m
ESP_LOGE(TAG, "Invalid MSG_TYPE response: %d, read_len: %d", mqtt_get_type(client->mqtt_state.in_buffer), read_len);
return ESP_FAIL;
}
client->mqtt_state.in_buffer_read_len = 0;
connect_rsp_code = mqtt_get_connect_return_code(client->mqtt_state.in_buffer);
if (connect_rsp_code == MQTT_CONNECTION_ACCEPTED) {
ESP_LOGD(TAG, "Connected");
return ESP_OK;
}
switch (connect_rsp_code) {
case MQTT_CONNECTION_REFUSE_PROTOCOL:
ESP_LOGW(TAG, "Connection refused, bad protocol");
break;
case MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE:
ESP_LOGW(TAG, "Connection refused, server unavailable");
break;
case MQTT_CONNECTION_REFUSE_BAD_USERNAME:
ESP_LOGW(TAG, "Connection refused, bad username or password");
break;
case MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED:
ESP_LOGW(TAG, "Connection refused, not authorized");
break;
default:
ESP_LOGW(TAG, "Connection refused, Unknow reason");
break;
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
if (esp_mqtt5_parse_connack(client, &connect_rsp_code) == ESP_OK) {
return ESP_OK;
}
#endif
} else {
client->mqtt_state.in_buffer_read_len = 0;
connect_rsp_code = mqtt_get_connect_return_code(client->mqtt_state.in_buffer);
if (connect_rsp_code == MQTT_CONNECTION_ACCEPTED) {
ESP_LOGD(TAG, "Connected");
return ESP_OK;
}
switch (connect_rsp_code) {
case MQTT_CONNECTION_REFUSE_PROTOCOL:
ESP_LOGW(TAG, "Connection refused, bad protocol");
break;
case MQTT_CONNECTION_REFUSE_SERVER_UNAVAILABLE:
ESP_LOGW(TAG, "Connection refused, server unavailable");
break;
case MQTT_CONNECTION_REFUSE_BAD_USERNAME:
ESP_LOGW(TAG, "Connection refused, bad username or password");
break;
case MQTT_CONNECTION_REFUSE_NOT_AUTHORIZED:
ESP_LOGW(TAG, "Connection refused, not authorized");
break;
default:
ESP_LOGW(TAG, "Connection refused, Unknow reason");
break;
}
}
/* propagate event with connection refused error */
client->event.event_id = MQTT_EVENT_ERROR;
@ -888,7 +811,11 @@ esp_mqtt_client_handle_t esp_mqtt_client_init(const esp_mqtt_client_config_t *co
mqtt_msg_init(&client->mqtt_state.mqtt_connection, client->mqtt_state.out_buffer,
client->mqtt_state.out_buffer_length);
#ifdef MQTT_PROTOCOL_5
if (esp_mqtt5_create_default_config(client) != ESP_OK) {
goto _mqtt_init_failed;
}
#endif
return client;
_mqtt_init_failed:
esp_mqtt_client_destroy(client);
@ -998,12 +925,21 @@ static esp_err_t mqtt_write_data(esp_mqtt_client_handle_t client)
esp_mqtt_client_dispatch_transport_error(client);
return ESP_FAIL;
}
#ifdef MQTT_PROTOCOL_5
esp_mqtt5_flow_control(client);
#endif
return ESP_OK;
}
static esp_err_t esp_mqtt_dispatch_event_with_msgid(esp_mqtt_client_handle_t client)
{
client->event.msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->event.msg_id = mqtt5_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length);
#endif
} else {
client->event.msg_id = mqtt_get_id(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_length);
}
return esp_mqtt_dispatch_event(client);
}
@ -1011,17 +947,26 @@ static esp_err_t esp_mqtt_dispatch_event(esp_mqtt_client_handle_t client)
{
client->event.user_context = client->config->user_context;
client->event.client = client;
client->event.protocol_ver = client->connect_info.protocol_ver;
esp_err_t ret = ESP_FAIL;
if (client->config->event_handle) {
return client->config->event_handle(&client->event);
ret = client->config->event_handle(&client->event);
} else {
#ifdef MQTT_SUPPORTED_FEATURE_EVENT_LOOP
esp_event_post_to(client->config->event_loop_handle, MQTT_EVENTS, client->event.event_id, &client->event, sizeof(client->event), portMAX_DELAY);
return esp_event_loop_run(client->config->event_loop_handle, 0);
ret = esp_event_loop_run(client->config->event_loop_handle, 0);
#else
return ESP_FAIL;
#endif
}
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
esp_mqtt5_client_delete_user_property(client->event.property->user_property);
client->event.property->user_property = NULL;
#endif
}
return ret;
}
static esp_err_t deliver_publish(esp_mqtt_client_handle_t client)
@ -1033,28 +978,41 @@ static esp_err_t deliver_publish(esp_mqtt_client_handle_t client)
size_t msg_data_offset = 0;
char *msg_topic = NULL, *msg_data = NULL;
// get topic
msg_topic = mqtt_get_publish_topic(msg_buf, &msg_topic_len);
if (msg_topic == NULL) {
ESP_LOGE(TAG, "%s: mqtt_get_publish_topic() failed", __func__);
return ESP_FAIL;
}
ESP_LOGD(TAG, "%s: msg_topic_len=%zu", __func__, msg_topic_len);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
if (esp_mqtt5_get_publish_data(client, msg_buf, msg_read_len, &msg_topic, &msg_topic_len, &msg_data, &msg_data_len) != ESP_OK) {
ESP_LOGE(TAG, "%s: esp_mqtt5_get_publish_data() failed", __func__);
return ESP_FAIL;
}
#endif
} else {
// get topic
msg_topic = mqtt_get_publish_topic(msg_buf, &msg_topic_len);
if (msg_topic == NULL) {
ESP_LOGE(TAG, "%s: mqtt_get_publish_topic() failed", __func__);
return ESP_FAIL;
}
ESP_LOGD(TAG, "%s: msg_topic_len=%zu", __func__, msg_topic_len);
// get payload
msg_data = mqtt_get_publish_data(msg_buf, &msg_data_len);
if (msg_data_len > 0 && msg_data == NULL) {
ESP_LOGE(TAG, "%s: mqtt_get_publish_data() failed", __func__);
return ESP_FAIL;
// get payload
msg_data = mqtt_get_publish_data(msg_buf, &msg_data_len);
if (msg_data_len > 0 && msg_data == NULL) {
ESP_LOGE(TAG, "%s: mqtt_get_publish_data() failed", __func__);
return ESP_FAIL;
}
}
// post data event
client->event.retain = mqtt_get_retain(msg_buf);
client->event.msg_id = mqtt_get_id(msg_buf, msg_data_len);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->event.msg_id = mqtt5_get_id(msg_buf, msg_read_len);
#endif
} else {
client->event.msg_id = mqtt_get_id(msg_buf, msg_read_len);
}
client->event.qos = mqtt_get_qos(msg_buf);
client->event.dup = mqtt_get_dup(msg_buf);
client->event.total_data_len = msg_data_len + msg_total_len - msg_read_len;
post_data_event:
ESP_LOGD(TAG, "Get data len= %zu, topic len=%zu, total_data: %d offset: %zu", msg_data_len, msg_topic_len,
client->event.total_data_len, msg_data_offset);
@ -1093,7 +1051,13 @@ static esp_err_t deliver_suback(esp_mqtt_client_handle_t client)
size_t msg_data_len = client->mqtt_state.in_buffer_read_len;
char *msg_data = NULL;
msg_data = mqtt_get_suback_data(msg_buf, &msg_data_len);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
msg_data = mqtt5_get_suback_data(msg_buf, &msg_data_len, &client->event.property->user_property);
#endif
} else {
msg_data = mqtt_get_suback_data(msg_buf, &msg_data_len);
}
if (msg_data_len <= 0) {
ESP_LOGE(TAG, "Failed to acquire suback data");
return ESP_FAIL;
@ -1277,9 +1241,8 @@ err:
static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client)
{
uint8_t msg_type;
uint8_t msg_qos;
uint16_t msg_id;
uint8_t msg_type = 0, msg_qos = 0;
uint16_t msg_id = 0;
/* non-blocking receive in order not to block other tasks */
int recv = mqtt_message_receive(client, 0);
@ -1295,13 +1258,22 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client)
// If the message was valid, get the type, quality of service and id of the message
msg_type = mqtt_get_type(client->mqtt_state.in_buffer);
msg_qos = mqtt_get_qos(client->mqtt_state.in_buffer);
msg_id = mqtt_get_id(client->mqtt_state.in_buffer, read_len);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
msg_id = mqtt5_get_id(client->mqtt_state.in_buffer, read_len);
#endif
} else {
msg_id = mqtt_get_id(client->mqtt_state.in_buffer, read_len);
}
ESP_LOGD(TAG, "msg_type=%d, msg_id=%d", msg_type, msg_id);
switch (msg_type) {
case MQTT_MSG_TYPE_SUBACK:
if (is_valid_mqtt_msg(client, MQTT_MSG_TYPE_SUBSCRIBE, msg_id)) {
#ifdef MQTT_PROTOCOL_5
esp_mqtt5_parse_suback(client);
#endif
ESP_LOGD(TAG, "deliver_suback, message_length_read=%zu, message_length=%zu", client->mqtt_state.in_buffer_read_len, client->mqtt_state.message_length);
if (deliver_suback(client) != ESP_OK) {
ESP_LOGE(TAG, "Failed to deliver suback message id=%d", msg_id);
@ -1311,6 +1283,9 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client)
break;
case MQTT_MSG_TYPE_UNSUBACK:
if (is_valid_mqtt_msg(client, MQTT_MSG_TYPE_UNSUBSCRIBE, msg_id)) {
#ifdef MQTT_PROTOCOL_5
esp_mqtt5_parse_unsuback(client);
#endif
ESP_LOGD(TAG, "UnSubscribe successful");
client->event.event_id = MQTT_EVENT_UNSUBSCRIBED;
esp_mqtt_dispatch_event_with_msgid(client);
@ -1323,9 +1298,21 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client)
return ESP_FAIL;
}
if (msg_qos == 1) {
client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->mqtt_state.outbound_message = mqtt5_msg_puback(&client->mqtt_state.mqtt_connection, msg_id);
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_puback(&client->mqtt_state.mqtt_connection, msg_id);
}
} else if (msg_qos == 2) {
client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->mqtt_state.outbound_message = mqtt5_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id);
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_pubrec(&client->mqtt_state.mqtt_connection, msg_id);
}
}
if (client->mqtt_state.outbound_message->length == 0) {
ESP_LOGE(TAG, "Publish response message PUBACK or PUBREC cannot be created");
@ -1344,13 +1331,23 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client)
case MQTT_MSG_TYPE_PUBACK:
if (is_valid_mqtt_msg(client, MQTT_MSG_TYPE_PUBLISH, msg_id)) {
ESP_LOGD(TAG, "received MQTT_MSG_TYPE_PUBACK, finish QoS1 publish");
#ifdef MQTT_PROTOCOL_5
esp_mqtt5_parse_puback(client);
#endif
client->event.event_id = MQTT_EVENT_PUBLISHED;
esp_mqtt_dispatch_event_with_msgid(client);
}
break;
case MQTT_MSG_TYPE_PUBREC:
ESP_LOGD(TAG, "received MQTT_MSG_TYPE_PUBREC");
client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBREC return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
client->mqtt_state.outbound_message = mqtt5_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id);
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_pubrel(&client->mqtt_state.mqtt_connection, msg_id);
}
if (client->mqtt_state.outbound_message->length == 0) {
ESP_LOGE(TAG, "Publish response message PUBREL cannot be created");
return ESP_FAIL;
@ -1361,7 +1358,14 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client)
break;
case MQTT_MSG_TYPE_PUBREL:
ESP_LOGD(TAG, "received MQTT_MSG_TYPE_PUBREL");
client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
ESP_LOGI(TAG, "MQTT_MSG_TYPE_PUBREL return code is %d", mqtt5_msg_get_reason_code(client->mqtt_state.in_buffer, client->mqtt_state.in_buffer_read_len));
client->mqtt_state.outbound_message = mqtt5_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id);
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_pubcomp(&client->mqtt_state.mqtt_connection, msg_id);
}
if (client->mqtt_state.outbound_message->length == 0) {
ESP_LOGE(TAG, "Publish response message PUBCOMP cannot be created");
return ESP_FAIL;
@ -1373,6 +1377,9 @@ static esp_err_t mqtt_process_receive(esp_mqtt_client_handle_t client)
ESP_LOGD(TAG, "received MQTT_MSG_TYPE_PUBCOMP");
if (is_valid_mqtt_msg(client, MQTT_MSG_TYPE_PUBLISH, msg_id)) {
ESP_LOGD(TAG, "Receive MQTT_MSG_TYPE_PUBCOMP, finish QoS2 publish");
#ifdef MQTT_PROTOCOL_5
esp_mqtt5_parse_pubcomp(client);
#endif
client->event.event_id = MQTT_EVENT_PUBLISHED;
esp_mqtt_dispatch_event_with_msgid(client);
}
@ -1500,7 +1507,9 @@ static void esp_mqtt_task(void *pv)
break;
}
client->event.event_id = MQTT_EVENT_CONNECTED;
client->event.session_present = mqtt_get_connect_session_present(client->mqtt_state.in_buffer);
if (client->connect_info.protocol_ver != MQTT_PROTOCOL_V_5) {
client->event.session_present = mqtt_get_connect_session_present(client->mqtt_state.in_buffer);
}
client->state = MQTT_STATE_CONNECTED;
esp_mqtt_dispatch_event_with_msgid(client);
client->refresh_connection_tick = platform_tick_get_ms();
@ -1654,7 +1663,18 @@ esp_err_t esp_mqtt_client_reconnect(esp_mqtt_client_handle_t client)
static esp_err_t send_disconnect_msg(esp_mqtt_client_handle_t client)
{
// Notify the broker we are disconnecting
client->mqtt_state.outbound_message = mqtt_msg_disconnect(&client->mqtt_state.mqtt_connection);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->mqtt_state.outbound_message = mqtt5_msg_disconnect(&client->mqtt_state.mqtt_connection, &client->mqtt5_config->disconnect_property_info);
if (client->mqtt_state.outbound_message->length) {
esp_mqtt5_client_delete_user_property(client->mqtt5_config->disconnect_property_info.user_property);
client->mqtt5_config->disconnect_property_info.user_property = NULL;
memset(&client->mqtt5_config->disconnect_property_info, 0, sizeof(esp_mqtt5_disconnect_property_config_t));
}
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_disconnect(&client->mqtt_state.mqtt_connection);
}
if (client->mqtt_state.outbound_message->length == 0) {
ESP_LOGE(TAG, "Disconnect message cannot be created");
return ESP_FAIL;
@ -1729,9 +1749,25 @@ int esp_mqtt_client_subscribe(esp_mqtt_client_handle_t client, const char *topic
MQTT_API_UNLOCK(client);
return -1;
}
client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection,
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
if (esp_mqtt5_client_subscribe_check(client, qos) != ESP_OK) {
ESP_LOGI(TAG, "MQTT5 subscribe check fail");
MQTT_API_UNLOCK(client);
return -1;
}
client->mqtt_state.outbound_message = mqtt5_msg_subscribe(&client->mqtt_state.mqtt_connection,
topic, qos,
&client->mqtt_state.pending_msg_id, client->mqtt5_config->subscribe_property_info);
if (client->mqtt_state.outbound_message->length) {
client->mqtt5_config->subscribe_property_info = NULL;
}
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_subscribe(&client->mqtt_state.mqtt_connection,
topic, qos,
&client->mqtt_state.pending_msg_id);
}
if (client->mqtt_state.outbound_message->length == 0) {
ESP_LOGE(TAG, "Subscribe message cannot be created");
MQTT_API_UNLOCK(client);
@ -1770,9 +1806,20 @@ int esp_mqtt_client_unsubscribe(esp_mqtt_client_handle_t client, const char *top
ESP_LOGE(TAG, "Client has not connected");
return -1;
}
client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection,
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->mqtt_state.outbound_message = mqtt5_msg_unsubscribe(&client->mqtt_state.mqtt_connection,
topic,
&client->mqtt_state.pending_msg_id, client->mqtt5_config->unsubscribe_property_info);
if (client->mqtt_state.outbound_message->length) {
client->mqtt5_config->unsubscribe_property_info = NULL;
}
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_unsubscribe(&client->mqtt_state.mqtt_connection,
topic,
&client->mqtt_state.pending_msg_id);
}
if (client->mqtt_state.outbound_message->length == 0) {
MQTT_API_UNLOCK(client);
ESP_LOGE(TAG, "Unubscribe message cannot be created");
@ -1803,17 +1850,28 @@ static inline int mqtt_client_enqueue_priv(esp_mqtt_client_handle_t client, cons
int len, int qos, int retain, bool store)
{
uint16_t pending_msg_id = 0;
mqtt_message_t *publish_msg = mqtt_msg_publish(&client->mqtt_state.mqtt_connection,
topic, data, len,
qos, retain,
&pending_msg_id);
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
#ifdef MQTT_PROTOCOL_5
client->mqtt_state.outbound_message = mqtt5_msg_publish(&client->mqtt_state.mqtt_connection,
topic, data, len,
qos, retain,
&pending_msg_id, client->mqtt5_config->publish_property_info, client->mqtt5_config->server_resp_property_info.response_info);
if (client->mqtt_state.outbound_message->length) {
client->mqtt5_config->publish_property_info = NULL;
}
#endif
} else {
client->mqtt_state.outbound_message = mqtt_msg_publish(&client->mqtt_state.mqtt_connection,
topic, data, len,
qos, retain,
&pending_msg_id);
}
if (publish_msg->length == 0) {
if (client->mqtt_state.outbound_message->length == 0) {
ESP_LOGE(TAG, "Publish message cannot be created");
return -1;
}
/* We have to set as pending all the qos>0 messages */
client->mqtt_state.outbound_message = publish_msg;
if (qos > 0 || store) {
client->mqtt_state.pending_msg_type = mqtt_get_type(client->mqtt_state.outbound_message->data);
client->mqtt_state.pending_msg_id = pending_msg_id;
@ -1850,6 +1908,16 @@ int esp_mqtt_client_publish(esp_mqtt_client_handle_t client, const char *topic,
}
#endif
#ifdef MQTT_PROTOCOL_5
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
if (esp_mqtt5_client_publish_check(client, qos, retain) != ESP_OK) {
ESP_LOGI(TAG, "MQTT5 publish check fail");
MQTT_API_UNLOCK(client);
return -1;
}
}
#endif
/* Acceptable publish messages:
data == NULL, len == 0: publish null message
data valid, len == 0: publish all data, payload len is determined from string length
@ -1955,6 +2023,15 @@ int esp_mqtt_client_enqueue(esp_mqtt_client_handle_t client, const char *topic,
}
MQTT_API_LOCK(client);
#ifdef MQTT_PROTOCOL_5
if (client->connect_info.protocol_ver == MQTT_PROTOCOL_V_5) {
if (esp_mqtt5_client_publish_check(client, qos, retain) != ESP_OK) {
ESP_LOGI(TAG, "esp_mqtt_client_enqueue check fail");
MQTT_API_UNLOCK(client);
return -1;
}
}
#endif
int ret = mqtt_client_enqueue_priv(client, topic, data, len, qos, retain, store);
MQTT_API_UNLOCK(client);
if (ret == 0 && store == false) {
@ -2017,3 +2094,4 @@ int esp_mqtt_client_get_outbox_size(esp_mqtt_client_handle_t client)
return outbox_size;
}