# mqtt_client

`IMqttClient` is a MQTT 5 client interface.
Note: Only MQTT version 5 is officially supported. WebSocket transport is not supported. For more information, refer to MQTT v5.0 spec .

## File information

File | common/interface/mqtt_client.h
Public functions | Create MQTTConnect MQTTSubscribe MQTTPublish MQTTUnsubscribe MQTTPing MQTTDisconnect MQTTSendAuthData GetAliasByTopicName GetNameByTopicAlias
Classes | IMqttClient UMqttClient
Data types | MqttError MQTTReasonCode MQTTAuthReasonCode MQTTClientDisconnectReason
Structs | MqttUserProperty MqttConnectCredentials MqttWillOptions MqttConnectOptions MqttSubscribeOptions MqttPublishOptions MqttDisconnectOptions MqttAuthOptions MqttConnectAckInfo MqttPublishRecvInfo MqttPublishCompleteInfo
Examples | Code Example

# Classes

## IMqttClient

    class IMqttClient {
    public:
        static IMqttClient* Create(class IIoMux* const iomux,
            class ISocketProvider* const tcpSocketProvider,
            class ISocketProvider* const tlsSocketProvider,
            class UMqttClient* const user,
            class IInstanceLog* const log,
            class IDns* const dns);

        virtual MqttError MQTTConnect(const char* broker,
                                      char* outputClientId,
                                      size_t outputClientIdSize,
                                      const char* inputClientId = nullptr,
                                      const MqttConnectOptions& options = {}) = 0;

        virtual MqttError MQTTSubscribe(dword subscriptionIdentifier,
                                        const char* topicFilter,
                                        const MqttSubscribeOptions& options = {}) = 0;

        virtual MqttError MQTTPublish(const char* topicName,
                                      const byte* payload,
                                      size_t payloadSize,
                                      const MqttPublishOptions& options = {}) = 0;

        virtual MqttError MQTTUnsubscribe(const char* topicFilter,
                                          const MqttUserProperty& userProperty = {}) = 0;

        virtual MqttError MQTTPing() = 0;

        virtual MqttError MQTTDisconnect(const MqttDisconnectOptions& options = {}) = 0;

        virtual MqttError MQTTSendAuthData(const char* authenticationMethod,
                                           const byte* authenticationData,
                                           word authenticationDataSize,
                                           const MqttAuthOptions& options = {}) = 0;

        virtual word GetAliasByTopicName(const char* topicName) const = 0;
        virtual const char* GetNameByTopicAlias(word alias) const = 0;
    };

This class is the main interface for MQTT client functionality. It allows connecting to a broker via either TCP or TLS socket, publishing and subscribing to one or multiple topics (separated by commas), sending pings, handling authentication, and more. The ClientId can either be generated if nothing is passed, or an explicit ClientId can be used if provided. If you want to generate an ID, be sure to pass a buffer of at least 24 bytes because the generated ID is 23 bytes plus the null terminator.

Optional parameters are grouped into structs (e.g., `MqttConnectOptions`, `MqttPublishOptions`). All struct fields have sensible defaults, so only the fields that are actually needed have to be set.

Subscription Identifiers: When subscribing, a subscription identifier can be provided (e.g., `12`). The user of this interface should store these identifiers to handle incoming messages properly.

QoS: Allowed values are 0, 1, or 2. If another value is used, the function returns `IncorrectValue`.

User Properties: Can be provided with comma-separated names and comma-separated values via `MqttUserProperty`. They correspond index-wise, e.g. `userProp1,userProp2` for names and `val1,val2` for values. Each name must have a matching value. A mismatch in the count (e.g., two names, one value) leads to an error.

Topic Aliases: You can use `topicAlias` inside `MqttPublishOptions` to assign or reuse aliases. If a topic alias already exists, you can publish with the same alias to avoid resending the full topic name. (Details are described in the MQTT v5 specification.)

### Public functions

