CommsChampion Ecosystem MQTT v5 Client
MQTT v5 Client Library.
MQTT v5 Client Library

Table of Contents

Overview

The MQTT v5 Client Library from the CommsChampion Ecosystem provides simple, asynchronous, non-blocking, and easy to use interface to operate MQTT v5 client. The library doesn't make any assumption on the system it is running on, as well as on the type of I/O link being used to communicate its data to the MQTT v5 capable broker.

It is a responsibility of the calling application to manage network connectivity as well as measure time required for the correct operation of the MQTT v5 protocol.

The library allows the application to have a full control over the raw data for any extra analysis and/or manipulation, such as encryption or extra framing.

Version of the Library

The version is of the library applicable to this documentation is defined in the cc_mqtt5_client/common.h file using the following defines:

Header

To use this MQTT v5 Client Library use the following single include statement:

#include "cc_mqtt5_client/client.h"

Client Allocation

The library supports multiple independent MQTT v5 client sessions. The allocation of data structures relevant to a single client is performed using cc_mqtt5_client_alloc() function.

CC_Mqtt5ClientHandle cc_mqtt5_client_alloc()
Allocate new client.
CC_Mqtt5Client * CC_Mqtt5ClientHandle
Handle used to access client specific data structures.
Definition: common.h:213

All other functions are client specific, they receive the returned handle as their first parameter.

When work with allocated client is complete, it must be freed using cc_mqtt5_client_free() function.

void cc_mqtt5_client_free(CC_Mqtt5ClientHandle handle)
Free previously allocated client.

When working with C++ it is advised to use a smart pointer with a custom deleter.

struct MyDeleter
{
void operator()(CC_Mqtt5Client* ptr)
{
}
};
using MyClientPtr = std::unique_ptr<CC_Mqtt5Client, MyDeleter>;
MyClientPtr clientPtr(::cc_mqtt5_client_alloc());
CC_Mqtt5ClientHandle client = clientPtr.get();

IMPORTANT: The function cc_mqtt5_client_free() must NOT be called from within a callback. Use next event loop iteration.

"Must Have" Callbacks Registration

In order to properly function the library requires setting several callbacks.

Sending Data To Broker

To client application must assign a callback for the library to be able to send binary data out to the connected broker.

void my_send_data_cb(void* data, const unsigned char* buf, unsigned bufLen)
{
... /* send requested buffer to the broker */
}
cc_mqtt5_client_set_send_output_data_callback(client, &my_send_data_cb, data);
void cc_mqtt5_client_set_send_output_data_callback(CC_Mqtt5ClientHandle handle, CC_Mqtt5SendOutputDataCb cb, void *data)
Set callback to send raw data over I/O link.

See also the documentation of the CC_Mqtt5SendOutputDataCb callback function definition.

In the invoked callback the application is responsible to send the provided data over the I/O link. The application can also perform extra data manipulation like encryption.

The reported data resides in internal data structures of the client library, which can be updated / deleted right after the callback function returns. It means the data may need to be copied into some other buffer, which will be held intact until the send over I/O link operation is complete.

Reporting Unsolicited Broker Disconnection

The client application must assign a callback for the library to report discovered broker disconnection.

void my_broker_disconnected_cb(void* data, CC_Mqtt5BrokerDisconnectReason reason, const CC_Mqtt5DisconnectInfo* info)
{
... /* handle broker disconnection */
if (info != NULL) {
... /* Access disconnection report details */
}
}
cc_mqtt5_client_set_broker_disconnect_report_callback(client, &my_broker_disconnected_cb, data);
void cc_mqtt5_client_set_broker_disconnect_report_callback(CC_Mqtt5ClientHandle handle, CC_Mqtt5BrokerDisconnectReportCb cb, void *data)
Set callback to report unsolicited disconnection of the broker.
CC_Mqtt5BrokerDisconnectReason
Reason for reporting unsolicited broker disconnection.
Definition: common.h:203
Broker disconnection information.
Definition: common.h:369

See also the documentation of the CC_Mqtt5BrokerDisconnectReportCb callback function definition.

When the broker disconnection is due to reception of the DISCONNECT message from the broker the "info" parameter will report a disconnection reason as well as extra information reported by the broker. If the broker disconnection is due to other reasons, the "info" parameter will be NULL. See also Unsolicited Broker Disconnection below for details.

Reporting Received Message

The client application must assign a callback for the library to report application level messages received from the broker.

void my_message_received_cb(void* data, const CC_Mqtt5MessageInfo* info)
{
... /* handle the received message */
}
cc_mqtt5_client_set_message_received_report_callback(client, &my_message_received_cb, data);
void cc_mqtt5_client_set_message_received_report_callback(CC_Mqtt5ClientHandle handle, CC_Mqtt5MessageReceivedReportCb cb, void *data)
Set callback to report received message from the broker.
Received message information.
Definition: common.h:440

See also the documentation of the CC_Mqtt5MessageReceivedReportCb callback function definition.

Time Measurement

For the correct operation of the MQTT v5 client side of the protocol, the library requires an ability to measure time. This responsibility is delegated to the application.

The easiest (although not very efficient or very precise) method is to periodically (say every 20ms - 50ms) call the cc_mqtt5_client_tick() function reporting the amount of elapsed milliseconds:

cc_mqtt5_client_tick(client, 50U);
void cc_mqtt5_client_tick(CC_Mqtt5ClientHandle handle, unsigned ms)
Notify client about requested time expiry.

The library will check if some inner timer has expired and may initiate some response via invocation one of the registered callbacks.

Another (recommended) method is to register a callback so the library may request the time measurement from the application, and when the requested time expires, the application is expected to call the cc_mqtt5_client_tick() function reporting amount of elapsed milliseconds.

void my_tick_program_cb(void* data, unsigned ms)
{
... // program appropriate timer
}
cc_mqtt5_client_set_next_tick_program_callback(client, &my_tick_program_cb, data);
void cc_mqtt5_client_set_next_tick_program_callback(CC_Mqtt5ClientHandle handle, CC_Mqtt5NextTickProgramCb cb, void *data)
Set callback to call when time measurement is required.

It is allowed to invoke the cc_mqtt5_client_tick() before the actual requested timeout has expired, just make sure that the correct amount of elapsed milliseconds is reported. When the cc_mqtt5_client_tick() is invoked, it is assumed that the previously requested tick programming has been cancelled and the registered callback requesting to re-program the timer may be invoked again from within the cc_mqtt5_client_tick().

See also the documentation of the CC_Mqtt5NextTickProgramCb callback function definition.

In case of callback approach for time measurement is chosen, another callback function (in addition to requesting the new timer programming) to allow interruption of the previously programmed timer must also to be registered.

unsigned my_cancel_tick_program_cb(void* data)
{
...
return ... /* return amount of elapsed milliseconds since last tick program */
}
cc_mqtt5_client_set_cancel_next_tick_wait_callback(client, &my_cancel_tick_program_cb, data);
void cc_mqtt5_client_set_cancel_next_tick_wait_callback(CC_Mqtt5ClientHandle handle, CC_Mqtt5CancelNextTickWaitCb cb, void *data)
Set callback to terminate current time measurement.

See also the documentation of the CC_Mqtt5CancelNextTickWaitCb callback function definition.

Usually the callbacks of canceling the previously programmed tick and programming a new one will be invoked as a side effect of other events, like report of the incoming data or client requesting to perform one of the available operations.

Error Logging

Sometimes the library may exhibit unexpected behaviour, like rejecting some of the parameters. To allow getting extra guidance information of what went wrong it is possible to register optional error logging callback.

void my_error_log_cb(void* data, const char* msg)
{
printf("ERROR: %s\n", msg);
}
cc_mqtt5_client_set_error_log_callback(client, &my_error_log_cb, data);
void cc_mqtt5_client_set_error_log_callback(CC_Mqtt5ClientHandle handle, CC_Mqtt5ErrorLogCb cb, void *data)
Set callback to report error messages.

See also the documentation of the CC_Mqtt5ErrorLogCb callback function definition.

Reporting Incoming Data

It is the responsibility of the application to receive data from the broker and report it to the library. The report is performed using the cc_mqtt5_client_process_data() function.

uint8_t buf[MAX_BUF_SIZE];
... // Receive data into buffer
unsigned bufLen = ...; // Amount of received bytes in the buffer.
unsigned consumed = cc_mqtt5_client_process_data(client, buf, bufLen);
... // Remove the first "consumed" bytes from the buffer and retain the rest
// until the new data chunk arrives.
unsigned cc_mqtt5_client_process_data(CC_Mqtt5ClientHandle handle, const unsigned char *buf, unsigned bufLen)
Provide data (received over I/O link), to the library for processing.

The application is responsible to maintain the input buffer. The value returned from the cc_mqtt5_client_process_data() function reports amount of consumed bytes. In case not all of the reported bytes were consumed the application is responsible to keep them and report again with new appended data when such arrives.

When new data chunk is reported the library may invoke several callbacks, such as reporting received message, sending new data out, as well as canceling the old and programming new tick timeout.

Operating Concepts

The library abstracts away multiple MQTT v5 protocol based "operations". Every such operation has multiple stages:

  • prepare - The operation is "allocated" and relevant handle is returned.
  • configure - Apply one or multiple configurations to the prepared operation.
  • send - Send the configured operation message to the broker.

