Răsfoiți Sursa

experimental/mqtt_cxx: Adds a C++ Mqtt client wrapper

- Base class with separated event handlers for each Mqtt client event.
- Topic matcher added to support data events.
- Filter class to allow only mqtt valid topic filters.
- Initial code for unit test on the host.
Euripedes Rocha Filho 5 ani în urmă
părinte
comite
d53f0e074c
21 a modificat fișierele cu 1084 adăugiri și 0 ștergeri
  1. 25 0
      examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/CMakeLists.txt
  2. 5 0
      examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/component.mk
  3. 299 0
      examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/esp_mqtt_cxx.cpp
  4. 229 0
      examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/include/esp_mqtt.hpp
  5. 221 0
      examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/include/esp_mqtt_client_config.hpp
  6. 14 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/CMakeLists.txt
  7. 12 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/Makefile
  8. 4 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/main/CMakeLists.txt
  9. 9 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/main/Kconfig.projbuild
  10. 1 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/main/component.mk
  11. 27 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/main/mqtt_eclipse_org.pem
  12. 30 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/main/mqtt_eclipseprojects_io.pem
  13. 85 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/main/mqtt_ssl_example.cpp
  14. 3 0
      examples/cxx/experimental/esp_mqtt_cxx/ssl/sdkconfig.defaults
  15. 12 0
      examples/cxx/experimental/esp_mqtt_cxx/tcp/CMakeLists.txt
  16. 12 0
      examples/cxx/experimental/esp_mqtt_cxx/tcp/Makefile
  17. 3 0
      examples/cxx/experimental/esp_mqtt_cxx/tcp/main/CMakeLists.txt
  18. 9 0
      examples/cxx/experimental/esp_mqtt_cxx/tcp/main/Kconfig.projbuild
  19. 0 0
      examples/cxx/experimental/esp_mqtt_cxx/tcp/main/component.mk
  20. 81 0
      examples/cxx/experimental/esp_mqtt_cxx/tcp/main/mqtt_tcp_example.cpp
  21. 3 0
      examples/cxx/experimental/esp_mqtt_cxx/tcp/sdkconfig.defaults

+ 25 - 0
examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/CMakeLists.txt

@@ -0,0 +1,25 @@
+idf_build_get_property(target IDF_TARGET)
+
+idf_component_register(SRCS "esp_mqtt_cxx.cpp"
+                    INCLUDE_DIRS "include"
+                    )
+
+target_compile_options(${COMPONENT_LIB} PRIVATE "-std=gnu++17")
+
+if(TEST_BUILD)
+    message(STATUS "Test build")
+    idf_component_get_property(mqtt_dir mqtt COMPONENT_DIR)
+    idf_component_get_property(experimental_cpp_component_dir experimental_cpp_component COMPONENT_DIR)
+    idf_component_get_property(esp_common_dir esp_common COMPONENT_DIR)
+    idf_component_get_property(esp_event_dir esp_event COMPONENT_DIR)
+    target_include_directories(${COMPONENT_LIB} PUBLIC ${mqtt_dir}/esp-mqtt/include
+                                                       ${esp_event_dir}/include
+                                                       ${experimental_cpp_component_dir}/include
+                                                       ${esp_common_dir}/include)
+
+else()
+    idf_component_get_property(mqtt_lib mqtt COMPONENT_LIB)
+    idf_component_get_property(log_lib log COMPONENT_LIB)
+    idf_component_get_property(experimental_cpp_component_lib experimental_cpp_component COMPONENT_LIB)
+    target_link_libraries(${COMPONENT_LIB} PUBLIC ${log_lib} ${mqtt_lib} ${experimental_cpp_component_lib})
+endif()

+ 5 - 0
examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/component.mk

@@ -0,0 +1,5 @@
+COMPONENT_ADD_INCLUDEDIRS := include
+
+COMPONENT_SRCDIRS := ./
+
+CXXFLAGS += -std=gnu++17

+ 299 - 0
examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/esp_mqtt_cxx.cpp

