CommsChampion Ecosystem MQTT-SN Client
MQTT-SN client library.
Loading...
Searching...
No Matches
MQTT-SN Client Library

Table of Contents

Overview

The MQTT-SN Client Library from the CommsChampion Ecosystem provides simple, asynchronous, non-blocking, and easy to use interface to operate MQTT-SN 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-SN gateway.

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-SN 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_mqttsn_client/common.h file using the following defines:

Header

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

#include "cc_mqttsn_client/client.h"

Client Allocation

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

struct CC_MqttsnClient * CC_MqttsnClientHandle
Handler used to access client specific data structures.
Definition common.h:144
CC_MqttsnClientHandle cc_mqttsn_client_alloc()
Allocate new client.

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_mqttsn_client_free() function.

void cc_mqttsn_client_free(CC_MqttsnClientHandle client)
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_MqttsnClient* ptr)
{
}
};
using MyClientPtr = std::unique_ptr<CC_MqttsnClient, MyDeleter>;
MyClientPtr clientPtr(::cc_mqttsn_client_alloc());
CC_MqttsnClientHandle client = clientPtr.get();

IMPORTANT: The function cc_mqttsn_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 Gateway

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

void my_send_data_cb(void* data, const unsigned char* buf, unsigned bufLen, unsigned broadcastRadius)
{
if (broadcastRadius > 0) {
... // broadcast the message data
}
else {
... // unicast the message data to a known gateway address
}
}
cc_mqttsn_client_set_send_output_data_callback(client, &my_send_data_cb, data);
void cc_mqttsn_client_set_send_output_data_callback(CC_MqttsnClientHandle client, CC_MqttsnSendOutputDataCb cb, void *data)
Set callback to send raw data over I/O link.

See also the documentation of the CC_MqttsnSendOutputDataCb 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 Gateway Disconnection

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

void my_gateway_disconnected_cb(void* data, CC_MqttsnGatewayDisconnectReason reason)
{
... /* handle gatewau disconnection */
}
cc_mqttsn_client_set_gw_disconnect_report_callback(client, &my_gateway_disconnected_cb, data);
void cc_mqttsn_client_set_gw_disconnect_report_callback(CC_MqttsnClientHandle client, CC_MqttsnGwDisconnectedReportCb cb, void *data)
Set callback to report unsolicited disconnection of the gateway.
CC_MqttsnGatewayDisconnectReason
Reason for reporting unsolicited gateway disconnection.
Definition common.h:101

See also the documentation of the CC_MqttsnGwDisconnectedReportCb callback function definition.

See also Unsolicited Gateway 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 gateway.

void my_message_received_cb(void* data, const CC_MqttsnMessageInfo* info)
{
... /* handle the received message */
}
cc_mqttsn_client_set_message_report_callback(client, &my_message_received_cb, data);
void cc_mqttsn_client_set_message_report_callback(CC_MqttsnClientHandle client, CC_MqttsnMessageReportCb cb, void *data)
Set callback to report incoming messages.
Incoming message information.
Definition common.h:225

See also the documentation of the CC_MqttsnMessageReportCb callback function definition.

Time Measurement

For the correct operation of the MQTT-SN 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_mqttsn_client_tick() function reporting the amount of elapsed milliseconds:

cc_mqttsn_client_tick(client, 50U);
void cc_mqttsn_client_tick(CC_MqttsnClientHandle client, 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_mqttsn_client_tick() function reporting amount of elapsed milliseconds.

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

It is allowed to invoke the cc_mqttsn_client_tick() before the actual requested timeout has expired, just make sure that the correct amount of elapsed milliseconds is reported. When the cc_mqttsn_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_mqttsn_client_tick().

See also the documentation of the CC_MqttsnNextTickProgramCb 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_mqttsn_client_set_cancel_next_tick_wait_callback(client, &my_cancel_tick_program_cb, data);
void cc_mqttsn_client_set_cancel_next_tick_wait_callback(CC_MqttsnClientHandle client, CC_MqttsnCancelNextTickWaitCb cb, void *data)
Set callback to terminate current time measurement.

See also the documentation of the CC_MqttsnCancelNextTickWaitCb 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_mqttsn_client_set_error_log_callback(client, &my_error_log_cb, data);
void cc_mqttsn_client_set_error_log_callback(CC_MqttsnClientHandle client, CC_MqttsnErrorLogCb cb, void *data)
Set callback to report error messages.

See also the documentation of the CC_MqttsnErrorLogCb callback function definition.

Reporting Incoming Data

It is the responsibility of the application to receive data from the other nodes on the network and report it to the library. The report is performed using the cc_mqttsn_client_process_data() function.

uint8_t buf[MAX_BUF_SIZE];
... // Receive data into buffer
unsigned bufLen = ...; // Amount of received bytes in the buffer.
CC_MqttsnDataOrigin origin = ...;
cc_mqttsn_client_process_data(client, buf, bufLen, origin);
void cc_mqttsn_client_process_data(CC_MqttsnClientHandle client, const unsigned char *buf, unsigned bufLen, CC_MqttsnDataOrigin origin)
Provide data, received over I/O link, to the library for processing.
CC_MqttsnDataOrigin
Data origin.
Definition common.h:131

The application is responsible to maintain the input buffer. The MQTT-SN protocol assumes datagram packets, where each packets contains a valid single MQTT-SN message. The client library decodes and processes the message at the beginning of the reported buffer and discards any extra bytes that might follow.

The application is also responsible to allow input from multiple sources and determine the origin of the input data. It allows the library to differentiate between the messages from the legit gateway to which the application is connected and other nodes on the network.

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-SN 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 gateway.

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_MqttsnAsyncOpStatus type. It indicates whether the operation was successfully complete. In addition to the status it reports some extra information reported by the gateway. The information from the gateway is available if and only if the status is CC_MqttsnAsyncOpStatus_Complete.

The send stage function also returns CC_MqttsnErrorCode value to indicate whether the send was successfully performed. The provided callback will be invoked if and only if the send returns CC_MqttsnErrorCode_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 gateway, cancelling the outstanding operation can be dangerous. When gateway sends a response and client is not expecting it any more, unexpected behaviour 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 != NULL);
// The following attempt to prepare the "subscribe" operation will fail because
// previously allocated "search" hasn't been sent or cancelled yet.
assert(subscribe == NULL);
struct CC_MqttsnConnect * CC_MqttsnConnectHandle
Handle for "connect" operation.
Definition common.h:162
CC_MqttsnErrorCode
Error code returned by various API functions.
Definition common.h:53
@ CC_MqttsnErrorCode_Success
The requested operation was successfully started.
Definition common.h:54
CC_MqttsnSearchHandle cc_mqttsn_client_search_prepare(CC_MqttsnClientHandle client, CC_MqttsnErrorCode *ec)
Prepare "search" operation.
CC_MqttsnSubscribeHandle cc_mqttsn_client_subscribe_prepare(CC_MqttsnClientHandle client, CC_MqttsnErrorCode *ec)
Prepare "subscribe" operation.
struct CC_MqttsnSubscribe * CC_MqttsnSubscribeHandle
Handle for "subscribe" operation.
Definition common.h:180