Create (static function)
  Creates an `IMqttClient` instance.

  #### Parameters

  IIoMux * const iomux | The iomux instance needed for socket communication.
  ISocketProvider * const tcpSocketProvider | A socket provider to create a TCP socket for MQTT transport.
  ISocketProvider * const tlsSocketProvider | A socket provider to create a TLS socket for secured MQTT (MQTT over TLS).
  UMqttClient * const user | An `UMqttClient` instance to receive callbacks from `IMqttClient`.
  IInstanceLog * const log | The log instance for logging.
  IDns * const dns | An optional DNS instance for DNS resolution.

  #### Return Value

  The `IMqttClient*` instance. Must be deleted if no longer used.
MQTTConnect
  Connects to an MQTT broker. Optional parameters are grouped in `MqttConnectOptions`, which itself embeds `MqttConnectCredentials` and `MqttWillOptions`. If the broker is secured (TLS), set `options.tls = true` and use the corresponding port (e.g., 8883) via `options.port`.

  #### Parameters

  const char * broker | The broker address or hostname.
  char * outputClientId | A buffer to store the generated or used client ID. If generating an ID, pass at least 24 bytes. Pass `nullptr` if an inputClientId is provided.
  size_t outputClientIdSize | Size of the outputClientId buffer. Pass `0` if an inputClientId is provided.
  const char * inputClientId | (Default: nullptr) If specified, this client ID is used for the MQTT connection. If `nullptr`, a client ID is generated and written to outputClientId.
  const MqttConnectOptions & options | (Default: {}) Optional connection parameters. See `MqttConnectOptions`.

  #### Return Value

  An `MqttError` indicating success or a specific error reason.
MQTTSubscribe
  Subscribes to one or multiple topic filters separated by commas. For example: `"test/light1, test/light2"`. QoS and other per-topic flags are specified in `MqttSubscribeOptions` as comma-separated strings matching the topic order, e.g. `options.qosLevels = "0,2"`.

  #### Parameters

  dword subscriptionIdentifier | Subscription identifier. The user should store this to correlate incoming messages.
  const char * topicFilter | The topic filter(s) to subscribe to, separated by commas if more than one.
  const MqttSubscribeOptions & options | (Default: {}) Optional per-topic flags. See `MqttSubscribeOptions`.

  #### Return Value

  An `MqttError` indicating success or a specific error reason.
MQTTPublish
  Publishes a message to a topic. The payload must be provided directly as a parameter, while QoS, retain flag, and all other optional properties are provided via `MqttPublishOptions`.

  #### Parameters

  const char * topicName | The topic to publish to.
  const byte * payload | The message payload to publish. May be `nullptr` if payloadSize is 0.
  size_t payloadSize | The size of the payload buffer in bytes.
  const MqttPublishOptions & options | (Default: {}) QoS, retain, topic alias, and optional properties. See `MqttPublishOptions`.

  #### Return Value

  An `MqttError` indicating success or a specific error reason.
MQTTUnsubscribe
  Unsubscribes from one or multiple topic filters (comma-separated).

  #### Parameters

  const char* topicFilter | The topic filter(s) to unsubscribe from, separated by commas if more than one.
  const MqttUserProperty & userProperty | (Default: {}) Optional user property to attach to the UNSUBSCRIBE packet. See `MqttUserProperty`.

  #### Return Value

  An `MqttError` indicating success or a specific error reason.
MQTTPing
  Sends a PINGREQ to the broker to keep the connection alive or check if the server is responsive. Typically used if no other MQTT traffic is ongoing for a longer period than the keep-alive interval.

  #### Return Value

  An `MqttError` indicating success or a specific error reason.
MQTTDisconnect
  Disconnects from the broker. All optional parameters (reason string, session expiry, user property, server reference) are provided via `MqttDisconnectOptions`.

  #### Parameters

  const MqttDisconnectOptions & options | (Default: {}) Disconnect reason and optional properties. See `MqttDisconnectOptions`.

  #### Return Value

  An `MqttError` indicating success or a specific error reason.