@@ -0,0 +1,299 @@
+// Copyright 2021 Espressif Systems (Shanghai) CO LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//         http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <algorithm>
+
+#include "mqtt_client.h"
+#include "esp_log.h"
+
+#include "esp_mqtt.hpp"
+#include "esp_mqtt_client_config.hpp"
+
+namespace {
+
+// Helper for static assert.
+template<class T>
+constexpr bool always_false = false;
+
+template<class... Ts> struct overloaded : Ts... {
+    using Ts::operator()...;
+};
+template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
+
+using namespace idf::mqtt;
+
+/*
+ *  This function is responsible for fill in the configurations for the broker related data
+ *  of mqtt_client_config_t
+ */
+void config_broker(esp_mqtt_client_config_t &mqtt_client_cfg, BrokerConfiguration const &broker)
+{
+    std::visit(overloaded{
+        [&mqtt_client_cfg](Host const & host)
+        {
+            mqtt_client_cfg.host = host.address.c_str();
+            mqtt_client_cfg.path = host.path.c_str();
+            mqtt_client_cfg.transport = host.transport;
+        },
+        [&mqtt_client_cfg](URI const & uri)
+        {
+            mqtt_client_cfg.uri = uri.address.c_str();
+        },
+        []([[maybe_unused ]]auto & unknown)
+        {
+            static_assert(always_false<decltype(unknown)>, "Missing type handler for variant handler");
+        }
+    },
+    broker.address.address);
+
+    std::visit(overloaded{
+        []([[maybe_unused]]Insecure const & insecure) {},
+        [&mqtt_client_cfg](GlobalCAStore const & use_global_store)
+        {
+            mqtt_client_cfg.use_global_ca_store = true;
+        },
+        [&mqtt_client_cfg](CryptographicInformation const & certificates)
+        {
+            std::visit(overloaded{
+                [&mqtt_client_cfg](PEM const & pem)
+                {
+                    mqtt_client_cfg.cert_pem = pem.data;
+                }, [&mqtt_client_cfg](DER const & der)
+                {
+                    mqtt_client_cfg.cert_pem = der.data;
+                    mqtt_client_cfg.cert_len = der.len;
+                }}, certificates);
+        },
+        []([[maybe_unused]] PSK const & psk) {},
+        []([[maybe_unused]] auto & unknown)
+        {
+            static_assert(always_false<decltype(unknown)>, "Missing type handler for variant handler");
+        }
+    },
+    broker.security);
+    mqtt_client_cfg.port = broker.address.port;
+}
+
+/*
+ *  This function is responsible for fill in the configurations for the client credentials related data
+ *  of mqtt_client_config_t
+ */
+void config_client_credentials(esp_mqtt_client_config_t &mqtt_client_cfg, ClientCredentials const &credentials)
+{
+    mqtt_client_cfg.client_id = credentials.client_id.has_value() ?  credentials.client_id.value().c_str() : nullptr ;
+    mqtt_client_cfg.username = credentials.username.has_value() ?  credentials.username.value().c_str() : nullptr ;
+    std::visit(overloaded{
+        [&mqtt_client_cfg](Password const & password)
+        {
+            mqtt_client_cfg.password = password.data.c_str();
+        },
+        [](ClientCertificate const & certificate) {},
+        [](SecureElement const & enable_secure_element) {},
+        []([[maybe_unused ]]auto & unknown)
+        {
+            static_assert(always_false<decltype(unknown)>, "Missing type handler for variant handler");
+        }
+    }, credentials.authentication);
+}
+
+esp_mqtt_client_config_t make_config(BrokerConfiguration const &broker, ClientCredentials const  &credentials, Configuration const &config)
+{
+    esp_mqtt_client_config_t mqtt_client_cfg{};
+    config_broker(mqtt_client_cfg, broker);
+    config_client_credentials(mqtt_client_cfg, credentials);
+    return mqtt_client_cfg;
+}
+}
+
+namespace idf::mqtt {
+
+Client::Client(BrokerConfiguration const &broker, ClientCredentials const  &credentials, Configuration const &config): Client(make_config(broker, credentials, config))  {}
+
+Client::Client(esp_mqtt_client_config_t const &config) :  handler(esp_mqtt_client_init(&config))
+{
+    if (handler == nullptr) {
+        throw MQTTException(ESP_FAIL);
+    };
+    CHECK_THROW_SPECIFIC(esp_mqtt_client_register_event(handler.get(), MQTT_EVENT_ANY, mqtt_event_handler, this), mqtt::MQTTException);
+    CHECK_THROW_SPECIFIC(esp_mqtt_client_start(handler.get()), mqtt::MQTTException);
+}
+
+
+void Client::mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) noexcept
+{
+    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
+    auto *event = static_cast<esp_mqtt_event_t *>(event_data);
+    auto &client = *static_cast<Client *>(handler_args);
+    switch (event->event_id) {
+    case MQTT_EVENT_CONNECTED:
+        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
+        client.on_connected(event);
+        break;
+    case MQTT_EVENT_DISCONNECTED:
+        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
+        client.on_disconnected(event);
+        break;
+
+    case MQTT_EVENT_SUBSCRIBED:
+        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
+        client.on_subscribed(event);
+        break;
+    case MQTT_EVENT_UNSUBSCRIBED:
+        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
+        client.on_unsubscribed(event);
+        break;
+    case MQTT_EVENT_PUBLISHED:
+        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
+        client.on_published(event);
+        break;
+    case MQTT_EVENT_DATA:
+        ESP_LOGI(TAG, "MQTT_EVENT_DATA");
+        client.on_data(event);
+        break;
+    case MQTT_EVENT_ERROR:
+        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
+        client.on_error(event);
+        break;
+    case MQTT_EVENT_BEFORE_CONNECT:
+        ESP_LOGI(TAG, "MQTT_EVENT_BEFORE_CONNECT");
+        client.on_before_connect(event);
+        break;
+    default:
+        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
+        break;
+    }
+}
+
+void Client::on_error(esp_mqtt_event_handle_t const event)
+{
+    auto log_error_if_nonzero = [](const char *message, int error_code) {
+        if (error_code != 0) {
+            ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
+        }
+    };
+    if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
+        log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
+        log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
+        log_error_if_nonzero("captured as transport's socket errno",  event->error_handle->esp_transport_sock_errno);
+        ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
+    }
+}
+void Client::on_disconnected(esp_mqtt_event_handle_t const event)
+{
+}
+void Client::on_subscribed(esp_mqtt_event_handle_t const event)
+{
+    printf("Subscribed to %.*s\r\n", event->topic_len, event->topic);
+}
+void Client::on_unsubscribed(esp_mqtt_event_handle_t const event)
+{
+}
+void Client::on_published(esp_mqtt_event_handle_t const event)
+{
+}
+void Client::on_before_connect(esp_mqtt_event_handle_t const event)
+{
+}
+void Client::on_connected(esp_mqtt_event_handle_t const event)
+{
+}
+void Client::on_data(esp_mqtt_event_handle_t const event)
+{
+}
+
+std::optional<MessageID> Client::subscribe(std::string const &topic, QoS qos)
+{
+    auto res = esp_mqtt_client_subscribe(handler.get(), topic.c_str(),
+                                         static_cast<int>(qos));
+    if (res < 0) {
+        return std::nullopt;
+    }
+    return MessageID{res};
+}
+
+bool is_valid(std::string::const_iterator first, std::string::const_iterator last)
+{
+    if (first == last) {
+        return false;
+    }
+    auto number = std::find(first, last, '#');
+    if (number != last) {
+        if (std::next(number) != last) {
+            return false;
+        }
+        if (*std::prev(number) != '/' && number != first) {
+            return false;
+        }
+    }
+
+    auto plus = std::find(first, last, '+');
+    if (plus != last) {
+        if (*(std::prev(plus)) != '/' && plus != first) {
+            return false;
+        }
+        if (std::next(plus) != last && *(std::next(plus)) != '/') {
+            return false;
+        }
+    }
+    return true;
+}
+
+Filter::Filter(std::string user_filter) : filter(std::move(user_filter))
+{
+    if (!is_valid(filter.begin(), filter.end())) {
+        throw std::domain_error("Forbidden Filter string");
+    }
+}
+
+[[nodiscard]] bool Filter::match(std::string::const_iterator topic_begin, std::string::const_iterator topic_end) const noexcept
+{
+    auto filter_begin = filter.begin();
+    auto filter_end = filter.end();
+    for (auto mismatch = std::mismatch(filter_begin, filter_end, topic_begin);
+            mismatch.first != filter.end() and mismatch.second != topic_end;
+            mismatch = std::mismatch(filter_begin, filter_end, topic_begin)) {
+        if (*mismatch.first != '#' and * mismatch.first != '+') {
+            return false;
+        }
+        if (*mismatch.first == '#') {
+            return true;
+        }
+        if (*mismatch.first == '+') {
+            filter_begin = advance(mismatch.first, filter_end);
+            topic_begin = advance(mismatch.second, topic_end);
+            if (filter_begin == filter_end and topic_begin != topic_end) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+const std::string &Filter::get()
+{
+    return filter;
+}
+
+[[nodiscard]] bool Filter::match(char const *const first, int size) const noexcept
+{
+    auto it = static_cast<std::string::const_iterator>(first);
+    return match(it, it + size);
+}
+std::string::const_iterator Filter::advance(std::string::const_iterator first, std::string::const_iterator last) const
+{
+    constexpr auto separator = '/';
+    return std::find(first, last, separator);
+}
+
+}

+ 229 - 0
examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/include/esp_mqtt.hpp

@@ -0,0 +1,229 @@
+// Copyright 2021 Espressif Systems (Shanghai) CO LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//         http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#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.
+ *
+ */
+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.
+ * @throws std::domain_error if the filter is invalid.
+ *
+ * Topic filter.
+ *
+ */
+class Filter {
+public:
+
+    explicit Filter(std::string user_filter);
+
+
+    /**
+     * @brief Get the filter string used.
+     *
+     */
+    const std::string &get();
+
+    /**
+     * @brief Checks the filter string against a topic name.
+     *
+     * @param first Iterator to the beginning of the sequence.
+     * @param last  Iterator to the end of the sequence.
+     *
+     * @return true if the topic name match the filter
+     */
+    [[nodiscard]] bool match(std::string::const_iterator first,
+                             std::string::const_iterator last) 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 first Char array with topic name.
+     * @param last  Size of given topic name.
+     *
+     * @return true if the topic name match the filter
+     */
+    [[nodiscard]] bool match(const char *const 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:
+
+    Client(const BrokerConfiguration &broker,const  ClientCredentials &credentials,const  Configuration &config);
+
+    Client(const esp_mqtt_client_config_t &config);
+
+    /**
+     * @brief Subscribe to topic
+     *
+     * @param 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 &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 message Message struct containing data, qos and retain configuration.
+     *
+     * @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:
+    struct MqttClientDeleter {
+        void operator()(esp_mqtt_client *client_handler)
+        {
+            esp_mqtt_client_destroy(client_handler);
+        }
+    };
+
+    using ClientHandler = std::unique_ptr<esp_mqtt_client, MqttClientDeleter>;
+    ClientHandler handler;
+
+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);
+    virtual void on_error(const esp_mqtt_event_handle_t event);
+    virtual void on_disconnected(const esp_mqtt_event_handle_t event);
+    virtual void on_subscribed(const esp_mqtt_event_handle_t event);
+    virtual void on_unsubscribed(const esp_mqtt_event_handle_t event);
+    virtual void on_published(const esp_mqtt_event_handle_t event);
+    virtual void on_before_connect(const esp_mqtt_event_handle_t event);
+    virtual void on_connected(const esp_mqtt_event_handle_t event) = 0;
+    virtual void on_data(const esp_mqtt_event_handle_t event) = 0;
+};
+} // namespace idf::mqtt

+ 221 - 0
examples/cxx/experimental/esp_mqtt_cxx/components/esp_mqtt_cxx/include/esp_mqtt_client_config.hpp

@@ -0,0 +1,221 @@
+// Copyright 2021 Espressif Systems (Shanghai) CO LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//         http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#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

+ 14 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/CMakeLists.txt

@@ -0,0 +1,14 @@
+# The following four lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+# (Not part of the boilerplate)
+# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
+set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common
+                         $ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component
+                         $ENV{IDF_PATH}/examples/cxx/experimental/esp_mqtt_cxx/components)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(mqtt_ssl_cxx)
+
+target_add_binary_data(mqtt_ssl_cxx.elf "main/mqtt_eclipseprojects_io.pem" TEXT)

+ 12 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/Makefile

@@ -0,0 +1,12 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+PROJECT_NAME := mqtt_tcp_cxx
+
+EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/common_components/protocol_examples_common
+EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/cxx/experimental/esp_mqtt_cxx/components
+EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/cxx/experimental/experimental_cpp_component
+
+CXXFLAGS += -std=gnu++17
+include $(IDF_PATH)/make/project.mk

+ 4 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/main/CMakeLists.txt

@@ -0,0 +1,4 @@
+idf_component_register(SRCS "mqtt_ssl_example.cpp"
+                    INCLUDE_DIRS ".")
+
+target_compile_options(${COMPONENT_LIB} PRIVATE "-std=gnu++17")

+ 9 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/main/Kconfig.projbuild

@@ -0,0 +1,9 @@
+menu "Example Configuration"
+
+    config BROKER_URI
+        string "Broker URL"
+        default "mqtts://mqtt.eclipse.org:8883"
+        help
+            URL of the broker to connect to
+
+endmenu

+ 1 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/main/component.mk

@@ -0,0 +1 @@
+COMPONENT_EMBED_TXTFILES := mqtt_eclipseprojects_io.pem

+ 27 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/main/mqtt_eclipse_org.pem

@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/
+MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
+DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow
+SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
+GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
+q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8
+SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
+Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA
+a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
+/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T
+AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
+CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv
+bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
+c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw
+VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
+ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz
+MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
+Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF
+AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
+uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/
+wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
+X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG
+PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
+KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
+-----END CERTIFICATE-----

+ 30 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/main/mqtt_eclipseprojects_io.pem

@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
+TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
+cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw
+WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg
+RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP
+R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx
+sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm
+NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg
+Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG
+/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC
+AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB
+Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA
+FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw
+AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw
+Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB
+gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W
+PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl
+ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz
+CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm
+lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4
+avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2
+yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O
+yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids
+hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+
+HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv
+MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
+nLRbwHOoq7hHwg==
+-----END CERTIFICATE-----

+ 85 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/main/mqtt_ssl_example.cpp

@@ -0,0 +1,85 @@
+/* C++ MQTT (over TCP) Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <cstdint>
+#include <string>
+#include "esp_mqtt_client_config.hpp"
+#include "nvs_flash.h"
+#include "protocol_examples_common.h"
+
+
+#include "esp_log.h"
+#include "esp_mqtt.hpp"
+
+namespace {
+constexpr auto *TAG = "MQTT_EXAMPLE";
+
+extern const char mqtt_eclipse_org_pem_start[]   asm("_binary_mqtt_eclipseprojects_io_pem_start");
+extern const char mqtt_eclipse_org_pem_end[]   asm("_binary_mqtt_eclipseprojects_io_pem_end");
+
+class MyClient final : public idf::mqtt::Client {
+public:
+    using idf::mqtt::Client::Client;
+private:
+    void on_connected(esp_mqtt_event_handle_t const event) override
+    {
+        using idf::mqtt::QoS;
+        subscribe(messages.get());
+        subscribe(sent_load.get(), QoS::AtMostOnce);
+    }
+    void on_data(esp_mqtt_event_handle_t const event) override
+    {
+        if (messages.match(event->topic, event->topic_len)) {
+            ESP_LOGI(TAG, "Received in the messages topic");
+        }
+    }
+    idf::mqtt::Filter messages{std::string{"$SYS/broker/messages/received"}};
+    idf::mqtt::Filter sent_load{std::string{"$SYS/broker/load/+/sent"}};
+};
+}
+
+namespace mqtt = idf::mqtt;
+
+extern "C" void app_main(void)
+{
+    ESP_LOGI(TAG, "[APP] Startup..");
+    ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
+    ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
+
+    esp_log_level_set("*", ESP_LOG_INFO);
+    esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
+    esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE);
+    esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);
+    esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE);
+    esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
+    esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);
+
+    ESP_ERROR_CHECK(nvs_flash_init());
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+
+    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
+     * Read "Establishing Wi-Fi or Ethernet Connection" section in
+     * examples/protocols/README.md for more information about this function.
+     */
+    ESP_ERROR_CHECK(example_connect());
+
+    mqtt::BrokerConfiguration broker{
+        .address = {mqtt::URI{std::string{CONFIG_BROKER_URI}}},
+        .security = mqtt::CryptographicInformation{mqtt::PEM{mqtt_eclipse_org_pem_start}}
+    };
+    idf::mqtt::ClientCredentials credentials{};
+    idf::mqtt::Configuration config{};
+
+    MyClient client{broker, credentials, config};
+    while (true) {
+        constexpr TickType_t xDelay = 500 / portTICK_PERIOD_MS;
+        vTaskDelay( xDelay );
+    }
+}

+ 3 - 0
examples/cxx/experimental/esp_mqtt_cxx/ssl/sdkconfig.defaults

@@ -0,0 +1,3 @@
+# Enable C++ exceptions and set emergency pool size for exception objects
+CONFIG_COMPILER_CXX_EXCEPTIONS=y
+CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024

+ 12 - 0
examples/cxx/experimental/esp_mqtt_cxx/tcp/CMakeLists.txt

@@ -0,0 +1,12 @@
+# The following four lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+# (Not part of the boilerplate)
+# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
+set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common
+                         $ENV{IDF_PATH}/examples/cxx/experimental/experimental_cpp_component
+                         $ENV{IDF_PATH}/examples/cxx/experimental/esp_mqtt_cxx/components)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(mqtt_tcp_cxx)

+ 12 - 0
examples/cxx/experimental/esp_mqtt_cxx/tcp/Makefile

@@ -0,0 +1,12 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+PROJECT_NAME := mqtt_tcp_cxx
+
+EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/common_components/protocol_examples_common
+EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/cxx/experimental/esp_mqtt_cxx/components
+EXTRA_COMPONENT_DIRS += $(IDF_PATH)/examples/cxx/experimental/experimental_cpp_component
+
+CXXFLAGS += -std=gnu++17
+include $(IDF_PATH)/make/project.mk

+ 3 - 0
examples/cxx/experimental/esp_mqtt_cxx/tcp/main/CMakeLists.txt

@@ -0,0 +1,3 @@
+idf_component_register(SRCS "mqtt_tcp_example.cpp"
+                    INCLUDE_DIRS ".")
+target_compile_options(${COMPONENT_LIB} PRIVATE "-std=gnu++17")

+ 9 - 0
examples/cxx/experimental/esp_mqtt_cxx/tcp/main/Kconfig.projbuild

@@ -0,0 +1,9 @@
+menu "Example Configuration"
+
+    config BROKER_URL
+        string "Broker URL"
+        default "mqtt://mqtt.eclipse.org"
+        help
+            URL of the broker to connect to
+
+endmenu

+ 0 - 0
examples/cxx/experimental/esp_mqtt_cxx/tcp/main/component.mk


+ 81 - 0
examples/cxx/experimental/esp_mqtt_cxx/tcp/main/mqtt_tcp_example.cpp

@@ -0,0 +1,81 @@
+/* C++ MQTT (over TCP) Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include "nvs_flash.h"
+#include "protocol_examples_common.h"
+
+#include "esp_log.h"
+#include "esp_mqtt.hpp"
+#include "esp_mqtt_client_config.hpp"
+
+namespace mqtt = idf::mqtt;
+
+namespace {
+constexpr auto *TAG = "MQTT_EXAMPLE";
+
+class MyClient final : public mqtt::Client {
+public:
+    using mqtt::Client::Client;
+
+private:
+    void on_connected(esp_mqtt_event_handle_t const event) override
+    {
+        using mqtt::QoS;
+        subscribe(messages.get());
+        subscribe(sent_load.get(), QoS::AtMostOnce);
+    }
+    void on_data(esp_mqtt_event_handle_t const event) override
+    {
+        if (messages.match(event->topic, event->topic_len)) {
+            ESP_LOGI(TAG, "Received in the messages topic");
+        }
+    }
+    mqtt::Filter messages{"$SYS/broker/messages/received"};
+    mqtt::Filter sent_load{"$SYS/broker/load/+/sent"};
+};
+}
+
+extern "C" void app_main(void)
+{
+    ESP_LOGI(TAG, "[APP] Startup..");
+    ESP_LOGI(TAG, "[APP] Free memory: %d bytes", esp_get_free_heap_size());
+    ESP_LOGI(TAG, "[APP] IDF version: %s", esp_get_idf_version());
+
+    esp_log_level_set("*", ESP_LOG_INFO);
+    esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
+    esp_log_level_set("MQTT_EXAMPLE", ESP_LOG_VERBOSE);
+    esp_log_level_set("TRANSPORT_TCP", ESP_LOG_VERBOSE);
+    esp_log_level_set("TRANSPORT_SSL", ESP_LOG_VERBOSE);
+    esp_log_level_set("TRANSPORT", ESP_LOG_VERBOSE);
+    esp_log_level_set("OUTBOX", ESP_LOG_VERBOSE);
+
+    ESP_ERROR_CHECK(nvs_flash_init());
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+
+    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
+     * Read "Establishing Wi-Fi or Ethernet Connection" section in
+     * examples/protocols/README.md for more information about this function.
+     */
+    ESP_ERROR_CHECK(example_connect());
+
+    mqtt::BrokerConfiguration broker{
+        .address = {mqtt::URI{std::string{CONFIG_BROKER_URL}}},
+        .security =  mqtt::Insecure{}
+    };
+    mqtt::ClientCredentials credentials{};
+    mqtt::Configuration config{};
+
+    MyClient client{broker, credentials, config};
+
+    while (true) {
+        constexpr TickType_t xDelay = 500 / portTICK_PERIOD_MS;
+        vTaskDelay(xDelay);
+    }
+}

+ 3 - 0
examples/cxx/experimental/esp_mqtt_cxx/tcp/sdkconfig.defaults

@@ -0,0 +1,3 @@
+# Enable C++ exceptions and set emergency pool size for exception objects
+CONFIG_COMPILER_CXX_EXCEPTIONS=y
+CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024