Default Retry Period

After sending any operation request to the gateway, the client library has to allow some time for the gateway to process the request (Tretry from the spec). If it takes too much time, the client must retry it several times (see Default Retry Count) and report that operation has failed via the set callback in case there is no response. By default the client library allows 10 seconds for such response to arrive. Changing this default value is possible using the cc_mqttsn_client_set_default_retry_period() function, and retrieving of the currently configured value can be done using the cc_mqttsn_client_get_default_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_set_default_retry_period(CC_MqttsnClientHandle client, unsigned value)
Set retry period to wait between resending unacknowledged message to the gateway (Tretry from spec).

Default Retry Count

As was mentioned in the Default Retry Period section above the client library retries to send unacknowledged messaged several times before giving up (Nretry from the spec). By default the client library allows 3 times to re-send the message. Changing this default value is possible using the cc_mqttsn_client_set_default_retry_count() function, and retrieving of the currently configured value can be done using the cc_mqttsn_client_get_default_retry_count() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_set_default_retry_count(CC_MqttsnClientHandle client, unsigned value)
Set number of retry attempts to perform before reporting unsuccessful result of the operation (Nretry...

Note that the function specify number of retries, i.e. when number of retries is 1 the relevant message will be sent twice before reporting failure (in case there is no response of course): once when the send is explicitly requested and the second time is an actual retry. Following the same logic, when the configured number of retries is 0, the message will be send only once (when the request is issued) and will not try to re-send it when the response is not received in time.

Default Broadcast Radius

The MQTT-SN protocol defines several messages that are broadcasted. When requesting to broadcast the message, the client library also specifies the broadcast radius. The default broadcast radius value is 3. To change the default configuration value use cc_mqttsn_client_set_default_broadcast_radius() function. To retrieve the current configuration use cc_mqttsn_client_get_default_broadcast_radius().

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_set_default_broadcast_radius(CC_MqttsnClientHandle client, unsigned value)
Set broadcast radius.

Gateway Discovery

The MQTT-SN protocol supports having multiple gateway on the same network and their discovery. When the application allocates the client object, it can either connect directly to the gateway (if such is known) or initiate gateway discovery by using search operation. The MQTT-SN specification recommends to wait some time (up to Tsearchgw) to prevent sending broadcast storm of the SEARCHGW messages when all clients start at once. It is up to the application to decide whether and when to issue the search operation.

Preparing "Search" Operation.

if (search == NULL) {
printf("ERROR: Search allocation failed with ec=%d\n", ec);
}
struct CC_MqttsnSearch * CC_MqttsnSearchHandle
Handle for "search" operation.
Definition common.h:153

Configuring "Search" Retry Period

When created, the "search" operation inherits the Default Retry Period configuration. It can be changed for the allocated operation using the cc_mqttsn_client_search_set_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_search_set_retry_period(CC_MqttsnSearchHandle handle, unsigned ms)
Configure the retry period for the "search" operation.

To retrieve the configured retry period use the cc_mqttsn_client_search_get_retry_period() function.

Configuring "Search" Retry Count

When created, the "search" operation inherits the Default Retry Count configuration. It can be changed for the allocated operation using the cc_mqttsn_client_search_set_retry_count() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_search_set_retry_count(CC_MqttsnSearchHandle handle, unsigned count)
Configure the retry count for the "search" operation.

To retrieve the configured retry count use the cc_mqttsn_client_search_get_retry_count() function.

Configuring "Search" Broadcast Radius

The "search" operation is expected to broadcast the SEARCHGW message. When created, the "search" operation inherits the Default Broadcast Radius configuration. It can be changed for the allocated operation using the cc_mqttsn_client_search_set_broadcast_radius() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_search_set_broadcast_radius(CC_MqttsnSearchHandle handle, unsigned broadcastRadius)
Configure the broadcast radius for the "search" operation.

To retrieve the configured retry count use the cc_mqttsn_client_search_get_broadcast_radius() function.

Sending Search Gateway Request

When all the necessary configurations are performed for the allocated "search" operation it can actually be sent on the network. To initiate sending use the cc_mqttsn_client_search_send() function.

void my_search_complete_cb(void* data, CC_MqttsnAsyncOpStatus status, const CC_MqttsnGatewayInfo* info)
{
printf("ERROR: The search operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "info" is not NULL when status is CC_MqttsnAsyncOpStatus_Complete.
assert(info != NULL);
... // Analyze found gateway information.
}
ec = cc_mqttsn_client_search_send(search, &my_search_complete_cb, data);
printf("ERROR: Failed to send search request with ec=%d\n", ec);
...
}
CC_MqttsnAsyncOpStatus
Status of the asynchronous operation.
Definition common.h:87
@ CC_MqttsnAsyncOpStatus_Complete
The requested operation has been completed, refer to reported extra details for information.
Definition common.h:88
CC_MqttsnErrorCode cc_mqttsn_client_search_send(CC_MqttsnSearchHandle handle, CC_MqttsnSearchCompleteCb cb, void *cbData)
Send the "search" operation.
Gateway information.
Definition common.h:237

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

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

When the "search" operation completion callback is invoked the reported "info" information is present if and only if the "status" is CC_MqttsnAsyncOpStatus_Complete.

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

The "search" operation is complete when the valid GWINFO message is received. Such message can be sent either by the gateway itself or by another client on behalf of the gateway. When they GWINFO message is sent by another client it specifies the actual address of the gateway (reported via the CC_MqttsnGatewayInfo::m_addr and CC_MqttsnGatewayInfo::m_addrLen). When the GWINFO message is sent by the gateway itself, the address information inside the reported CC_MqttsnGatewayInfo structure will be empty.

Upon reception of any new data packet from the I/O link, the application is expected to detect and save the origin address of the sender. In case the reported gateway address is empty, the application is expected to use the saved origin address of the data packet as the one of the gateway. The application is also expected to update the address info stored by the library using the cc_mqttsn_client_set_available_gateway_info() function. The details are in the Monitoring and Managing Available Gateways section below.

Cancel the "Search" Operation.

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

printf("ERROR: Failed to cancel search with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_search_cancel(CC_MqttsnSearchHandle handle)
Cancel the allocated "search" operation.

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

Simplifying the "Search" Operation Preparation

In many use cases the "search" 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 the cc_mqttsn_client_search() wrapper function.

For example:

CC_MqttsnErrorCode ec = cc_mqttsn_client_search(client, &my_search_complete_cb, data);
printf("ERROR: Failed to send search request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_search(CC_MqttsnClientHandle handle, CC_MqttsnSearchCompleteCb cb, void *cbData)
Prepare and send "search" request in one go.

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

Monitoring and Managing Available Gateways

The MQTT-SN gateways are also expected to periodically advertise their presence. The client library keeps track of the discovered gateways on the network. It is possible to retrieve the information on the available gateways at any time.

unsigned numOfAvailableGateways = cc_mqttsn_client_get_available_gateways_count(client);
for (idx=0; idx < numOfAvailableGateways; ++idx) {
printf("Something is wrong\n");
continue;
}
... // Process updated info.
}
CC_MqttsnErrorCode cc_mqttsn_client_get_available_gateway_info(CC_MqttsnClientHandle client, unsigned idx, CC_MqttsnGatewayInfo *info)
Retrieve stored available gateway information.
unsigned cc_mqttsn_client_get_available_gateways_count(CC_MqttsnClientHandle client)
Get number of available gateways.

The application can receive ongoing notifications if a status of any gateway changes.

void my_gw_status_report_cb(void* data, CC_MqttsnGwStatus status, const CC_MqttsnGatewayInfo* info)
{
...
}
cc_mqttsn_client_set_gw_status_report_callback(client, &my_gw_status_report_cb, data);
void cc_mqttsn_client_set_gw_status_report_callback(CC_MqttsnClientHandle client, CC_MqttsnGwStatusReportCb cb, void *data)
Set callback to report status of the gateway.
CC_MqttsnGwStatus
Status of the gateway.
Definition common.h:74

The status parameter to the callback function indicates the status of the gateway, while info reports the currently stored gateway information. The callback (if registered) is invoked every time the ADVERTISE as well as GWINFO messages are received or not received in time. The status value indicates of whether the gateway information is newly added, updated or the old info is confirmed.

When the expected ADVERTISE message doesn't arrive in time, it can be due to packet loss on the network rather than the gateway itself going offline. The library allows configuration of allowed losses of the ADVERTISE message before the gateway is reported to be CC_MqttsnGwStatus_Removed and its info removed from the internal data structures.

CC_MqttsnErrorCode cc_mqttsn_client_set_allowed_adv_losses(CC_MqttsnClientHandle client, unsigned count)
Set number of allowed ADVERTISE message misses.

The configured count is how many ADVERTISE messages can be lost, i.e. when configured to be 0, the gateway info is removed on the first missing ADVERTISE packet. When configured to be 1 (or greater), the reported gateway status will be CC_MqttsnGwStatus_Tentative when the expected advertising packet doesn't arrive in time until the inner count of allowed losses goes to 0 and then the gateway status is reported to be CC_MqttsnGwStatus_Removed. The default allowed count of the ADVERTISE losses is 1. The current configuration can be retrieved using cc_mqttsn_client_get_allowed_adv_losses(). When the ADVERTISE or GWINFO from the gateway message arrive, the library reports CC_MqttsnGwStatus_Alive as the status gateway.

Upon reception of the ADVERTISE or GWINFO from the gateway, the address information may be missing (or wrong). The application is expected to record the origin of the recent message and this information can be used to update the recorded information. The application can invoke the cc_mqttsn_client_set_available_gateway_info() function at any time to update / override the stored address information about the gateway. It is also applicable to the search operation. The cc_mqttsn_client_set_available_gateway_info() can be called from withing the operation completion callback to update the stored gateway address.

info.m_gwId = ...;
info.m_addr = ...;
info.m_addrLen = ...;
printf("Something is wrong");
}
void cc_mqttsn_client_init_gateway_info(CC_MqttsnGatewayInfo *info)
Initialize the CC_MqttsnGatewayInfo structure.
CC_MqttsnErrorCode cc_mqttsn_client_set_available_gateway_info(CC_MqttsnClientHandle client, const CC_MqttsnGatewayInfo *info)
Update stored available gateway information.
unsigned m_addrLen
Length of the address.
Definition common.h:240
const unsigned char * m_addr
Address of the gateway if known, NULL if not.
Definition common.h:239
unsigned char m_gwId
Gateway ID.
Definition common.h:238

It is recommended to initialize CC_MqttsnGatewayInfo structure using the cc_mqttsn_client_init_gateway_info() function before update, even though all the member fields are assigned new values.

The application can force the library to discard the available gateway information by issuing the cc_mqttsn_client_discard_available_gateway_info() function.

printf("Something is wrong");
}
CC_MqttsnErrorCode cc_mqttsn_client_discard_available_gateway_info(CC_MqttsnClientHandle client, unsigned char gwId)
Discard stored available gateway information.