MQTTSendAuthData
  Sends authentication data to the broker if needed (enhanced authentication). The reason code, optional reason string, and user property are provided via `MqttAuthOptions`.

  #### Parameters

  const char* authenticationMethod | The authentication method (must match the one set during `MQTTConnect` if used).
  const byte* authenticationData | The binary authentication data to send.
  word authenticationDataSize | The size of the authentication data buffer.
  const MqttAuthOptions & options | (Default: {}) Reason code, optional reason string, and user property. See `MqttAuthOptions`.

  #### Return Value

  An `MqttError` indicating success or a specific error reason.
GetAliasByTopicName
  Retrieves the topic alias if defined for the given topic name. Returns 0 if no alias is set.

  #### Parameters

  const char* topicName | The topic name for which to retrieve the alias.

  #### Return Value

  The alias (a `word`) for the specified topic name. Returns 0 if none is found.
GetNameByTopicAlias
  Retrieves the topic name for the given topic alias if available.

  #### Parameters

  word alias | The alias for which to retrieve the topic name.

  #### Return Value

  The topic name (`const char*`) or `nullptr` if no name is found for the alias.

## UMqttClient

    class UMqttClient {
    public:
        virtual ~UMqttClient() = default;

        virtual void MQTTClientConnectComplete(IMqttClient* const mqttClient,
                                               const MqttConnectAckInfo& info) = 0;

        virtual void MQTTClientConnectFail(IMqttClient* const mqttClient,
                                           MQTTReasonCode reasonCode,
                                           const char* reasonString) = 0;

        virtual void MQTTClientDisconnect(IMqttClient* const mqttClient,
                                          MQTTReasonCode reasonCode) = 0;

        virtual void MQTTClientSubscribeComplete(IMqttClient* const mqttClient,
                                                 const char* topic,
                                                 const MqttUserProperty& userProperty) = 0;

        virtual void MQTTClientSubscribeFail(IMqttClient* const mqttClient,
                                             const char* topic,
                                             MQTTReasonCode reasonCode) = 0;

        virtual void MQTTClientPublishRecv(IMqttClient* const mqttClient,
                                           const MqttPublishRecvInfo& info) = 0;

        virtual void MQTTClientUnsubscribeComplete(IMqttClient* const mqttClient,
                                                   const char* topic,
                                                   const MqttUserProperty& userProperty) = 0;

        virtual void MQTTClientUnsubscribeFail(IMqttClient* const mqttClient,
                                               const char* topic,
                                               MQTTReasonCode reasonCode) = 0;

        virtual void MQTTClientPublishComplete(IMqttClient* const mqttClient,
                                               const MqttPublishCompleteInfo& info) = 0;

        virtual void MQTTClientPublishFail(IMqttClient* const mqttClient,
                                           MQTTReasonCode reasonCode) = 0;

        virtual void MQTTClientPingComplete(IMqttClient* const mqttClient) = 0;

        virtual void MQTTClientAuthenticate(IMqttClient* const mqttClient,
                                            MQTTReasonCode reasonCode) = 0;

        virtual void MQTTClientError(IMqttClient* const mqttClient,
                                     MqttError reason) = 0;
    };

This class is for receiving callbacks from the `IMqttClient`. You must pass a subclass of `UMqttClient` to an `IMqttClient` as user in order to handle events such as connection completion, disconnection, subscription success/failure, publish completion, incoming messages, errors, etc.

### Callback functions

MQTTClientConnectComplete
  Called when the connection to the broker completes successfully. All CONNACK details are provided in a `MqttConnectAckInfo` struct (session flag, expiry interval, receive maximum, max QoS, assigned client ID, topic alias max, etc.).
MQTTClientConnectFail
  Called if the connection to the broker cannot be established or is rejected by the broker.
MQTTClientDisconnect
  Called after a normal or abnormal disconnection from the broker.
MQTTClientSubscribeComplete
  Called after a successful subscription. The optional user property returned by the broker is provided as a `MqttUserProperty`.
MQTTClientSubscribeFail
  Called if subscribing to a topic (or multiple topics) fails.