During the send stage the application is expected to provide the callback to report to the application when the operation is complete. One of the parameters of the callback is always "status" of the CC_Mqtt5AsyncOpStatus type. It indicates whether the operation was successfully complete. In addition to the status it reports some extra information reported by the broker. The information from the broker is available if and only if the status is CC_Mqtt5AsyncOpStatus_Complete.

The send stage function also returns CC_Mqtt5ErrorCode value to indicate whether the send was successfully performed. The provided callback will be invoked if and only if the send returns CC_Mqtt5ErrorCode_Success.

After the send stage the handle returned in the prepare stage can be discarded (no explicit de-allocation is needed / supported) regardless of the return code. After successful send the handle still remains valid until the callback invocation and can be used to cancel the operation. Note that in case the appropriate message has already be sent to the broker, cancelling the outstanding operation can be dangerous. When broker sends a response and client is not expecting it any more, unexpected behaviour (like treating the unexpected message as "protocol error" and disconnecting from the broker) may happen.

In case something went wrong during the configure stage, it is possible to de-allocate the prepared operation using the cancel request. After performing the cancel stage the allocated handle is no longer valid.

IMPORTANT LIBRARY LIMITATION: Once an operation is prepared, it must be be immediately configured and sent (or cancelled) before any other other operation can be prepared. For example:

assert(connect != nullptr);
// The following attempt to prepare the "subscribe" operation will fail because
// previously allocated "connect" hasn't been sent or cancelled yet.
assert(subscribe == NULL);
CC_Mqtt5Connect * CC_Mqtt5ConnectHandle
Handle for "connect" operation.
Definition: common.h:222
CC_Mqtt5ConnectHandle cc_mqtt5_client_connect_prepare(CC_Mqtt5ClientHandle handle, CC_Mqtt5ErrorCode *ec)
Prepare "connect" operation.
CC_Mqtt5ErrorCode
Error code returned by various API functions.
Definition: common.h:65
@ CC_Mqtt5ErrorCode_Success
The requested function executed successfully.
Definition: common.h:66
CC_Mqtt5SubscribeHandle cc_mqtt5_client_subscribe_prepare(CC_Mqtt5ClientHandle handle, CC_Mqtt5ErrorCode *ec)
Prepare "subscribe" operation.
CC_Mqtt5Subscribe * CC_Mqtt5SubscribeHandle
Handle for "subscribe" operation.
Definition: common.h:240

Default Response Timeout

After sending any operation request to the broker, the client library has to allow some time for the broker to process the request. If it takes too much time, the client must report that operation has failed via the set callback. By default the client library allows 2 seconds for such response to arrive. Changing this default value is possible using the cc_mqtt5_client_set_default_response_timeout() function, and retrieving of the currently configured value can be done using the cc_mqtt5_client_get_default_response_timeout() function.

... /* Something went wrong */
}
CC_Mqtt5ErrorCode cc_mqtt5_client_set_default_response_timeout(CC_Mqtt5ClientHandle handle, unsigned ms)
Configure default response timeout period.

Connecting to Broker

To connect to broker use connect operation.

Preparing "Connect" Operation.

if (connect == NULL) {
printf("ERROR: Connect allocation failed with ec=%d\n", ec);
}

NOTE that the cc_mqtt5_client_connect_prepare() cannot be called from within a callback. For example, if the broker disconnection is reported via callback then the cc_mqtt5_client_connect_prepare() cannot be invoked right away. It needs to be postponed until the next event loop iteration.

Configuring "Connect" Response Timeout

When created, the "connect" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_connect_set_response_timeout() function.

... /* Something went wrong */
}
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_set_response_timeout(CC_Mqtt5ConnectHandle handle, unsigned ms)
Configure the broker response timeout for the "connect" operation.

To retrieve the configured response timeout use the cc_mqtt5_client_connect_get_response_timeout() function.

Basic Configuration of "Connect" Operation

The "basic" configuration means no extra MQTT v5 properties assigned to the message.

// Assign default values to the "basicConfig"
// Update the values if needed:
basicConfig.m_clientId = "some_client";
basicConfig.m_cleanStart = true;
// Perform the configuration
ec = cc_mqtt5_client_connect_config_basic(connect, &basicConfig);
printf("ERROR: Basic configuration failed with ec=%d\n", ec);
...
}
void cc_mqtt5_client_connect_init_config_basic(CC_Mqtt5ConnectBasicConfig *config)
Intialize the CC_Mqtt5ConnectBasicConfig configuration structure.
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_config_basic(CC_Mqtt5ConnectHandle handle, const CC_Mqtt5ConnectBasicConfig *config)
Perform basic configuration of the "connect" operation.
Configuration structure to be passed to the cc_mqtt5_client_connect_config_basic().
Definition: common.h:287
const char * m_clientId
Zero terminated Client ID string, can be NULL. When NULL means empty "Client ID". Defaults to NULL.
Definition: common.h:288
bool m_cleanStart
Clean start flag, defaults to false.
Definition: common.h:293

IMPORTANT: MQTT v5 specification allows reconnection to the broker while requesting previous session restoration (via "clean start" bit). By default, the client library verifies that the message received from the broker was actually subscribed to before reporting the message to the application (see Receiving Messages for details). To prevent potential errors of the client and broker inner states being out of sync, the first "connect" operation requires setting the CC_Mqtt5ConnectBasicConfig::m_cleanStart value to true. The only exception to this rule is when the subscription verification on message reception was disabled (described in the Receiving Messages section below). In case the subscription verification is still enabled and the CC_Mqtt5ConnectBasicConfig::m_cleanStart value is NOT set to true, the function rejects the configuration with the CC_Mqtt5ErrorCode_BadParam error code. Any subsequent reconnection attempts will allow setting the value to false.

See also documentation of the CC_Mqtt5ConnectBasicConfig structure.

Will Configuration

To perform will configuration use the cc_mqtt5_client_connect_config_will() function.