It is possible to discard information on all the gateways in one go:

printf("Something is wrong");
}
void cc_mqttsn_client_discard_all_gateway_infos(CC_MqttsnClientHandle client)
Discard stored information on all available gateways.

When the client receives GWINFO message before ADVERTISE, the advertising period of the gateway is not known yet. For the gateway tracking and management functionality there is a need to assume some kind of default duration. The default value is 15 minutes. To update it use cc_mqttsn_client_set_default_gw_adv_duration() function.

CC_MqttsnErrorCode ec = cc_mqttsn_client_set_default_gw_adv_duration(client, 20 * 60 * 1000 /* 20 minutes in milliseconds */);
printf("Something is wrong");
}
CC_MqttsnErrorCode cc_mqttsn_client_set_default_gw_adv_duration(CC_MqttsnClientHandle client, unsigned ms)
Set default gateway advertise duration.

To retrieve the current configuration use cc_mqttsn_client_get_default_gw_adv_duration() function.

Connecting to Gateway

To connect to gateway use connect operation.

Preparing "Connect" Operation.

if (connect == NULL) {
printf("ERROR: Connect allocation failed with ec=%d\n", ec);
}
CC_MqttsnConnectHandle cc_mqttsn_client_connect_prepare(CC_MqttsnClientHandle client, CC_MqttsnErrorCode *ec)
Prepare "connect" operation.

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

Configuring "Connect" Retry Period

When created, the "connect" operation inherits the Default Retry Period configuration. It can be changed for the allocated operation using the cc_mqttsn_client_connect_set_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_connect_set_retry_period(CC_MqttsnConnectHandle handle, unsigned ms)
Configure the retry period for the "connect" operation.

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

Configuring "Connect" Retry Count

When created, the "connect" operation inherits the Default Retry Count configuration. It can be changed for the allocated operation using the cc_mqttsn_client_connect_set_retry_count() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_connect_set_retry_count(CC_MqttsnConnectHandle handle, unsigned count)
Configure the retry count for the "connect" operation.

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

Configuration of "Connect" Operation

To configure "connect" operation use cc_mqttsn_client_connect_config() function.

// Assign default values to the "config"
// Update the values if needed:
config.m_clientId = "some_client";
config.m_cleanSession = true;
// Perform the configuration
ec = cc_mqttsn_client_connect_config(connect, &config);
printf("ERROR: Connect configuration failed with ec=%d\n", ec);
...
}
void cc_mqttsn_client_connect_init_config(CC_MqttsnConnectConfig *config)
Intialize the CC_MqttsnConnectConfig configuration structure.
CC_MqttsnErrorCode cc_mqttsn_client_connect_config(CC_MqttsnConnectHandle handle, const CC_MqttsnConnectConfig *config)
Perform configuration of the "connect" operation.
Configuration the "connect" operation.
Definition common.h:246
bool m_cleanSession
Clean session configuration.
Definition common.h:249
const char * m_clientId
Client ID.
Definition common.h:247