MQTTClientPublishRecv
  Called when a PUBLISH message is received for a subscribed topic. All message details (topic, payload, QoS, retain flag, topic alias, content type, expiry interval, response topic, correlation data, subscription identifier, and user property) are provided in a `MqttPublishRecvInfo` struct.
MQTTClientUnsubscribeComplete
  Called after successfully unsubscribing from a topic. The optional user property returned by the broker is provided as a `MqttUserProperty`.
MQTTClientUnsubscribeFail
  Called when unsubscribing fails.
MQTTClientPublishComplete
  Called after a PUBLISH operation completes (e.g., after receiving PUBACK/PUBCOMP for QoS > 0). The QoS level and optional user property are provided in a `MqttPublishCompleteInfo` struct.
MQTTClientPublishFail
  Called if a publish operation fails.
MQTTClientPingComplete
  Called after a PINGREQ/PINGRESP cycle completes successfully.
MQTTClientAuthenticate
  Called if enhanced auth is in progress and the broker sends an AUTH packet. The client can respond by calling `MQTTSendAuthData`.
MQTTClientError
  Called if there is an internal error on the client side (e.g., malformed packet).

# Data types

## MqttError

    enum MqttError {
        Success = 0,
        WrongPacketType,
        Malformed,
        BufferAllocationFailed,
        NullBufferRecived,
        MaxBinDataExceeded,
        StringSizeExceeded,
        VariableHeaderError,
        PayloadError,
        FlagSetButNoTopic,
        FlagSetButNoPayload,
        FlagSetButNoUserName,
        FlagSetButNoPassword,
        EmptyData,
        IndexOutOfRange,
        SubscriptionIdentifierMissing,
        IncorrectValue,
        NoConnectionEstablished,
        UnspecifierError,
        PacketTooLarge,
        StringTooLarge,
        OutputBufferToSmall,
        InputClientIdIncorrect,
        RetainNotSupportedByBroker,
        TopicAliasNonExistant,
        TopicAliasNotSupported,
        TopicAliasUnavailable,
        InvalidTopicName,
        NotAuthorized
    };

`MqttError` defines client-side error conditions during MQTT operations (e.g., `Malformed`, `IncorrectValue`).

## MQTTReasonCode

    enum MQTTReasonCode {
        SUCCESS = 0x00,
        NORMAL_DISCONNECTION = 0x00,
        GRANTED_QOS0 = 0x00,
        GRANTED_QOS1 = 0x01,
        GRANTED_QOS2 = 0x02,
        DISCONNECT_WITH_WILL_MESSAGE = 0x04,
        NO_MATCHING_SUBSCRIBERS = 0x10,
        NO_SUBSCRIPTION_EXISTED = 0x11,
        CONTINUE_AUTHENTICATION = 0x18,
        RE_AUTHENTICATE = 0x19,
        UNSPECIFIED_ERROR = 0x80,
        MALFORMED_PACKET = 0x81,
        PROTOCOL_ERROR = 0x82,
        IMPLEMENTATION_SPECIFIC_ERROR = 0x83,
        UNSUPPORTED_PROTOCOL_VERSION = 0x84,
        CLIENT_IDENTIFIER_NOT_VALID = 0x85,
        BAD_USER_NAME_OR_PASSWORD = 0x86,
        NOT_AUTHORIZED = 0x87,
        SERVER_UNAVAILABLE = 0x88,
        SERVER_BUSY = 0x89,
        BANNED = 0x8A,
        SERVER_SHUTTING_DOWN = 0x8B,
        BAD_AUTHENTICATION_METHOD = 0x8C,
        KEEP_ALIVE_TIMEOUT = 0x8D,
        SESSION_TAKEN_OVER = 0x8E,
        TOPIC_FILTER_INVALID = 0x8F,
        TOPIC_NAME_INVALID = 0x90,
        PACKET_IDENTIFIER_IN_USE = 0x91,
        PACKET_IDENTIFIER_NOT_FOUND = 0x92,
        RECEIVE_MAXIMUM_EXCEEDED = 0x93,
        TOPIC_ALIAS_INVALID = 0x94,
        PACKET_TOO_LARGE = 0x95,
        MESSAGE_RATE_TOO_HIGH = 0x96,
        QUOTA_EXCEEDED = 0x97,
        ADMINISTRATIVE_ACTION = 0x98,
        PAYLOAD_FORMAT_INVALID = 0x99,
        RETAIN_NOT_SUPPORTED = 0x9A,
        QOS_NOT_SUPPORTED = 0x9B,
        USE_ANOTHER_SERVER = 0x9C,
        SERVER_MOVED = 0x9D,
        SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 0x9E,
        CONNECTION_RATE_EXCEEDED = 0x9F,
        MAXIMUM_CONNECT_TIME = 0xA0,
        SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 0xA1,
        WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 0xA2
    };

