Adds esp_mqtt_cxx component

- Component moved from esp-idf
- Files moved to adjust to esp-protocol structure
This commit is contained in:
Euripedes Rocha
2022-10-13 17:17:15 +02:00
parent 34c6f52f70
commit 1407dfc2e7
32 changed files with 526 additions and 107 deletions

View File

@ -0,0 +1,296 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <string_view>
#ifndef __cpp_exceptions
#error MQTT class can only be used when __cpp_exceptions is enabled. Enable CONFIG_COMPILER_CXX_EXCEPTIONS in Kconfig
#endif
#include <optional>
#include <variant>
#include <utility>
#include <memory>
#include <string>
#include "esp_exception.hpp"
#include "esp_mqtt_client_config.hpp"
#include "mqtt_client.h"
namespace idf::mqtt {
constexpr auto *TAG = "mqtt_client_cpp";
struct MQTTException : ESPException {
using ESPException::ESPException;
};
/**
* @brief QoS for publish and subscribe
*
* Sets the QoS as:
* AtMostOnce : Best effort delivery of messages. Message loss can occur.
* AtLeastOnce : Guaranteed delivery of messages. Duplicates can occur.
* ExactlyOnce : Guaranteed delivery of messages exactly once.
*
* @note
* When subscribing to a topic the QoS means the maximum QoS that should be sent to
* client on this topic
*/
enum class QoS { AtMostOnce = 0, AtLeastOnce = 1, ExactlyOnce = 2 };
/**
* @brief Sets if a message must be retained.
*
* Retained messages are delivered to future subscribers that match the topic name.
*
*/
enum class Retain : bool { NotRetained = false, Retained = true };
/**
* @brief Message class template to publish.
*
*/
template <typename T> struct Message {
T data; /*!< Data for publish. Should be a contiguous type*/
QoS qos = QoS::AtLeastOnce; /*!< QoS for the message*/
Retain retain = Retain::NotRetained; /*!< Retention mark for the message.*/
};
/**
* @brief Message type that holds std::string for data
*
*/
using StringMessage = Message<std::string>;
[[nodiscard]] bool filter_is_valid(std::string::const_iterator first, std::string::const_iterator last);
/**
* @brief Filter for mqtt topic subscription.
*
* Topic filter.
*
*/
class Filter {
public:
/**
* @brief Constructs the topic filter from the user filter
* @throws std::domain_error if the filter is invalid.
*
* @param user_filter Filter to be used.
*/
explicit Filter(std::string user_filter);
/**
* @brief Get the filter string used.
*
* @return Reference to the topic filter.
*/
const std::string &get();
/**
* @brief Checks the filter string against a topic name.
*
* @param topic_begin Iterator to the beginning of the sequence.
* @param topic_end Iterator to the end of the sequence.
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(std::string::const_iterator topic_begin, std::string::const_iterator topic_end) const noexcept;
/**
* @brief Checks the filter string against a topic name.
*
* @param topic topic name
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(const std::string &topic) const noexcept;
/**
* @brief Checks the filter string against a topic name.
*
* @param begin Char array with topic name.
* @param size Size of given topic name.
*
* @return true if the topic name match the filter
*/
[[nodiscard]] bool match(char *begin, int size) const noexcept;
private:
/**
* @brief Advance the topic to the next level.
*
* An mqtt topic ends with a /. This function is used to iterate in topic levels.
*
* @return Iterator to the start of the topic.
*/
[[nodiscard]] std::string::const_iterator advance(std::string::const_iterator begin, std::string::const_iterator end) const;
std::string filter;
};
/**
* @brief Message identifier to track delivery.
*
*/
enum class MessageID : int {};
/**
* @brief Base class for MQTT client
*
* Should be inherited to provide event handlers.
*/
class Client {
public:
/**
* @brief Constructor of the client
*
* @param broker Configuration for broker connection
* @param credentials client credentials to be presented to the broker
* @param config Mqtt client configuration
*/
Client(const BrokerConfiguration &broker, const ClientCredentials &credentials, const Configuration &config);
/**
* @brief Constructs Client using the same configuration used for
* `esp_mqtt_client`
* @param config config struct to `esp_mqtt_client`
*/
Client(const esp_mqtt_client_config_t &config);
/**
* @brief Subscribe to topic
*
* @param topic_filter MQTT topic filter
* @param qos QoS subscription, defaulted as QoS::AtLeastOnce
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
std::optional<MessageID> subscribe(const std::string &topic_filter, QoS qos = QoS::AtLeastOnce);
/**
* @brief publish message to topic
*
* @tparam Container Type for data container. Must be a contiguous memory.
* @param topic Topic name
* @param message Message struct containing data, qos and retain
* configuration.
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
template <class Container>
std::optional<MessageID> publish(const std::string &topic, const Message<Container> &message)
{
return publish(topic, std::begin(message.data), std::end(message.data), message.qos, message.retain);
}
/**
* @brief publish message to topic
*
* @tparam InputIt Input data iterator type.
* @param topic Topic name
* @param first, last Iterator pair of data to publish
* @param qos Set qos message
* @param retain Set if message should be retained
*
* @return Optional MessageID. In case of failure std::nullopt is returned.
*/
template <class InputIt>
std::optional<MessageID> publish(const std::string &topic, InputIt first, InputIt last, QoS qos = QoS::AtLeastOnce, Retain retain = Retain::NotRetained)
{
auto size = std::distance(first, last);
auto res = esp_mqtt_client_publish(handler.get(), topic.c_str(), &(*first), size, static_cast<int>(qos),
static_cast<int>(retain));
if (res < 0) {
return std::nullopt;
}
return MessageID{res};
}
virtual ~Client() = default;
protected:
/**
* @brief Helper type to be used as custom deleter for std::unique_ptr.
*/
struct MqttClientDeleter {
void operator()(esp_mqtt_client *client_handler)
{
esp_mqtt_client_destroy(client_handler);
}
};
/**
* @brief Type of the handler for the underlying mqtt_client handler.
* It uses std::unique_ptr for lifetime management
*/
using ClientHandler = std::unique_ptr<esp_mqtt_client, MqttClientDeleter>;
/**
* @brief esp_mqtt_client handler
*
*/
ClientHandler handler;
/**
* @brief Called if there is an error event
*
* @param event mqtt event data
*/
virtual void on_error(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an disconnection event
*
* @param event mqtt event data
*/
virtual void on_disconnected(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an subscribed event
*
* @param event mqtt event data
*/
virtual void on_subscribed(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an unsubscribed event
*
* @param event mqtt event data
*/
virtual void on_unsubscribed(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an published event
*
* @param event mqtt event data
*/
virtual void on_published(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an before connect event
*
* @param event mqtt event data
*/
virtual void on_before_connect(const esp_mqtt_event_handle_t event);
/**
* @brief Called if there is an connected event
*
* @param event mqtt event data
*
*/
virtual void on_connected(const esp_mqtt_event_handle_t event) = 0;
/**
* @brief Called if there is an data event
*
* @param event mqtt event data
*
*/
virtual void on_data(const esp_mqtt_event_handle_t event) = 0;
private:
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id,
void *event_data) noexcept;
void init(const esp_mqtt_client_config_t &config);
};
} // namespace idf::mqtt

View File

@ -0,0 +1,213 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <cstddef>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "mqtt_client.h"
namespace idf::mqtt {
/**
* @brief Broker addresss
*
* Use this to set the broker without parsing the URI string.
*
*/
struct Host {
std::string address; /*!< Host name*/
std::string path; /*!< Route path of the broker in host*/
esp_mqtt_transport_t transport; /*!< Transport scheme to use. */
};
/**
* @brief Broker addresss URI
*
* Use this to set the broker address using the URI.
*
*/
struct URI {
std::string address; /*!< Broker adddress URI*/
};
/**
* @brief Broker addresss.
*
*/
struct BrokerAddress {
std::variant<Host, URI> address; /*!< Address, defined by URI or Host struct */
uint32_t port = 0; /*!< Port used, defaults to 0 to select common port for the scheme used */
};
/**
* @brief PEM formated data
*
* Store certificates, keys and cryptographic data.
*
*/
struct PEM {
const char *data;
};
/**
* @brief DER formated data
*
* Store certificates, keys and cryptographic data.
*
*/
struct DER {
const char *data;
size_t len;
};
/**
* @brief Holds cryptography related information
*
* Hold PEM or DER formated cryptographic data.
*
*/
using CryptographicInformation = std::variant<PEM, DER>;
/**
* @brief Do not verify broker certificate.
*
* To be used when doing MQTT over TLS connection but not verify broker's certificates.
*
*/
struct Insecure {};
/**
* @brief Use global CA store
*
* To be used when client should use the Global CA Store to get trusted certificates for the broker.
*
*/
struct GlobalCAStore {};
/**
* @brief Use a pre shared key for broker authentication.
*
* To be used when client should use a PSK to authenticate the broker.
*
*/
struct PSK {
const struct psk_key_hint *hint_key;/* Pointer to PSK struct defined in esp_tls.h to enable PSK authentication */
};
/**
* @brief Authentication method for Broker
*
* Selects the method for authentication based on the type it holds.
*
*/
using BrokerAuthentication = std::variant<Insecure, GlobalCAStore, CryptographicInformation, PSK>;
/**
* @brief Password related data.
*
*/
struct Password {
std::string data;
};
/**
* @brief Data to authenticate client with certificates.
*
*/
struct ClientCertificate {
CryptographicInformation certificate; /*!< Certificate in PEM or DER format.*/
CryptographicInformation key; /*!< Key data in PEM or DER format.*/
std::optional<Password> key_password = std::nullopt; /*!< Optional password for key */
};
/**
* @brief Used to select usage of Secure Element
*
* Enables the usage of the secure element present in ESP32-WROOM-32SE.
*
*/
struct SecureElement {};
/**
* @brief Used to select usage of Digital Signature Peripheral.
*
* Enables the usage of the Digital Signature hardware accelerator.
*
*/
struct DigitalSignatureData {
void *ds_data; /* carrier of handle for digital signature parameters */
};
using AuthenticationFactor = std::variant<Password, ClientCertificate, SecureElement>;
struct BrokerConfiguration {
BrokerAddress address;
BrokerAuthentication security;
};
struct ClientCredentials {
std::optional<std::string> username; // MQTT username
AuthenticationFactor authentication;
std::vector<std::string> alpn_protos; /*!< List of supported application protocols to be used for ALPN */
/* default is ``ESP32_%CHIPID%`` where %CHIPID% are last 3 bytes of MAC address in hex format */
std::optional<std::string > client_id = std::nullopt;
};
struct Event {
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 */
};
struct LastWill {
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 */
};
struct Session {
LastWill last_will;
int disable_clean_session; /*!< mqtt clean session, default clean_session is true */
int keepalive; /*!< mqtt keepalive, default is 120 seconds */
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_protocol_ver_t protocol_ver; /*!< MQTT protocol version used for connection, defaults to value from menuconfig*/
};
struct Task {
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`` */
};
struct Connection {
esp_mqtt_transport_t transport; /*!< overrides URI transport */
int reconnect_timeout_ms; /*!< Reconnect to the broker after this value in miliseconds if auto reconnect is not disabled (defaults to 10s) */
int network_timeout_ms; /*!< Abort network operation if it is not completed after this value, in milliseconds (defaults to 10s) */
int refresh_connection_after_ms; /*!< Refresh connection after this value (in milliseconds) */
bool disable_auto_reconnect; /*!< this mqtt client will reconnect to server (when errors/disconnect). Set disable_auto_reconnect=true to disable */
};
struct Configuration {
Event event;
Task task;
Session session;
Connection connection;
void *user_context; /*!< pass user context to this option, then can receive that context in ``event->user_context`` */
int buffer_size; /*!< size of MQTT send/receive buffer, default is 1024 (only receive buffer size if ``out_buffer_size`` defined) */
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`` */
};
} // idf::mqtt