IMPORTANT: MQTT-SN specification allows reconnection to the gateway while requesting previous session restoration (via "clean session" bit). By default, the client library verifies that the message received from the gateway was actually subscribed to before reporting the message to the application (see Receiving Messages for details). To prevent potential errors of the client and gateway inner states being out of sync, the first "connect" operation requires setting the CC_MqttsnConnectConfig::m_cleanSession 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_MqttsnConnectConfig::m_cleanSession value is NOT set to true, the function rejects the configuration with the CC_MqttsnErrorCode_BadParam error code. Any subsequent reconnection attempts will allow setting the value to false.

See also documentation of the CC_MqttsnConnectConfig structure.

Will Configuration

To perform will configuration use the cc_mqttsn_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_mqttsn_client_connect_config_will(connect, &willConfig);
printf("ERROR: Will configuration failed with ec=%d\n", ec);
...
}
void cc_mqttsn_client_connect_init_config_will(CC_MqttsnWillConfig *config)
Intialize the CC_MqttsnWillConfig configuration structure.
CC_MqttsnErrorCode cc_mqttsn_client_connect_config_will(CC_MqttsnConnectHandle handle, const CC_MqttsnWillConfig *config)
Perform will configuration of the "connect" operation.
Configuration the will for "connect" and "will" operations.
Definition common.h:262
const unsigned char * m_data
Will data (message).
Definition common.h:264
const char * m_topic
Will topic.
Definition common.h:263

See also documentation of the CC_MqttsnWillConfig structure.

Sending Connection Request

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