`MQTTReasonCode` enumerates the possible reason codes in MQTT for connection, disconnection, and other operations. Reason Codes .

## MQTTAuthReasonCode

    enum MQTTAuthReasonCode {
        ContinueAuthentication = 0x18,
        Reauthenticate = 0x19
    };

## MQTTClientDisconnectReason

    enum MQTTClientDisconnectReason {
        NormalDisconnection = 0x00,
    };

`MQTTClientDisconnectReason` indicates the reason for disconnecting. `NormalDisconnection` (0x00) is used for a graceful disconnect.

# Structs

## MqttUserProperty

    struct MqttUserProperty {
        const char* name  = nullptr;
        const char* value = nullptr;
    };

Represents a single MQTT user property as a name/value pair. Both fields can be comma-separated strings to pass multiple properties in one call. Used in most API functions and callbacks.

## MqttConnectCredentials

    struct MqttConnectCredentials {
        const char* userName     = nullptr;
        const byte* password     = nullptr;
        word        passwordSize = 0;
    };

Groups the username and binary password for MQTT authentication. Embedded in `MqttConnectOptions`.

## MqttWillOptions

    struct MqttWillOptions {
        const char* topic                  = nullptr;
        const byte* payload                = nullptr;
        word        payloadSize            = 0;
        byte        qos                    = 0;
        bool        retain                 = false;
        dword       delayInterval          = 0;
        bool        payloadFormatIndicator = false;
        dword       messageExpiryInterval  = 0;
        const char* contentType            = nullptr;
        const char* responseTopic          = nullptr;
        const byte* correlationData        = nullptr;
        word        correlationDataSize    = 0;
        MqttUserProperty userProperty      = {};
    };

Groups all Last Will and Testament (LWT) parameters. If `topic` and `payload` are set, the Will flag is set automatically. Embedded in `MqttConnectOptions`.

## MqttConnectOptions

    struct MqttConnectOptions {
        word        port                       = 1883;
        bool        tls                        = false;
        word        keepAlive                  = 0;
        dword       sessionExpiryInterval      = 0;
        const char* authenticationMethod       = nullptr;
        const byte* authenticationData         = nullptr;
        word        authenticationDataSize     = 0;
        word        receiveMaximum             = 65535;
        dword       maximumPacketSize          = 65535;
        word        topicAliasMaximum          = 0;
        bool        requestResponseInformation = false;
        bool        requestProblemInformation  = true;
        MqttUserProperty       userProperty    = {};
        MqttConnectCredentials credentials     = {};
        MqttWillOptions        will            = {};
    };

Groups all optional parameters for `MQTTConnect`. All fields have sensible defaults so only the relevant fields need to be set.

## MqttSubscribeOptions

    struct MqttSubscribeOptions {
        const char* qosLevels           = nullptr;
        const char* noLocalFlags        = nullptr;
        const char* retainFlags         = nullptr;
        const char* retainHandlingFlags = nullptr;
    };

Groups the per-topic subscription flags for `MQTTSubscribe`. Each field is a comma-separated string matching the topic order, e.g. `qosLevels = "0,2"` for two topics with QoS 0 and 2 respectively.