// Assign default values to the configuration
// Update values if needed
willConfig.m_topic = "some/topic";
willConfig.m_data = ...;
willConfig.m_dataLen = ...;
...
// Perform the configuration
ec = cc_mqtt5_client_connect_config_will(connect, &willConfig);
printf("ERROR: Will configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_config_will(CC_Mqtt5ConnectHandle handle, const CC_Mqtt5ConnectWillConfig *config)
Perform will configuration of the "connect" operation.
void cc_mqtt5_client_connect_init_config_will(CC_Mqtt5ConnectWillConfig *config)
Intialize the CC_Mqtt5ConnectWillConfig configuration structure.
Configuration structure to be passed to the cc_mqtt5_client_connect_config_will().
Definition: common.h:300
const unsigned char * m_data
Will message data, can be NULL. Defaults to NULL.
Definition: common.h:302
const char * m_topic
Will topic string, must NOT be NULL or empty. Defaults to NULL.
Definition: common.h:301
unsigned m_dataLen
Number of will data bytes. When 0 means no data. Defaults to 0.
Definition: common.h:303

See also documentation of the CC_Mqtt5ConnectWillConfig structure.

IMPORTANT: The cc_mqtt5_client_connect_config_will() function mustn't be called more than once for a single "connect" operation. Otherwise it may result in setting multiple will properties of the same type, which is the "Protocol Error" according to the MQTT v5 specification.

The MQTT v5 specification allows adding several "User Properties" specific to the will. The library allows such assignment using multiple invocations of the cc_mqtt5_client_connect_add_will_user_prop() function.

prop.m_key = "some_key";
prop.m_value = "some_value";
printf("ERROR: Will user property configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_add_will_user_prop(CC_Mqtt5ConnectHandle handle, const CC_Mqtt5UserProp *prop)
Add user property to the will configuration of the "connect" operation.
Wraping structre of the single "User Property".
Definition: common.h:278
const char * m_value
Value string, can be NULL.
Definition: common.h:280
const char * m_key
Key string, mustn't be NULL.
Definition: common.h:279

See also documentation of the CC_Mqtt5UserProp structure.

Extra Properties Configuration

To add extra MQTT v5 specific properties to the connection request use cc_mqtt5_client_connect_config_extra() function.

// Initialize the configuration structure to the default values
// Assign necessary properties:
extraConfig.m_sessionExpiryInterval = 5;
extraConfig.m_topicAliasMaximum = 20;
// Perform the configuration
ec = cc_mqtt5_client_connect_config_extra(connect, &extraConfig);
printf("ERROR: Extra properties configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_config_extra(CC_Mqtt5ConnectHandle handle, const CC_Mqtt5ConnectExtraConfig *config)
Perform extra properties configuration of the "connect" operation.
void cc_mqtt5_client_connect_init_config_extra(CC_Mqtt5ConnectExtraConfig *config)
Intialize the CC_Mqtt5ConnectExtraConfig configuration structure.
Extra properties configuration of the "connect" operation.
Definition: common.h:319
unsigned m_topicAliasMaximum
"Topic Alias Maximum" property, defaults to 0, not added when 0.
Definition: common.h:324
unsigned m_sessionExpiryInterval
"Session Expiry Interval" property, defaults to 0, not added when 0.
Definition: common.h:320

See also documentation of the CC_Mqtt5ConnectExtraConfig structure.

IMPORTANT: The cc_mqtt5_client_connect_config_extra() function mustn't be called more than once for a single "connect" operation. Otherwise it may result in setting multiple properties of the same type, which is the "Protocol Error" according to the MQTT v5 specification.

Extended Authentication Handshake Configuration

In case the extended authentication handshake is needed use cc_mqtt5_client_connect_config_auth() function.

CC_Mqtt5AuthErrorCode my_auth_handshake_cb(void* data, const CC_Mqtt5AuthInfo* authInfoIn, CC_Mqtt5AuthInfo* authInfoOut)
{
... // Analyse authInfoIn
if (auth_failed) {
return CC_Mqtt5AuthErrorCode_Disconnect; // terminate handshake with error
}
... // Populate authInfoOut
return CC_Mqtt5AuthErrorCode_Continue; // continue handshake
}
CC_Mqtt5AuthConfig authConfig;
// Initialize the configuration structure to the default values
// Assign all the necessary values
authConfig.m_authMethod = "some_method"
authConfig.m_authData = ...;
authConfig.m_authDataLen = ...;
authConfig.m_authCb = &my_auth_handshake_cb;
authConfig.m_authCbData = ...;
// Perform the configuration
ec = cc_mqtt5_client_connect_config_auth(connect, &authConfig);
printf("ERROR: Authentication handshake configuration failed with ec=%d\n", ec);
...
}
void cc_mqtt5_client_connect_init_config_auth(CC_Mqtt5AuthConfig *config)
Intialize the CC_Mqtt5AuthConfig configuration structure.
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_config_auth(CC_Mqtt5ConnectHandle handle, const CC_Mqtt5AuthConfig *config)
Perform authentication handshake configuration of the "connect" operation.
CC_Mqtt5AuthErrorCode
Error code returned by the CC_Mqtt5AuthCb callback.
Definition: common.h:112
@ CC_Mqtt5AuthErrorCode_Continue
Continue the authentication process.
Definition: common.h:113
@ CC_Mqtt5AuthErrorCode_Disconnect
Stop the authentication, send DISCONNECT to broker.
Definition: common.h:114
Authentication Configuration.
Definition: common.h:628
void * m_authCbData
Pointer to user data object, passed as first parameter to the "m_authCb" callback.
Definition: common.h:633
unsigned m_authDataLen
Amount of bytes in the authentication data buffer.
Definition: common.h:631
CC_Mqtt5AuthCb m_authCb
Callback to be invoked during the authentication handshake.
Definition: common.h:632
const unsigned char * m_authData
Pointer to the authentication data buffer, can be NULL.
Definition: common.h:630
const char * m_authMethod
Authentication method string, must NOT be NULL.
Definition: common.h:629
Authentication handshake information.
Definition: common.h:358

During the "connect" operation, both client and the broker can exchange multiple AUTH messages. When such message arrive the library will invoke provided handshake callback. The responsibility of the callback is to analyze the incoming authentication data and populate the response information. The client can fail the handshake by returning the CC_Mqtt5AuthErrorCode_Disconnect instead of the CC_Mqtt5AuthErrorCode_Continue.

When client side fails the authentication, the DISCONNECT message is sent to the broker and "connect" operation callback (see Sending Connection Request below) will be invoked with CC_Mqtt5AsyncOpStatus_Aborted as status report. The broker disconnection report callback will NOT be invoked, because the connection hasn't really happened yet.

IMPORTANT: The cc_mqtt5_client_connect_config_auth() function mustn't be called more than once for a single "connect" operation. Otherwise it may result in setting multiple properties of the same type, which is the "Protocol Error" according to the MQTT v5 specification.

Adding "User Properties"

The MQTT v5 specification allows attaching any number of the "User Properties" to the CONNECT message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_connect_add_user_prop() function.

prop.m_key = "some_key";
prop.m_value = "some_value";
printf("ERROR: User property configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_add_user_prop(CC_Mqtt5ConnectHandle handle, const CC_Mqtt5UserProp *prop)
Add user property to the configuration of the "connect" operation.

See also documentation of the CC_Mqtt5UserProp structure.

Sending Connection Request

When all the necessary configurations are performed for the allocated "connect" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_connect_send() function.

void my_connect_complete_cb(void* data, CC_Mqtt5AsyncOpStatus status, const CC_Mqtt5ConnectResponse* response)
{
printf("ERROR: The connection operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "response" is not NULL when status is CC_Mqtt5AsyncOpStatus_Complete.
assert(response != NULL);
... // Analyze response values.
}
ec = cc_mqtt5_client_connect_send(connect, &my_connect_complete_cb, data);
printf("ERROR: Failed to send connect request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_send(CC_Mqtt5ConnectHandle handle, CC_Mqtt5ConnectCompleteCb cb, void *cbData)
Send the configured "connect" operation to broker.
CC_Mqtt5AsyncOpStatus
Status of the asynchronous operation.
Definition: common.h:97
@ CC_Mqtt5AsyncOpStatus_Complete
The requested operation has been completed, refer to reported extra details for information.
Definition: common.h:98
Response information from broker to "connect" request.
Definition: common.h:332

The provided callback will be invoked when the "connect" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.

The handle returned by the cc_mqtt5_client_connect_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_connect_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.

When the "connect" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete.

NOTE that only single "connect" operation is allowed at a time, any attempt to prepare a new one via cc_mqtt5_client_connect_prepare() will be rejected until the "connect" operation completion callback is invoked or the operation is cancelled.

IMPORTANT: The response information from the broker may report that some features on the broker side are disabled. The library will reject any subsequent operation configuration which contradict the broker's capabilities. For example, attempt to subscribe to the topic containing wildcards, when the reported CC_Mqtt5ConnectResponse::m_wildcardSubAvailable is false, will be rejected with CC_Mqtt5ErrorCode_BadParam.

Quote from the MQTT v5 specification:

If a Server sends a CONNACK packet containing a Reason code of 128 or greater
it MUST then close the Network Connection [MQTT-3.2.2-7]

When the callback reporting the connection status is invoked, it is responsibility of the application to check the CC_Mqtt5ConnectResponse::m_reasonCode value. If it's CC_Mqtt5ReasonCode_UnspecifiedError or greater, the application is responsible to close the network connection and retry the "connect" operation after the network connection to the broker is re-established. The same should be done when the "connect" operation is not properly completed, i.e. the reported status is NOT CC_Mqtt5AsyncOpStatus_Complete.

Cancel the "Connect" Operation.

While the handle returned by the cc_mqtt5_client_connect_prepare() is still valid it is possible to cancel / discard the operation.

printf("ERROR: Failed to cancel connect with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_cancel(CC_Mqtt5ConnectHandle handle)
Cancel the allocated "connect" operation.

In case the cc_mqtt5_client_connect_send() function was successfully called before the cc_mqtt5_client_connect_cancel(), the operation is cancelled without callback invocation.

Simplifying the "Connect" Operation Preparation.

In many use cases the "connect" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides several wrapper functions that can be used:

For example:

// Assign default values to the "basicConfig"
// Update the values if needed:
basicConfig.m_clientId = "some_client";
basicConfig.m_cleanStart = true;
ec = cc_mqtt5_client_connect_simple(client, basicConfig, &my_connect_complete_cb, data);
printf("ERROR: Failed to send connect request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_connect_simple(CC_Mqtt5ClientHandle handle, const CC_Mqtt5ConnectBasicConfig *basicConfig, CC_Mqtt5ConnectCompleteCb cb, void *cbData)
Prepare, configure, and send "connect" request in one go (simple version)

Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_connect_prepare(). It means that it's not possible to cancel the "connect" operation before its completion.

Check The Library Remains Connected

At any time it is possible to check the internal state of the library of whether it's properly connected to the broker.

bool isConnected = cc_mqtt5_client_is_connected(client);
bool cc_mqtt5_client_is_connected(CC_Mqtt5ClientHandle handle)
Check the inner state of the library of whether it's connected to the broker.

Disconnecting From Broker

To intentionally disconnect from broker use disconnect operation. The unsolicited disconnection from the broker is described in ref Unsolicited Broker Disconnection section below.

Preparing "Disconnect" Operation.

if (disconnect == NULL) {
printf("ERROR: Disconnect allocation failed with ec=%d\n", ec);
}
CC_Mqtt5DisconnectHandle cc_mqtt5_client_disconnect_prepare(CC_Mqtt5ClientHandle handle, CC_Mqtt5ErrorCode *ec)
Prepare "disconnect" operation.
CC_Mqtt5Disconnect * CC_Mqtt5DisconnectHandle
Handle for "disconnect" operation.
Definition: common.h:231

Configuration of "Disconnect" Operation

The configuration of the "disconnect" operation is performed using a single cc_mqtt5_client_disconnect_config() function.

// Assign default values to the "config"
// Update the values if needed:
// Perform the configuration
ec = cc_mqtt5_client_disconnect_config(disconnect, &config);
printf("ERROR: Disconnect configuration failed with ec=%d\n", ec);
...
}
void cc_mqtt5_client_disconnect_init_config(CC_Mqtt5DisconnectConfig *config)
Intialize the CC_Mqtt5DisconnectConfig configuration structure.
CC_Mqtt5ErrorCode cc_mqtt5_client_disconnect_config(CC_Mqtt5DisconnectHandle handle, const CC_Mqtt5DisconnectConfig *config)
Perform configuration of the "disconnect" operation.
@ CC_Mqtt5ReasonCode_DisconnectWithWill
value Disconnect w/ Will.
Definition: common.h:127
Configuration structure of the "disconnect" operation.
Definition: common.h:381
CC_Mqtt5ReasonCode m_reasonCode
"Reason Code" to report to the broker
Definition: common.h:382

Adding "User Properties"

The MQTT v5 specification allows attaching any number of the "User Properties" to the DISCONNECT message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_disconnect_add_user_prop() function.

prop.m_key = "some_key";
prop.m_value = "some_value";
printf("ERROR: User property configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_disconnect_add_user_prop(CC_Mqtt5DisconnectHandle handle, const CC_Mqtt5UserProp *prop)
Add user property to the configuration of the "disconnect" operation.

See also documentation of the CC_Mqtt5UserProp structure.

Sending Disconnection Request

When the necessary configuration is performed for the allocated "disconnect" operation it can be sent to the broker. To initiate sending use the cc_mqtt5_client_disconnect_send() function.

printf("ERROR: Failed to send disconnect request with ec=%d\n", ec);
...
return;
}
assert(!cc_mqtt5_client_is_connected(client)); // Expect immediate disconnection after the successful send
CC_Mqtt5ErrorCode cc_mqtt5_client_disconnect_send(CC_Mqtt5DisconnectHandle handle)
Send the configured "disconnect" operation to broker.

NOTE that the cc_mqtt5_client_disconnect_send() function doesn't receive any callback because there is no expected response to the DISCONNECT message from the broker. The disconnection effect is immediate. The application is expected to terminate the network connection (while making sure that the the requested data is actually sent). The handle returned by the cc_mqtt5_client_disconnect_prepare() must be discarded.

In case there are other asynchronous operations that hasn't been completed yet, their completion callback is automatically invoked with CC_Mqtt5AsyncOpStatus_Aborted status.

IMPORTANT: In case of sending the explicit disconnection request the registered unsolicited disconnection callback is NOT invoked.

After the disconnection the application can re-establish network connection to the broker and perform the connect operation again.

Cancel the "Disconnect" Operation.

While the handle returned by the cc_mqtt5_client_disconnect_prepare() is still valid it is possible to cancel / discard the operation.

printf("ERROR: Failed to cancel disconnect with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_disconnect_cancel(CC_Mqtt5DisconnectHandle handle)
Cancel the allocated "disconnect" operation.

Simplifying the "Disonnect" Operation Preparation.

In many use cases the "disconnect" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides wrapper function(s) that can be used:

For example:

// Assign default values to the "config"
// Update the values if needed:
ec = cc_mqtt5_client_disconnect(client, config);
printf("ERROR: Failed to send disconnect request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_disconnect(CC_Mqtt5ClientHandle handle, const CC_Mqtt5DisconnectConfig *config)
Prepare, configure, and send "disconnect" request in one go.

Subscribing to Receive Messages

To subscribe to receive incoming messages use subscribe operation. The application can issue multiple "subscribe" operations in parallel.

Preparing "Subscribe" Operation.

if (subscribe == NULL) {
printf("ERROR: Subscribe allocation failed with ec=%d\n", ec);
}

Configuring "Subscribe" Response Timeout

When created, the "subscribe" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_subscribe_set_response_timeout() function.

... /* Something went wrong */
}
CC_Mqtt5ErrorCode cc_mqtt5_client_subscribe_set_response_timeout(CC_Mqtt5SubscribeHandle handle, unsigned ms)
Configure the broker response timeout for the "subscribe" operation.

To retrieve the configured response timeout use the cc_mqtt5_client_subscribe_get_response_timeout() function.

Topic Configuration

Single SUBSCRIBE message can carry multiple topic subscriptions. Use separate cc_mqtt5_client_subscribe_config_topic() function invocation to configure each such subscription.

// Assign default values to the configuration
// Update values if needed
topicConfig.m_topic = "some/topic";
topicConfig.m_noLocal = true;
...
// Perform the configuration
ec = cc_mqtt5_client_subscribe_config_topic(subscribe, &topicConfig);
printf("ERROR: Topic configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_subscribe_config_topic(CC_Mqtt5SubscribeHandle handle, const CC_Mqtt5SubscribeTopicConfig *config)
Add topic configuration of the "subscribe" operation.
void cc_mqtt5_client_subscribe_init_config_topic(CC_Mqtt5SubscribeTopicConfig *config)
Intialize the CC_Mqtt5SubscribeTopicConfig configuration structure.
Topic filter configuration structure of the "subscribe" operation.
Definition: common.h:391
const char * m_topic
"Topic Filter" string, mustn't be NULL
Definition: common.h:392
bool m_noLocal
"No Local" subscription option, defaults to false.
Definition: common.h:395

See also documentation of the CC_Mqtt5SubscribeTopicConfig structure.

By default the library will perform the analysis of the submitted topic format and reject it if topic format is incorrect. However, for performance reasons it is possible to disable such verification when client application ensures that no invalid topics are used. That includes not submitting topics with wildcards when the broker doesn't support them (see CC_Mqtt5ConnectResponse), or not using shared subscription for the same reason.

... /* Something is wrong */
}
CC_Mqtt5ErrorCode cc_mqtt5_client_set_verify_outgoing_topic_enabled(CC_Mqtt5ClientHandle handle, bool enabled)
Control outgoing topic format verification.

NOTE that the configuration is global per client and not per "subscribe" operation.

Also note that the same function controls the verification of the "subscribe", "unsubscribe" and "publish" topic formats.

To retrieve the current configuration use cc_mqtt5_client_get_verify_outgoing_topic_enabled() function.

Extra Properties Configuration

To add extra MQTT v5 specific properties to the subscription request use cc_mqtt5_client_subscribe_config_extra() function.

// Assign default values to the configuration
// Assign necessary properties:
extraConfig.m_subId = ...;
// Perform the configuration
ec = cc_mqtt5_client_subscribe_config_extra(subscribe, &extraConfig);
printf("ERROR: Extra properties configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_subscribe_config_extra(CC_Mqtt5SubscribeHandle handle, const CC_Mqtt5SubscribeExtraConfig *config)
Perform extra properties configuration of the "subscribe" operation.
void cc_mqtt5_client_subscribe_init_config_extra(CC_Mqtt5SubscribeExtraConfig *config)
Intialize the CC_Mqtt5SubscribeExtraConfig configuration structure.
Extra subscription properties configuration structure.
Definition: common.h:403
unsigned m_subId
"Subscription Identifier" property, defaults to invalid value of 0.
Definition: common.h:404

See also documentation of the CC_Mqtt5SubscribeExtraConfig structure.

Note that explicitly initializing the CC_Mqtt5SubscribeExtraConfig configuration object is not really necessary, because it has only a single CC_Mqtt5SubscribeExtraConfig::m_subId member, which is going to be overwritten as part of the configuration. However, calling the cc_mqtt5_client_subscribe_init_config_extra() is still recommended to make the application's code future updates proof. Potentially the MQTT v5 specification and as the result this library can be updated by adding new property to the CC_Mqtt5SubscribeExtraConfig struct. Having the "unnecessary" call to the cc_mqtt5_client_subscribe_init_config_extra() function makes sure that newly added property gets initialized to the default value without any need to update the application's code.

Also note that allocating and managing the "Subscription Identifier" property values is completely application's responsibility. The broker is responsible to report the assigned value when sending relevant message, and the client can use the reported value for easier dispatching of the received message to appropriate handling functionality.

Adding "User Properties"

The MQTT v5 specification allows attaching any number of the "User Properties" to the SUBSCRIBE message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_subscribe_add_user_prop() function.

prop.m_key = "some_key";
prop.m_value = "some_value";
printf("ERROR: User property configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_subscribe_add_user_prop(CC_Mqtt5SubscribeHandle handle, const CC_Mqtt5UserProp *prop)
Add user property to the configuration of the "subscribe" operation.

See also documentation of the CC_Mqtt5UserProp structure.

Sending Subscription Request

When all the necessary configurations are performed for the allocated "subscribe" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_subscribe_send() function.

void my_subscribe_complete_cb(void* data, CC_Mqtt5SubscribeHandle handle, CC_Mqtt5AsyncOpStatus status, const CC_Mqtt5SubscribeResponse* response)
{
printf("ERROR: The subscription operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "response" is not NULL when status is CC_Mqtt5AsyncOpStatus_Complete.
assert(response != NULL);
... // Analyze response values.
}
ec = cc_mqtt5_client_subscribe_send(subscribe, &my_subscribe_complete_cb, data);
printf("ERROR: Failed to send subscribe request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_subscribe_send(CC_Mqtt5SubscribeHandle handle, CC_Mqtt5SubscribeCompleteCb cb, void *cbData)
Send the configured "subscribe" operation to broker.
Response information from broker to "subscribe" request.
Definition: common.h:410

The provided callback will be invoked when the "subscribe" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.

The handle returned by the cc_mqtt5_client_subscribe_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_subscribe_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.

Note that the callback function receives the "subscribe" operation handle as its second parameter. Although the handle is already invalid and cannot be used in any other function, it allows the application to identify the original "subscribe" request if multiple have been issued in parallel and use the same callback function for all of them.

When the "subscribe" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete.

Cancel the "Subscribe" Operation.

While the handle returned by the cc_mqtt5_client_subscribe_prepare() is still valid it is possible to cancel / discard the operation.

printf("ERROR: Failed to cancel subscribe with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_subscribe_cancel(CC_Mqtt5SubscribeHandle handle)
Cancel the allocated "subscribe" operation.

In case the cc_mqtt5_client_subscribe_send() function was successfully called before the cc_mqtt5_client_subscribe_cancel(), the operation is cancelled without callback invocation.

Simplifying the "Subscribe" Operation Preparation.

In many use cases the "subscribe" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides several wrapper functions that can be used:

For example:

// Assign default values to the configuration
// Update values if needed
topicConfig.m_topic = "some/topic";
topicConfig.m_noLocal = true;
ec = cc_mqtt5_client_subscribe_simple(client, &topicConfig, &my_subscribe_complete_cb, data);
printf("ERROR: Failed to send subscribe request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_subscribe_simple(CC_Mqtt5ClientHandle handle, const CC_Mqtt5SubscribeTopicConfig *topicConfig, CC_Mqtt5SubscribeCompleteCb cb, void *cbData)
Prepare, configure, and send "subscribe" request in one go (simple version)

Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_subscribe_prepare(). It means that it's not possible to cancel the "subscribe" operation before its completion or identify the subscribe operation by the reported handle when the completion callback is invoked.

Unsubscribing from Message Reception

To unsubscribe from receiving incoming messages use unsubscribe operation. The application can issue multiple "unsubscribe" operations in parallel.

Preparing "Unsubscribe" Operation.

if (unsubscribe == NULL) {
printf("ERROR: Unsubscribe allocation failed with ec=%d\n", ec);
}
CC_Mqtt5Unsubscribe * CC_Mqtt5UnsubscribeHandle
Handle for "unsubscribe" operation.
Definition: common.h:249
CC_Mqtt5UnsubscribeHandle cc_mqtt5_client_unsubscribe_prepare(CC_Mqtt5ClientHandle handle, CC_Mqtt5ErrorCode *ec)
Prepare "unsubscribe" operation.

Configuring "Unsubscribe" Response Timeout

When created, the "unsubscribe" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_unsubscribe_set_response_timeout() function.

... /* Something went wrong */
}
CC_Mqtt5ErrorCode cc_mqtt5_client_unsubscribe_set_response_timeout(CC_Mqtt5UnsubscribeHandle handle, unsigned ms)
Configure the broker response timeout for the "unsubscribe" operation.

To retrieve the configured response timeout use the cc_mqtt5_client_unsubscribe_get_response_timeout() function.

Topic Configuration

Single UNSUBSCRIBE message can carry multiple topic unsubscriptions. Use cc_mqtt5_client_unsubscribe_config_topic() function to configure each such unsubscription.

// Assign default values to the configuration
// Update values
topicConfig.m_topic = "some/topic";
// Perform the configuration
ec = cc_mqtt5_client_unsubscribe_config_topic(unsubscribe, &topicConfig);
printf("ERROR: Topic configuration failed with ec=%d\n", ec);
...
}
void cc_mqtt5_client_unsubscribe_init_config_topic(CC_Mqtt5UnsubscribeTopicConfig *config)
Intialize the CC_Mqtt5UnsubscribeTopicConfig configuration structure.
CC_Mqtt5ErrorCode cc_mqtt5_client_unsubscribe_config_topic(CC_Mqtt5UnsubscribeHandle handle, const CC_Mqtt5UnsubscribeTopicConfig *config)
Add topic configuration of the "unsubscribe" operation.
Topic filter configuration structure of the "unsubscribe" operation.
Definition: common.h:422
const char * m_topic
"Topic Filter" string, mustn't be NULL
Definition: common.h:423

See also documentation of the CC_Mqtt5UnsubscribeTopicConfig structure.

Note that explicitly initializing the CC_Mqtt5UnsubscribeTopicConfig configuration object is not really necessary, because it has only a single CC_Mqtt5UnsubscribeTopicConfig::m_topic member, which is going to be overwritten as part of the configuration. However, calling the cc_mqtt5_client_unsubscribe_init_config_topic() is still recommended to make the application's code future updates proof. Potentially the MQTT v5 specification and as the result this library can be updated by adding new fields to the CC_Mqtt5UnsubscribeTopicConfig struct. Having the "unnecessary" call to the cc_mqtt5_client_unsubscribe_init_config_topic() function makes sure that newly added field gets initialized to the default value without any need to update the application's code.

By default the library will perform the analysis of the submitted topic format and reject it if topic format is incorrect. However, for performance reasons it is possible to disable such verification when client application ensures that no invalid topics are used.

... /* Something is wrong */
}

NOTE that the configuration is global per client and not per "unsubscribe" operation.

Also note that the same function controls the verification of the "subscribe", "unsubscribe" and "publish" topic formats.

To retrieve the current configuration use cc_mqtt5_client_get_verify_outgoing_topic_enabled() function.

Adding "User Properties"

The MQTT v5 specification allows attaching any number of the "User Properties" to the UNSUBSCRIBE message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_unsubscribe_add_user_prop() function.

prop.m_key = "some_key";
prop.m_value = "some_value";
printf("ERROR: User property configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_unsubscribe_add_user_prop(CC_Mqtt5UnsubscribeHandle handle, const CC_Mqtt5UserProp *prop)
Add user property to the configuration of the "unsubscribe" operation.

See also documentation of the CC_Mqtt5UserProp structure.

Sending Unsubscription Request

When all the necessary configurations are performed for the allocated "unsubscribe" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_unsubscribe_send() function.

void my_unsubscribe_complete_cb(void* data, CC_Mqtt5UnsubscribeHandle handle, CC_Mqtt5AsyncOpStatus status, const CC_Mqtt5UnsubscribeResponse* response)
{
printf("ERROR: The unsubscription operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "response" is not NULL when status is CC_Mqtt5AsyncOpStatus_Complete.
assert(response != NULL);
... // Analyze response values.
}
ec = cc_mqtt5_client_unsubscribe_send(unsubscribe, &my_unsubscribe_complete_cb, data);
printf("ERROR: Failed to send unsubscribe request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_unsubscribe_send(CC_Mqtt5UnsubscribeHandle handle, CC_Mqtt5UnsubscribeCompleteCb cb, void *cbData)
Send the configured "unsubscribe" operation to broker.
Response information from broker to "unsubscribe" request.
Definition: common.h:429

The provided callback will be invoked when the "unsubscribe" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.

The handle returned by the cc_mqtt5_client_unsubscribe_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_unsubscribe_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.

Note that the callback function receives the "unsubscribe" operation handle as its second parameter. Although the handle is already invalid and cannot be used in any other function, it allows the application to identify the original "unsubscribe" request if multiple have been issued in parallel and use the same callback function for all of them.

When the "unsubscribe" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete.

Cancel the "Unsubscribe" Operation.

While the handle returned by the cc_mqtt5_client_unsubscribe_prepare() is still valid it is possible to cancel / discard the operation.

printf("ERROR: Failed to cancel unsubscribe with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_unsubscribe_cancel(CC_Mqtt5UnsubscribeHandle handle)
Cancel the allocated "unsubscribe" operation.

In case the cc_mqtt5_client_unsubscribe_send() function was successfully called before the cc_mqtt5_client_unsubscribe_cancel(), the operation is cancelled without callback invocation.

Simplifying the "Unsubscribe" Operation Preparation.

In many use cases the "unsubscribe" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides several wrapper functions that can be used:

For example:

// Assign default values to the configuration
// Update values
topicConfig.m_topic = "some/topic";
ec = cc_mqtt5_client_unsubscribe_simple(client, &topicConfig, &my_unsubscribe_complete_cb, data);
printf("ERROR: Failed to send unsubscribe request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_unsubscribe_simple(CC_Mqtt5ClientHandle handle, const CC_Mqtt5UnsubscribeTopicConfig *topicConfig, CC_Mqtt5UnsubscribeCompleteCb cb, void *cbData)
Prepare, configure, and send "unsubscribe" request in one go (simple version)

Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_unsubscribe_prepare(). It means that it's not possible to cancel the "unsubscribe" operation before its completion or identify the unsubscribe operation by the reported handle when the completion callback is invoked.

Publishing Messages

To publish messages to the broker use publish operation.

Preparing "Publish" Operation.

if (publish == NULL) {
printf("ERROR: Publish allocation failed with ec=%d\n", ec);
}
CC_Mqtt5Publish * CC_Mqtt5PublishHandle
Handle for "publish" operation.
Definition: common.h:258
CC_Mqtt5PublishHandle cc_mqtt5_client_publish_prepare(CC_Mqtt5ClientHandle handle, CC_Mqtt5ErrorCode *ec)
Prepare "publish" operation.

Configuring "Publish" Response Timeout

When publishing messages with QoS value CC_Mqtt5QoS_AtLeastOnceDelivery or above, the response from the broker is expected.

When created, the "publish" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_publish_set_response_timeout() function.

... /* Something went wrong */
}
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_set_response_timeout(CC_Mqtt5PublishHandle handle, unsigned ms)
Configure the broker response timeout for the "publish" operation.

To retrieve the configured response timeout use the cc_mqtt5_client_publish_get_response_timeout() function.

Configuring "Publish" Re-Send Attempts

The MQTT v5 specification has a mechanism of insured delivery of the published message to the broker. In the case of not 100% reliable connection the messages can get lost and needs to be re-sent. The default amount of re-sends is 2, i.e. when the first send is not acknowledged by the broker, it is tried again with DUP flag is set in the PUBLISH message. When the second attempt is not acknowledged, then the publish operation is terminated with appropriate status report. It is possible to change the default by using cc_mqtt5_client_publish_set_resend_attempts() function. The DUP flag will be set in all the re-sent PUBLISH messages.

... /* Something went wrong */
}
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_set_resend_attempts(CC_Mqtt5PublishHandle handle, unsigned attempts)
Configure the amount of attempts to resend "publish" operation until the acknowledgement is received.

To retrieve the configured resend attempts number use the cc_mqtt5_client_publish_get_resend_attempts() function.

Basic Configuration of "Publish" Operation

The "basic" configuration means no extra MQTT v5 properties assigned to the message, with one small exception of using topic alias (covered below as a separate subject).

// Assign default values to the "basicConfig"
// Update the required values
basicConfig.m_topic = "some/topic";
basicConfig.m_data = &some_buf[0];
basicConfig.m_dataLen = ...;
basicConfig.m_qos = ...;
// Perform the configuration
ec = cc_mqtt5_client_publish_config_basic(publish, &basicConfig);
printf("ERROR: Basic configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_config_basic(CC_Mqtt5PublishHandle handle, const CC_Mqtt5PublishBasicConfig *config)
Perform basic configuration of the "publish" operation.
void cc_mqtt5_client_publish_init_config_basic(CC_Mqtt5PublishBasicConfig *config)
Intialize the CC_Mqtt5PublishBasicConfig configuration structure.
Configuration structure to be passed to the cc_mqtt5_client_publish_config_basic().
Definition: common.h:462
const unsigned char * m_data
Pointer to publish data buffer, defaults to NULL.
Definition: common.h:464
CC_Mqtt5QoS m_qos
Publish QoS value, defaults to CC_Mqtt5QoS_AtMostOnceDelivery.
Definition: common.h:466
unsigned m_dataLen
Amount of bytes in the publish data buffer, defaults to 0.
Definition: common.h:465
const char * m_topic
Publish topic, cannot be NULL.
Definition: common.h:463

See also documentation of the CC_Mqtt5PublishBasicConfig structure.

By default the library will perform the analysis of the submitted topic format and reject it if topic format is incorrect. However, for performance reasons it is possible to disable such verification when client application ensures that no invalid topics are used.

... /* Something is wrong */
}

To retrieve the current configuration use the cc_mqtt5_client_get_verify_outgoing_topic_enabled() function.

NOTE that the configuration is global per client and not per "publish" operation.

Also note that the same function controls the verification of the "subscribe", "unsubscribe", and "publish" filter / topic formats.

Extra Properties Configuration

To add extra MQTT v5 specific properties to the publish request use cc_mqtt5_client_publish_config_extra() function.

// Initialize the configuration structure to the default values
// Assign necessary properties:
extraConfig.m_messageExpiryInterval = 5;
// Perform the configuration
ec = cc_mqtt5_client_publish_config_extra(publish, &extraConfig);
printf("ERROR: Extra properties configuration failed with ec=%d\n", ec);
...
}
@ CC_Mqtt5PayloadFormat_Utf8
UTF-8 string.
Definition: common.h:90
void cc_mqtt5_client_publish_init_config_extra(CC_Mqtt5PublishExtraConfig *config)
Intialize the CC_Mqtt5PublishExtraConfig configuration structure.
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_config_extra(CC_Mqtt5PublishHandle handle, const CC_Mqtt5PublishExtraConfig *config)
Perform extra properties configuration of the "publish" operation.
Configuration structure to be passed to the cc_mqtt5_client_publish_config_extra().
Definition: common.h:475
unsigned m_messageExpiryInterval
"Message Expiry Interval" property, defaults to 0, not added when 0.
Definition: common.h:480
CC_Mqtt5PayloadFormat m_format
"Payload Format Indicator" property, defaults to CC_Mqtt5PayloadFormat_Unspecified,...
Definition: common.h:481

See also documentation of the CC_Mqtt5PublishExtraConfig structure.

Adding "User Properties"

The MQTT v5 specification allows attaching any number of the "User Properties" to the PUBLISH message. The library allows such assignment using multiple invocations of the cc_mqtt5_client_publish_add_user_prop() function.

prop.m_key = "some_key";
prop.m_value = "some_value";
printf("ERROR: User property configuration failed with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_add_user_prop(CC_Mqtt5PublishHandle handle, const CC_Mqtt5UserProp *prop)
Add user property to the configuration of the "publish" operation.

See also documentation of the CC_Mqtt5UserProp structure.

Sending Publish Request

When all the necessary configurations are performed for the allocated "publish" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_publish_send() function.

void my_publish_complete_cb(void* data, CC_Mqtt5PublishHandle handle, CC_Mqtt5AsyncOpStatus status, const CC_Mqtt5PublishResponse* response)
{
printf("ERROR: The publish operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "response" may be NULL when there is no response from broker
// applicable when QoS is CC_Mqtt5QoS_AtMostOnceDelivery.
if (response == NULL) {
return;
}
... // Analyze response values.
}
ec = cc_mqtt5_client_publish_send(publish, &my_publish_complete_cb, data);
printf("ERROR: Failed to send publish request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_send(CC_Mqtt5PublishHandle handle, CC_Mqtt5PublishCompleteCb cb, void *cbData)
Send the configured "publish" operation to broker.
Response information from broker to "publish" request.
Definition: common.h:487

The provided callback will be invoked when the "publish" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.

The callback pointer can also be NULL. In such case the completion of the publish operation is silent.

When QoS value is CC_Mqtt5QoS_AtMostOnceDelivery (see the basic configuration), there is no broker response to wait for and the callback is invoked right away after sending the serialized data.

The handle returned by the cc_mqtt5_client_publish_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_publish_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.

Note that the callback function receives the "publish" operation handle as its second parameter. Although the handle is already invalid and cannot be used in any other function, it allows the application to identify the original "publish" request if multiple have been issued in parallel and use the same callback function for all of them.

When the "publish" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete and the publish QoS was 1 (CC_Mqtt5QoS_AtLeastOnceDelivery) or higher.

When the callback reporting the publish status is invoked, it is responsibility of the application to check the CC_Mqtt5PublishResponse::m_reasonCode value. If it's CC_Mqtt5ReasonCode_UnspecifiedError or greater means the "publish" operation wasn't successful.

Cancel the "Publish" Operation.

While the handle returned by the cc_mqtt5_client_publish_prepare() is still valid it is possible to cancel / discard the operation.

printf("ERROR: Failed to cancel publish with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_cancel(CC_Mqtt5PublishHandle handle)
Cancel the allocated "publish" operation.

In case the cc_mqtt5_client_publish_send() function was successfully called before the cc_mqtt5_client_publish_cancel(), the operation is cancelled without callback invocation.

Using Topic Alias

The MQTT v5 specification allows usage of the numeric topic aliases to be able to reduce amount of bytes per publish. In order for the library to start using topic aliases, they need to be allocated (registered) using the cc_mqtt5_client_pub_topic_alias_alloc() function.

CC_Mqtt5ErrorCode ec = cc_mqtt5_client_pub_topic_alias_alloc(client, "some/topic", 2 /* qos0RegsCount */);
CC_Mqtt5ErrorCode cc_mqtt5_client_pub_topic_alias_alloc(CC_Mqtt5ClientHandle handle, const char *topic, unsigned qos0RegsCount)
Allocate alias for topic.

Note that the broker reports maximum amount of allowed topic aliases in its response to the "connect" request via the CC_Mqtt5ConnectResponse::m_topicAliasMax ("Topic Alias Maximum" property). It means that the allocation of the topic aliases is allowed only after the successful "connect" operation.

Also note that the topic aliases are not part of the session state. For every re-connection attempt all the previously allocated topic aliases get cleared in the internal data structures and they need to be allocated again after the successful connection.

The last parameter passed to the cc_mqtt5_client_pub_topic_alias_alloc() function is used to specify how many times the topic alias is reported to the broker alongside the topic string in the "publish" operation when only QoS0 publishes are used. For the QoS1 and QoS2 "publish" operations there is an acknowledgement message from the broker indicating that the topic alias is received and known to the broker. However, when QoS0 only publishes are performed, the reception of the topic alias registration request by the broker is not ensured. Also the MQTT v5 specification explicitly states that it is "Protocol Error" to use unknown topic alias.

If the PUBLISH packet contains a Topic Alias, the receiver processes it as follows:
...
3) If the receiver does not already have a mapping for this Topic Alias
a) If the packet has a zero length Topic Name field it is a Protocol Error and the receiver uses
DISCONNECT with Reason Code of 0x82 (Protocol Error) as described in section 4.13.

Based on the said above, it can be prone to unexpected protocol error disconnection to use single QoS0 publish message to report topic alias to the broker, so the last parameter of the cc_mqtt5_client_pub_topic_alias_alloc() function indicates how many times the topic alias needs to be reported to the broker alongside the topic string itself before client is allowed to send topic alias only without the topic string. However if any QoS1 or QoS2 messages are successfully published to the broker with the same topic alias, then all the subsequent QoS0 message will use topic alias without the topic string.

To stop using topic alias for the specific topic, use cc_mqtt5_client_pub_topic_alias_free() function to free the allocated alias.

CC_Mqtt5ErrorCode cc_mqtt5_client_pub_topic_alias_free(CC_Mqtt5ClientHandle handle, const char *topic)
Free alias for topic for another use.

To inquire how many topic aliases have already been allocated, use the cc_mqtt5_client_pub_topic_alias_count() function.

unsigned count = cc_mqtt5_client_pub_topic_alias_count(client);
unsigned cc_mqtt5_client_pub_topic_alias_count(CC_Mqtt5ClientHandle handle)
Get amount of allocated topic aliases.

To check whether a topic alias is allocated for specific topic use the cc_mqtt5_client_pub_topic_alias_is_allocated() function.

bool allocated = cc_mqtt5_client_pub_topic_alias_is_allocated(client, "some/topic");
bool cc_mqtt5_client_pub_topic_alias_is_allocated(CC_Mqtt5ClientHandle handle, const char *topic)
Check if topic alias is allocated for topic.

Once the topic alias is successfully allocated, the basic configuration of the "publish" operation allows control of whether and how to use topic alias via the CC_Mqtt5PublishBasicConfig::m_topicAliasPref data member.

Exceeding "Receive Maximum" Set by the Broker

When the connect operation is complete, the "Receive Maximum" property set by the broker is reported via the CC_Mqtt5ConnectResponse::m_highQosSendLimit value. The application can monitor amount of incomplete "publish" operations using the cc_mqtt5_client_publish_count() function and decide whether to issue any new publishes.

unsigned count = cc_mqtt5_client_publish_count(client);
if (count >= max_allowed) {
...
}
unsigned cc_mqtt5_client_publish_count(CC_Mqtt5ClientHandle handle)
Get amount incomplete "publish" operations.

However, when the "Receive Maximum" limit is reached and a new publish is issued by the application, the library will postpone sending the actual PUBLISH message until one of the previous "publish" operations is complete. It is possible to inquire whether the operation was initiated (PUBLISH message is sent) via the cc_mqtt5_client_publish_was_initiated() function. If the function returns false, the "publish" operation can be safely cancelled without any possible side effects.

}
bool cc_mqtt5_client_publish_was_initiated(CC_Mqtt5PublishHandle handle)
Check whether the "publish" operation was actually initiated (PUBLISH was sent)

Beware of calling cc_mqtt5_client_publish_was_initiated() when QoS0 publish operation is issued. In most cases the operation is completed right away and the operation handle is not valid any more when the cc_mqtt5_client_publish_send() invocation returns.

Message Ordering

The library implements strict message ordering for the same QoS messages required by the MQTT v5 specification. However, when different QoS messages are sent, the messages may arrive to the broker as well as recipient client in different order. For example, if all the publishes below issued together in the specified order, the Message2 and Message3 will probably arrive before Message1.

  • Message1 - QoS2
  • Message2 - Qos1
  • Message3 - QoS0

The library also provides an ability to force strict message ordering for all values of QoS using the cc_mqtt5_client_publish_set_ordering() function.

printf("ERROR: Publish ordering configuration failed with ec=%d\n", ec);
}
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_set_ordering(CC_Mqtt5ClientHandle handle, CC_Mqtt5PublishOrdering ordering)
Configure the ordering of the published messages.
@ CC_Mqtt5PublishOrdering_Full
Preserve strict order between all messages.
Definition: common.h:196

The current configuration can be retrieved using cc_mqtt5_client_publish_get_ordering() function. The default publish ordering is CC_Mqtt5PublishOrdering_SameQos.

When a strict message ordering for all the messages is enabled (by setting CC_Mqtt5PublishOrdering_Full), The PUBLISH message is postponed if any of the statements below are true:

  • There are other postponed PUBLISH messages of previously issued "publish" operations.
  • There are previously issued and incomplete "publish" operation of higher QoS value.

The rules above allow sending the same as well as increasing QoS messages in parallel. For example, if all the publishes below issued together in the specified order, the Message11 and Message12 will be sent, while Message13 and Message14 are postponed until the delivery of the Message12 is complete.

  • Message11 - QoS1
  • Message12 - Qos2
  • Message13 - QoS0
  • Message14 - QoS2

Simplifying the "Publish" Operation Preparation.

In many use cases the "publish" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides several wrapper functions that can be used:

For example:

// Assign default values to the configuration
// Update values
basicConfig.m_topic = "some/topic";
basicConfig.m_data = ...;
...
ec = cc_mqtt5_client_publish_simple(client, &basicConfig, &my_publish_complete_cb, data);
printf("ERROR: Failed to send publish request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_publish_simple(CC_Mqtt5ClientHandle handle, const CC_Mqtt5PublishBasicConfig *basicConfig, CC_Mqtt5PublishCompleteCb cb, void *cbData)
Prepare, configure, and send "publish" request in one go (simple version)

Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_publish_prepare(). It means that it's not possible to cancel the "publish" operation before its completion or identify the publish operation by the reported handle when the completion callback is invoked.

Receiving Messages

Right after the successful "connect" operation, the library starts expecting the arrival of the new messages and reports it via the registered callback. By default the library monitors the topics the client application subscribed to and does not report "rogue" messages from the broker.

Note that only topics themselves are monitored, and not the requested maximal QoS values. It means that if application requested maximal QoS1 for a particular subscription, but the broker sends message using the QoS2, the message is still going to be reported to the application.

However, if the broker is trusted to do the right thing, i.e. fully comply to the specification, it is possible to enable / disable the incoming topics check using the cc_mqtt5_client_set_verify_incoming_msg_subscribed() for performance reasons.

CC_Mqtt5ErrorCode cc_mqtt5_client_set_verify_incoming_msg_subscribed(CC_Mqtt5ClientHandle handle, bool enabled)
Control verification of the incoming message being correctly subscribed.

To retrieve the current configuration use the cc_mqtt5_client_get_verify_incoming_msg_subscribed() function.

WARNING: When the incoming message subscription verification is disabled, the library does NOT track any new subscription requests, which can result in dropping legit messages and not reporting them to the application after the subscription verification is re-enabled later.

Similarly, the library also by default verifies the incoming topic format, that it doesn't contain wildcard characters. To enable / disable such verification use the cc_mqtt5_client_set_verify_incoming_topic_enabled() function.

CC_Mqtt5ErrorCode cc_mqtt5_client_set_verify_incoming_topic_enabled(CC_Mqtt5ClientHandle handle, bool enabled)
Control incoming topic format verification.

To retrieve the current configuration use the cc_mqtt5_client_get_verify_incoming_topic_enabled() function.

To prioritize the in-order reception of the messages, the message report callback is invoked immediately on reception of the QoS2 PUBLISH message. Just like it is shown in the "Figure 4.3" of the MQTT v5 specification.

The QoS2 publish operation initiated by the broker requires exchange of multiple messages between the broker and the client. When the library responds with the PUBREC message, the broker is expected to send PUBREL back. The library uses the Default Response Timeout configuration to measure the time frame during which it allows the reception of the corresponding PUBREL message. If the latter doesn't arrive in time, the inner state of the message reception gets discarded resulting in the future rejection of the relevant PUBREL from the broker (if such arrives). Depending on case whether the PUBREC message was actually received by the broker and how the broker is implemented one of the following scenarios is possible:

  • The broker has NOT received the PUBREC and will resend its PUBLISH resulting in the duplicate delivery of the message.
  • The broker has received the PUBREC and according to MQTT v5 specification it is not allowed to perform PUBLISH again. Th broker might decide to either drop its message delivery state (best case scenario) or treat it as protocol error and disconnect the client.

With all said above it might be necessary to increase the response timeout for slow networks.

Re-Authenticating

The MQTT v5 specification allows performing re-authentication after the successful "connect" operation using the same authentication method. To do so use the reauth operation.

Preparing "Rauth" Operation.

if (reauth == NULL) {
printf("ERROR: Reauth allocation failed with ec=%d\n", ec);
}
CC_Mqtt5Reauth * CC_Mqtt5ReauthHandle
Handle for "reauth" operation.
Definition: common.h:267
CC_Mqtt5ReauthHandle cc_mqtt5_client_reauth_prepare(CC_Mqtt5ClientHandle handle, CC_Mqtt5ErrorCode *ec)
Prepare "reauth" operation.

Configuring "Reauth" Response Timeout

When created, the "reauth" operation inherits the Default Response Timeout configuration. It can be changed for the allocated operation using the cc_mqtt5_client_reauth_set_response_timeout() function.

... /* Something went wrong */
}
CC_Mqtt5ErrorCode cc_mqtt5_client_reauth_set_response_timeout(CC_Mqtt5ReauthHandle handle, unsigned ms)
Configure the broker response timeout for the "reauth" operation.

To retrieve the configured response timeout use the cc_mqtt5_client_reauth_get_response_timeout() function.

(Re)Authentication Handshake Configuration

To configure the re-authentication handshake use the cc_mqtt5_client_reauth_config_auth() function. It is very similar to the Extended Authentication Handshake Configuration of the "connect" operation.

CC_Mqtt5AuthErrorCode my_auth_handshake_cb(void* data, const CC_Mqtt5AuthInfo* authInfoIn, CC_Mqtt5AuthInfo* authInfoOut)
{
... // Analyse authInfoIn
if (auth_failed) {
return CC_Mqtt5AuthErrorCode_Disconnect; // terminate handshake with error
}
... // Populate authInfoOut
return CC_Mqtt5AuthErrorCode_Continue; // continue handshake
}
CC_Mqtt5AuthConfig authConfig;
// Initialize the configuration structure to the default values
// Assign all the necessary values
authConfig.m_authMethod = "some_method"
authConfig.m_authData = ...;
authConfig.m_authDataLen = ...;
authConfig.m_authCb = &my_auth_handshake_cb;
authConfig.m_authCbData = ...;
// Perform the configuration
ec = cc_mqtt5_client_reauth_config_auth(reauth, &authConfig);
printf("ERROR: Re-Authentication handshake configuration failed with ec=%d\n", ec);
...
}
void cc_mqtt5_client_reauth_init_config_auth(CC_Mqtt5AuthConfig *config)
Intialize the CC_Mqtt5AuthConfig configuration structure.
CC_Mqtt5ErrorCode cc_mqtt5_client_reauth_config_auth(CC_Mqtt5ReauthHandle handle, const CC_Mqtt5AuthConfig *config)
Perform authentication handshake configuration of the "reauth" operation.

During the "reauth" operation, both client and the broker exchange multiple AUTH messages. When such message arrive the library will invoke provided handshake callback. The responsibility of the callback is to analyze the incoming authentication data and populate the response information. The client can fail the handshake by returning the CC_Mqtt5AuthErrorCode_Disconnect instead of the CC_Mqtt5AuthErrorCode_Continue.

In case the application fails the re-authentication handshake by returning the CC_Mqtt5AuthErrorCode_Disconnect value library preforms the following steps:

IMPORTANT: The cc_mqtt5_client_reauth_config_auth() function mustn't be called more than once for a single "reauth" operation. Otherwise it may result in setting multiple properties of the same type, which is the "Protocol Error" according to the MQTT v5 specification.

Sending Re-Authentication Request

When all the necessary configurations are performed for the allocated "reauth" operation it can actually be sent to the broker. To initiate sending use the cc_mqtt5_client_reauth_send() function.

void my_reauth_complete_cb(void* data, CC_Mqtt5AsyncOpStatus status, const CC_Mqtt5AuthInfo* response)
{
printf("ERROR: The connection operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "response" is not NULL when status is CC_Mqtt5AsyncOpStatus_Complete.
assert(response != NULL);
... // Analyze response values.
}
ec = cc_mqtt5_client_reauth_send(reauth, &my_reauth_complete_cb, data);
printf("ERROR: Failed to send reauth request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_reauth_send(CC_Mqtt5ReauthHandle handle, CC_Mqtt5ReauthCompleteCb cb, void *cbData)
Send the configured "reauth" operation to broker.

The provided callback will be invoked when the "reauth" operation is complete if and only if the function returns CC_Mqtt5ErrorCode_Success.

The handle returned by the cc_mqtt5_client_reauth_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqtt5_client_reauth_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_Mqtt5ErrorCode_Success was returned). The valid handle can be used to cancel the operation before the completion callback is invoked.

When the "reauth" operation completion callback is invoked the reported "response" information is present if and only if the "status" is CC_Mqtt5AsyncOpStatus_Complete.

NOTE that only single "reauth" operation is allowed at a time, any attempt to prepare a new one via cc_mqtt5_client_reauth_prepare() will be rejected until the "reauth" operation completion callback is invoked or the operation is cancelled.

Cancel the "Reauth" Operation.

While the handle returned by the cc_mqtt5_client_reauth_prepare() is still valid it is possible to cancel / discard the operation.

printf("ERROR: Failed to cancel reauth with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_reauth_cancel(CC_Mqtt5ReauthHandle handle)
Cancel the allocated "reauth" operation.

In case the cc_mqtt5_client_reauth_send() function was successfully called before the cc_mqtt5_client_reauth_cancel(), the operation is cancelled without callback invocation.

In case the re-authentication is rejected by the broker, the latter is expected to send the DISCONNECT message resulting in the Unsolicited Broker Disconnection. In such case the completion callback will be invoked with the CC_Mqtt5AsyncOpStatus_BrokerDisconnected status.

Simplifying the "Reauth" Operation Preparation.

In many use cases the "reauth" operation can be quite simple with a lot of defaults. To simplify the sequence of the operation preparation and handling of errors, the library provides wrapper function(s) that can be used:

  • cc_mqtt5_client_reauth_()

For example:

CC_Mqtt5AuthConfig authConfig;
// Initialize the configuration structure to the default values
// Assign all the necessary values
authConfig.m_authMethod = "some_method"
authConfig.m_authData = ...;
...
ec = cc_mqtt5_client_reauth(client, authConfig, &my_reauth_complete_cb, data);
printf("ERROR: Failed to send publish request with ec=%d\n", ec);
...
}
CC_Mqtt5ErrorCode cc_mqtt5_client_reauth(CC_Mqtt5ClientHandle handle, const CC_Mqtt5AuthConfig *config, CC_Mqtt5ReauthCompleteCb cb, void *cbData)
Prepare, configure, and send "reauth" request in one go.

Note that the wrapper functions do NOT expose the handle returned by the cc_mqtt5_client_reauth_prepare(). It means that it's not possible to cancel the "reauth" operation before its completion.

Unsolicited Broker Disconnection

When broker disconnection is detected all the incomplete asynchronous operations (except publish) will be terminated with the an appropriate status report. The incomplete publish operations may be preserved (depending on "Session Expiry Interval" configuration during the connect operation) due to the following spec clause:

When a Client reconnects with Clean Start set to 0 and a session is present, both the Client and Server
MUST resend any unacknowledged PUBLISH packets (where QoS > 0) and PUBREL packets using their
original Packet Identifiers. This is the only circumstance where a Client or Server is REQUIRED to resend
messages. Clients and Servers MUST NOT resend messages at any other time [MQTT-4.4.0-1].

The unsolicited broker disconnection can happen in one of the following scenarios:

Receiving DISCONNECT message

The broker can initiate disconnection any time by simply sending DISCONNECT message. In such case the library responds in the following way:

Keep Alive Timeout

When there was no message from the broker for the "keep alive" timeout (configured during the connect operation) and the broker doesn't respond to the PING message. In such case the library responds in the following way:

Detecting Protocol Error

In case the broker doesn't fully comply with the MQTT v5 specification or there is some unexpected data corruption the library responds in the following way:

With all said above it means that if application receives CC_Mqtt5AsyncOpStatus_ProtocolError or CC_Mqtt5AsyncOpStatus_BrokerDisconnected status in the operation callback, then the application is expected to wait for the disconnection report callback which will follow.

When the disconnection report callback is called the application is expected to go through the following steps:

  • Close network connection.
  • Re-establish network connection to the broker.
  • Perform the "connect" operation from within a next event loop iteration.

In case the "Session Expiry Interval" was set during the connect operation, the library may still require timer measurement, depending on whether there are incomplete publish operations and/or incomplete message reception that need to be preserved for some time until the session expires or client re-connects to the broker. In case the "Session Expiry Interval" is not set or when the session is actually expires (based on the reported ticks), then all the incomplete publish operations will also get terminated with the CC_Mqtt5AsyncOpStatus_BrokerDisconnected status and the incomplete message reception will be discarded.

In case the client re-connects to the broker before the "Session Expiry Interval" expires and the broker reports "clean session", then all the incomplete publish operations will be terminated with the CC_Mqtt5AsyncOpStatus_Aborted status.

Network Disconnection

In addition to the Unsolicited Broker Disconnection the network connection can be suddenly terminated. Such network disconnection is usually detected by the failing read or write operations on I/O socket. When the application detects such network disconnection, it is expected to report it to the library using cc_mqtt5_client_notify_network_disconnected() function.

// Report network disconnected
void cc_mqtt5_client_notify_network_disconnected(CC_Mqtt5ClientHandle handle)
Report network disconnected.

When the network disconnection is reported, the similar workflow to one described in the Unsolicited Broker Disconnection section above is performed, excluding the invocation of the broker disconnection report callback. When the incomplete operations get terminated the CC_Mqtt5AsyncOpStatus_BrokerDisconnected status is reported.

When the Unsolicited Broker Disconnection is detected prior to the detection of the network disconnection itself and the broker disconnection report callback is invoked, there is no need to report the network disconnection to the library.

Inquiry about current network disconnection status can be done using the cc_mqtt5_client_is_network_disconnected() function.

bool disconnected = cc_mqtt5_client_is_network_disconnected(client);
bool cc_mqtt5_client_is_network_disconnected(CC_Mqtt5ClientHandle handle)
Check current network disconnected status.

Note that when the new "connect" op is prepared, the library will assume that the network connection is re-established and the subsequent call to the cc_mqtt5_client_is_network_disconnected() function will return false.

Thread Safety

In general the library is NOT thread safe. To support multi-threading the application is expected to use appropriate locking mechanisms before calling relevant API functions. However, if each thread operates on a separate allocated client, then the locking is required only for the client allocation logic.

Debug Build

The client library uses COMMS_ASSERT() macro to check some internal pre- and post-conditions. It is a bug if the assertion fails, please report if encountered. By default it is like standard assert() but allows overriding the default failure behavour, which can be more suitable for bare-metal systems. Please refer to the documentation for details.