void my_connect_complete_cb(void* data, CC_MqttsnAsyncOpStatus status, const CC_MqttsnConnectInfo* info)
{
printf("ERROR: The connection operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "info" is not NULL when status is CC_MqttsnAsyncOpStatus_Complete.
assert(info != NULL);
... // Analyze info values.
}
ec = cc_mqttsn_client_connect_send(connect, &my_connect_complete_cb, data);
printf("ERROR: Failed to send connect request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_connect_send(CC_MqttsnConnectHandle handle, CC_MqttsnConnectCompleteCb cb, void *cbData)
Send the "connect" operation.
Information on the "connect" operation completion.
Definition common.h:255

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

The handle returned by the cc_mqttsn_client_connect_prepare() function can be discarded (there is no free / de-allocation) right after the cc_mqttsn_client_connect_send() invocation regardless of the returned error code. However, the handle remains valid until the callback is called (in case the CC_MqttsnErrorCode_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 "info" is present if and only if the "status" is CC_MqttsnAsyncOpStatus_Complete.

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

When the callback reporting the connection status is invoked, it is responsibility of the application to check the CC_MqttsnConnectInfo::m_returnCode value. If it's not CC_MqttsnReturnCode_Accepted, the application is responsible retry the "connect" operation later. The same should be done when the "connect" operation is not properly completed, i.e. the reported status is NOT CC_MqttsnAsyncOpStatus_Complete.

The "connect" operation can be sent at any time, even if it's already connected. However, upon failing attempt to re-connect the assumed inner state becomes CC_MqttsnConnectionStatus_Disconnected.

Cancel the "Connect" Operation.

While the handle returned by the cc_mqttsn_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_MqttsnErrorCode cc_mqttsn_client_connect_cancel(CC_MqttsnConnectHandle handle)
Cancel the allocated "connect" operation.

In case the cc_mqttsn_client_connect_send() function was successfully called before the cc_mqttsn_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 wrapper function(s) that can be used:

For example:

// Assign default values to the "config"
// Update the values if needed:
config.m_clientId = "some_client";
config.m_cleanSession = true;
ec = cc_mqttsn_client_connect(client, config, null, &my_connect_complete_cb, data);
printf("ERROR: Failed to send connect request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_connect(CC_MqttsnClientHandle client, const CC_MqttsnConnectConfig *config, const CC_MqttsnWillConfig *willConfig, CC_MqttsnConnectCompleteCb cb, void *cbData)
Prepare and send "connect" request in one go.

Note that the wrapper function does NOT expose the handle returned by the cc_mqttsn_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 gateway.

CC_MqttsnConnectionStatus cc_mqttsn_client_get_connection_status(CC_MqttsnClientHandle client)
Check the inner state of the library of whether it's connected to the gateway.
CC_MqttsnConnectionStatus
Connection state.
Definition common.h:121

Disconnecting From Gateway

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

Preparing "Disconnect" Operation.

if (disconnect == NULL) {
printf("ERROR: Disconnect allocation failed with ec=%d\n", ec);
}
struct CC_MqttsnDisconnect * CC_MqttsnDisconnectHandle
Handle for "disconnect" operation.
Definition common.h:171
CC_MqttsnDisconnectHandle cc_mqttsn_client_disconnect_prepare(CC_MqttsnClientHandle client, CC_MqttsnErrorCode *ec)
Prepare "disconnect" operation.

Configuring "Disconnect" Retry Period

When created, the "disconnect" operation inherits the Default Retry Period configuration. It can be changed for the allocated operation using the cc_mqttsn_client_disconnect_set_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_disconnect_set_retry_period(CC_MqttsnDisconnectHandle handle, unsigned ms)
Configure the retry period for the "disconnect" operation.

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

Configuring "Disconnect" Retry Count

When created, the "disconnect" operation inherits the Default Retry Count configuration. It can be changed for the allocated operation using the cc_mqttsn_client_disconnect_set_retry_count() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_disconnect_set_retry_count(CC_MqttsnDisconnectHandle handle, unsigned count)
Configure the retry count for the "disconnect" operation.

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

Sending Disconnection Request

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

void my_disconnect_complete_cb(void* data, CC_MqttsnAsyncOpStatus status)
{
printf("ERROR: The disconnection operation has failed with status=%d\n", status);
... // handle error.
return;
}
...
}
ec = cc_mqttsn_client_disconnect_send(disconnect, &my_disconnect_complete_cb, data);
printf("ERROR: Failed to send disconnect request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_disconnect_send(CC_MqttsnDisconnectHandle handle, CC_MqttsnDisconnectCompleteCb cb, void *cbData)
Send the "disconnect" operation.

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

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

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

In case there are other asynchronous operations that hasn't been completed yet, their completion callback is automatically invoked with CC_MqttsnAsyncOpStatus_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 gateway (if needed) and perform the connect operation again.

Cancel the "Disconnect" Operation.

While the handle returned by the cc_mqttsn_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_MqttsnErrorCode cc_mqttsn_client_disconnect_cancel(CC_MqttsnDisconnectHandle handle)
Cancel the allocated "disconnect" operation.

Simplifying the "Disconnect" Operation Preparation.

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

For example:

ec = cc_mqttsn_client_disconnect(client, &my_disconnect_complete_cb, data);
printf("ERROR: Failed to send disconnect request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_disconnect(CC_MqttsnClientHandle client, CC_MqttsnDisconnectCompleteCb cb, void *cbData)
Prepare 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" Retry Period

When created, the "subscribe" operation inherits the Default Retry Period configuration. It can be changed for the allocated operation using the cc_mqttsn_client_subscribe_set_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_subscribe_set_retry_period(CC_MqttsnSubscribeHandle handle, unsigned ms)
Configure the retry period for the "subscribe" operation.

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

Configuring "Subscribe" Retry Count

When created, the "subscribe" operation inherits the Default Retry Count configuration. It can be changed for the allocated operation using the cc_mqttsn_client_subscribe_set_retry_count() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_subscribe_set_retry_count(CC_MqttsnSubscribeHandle handle, unsigned count)
Configure the retry count for the "subscribe" operation.

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

Subscribe Configuration

To configure "subscribe" operation use cc_mqttsn_client_subscribe_config() function.

// Assign default values to the configuration
assert(config.m_qos == CC_MqttsnQoS_ExactlyOnceDelivery); // Initialization puts the maximum allowed QoS
// Update values if needed
config.m_topic = "some/topic";
// Perform the configuration
ec = cc_mqttsn_client_subscribe_config(subscribe, &config);
printf("ERROR: Topic configuration failed with ec=%d\n", ec);
...
}
@ CC_MqttsnQoS_ExactlyOnceDelivery
QoS=2. Exactly once delivery.
Definition common.h:46
CC_MqttsnErrorCode cc_mqttsn_client_subscribe_config(CC_MqttsnSubscribeHandle handle, const CC_MqttsnSubscribeConfig *config)
Perform configuration of the "subscribe" operation.
void cc_mqttsn_client_subscribe_init_config(CC_MqttsnSubscribeConfig *config)
Intialize the CC_MqttsnSubscribeConfig configuration structure.
Configuration the "subscribe" operation.
Definition common.h:273
const char * m_topic
Subscription topic, can be NULL when pre-defined topic ID is used.
Definition common.h:274
CC_MqttsnQoS m_qos
Max QoS value.
Definition common.h:276

When there is a need to use a predefined topic id instead of the topic string use m_topicId member instead of m_topic.

// Assign default values to the configuration
// Update values if needed
config.m_topicId = 123;
CC_MqttsnTopicId m_topicId
Pre-defined topic ID, should be 0 when topic is not NULL.
Definition common.h:275

See also documentation of the CC_MqttsnSubscribeConfig structure.

The MQTT-SN specification also specifies short topics of 2 byte length. The library detects this case by analysing the string assigned to the m_topic member and uses appropriate message configuration if needed.

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 */
}
CC_MqttsnErrorCode cc_mqttsn_client_set_verify_outgoing_topic_enabled(CC_MqttsnClientHandle client, 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_mqttsn_client_get_verify_outgoing_topic_enabled() function.

Sending Subscription Request

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

void my_subscribe_complete_cb(void* data, CC_MqttsnSubscribeHandle handle, CC_MqttsnAsyncOpStatus status, const CC_MqttsnSubscribeInfo* info)
{
printf("ERROR: The subscription operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "info" is not NULL when status is CC_MqttsnAsyncOpStatus_Complete.
assert(info != NULL);
... // Analyze response values.
}
ec = cc_mqttsn_client_subscribe_send(subscribe, &my_subscribe_complete_cb, data);
printf("ERROR: Failed to send subscribe request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_subscribe_send(CC_MqttsnSubscribeHandle handle, CC_MqttsnSubscribeCompleteCb cb, void *cbData)
Send the "subscribe" operation.
Information on the "subscribe" operation completion.
Definition common.h:282

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

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

The MQTT-SN spec demands that the SUBSCRIBE transactions be issued one at a time. However, the library allows preparing and sending multiple subscription requests in parallel before completion of the first one. The library will retain the requested "subscribe" operation requests internally and will issue them one after another to comply with the specification.

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_MqttsnAsyncOpStatus_Complete.

Cancel the "Subscribe" Operation.

While the handle returned by the cc_mqttsn_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_MqttsnErrorCode cc_mqttsn_client_subscribe_cancel(CC_MqttsnSubscribeHandle handle)
Cancel the allocated "subscribe" operation.

In case the cc_mqttsn_client_subscribe_send() function was successfully called before the cc_mqttsn_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 wrapper function that can be used:

For example:

// Assign default values to the configuration
// Update values if needed
config.m_topic = "some/topic";
ec = cc_mqttsn_client_subscribe(client, &config, &my_subscribe_complete_cb, data);
printf("ERROR: Failed to send subscribe request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_subscribe(CC_MqttsnClientHandle client, const CC_MqttsnSubscribeConfig *config, CC_MqttsnSubscribeCompleteCb cb, void *cbData)
Prepare and send "subscribe" request in one go.

Note that the wrapper function does NOT expose the handle returned by the cc_mqttsn_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 Receiving Messages

To unsubscribe to receive 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);
}
struct CC_MqttsnUnsubscribe * CC_MqttsnUnsubscribeHandle
Handle for "unsubscribe" operation.
Definition common.h:189
CC_MqttsnUnsubscribeHandle cc_mqttsn_client_unsubscribe_prepare(CC_MqttsnClientHandle client, CC_MqttsnErrorCode *ec)
Prepare "unsubscribe" operation.

Configuring "Unsubscribe" Retry Period

When created, the "unsubscribe" operation inherits the Default Retry Period configuration. It can be changed for the allocated operation using the cc_mqttsn_client_unsubscribe_set_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_unsubscribe_set_retry_period(CC_MqttsnUnsubscribeHandle handle, unsigned ms)
Configure the retry period for the "unsubscribe" operation.

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

Configuring "Unsubscribe" Retry Count

When created, the "unsubscribe" operation inherits the Default Retry Count configuration. It can be changed for the allocated operation using the cc_mqttsn_client_unsubscribe_set_retry_count() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_unsubscribe_set_retry_count(CC_MqttsnUnsubscribeHandle handle, unsigned count)
Configure the retry count for the "unsubscribe" operation.

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

Unsubscribe Configuration

To configure "unsubscribe" operation use cc_mqttsn_client_unsubscribe_config() function.

// Assign default values to the configuration
// Update values if needed
config.m_topic = "some/topic";
// Perform the configuration
ec = cc_mqttsn_client_unsubscribe_config(unsubscribe, &config);
printf("ERROR: Topic configuration failed with ec=%d\n", ec);
...
}
void cc_mqttsn_client_unsubscribe_init_config(CC_MqttsnUnsubscribeConfig *config)
Intialize the CC_MqttsnUnsubscribeConfig configuration structure.
CC_MqttsnErrorCode cc_mqttsn_client_unsubscribe_config(CC_MqttsnUnsubscribeHandle handle, const CC_MqttsnUnsubscribeConfig *config)
Perform configuration of the "unsubscribe" operation.
Configuration the "unsubscribe" operation.
Definition common.h:290
const char * m_topic
Subscription topic, can be NULL when pre-defined topic ID is used.
Definition common.h:291

When there is a need to use a predefined topic id instead of the topic string use m_topicId member instead of m_topic.

// Assign default values to the configuration
// Update values if needed
config.m_topicId = 123;
CC_MqttsnTopicId m_topicId
Pre-defined topic ID, should be 0 when topic is not NULL.
Definition common.h:292

See also documentation of the CC_MqttsnUnsubscribeConfig structure.

The MQTT-SN specification also specifies short topics of 2 byte length. The library detects this case by analysing the string assigned to the m_topic member and uses appropriate message configuration if needed.

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", "subscribe" and "publish" topic formats.

To retrieve the current configuration use cc_mqttsn_client_get_verify_outgoing_topic_enabled() function.

Sending Unsubscription Request

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

void my_unsubscribe_complete_cb(void* data, CC_MqttsnUnsubscribeHandle handle, CC_MqttsnAsyncOpStatus status)
{
printf("ERROR: The subscription operation has failed with status=%d\n", status);
... // handle error.
return;
}
}
ec = cc_mqttsn_client_unsubscribe_send(unsubscribe, &my_unsubscribe_complete_cb, data);
printf("ERROR: Failed to send unsubscribe request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_unsubscribe_send(CC_MqttsnUnsubscribeHandle handle, CC_MqttsnUnsubscribeCompleteCb cb, void *cbData)
Send the "unsubscribe" operation.

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

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

The MQTT-SN spec demands that the UNSUBSCRIBE transactions be issued one at a time. However, the library allows preparing and sending multiple unsubscription requests in parallel before completion of the first one. The library will retain the requested "unsubscribe" operation requests internally and will issue them one after another to comply with the specification.

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.

Cancel the "Unsubscribe" Operation.

While the handle returned by the cc_mqttsn_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_MqttsnErrorCode cc_mqttsn_client_unsubscribe_cancel(CC_MqttsnUnsubscribeHandle handle)
Cancel the allocated "unsubscribe" operation.

In case the cc_mqttsn_client_unsubscribe_send() function was successfully called before the cc_mqttsn_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 wrapper function that can be used:

For example:

// Assign default values to the configuration
// Update values if needed
config.m_topic = "some/topic";
ec = cc_mqttsn_client_unsubscribe(client, &config, &my_unsubscribe_complete_cb, data);
printf("ERROR: Failed to send unsubscribe request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_unsubscribe(CC_MqttsnClientHandle client, const CC_MqttsnUnsubscribeConfig *config, CC_MqttsnUnsubscribeCompleteCb cb, void *cbData)
Prepare and send "unsubscribe" request in one go.

Note that the wrapper function does NOT expose the handle returned by the cc_mqttsn_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 gateway use publish operation.

Preparing "Publish" Operation.

if (publish == NULL) {
printf("ERROR: Publish allocation failed with ec=%d\n", ec);
}
struct CC_MqttsnPublish * CC_MqttsnPublishHandle
Handle for "publish" operation.
Definition common.h:198
CC_MqttsnPublishHandle cc_mqttsn_client_publish_prepare(CC_MqttsnClientHandle client, CC_MqttsnErrorCode *ec)
Prepare "publish" operation.

Configuring "Publish" Retry Period

When created, the "publish" operation inherits the Default Retry Period configuration. It can be changed for the allocated operation using the cc_mqttsn_client_publish_set_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_publish_set_retry_period(CC_MqttsnPublishHandle handle, unsigned ms)
Configure the retry period for the "publish" operation.

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

Configuring "Publish" Retry Count

When created, the "publish" operation inherits the Default Retry Count configuration. It can be changed for the allocated operation using the cc_mqttsn_client_publish_set_retry_count() function.

... /* Something went wrong */
}

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

The publish operation can exchange multiple messages in a single transaction. For example, when publishing QoS2 messages which require registration, the client will send REGISTER, PUBLISH, and PUBREL messages and will expect REGACK, PUBREC, and PUBCOMP messages as their respective acknowledgements. The configuration of the retry count is per such single message exchange. In other words when 2 retries are allowed, the client will allow 2 retries for REGISTER <-> REGACK, 2 retries for PUBLISH <-> PUBREC and 2 retries for PUBREL <-> PUBCOMP exchanges.

Configuration of "Publish" Operation

// Assign default values to the "config"
// Update the required values
config.m_topic = "some/topic";
config.m_data = &some_buf[0];
config.m_dataLen = ...;
config.m_qos = ...;
// Perform the configuration
ec = cc_mqttsn_client_publish_config(publish, &config);
printf("ERROR: Configuration failed with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_publish_config(CC_MqttsnPublishHandle handle, const CC_MqttsnPublishConfig *config)
Perform configuration of the "publish" operation.
void cc_mqttsn_client_publish_init_config(CC_MqttsnPublishConfig *config)
Intialize the CC_MqttsnPublishConfig configuration structure.
Configuration the will for "publish" operations.
Definition common.h:298
unsigned m_dataLen
Publish data (message) length.
Definition common.h:301
const char * m_topic
Publish topic.
Definition common.h:299
const unsigned char * m_data
Publish data (message).
Definition common.h:300

See also documentation of the CC_MqttsnPublishConfig structure.

Similar to the subscribe operation it is possible to use predefined topic id by setting topicId member instead of m_topic.

// Update the required values
config.m_topicId = 123;

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_mqttsn_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.

Sending Publish Request

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

void my_publish_complete_cb(void* data, CC_MqttsnPublishHandle handle, CC_MqttsnAsyncOpStatus status, const CC_MqttsnPublishInfo* info)
{
printf("ERROR: The publish operation has failed with status=%d\n", status);
... // handle error.
return;
}
if (info != NULL) {
... // Analyse return code
}
...
}
ec = cc_mqttsn_client_publish_send(publish, &my_publish_complete_cb, data);
printf("ERROR: Failed to send publish request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_publish_send(CC_MqttsnPublishHandle handle, CC_MqttsnPublishCompleteCb cb, void *cbData)
Send the "publish" operation.
Information on the "publish" operation completion.
Definition common.h:310

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

When QoS value is CC_MqttsnQoS_AtMostOnceDelivery and no registration stage is required, there is no gateway response to wait for and the callback is invoked right away after sending the serialized data.

When the callback is invoked with CC_MqttsnAsyncOpStatus_Complete status the "info" parameter may or may not be NULL. If it's NULL then the operation is successful. If its not NULL, then extra analysis of the m_returnCode data member value is expected to be performed by the application.

  • If the return code reports CC_MqttsnReturnCode_Conjestion, then it's up to the application to retry the re-send later (specification requires waiting Twait time before retrying). The retry is performed by issuing another publish operation.
  • If the return code reports CC_MqttsnReturnCode_InvalidTopicId then it's a case when the gateway rejected used topic ID (probably pre-defined one) and the client library cannot silently perform re-registration because the topic information was missing in the configuration.

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

The MQTT-SN spec demands that the PUBLISH transactions be issued one at a time. However, the library allows preparing and sending multiple publish requests in parallel before completion of the first one. The library will retain the requested "publish" operation requests internally and will issue them one after another to comply with the specification.

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.

Cancel the "Publish" Operation.

While the handle returned by the cc_mqttsn_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_MqttsnErrorCode cc_mqttsn_client_publish_cancel(CC_MqttsnPublishHandle handle)
Cancel the allocated "publish" operation.

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

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 wrapper function that can be used:

For example:

// Assign default values to the configuration
// Update values
config.m_topic = "some/topic";
config.m_data = ...;
...
ec = cc_mqttsn_client_publish(client, &config, &my_publish_complete_cb, data);
printf("ERROR: Failed to send publish request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_publish(CC_MqttsnClientHandle client, const CC_MqttsnPublishConfig *config, CC_MqttsnPublishCompleteCb cb, void *cbData)
Prepare and send "publish" request in one go.

Note that the wrapper function does NOT expose the handle returned by the cc_mqttsn_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.

Limiting Stored Outgoing Topic IDs

When a client attempts to publish a message with non-short topic (length of which is not equal to 2 characters), the topic needs to be registered against the gateway, and receive a numeric topic ID in response. Such topic ID is preserved for future use to avoid registration process before the required message is actually PUBLISH-ed. Depending on the amount of various topic strings that the end application generates there might be a need to limit the uncontrolled growth of internal storage. To do so use cc_mqttsn_client_set_outgoing_topic_id_storage_limit() function

printf("ERROR: Failed to limit the outgoing topic ids storage with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_set_outgoing_topic_id_storage_limit(CC_MqttsnClientHandle client, unsigned long long limit)
Specify limit of the topic IDs stored for the outgoing messages.

When the set limit is reached the least recently used topic id record is dropped to free space for the newly registered one.

To get the current limit use cc_mqttsn_client_get_outgoing_topic_id_storage_limit() function.

To remove the previously set limit, i.e. reset it to the default pass 0 as the limit value to cc_mqttsn_client_set_outgoing_topic_id_storage_limit().

printf("ERROR: Failed to reset the limit the outgoing topic ids storage with ec=%d\n", ec);
...
}

Updating Will

To update will after the connection established use will operation.

Preparing "Will" Operation.

if (will == NULL) {
printf("ERROR: Will update allocation failed with ec=%d\n", ec);
}
CC_MqttsnWillHandle cc_mqttsn_client_will_prepare(CC_MqttsnClientHandle client, CC_MqttsnErrorCode *ec)
Prepare "will" operation.

Configuring "Will" Retry Period

When created, the "will" operation inherits the Default Retry Period configuration. It can be changed for the allocated operation using the cc_mqttsn_client_will_set_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_will_set_retry_period(CC_MqttsnWillHandle handle, unsigned ms)
Configure the retry period for the "will" operation.

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

Configuring "Will" Retry Count

When created, the "will" operation inherits the Default Retry Count configuration. It can be changed for the allocated operation using the cc_mqttsn_client_will_set_retry_count() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_will_set_retry_count(CC_MqttsnWillHandle handle, unsigned count)
Configure the retry count for the "will" operation.

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

Configuration of "Will" Operation

To configure "will" operation use cc_mqttsn_client_will_config() function.

// Assign default values to the "config"
// Update the values if needed:
config.m_topic = "some_topic";
config.m_data = ...;
config.m_dataLen = ...;
...
// Perform the configuration
ec = cc_mqttsn_client_will_config(will, &config);
printf("ERROR: Will configuration failed with ec=%d\n", ec);
...
}
void cc_mqttsn_client_will_init_config(CC_MqttsnWillConfig *config)
Intialize the CC_MqttsnWillConfig configuration structure.
CC_MqttsnErrorCode cc_mqttsn_client_will_config(CC_MqttsnWillHandle handle, const CC_MqttsnWillConfig *config)
Perform configuration of the "will" operation.

See also documentation of the CC_MqttsnWillConfig structure.

To remove the previously set will, just assign NULL (done by the invocation of the cc_mqttsn_client_will_init_config()) to m_topic data member (also without assigning anything to m_data).

Sending Will Update Request

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

void my_will_complete_cb(void* data, CC_MqttsnAsyncOpStatus status, const CC_MqttsnConnectInfo* info)
{
printf("ERROR: The will operation has failed with status=%d\n", status);
... // handle error.
return;
}
// "info" is not NULL when status is CC_MqttsnAsyncOpStatus_Complete.
assert(info != NULL);
... // Analyze info values.
}
ec = cc_mqttsn_client_will_send(will, &my_will_complete_cb, data);
printf("ERROR: Failed to send will update request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_will_send(CC_MqttsnWillHandle handle, CC_MqttsnWillCompleteCb cb, void *cbData)
Send the "will" operation.

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

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

When the "will" operation completion callback is invoked the reported "info" is present if and only if the "status" is CC_MqttsnAsyncOpStatus_Complete.

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

The will update operation can be split into up to 2 stages:

  • WILLTOPICUPD <-> WILLTOPICRESP messages exchange
  • WILLMSGUPD <-> WILLMSGRESP messages exchange

The library monitors the currently set will configuration and may skip one or more of the stages. When the will update operation is issued, but no will update is actually needed, the callback passed to the cc_mqttsn_client_will_send() will be invoked right away.

When the callback reporting the will update status is invoked, it is responsibility of the application to check the CC_MqttsnWillInfo::m_topicUpdReturnCode and CC_MqttsnWillInfo::m_msgUpdReturnCode values. If the value of any of them is CC_MqttsnReturnCode_ValuesLimit, then the relevant operation didn't take place. Otherwise, if the value is not CC_MqttsnReturnCode_Accepted, then the relevant operation stage has failed.

For example if any of the return codes is reported to be CC_MqttsnReturnCode_Conjestion, it means that the gateway may still be processing the previous will update stage and cannot process the next one. The application is responsible to re-issue the the same will update request a bit later. If one of the stages was successful (acknowledged by the gateway), then the appropriate stage will be skipped by the library and the failing stage is going to be repeated.

Cancel the "Will" Operation.

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

printf("ERROR: Failed to cancel will with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_will_cancel(CC_MqttsnWillHandle handle)
Cancel the allocated "will" operation.

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

Simplifying the "Will" Operation Preparation.

In many use cases the "will" 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:
config.m_topic = "some_topic";
config.m_data = ...;
config.m_dataLen = ...;
ec = cc_mqttsn_client_will(client, config, &my_will_complete_cb, data);
printf("ERROR: Failed to send will request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_will(CC_MqttsnClientHandle client, const CC_MqttsnWillConfig *config, CC_MqttsnWillCompleteCb cb, void *cbData)
Prepare and send "will" request in one go.

Note that the wrapper function does NOT expose the handle returned by the cc_mqttsn_client_will_prepare(). It means that it's not possible to cancel the "will" operation before its completion.

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 gateway.

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 gateway sends message using the QoS2, the message is still going to be reported to the application.

However, if the gateway 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_mqttsn_client_set_verify_incoming_msg_subscribed() for performance reasons.

CC_MqttsnErrorCode cc_mqttsn_client_set_verify_incoming_msg_subscribed(CC_MqttsnClientHandle client, bool enabled)
Control verification of the incoming message being correctly subscribed.

To retrieve the current configuration use the cc_mqttsn_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_mqttsn_client_set_verify_incoming_topic_enabled() function.

CC_MqttsnErrorCode cc_mqttsn_client_set_verify_incoming_topic_enabled(CC_MqttsnClientHandle client, bool enabled)
Control incoming topic format verification.

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

The message report callback is invoked immediately on reception of the PUBLISH message even for QoS2 message. Just like it is shown as "Method B" in the "Figure 4.3" of the MQTT v3.1.1 specification. For QoS2 messages the packet ID is stored and the rest of the relevant QoS2 messages are exchanged in the background.

When a gateway attempts to publish a message with non-short topic (length of which is not equal to 2 characters), the topic needs to be registered against the client with allocated topic ID. Such topic ID is preserved for future use to avoid redundant registration process in the future. Depending on the amount of various topic strings that the end application may receive there might be a need to limit the uncontrolled growth of internal storage. To do so use cc_mqttsn_client_set_incoming_topic_id_storage_limit() function

printf("ERROR: Failed to limit the incoming topic ids storage with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_set_incoming_topic_id_storage_limit(CC_MqttsnClientHandle client, unsigned long long limit)
Specify limit of the topic IDs stored for the incoming messages.

When the set limit is reached the least recently used topic id record is dropped to free space for the newly registered one.

To get the current limit use cc_mqttsn_client_get_incoming_topic_id_storage_limit() function.

To remove the previously set limit, i.e. reset it to the default pass 0 as the limit value to cc_mqttsn_client_set_incoming_topic_id_storage_limit().

printf("ERROR: Failed to reset the limit the incoming topic ids storage with ec=%d\n", ec);
...
}

Sleeping

To enter the "sleep" mode use sleep operation.

Preparing "Sleep" Operation.

if (sleep == NULL) {
printf("ERROR: Sleep allocation failed with ec=%d\n", ec);
}
struct CC_MqttsnSleep * CC_MqttsnSleepHandle
Handle for "sleep" operation.
Definition common.h:216
CC_MqttsnSleepHandle cc_mqttsn_client_sleep_prepare(CC_MqttsnClientHandle client, CC_MqttsnErrorCode *ec)
Prepare "sleep" operation.

Configuring "Sleep" Retry Period

When created, the "sleep" operation inherits the Default Retry Period configuration. It can be changed for the allocated operation using the cc_mqttsn_client_sleep_set_retry_period() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_sleep_set_retry_period(CC_MqttsnSleepHandle handle, unsigned ms)
Configure the retry period for the "sleep" operation.

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

Configuring "Sleep" Retry Count

When created, the "sleep" operation inherits the Default Retry Count configuration. It can be changed for the allocated operation using the cc_mqttsn_client_sleep_set_retry_count() function.

... /* Something went wrong */
}
CC_MqttsnErrorCode cc_mqttsn_client_sleep_set_retry_count(CC_MqttsnSleepHandle handle, unsigned count)
Configure the retry count for the "sleep" operation.

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

Configuration of "Sleep" Operation

To configure "sleep" operation use cc_mqttsn_client_sleep_config() function.

// Assign default values to the "config"
// Update the values:
config.m_duration = ...;
// Perform the configuration
ec = cc_mqttsn_client_sleep_config(sleep, &config);
printf("ERROR: Sleep configuration failed with ec=%d\n", ec);
...
}
void cc_mqttsn_client_sleep_init_config(CC_MqttsnSleepConfig *config)
Intialize the CC_MqttsnSleepConfig configuration structure.
CC_MqttsnErrorCode cc_mqttsn_client_sleep_config(CC_MqttsnSleepHandle handle, const CC_MqttsnSleepConfig *config)
Perform configuration of the "sleep" operation.
Configuration the "sleep" operation.
Definition common.h:325
unsigned m_duration
Duration configuration in seconds.
Definition common.h:326

See also documentation of the CC_MqttsnSleepConfig structure.

Note that configuration of the "sleep" operation specifies duration within which the client is expected to send PINGREQ message to check for the accumulated messages. The library will try to perform several attempts to send PINGREQ and receive PINGRESP in return. The configured duration is expected to be longer (when translated to milliseconds) than the Default Retry Period multiplied by the (Default Retry Count + 1).

Sending Sleep Request

When the necessary configuration is performed for the allocated "sleep" operation it can be sent to the gateway. To initiate sending use the cc_mqttsn_client_sleep_send() function.

void my_sleep_complete_cb(void* data, CC_MqttsnAsyncOpStatus status)
{
printf("ERROR: The sleep operation has failed with status=%d\n", status);
... // handle error.
return;
}
...
}
ec = cc_mqttsn_client_sleep_send(sleep, &my_sleep_complete_cb, data);
printf("ERROR: Failed to send sleep request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_sleep_send(CC_MqttsnSleepHandle handle, CC_MqttsnSleepCompleteCb cb, void *cbData)
Send the "sleep" operation.

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

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

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

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

After the intended sleep is over the application can re-establish network connection to the gateway (if needed) and perform the connect operation again.

Cancel the "Sleep" Operation.

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

printf("ERROR: Failed to cancel sleep with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_sleep_cancel(CC_MqttsnSleepHandle handle)
Cancel the allocated "sleep" operation.

Simplifying the "Sleep" Operation Preparation.

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

For example:

ec = cc_mqttsn_client_sleep(client, &config, &my_sleep_complete_cb, data);
printf("ERROR: Failed to send sleep request with ec=%d\n", ec);
...
}
CC_MqttsnErrorCode cc_mqttsn_client_sleep(CC_MqttsnClientHandle client, const CC_MqttsnSleepConfig *config, CC_MqttsnSleepCompleteCb cb, void *cbData)
Prepare and send "sleep" request in one go.

Receiving Messages During "Sleep" State.

When in "asleep" mode the client is expected to periodically send PINGREQ messages to inquire whether the gateway has accumulated pending messages for the client. The library keeps track of time and expects the application to still manage timers and notify the library about the elapsed ticks (see Time Measurement). The library will perform up to Default Retry Count + 1 attempts to get the PINGRESP back within the Default Retry Period and will report all the received messages in between.

The application can also independently and explicitly to initiate check of the pending messages using cc_mqttsn_client_asleep_check_messages() function.

In case the gateway doesn't respond with PINGRESP to the sent PINGREQ the library will report Unsolicited Gateway Disconnection.

Unsolicited Gateway Disconnection

When broker disconnection is detected all the incomplete asynchronous operations will be terminated with the an appropriate status report.

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

Keep Alive Timeout

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

Receiving DISCONNECT message

When something goes wrong the gateway is allowed to send DISCONNECT message to the client at any time. In such case the library responds in the following way:

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

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.