## MqttPublishOptions

    struct MqttPublishOptions {
        byte        qos                   = 0;
        word        topicAlias            = 0;
        const char* contentType           = nullptr;
        bool        retain                = false;
        MqttUserProperty userProperty     = {};
        dword       messageExpiryInterval = 0;
        const char* responseTopic         = nullptr;
        const byte* correlationData       = nullptr;
        word        correlationDataSize   = 0;
    };

Groups all optional parameters for `MQTTPublish`. The payload is passed directly as a parameter to `MQTTPublish`, not through this struct. Set `topicAlias` to assign or reuse a topic alias (see the MQTT v5 spec for details).

## MqttDisconnectOptions

    struct MqttDisconnectOptions {
        MQTTClientDisconnectReason reason              = NormalDisconnection;
        dword                      sessionExpiryInterval = 0;
        const char*                reasonString        = nullptr;
        MqttUserProperty           userProperty        = {};
        const char*                serverReference     = nullptr;
    };

Groups all optional parameters for `MQTTDisconnect`.

## MqttAuthOptions

    struct MqttAuthOptions {
        MQTTAuthReasonCode reasonCode   = MQTTAuthReasonCode::ContinueAuthentication;
        const char*        reasonString = nullptr;
        MqttUserProperty   userProperty = {};
    };

Groups the optional parameters for `MQTTSendAuthData`.

## MqttConnectAckInfo

    struct MqttConnectAckInfo {
        bool        isNewSession               = false;
        dword       sessionExpiryInterval      = 0;
        word        receiveMaximum             = 0;
        byte        maxQoS                     = 0;
        byte        retainAvailable            = 0;
        dword       maxPacketSize              = 0;
        const char* clientId                   = nullptr;
        dword       topicAliasMax              = 0;
        MqttUserProperty userProperty          = {};
        byte        wildCardAvailable          = 0;
        byte        sharedSubscriptionAvailable = 0;
        dword       serverKeepAlive            = 0;
    };

Carries all fields from the broker's CONNACK packet. Passed to `MQTTClientConnectComplete`.

## MqttPublishRecvInfo

    struct MqttPublishRecvInfo {
        const char* topic                  = nullptr;
        const char* msg                    = nullptr;
        size_t      msgLen                 = 0;
        word        subscriptionIdentifier = 0;
        MqttUserProperty userProperty      = {};
        byte        qos                    = 0;
        word        topicAlias             = 0;
        const char* contentType            = nullptr;
        bool        retain                 = false;
        dword       messageExpiryInterval  = 0;
        const char* responseTopic          = nullptr;
        const byte* correlationData        = nullptr;
        word        correlationDataSize    = 0;
    };

Carries all fields of an incoming PUBLISH packet. Passed to `MQTTClientPublishRecv`.

## MqttPublishCompleteInfo

    struct MqttPublishCompleteInfo {
        byte             qos          = 0;
        MqttUserProperty userProperty = {};
    };

Carries the QoS level and optional user property of a completed publish operation. Passed to `MQTTClientPublishComplete`.

# Code Example

## Prerequisites: Setting up the innovaphone MQTT Broker

This example connects to a local innovaphone MQTT broker. Before running the code, ensure the broker is installed and the instance is running:

1. Open the innovaphone App Platform in your browser and navigate to the Apps section.
2. Install the MQTT Broker app from the app store if it is not already present.
3. Create a new instance of the MQTT Broker app. Give it a name (e.g., `mqtt`) and confirm the configuration.
4. Start the instance and verify that it is in the Running state in the App Platform dashboard.
5. Note the hostname or IP address of your innovaphone device. The broker listens on port 1883 (plain TCP) by default. No credentials are required for a local connection unless configured otherwise in the instance settings.

Once the broker instance is running, the client code below can connect to it directly using the device's hostname or IP address.

## Example Code

The following example connects to the local innovaphone MQTT broker, subscribes to a wildcard topic, and publishes messages:

    void TaskMqttTest::Start(UTask* user) {
        const char* brokerHost = "192.168.1.1"; // Replace with your innovaphone device address

        client = IMqttClient::Create(instance->GetIIoMux(),
                                     instance->GetTcpSocketProvider(),
                                     instance->GetTlsSocketProvider(),
                                     this,
                                     instance->GetLog(),
                                     dns->Create(instance->GetIIoMux()));

        // If generating a client ID, ensure the buffer is at least 24 bytes
        // (23-byte generated ID plus null terminator).
        char generatedClientIdBuffer[24];

        MqttConnectOptions options;
        options.keepAlive = 60; // 60 seconds

        MqttError state = client->MQTTConnect(brokerHost,
                                              generatedClientIdBuffer,
                                              sizeof(generatedClientIdBuffer),
                                              nullptr, // generate client ID
                                              options);
        if (state) {
            printf("MQTTConnect error: %d", state);
        }
    }

    void TaskMqttTest::MQTTClientConnectComplete(IMqttClient* const mqttClient,
                                                 const MqttConnectAckInfo& info)
    {
        // Send a ping to verify the broker is responsive.
        client->MQTTPing();

        // Subscribe to all topics using the wildcard '#', QoS 0.
        // Subscription identifier 12 is stored by the application to correlate incoming messages.
        MqttSubscribeOptions subOptions;
        subOptions.qosLevels = "0";
        client->MQTTSubscribe(12, "#", subOptions);

        // Publish an initial message to test/light1.
        const char* msg = "innoClient MQTT5";
        client->MQTTPublish("test/light1", (byte*)msg, strlen(msg));
    }

    void TaskMqttTest::MQTTClientDisconnect(IMqttClient* const mqttClient, MQTTReasonCode reasonCode)
    {
        printf("Disconnected with reason: %x", reasonCode);
    }

    void TaskMqttTest::MQTTClientConnectFail(IMqttClient* const mqttClient,
                                             MQTTReasonCode reasonCode, const char* reasonString)
    {
        printf("Connect failed, reason: %x", reasonCode);
    }

    void TaskMqttTest::MQTTClientPublishRecv(IMqttClient* const mqttClient,
                                             const MqttPublishRecvInfo& info)
    {
        printf("Message received on topic: %s", info.topic);
        if (!strcmp(info.topic, "test/light2") && info.subscriptionIdentifier == 12) {
            const char* msg = "Next message from innoClient";
            client->MQTTPublish("test/light1", (byte*)msg, strlen(msg));
        }
        else {
            printf("%.*s", (int)info.msgLen, info.msg);
        }
    }

    void TaskMqttTest::MQTTClientPublishComplete(IMqttClient* const mqttClient,
                                                 const MqttPublishCompleteInfo& info)
    {
        printf("Publish completed for QoS %d.", (int)info.qos);
    }

    void TaskMqttTest::MQTTClientUnsubscribeComplete(IMqttClient* const mqttClient, const char* topic,
                                                     const MqttUserProperty& userProperty)
    {
        printf("Unsubscribed from topic: %s", topic);
    }

    void TaskMqttTest::MQTTClientUnsubscribeFail(IMqttClient* const mqttClient, const char* topic,
                                                 MQTTReasonCode reasonCode)
    {
        printf("Unsubscribe failed for topic: %s, reason: %x", topic, reasonCode);
    }

    void TaskMqttTest::MQTTClientPingComplete(IMqttClient* const mqttClient)
    {
        printf("Ping response received.");
    }

    void TaskMqttTest::MQTTClientSubscribeComplete(IMqttClient* const mqttClient, const char* topic,
                                                   const MqttUserProperty& userProperty)
    {
        printf("Subscribed to topic: %s", topic);
    }

    void TaskMqttTest::MQTTClientSubscribeFail(IMqttClient* const mqttClient, const char* topic,
                                               MQTTReasonCode reasonCode)
    {
        printf("Subscribe failed for topic: %s, reason: %x", topic, reasonCode);
    }