CommsChampion Ecosystem MQTT-SN Client
MQTT-SN client library.
|
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.
The version is of the library applicable to this documentation is defined in the cc_mqttsn_client/common.h file using the following defines:
To use this MQTT-SN Client Library use the following single include statement:
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.
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.
When working with C++ it is advised to use a smart pointer with a custom deleter.
IMPORTANT: The function cc_mqttsn_client_free() must NOT be called from within a callback. Use next event loop iteration.
In order to properly function the library requires setting several callbacks.
To client application must assign a callback for the library to be able to send binary data out to the connected gateway.
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.
The client application must assign a callback for the library to report discovered gateway disconnection.
See also the documentation of the CC_MqttsnGwDisconnectedReportCb callback function definition.
See also Unsolicited Gateway Disconnection below for details.
The client application must assign a callback for the library to report application level messages received from the gateway.
See also the documentation of the CC_MqttsnMessageReportCb callback function definition.
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:
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.
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.
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.
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.
See also the documentation of the CC_MqttsnErrorLogCb callback function definition.
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.
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.
The library abstracts away multiple MQTT-SN protocol based "operations". Every such operation has multiple stages:
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:
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.
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.
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.
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().
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.
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.
To retrieve the configured retry period use the cc_mqttsn_client_search_get_retry_period() function.
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.
To retrieve the configured retry count use the cc_mqttsn_client_search_get_retry_count() function.
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.
To retrieve the configured retry count use the cc_mqttsn_client_search_get_broadcast_radius() function.
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.
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.
While the handle returned by the cc_mqttsn_client_search_prepare() is still valid it is possible to cancel / discard the 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.
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:
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.
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.
The application can receive ongoing notifications if a status of any gateway changes.
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.
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.
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.
It is possible to discard information on all the gateways in one go:
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.
To retrieve the current configuration use cc_mqttsn_client_get_default_gw_adv_duration() function.
To connect to gateway use 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.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_connect_get_retry_period() function.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_connect_get_retry_count() function.
To configure "connect" operation use cc_mqttsn_client_connect_config() function.
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.
To perform will configuration use the cc_mqttsn_client_connect_config_will() function.
See also documentation of the CC_MqttsnWillConfig structure.
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.
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.
While the handle returned by the cc_mqttsn_client_connect_prepare() is still valid it is possible to cancel / discard the 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.
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:
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.
At any time it is possible to check the internal state of the library of whether it's properly connected to the gateway.
To intentionally disconnect from gateway use disconnect operation. The unsolicited disconnection from the gateway is described in ref Unsolicited Gateway Disconnection section below.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_disconnect_get_retry_period() function.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_disconnect_get_retry_count() function.
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.
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.
While the handle returned by the cc_mqttsn_client_disconnect_prepare() is still valid it is possible to cancel / discard the operation.
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:
To subscribe to receive incoming messages use subscribe operation. The application can issue multiple "subscribe" operations in parallel.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_subscribe_get_retry_period() function.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_subscribe_get_retry_count() function.
To configure "subscribe" operation use cc_mqttsn_client_subscribe_config() function.
When there is a need to use a predefined topic id instead of the topic string use m_topicId member instead of m_topic.
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.
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.
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.
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.
While the handle returned by the cc_mqttsn_client_subscribe_prepare() is still valid it is possible to cancel / discard the 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.
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:
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.
To unsubscribe to receive incoming messages use unsubscribe operation. The application can issue multiple "unsubscribe" operations in parallel.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_unsubscribe_get_retry_period() function.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_unsubscribe_get_retry_count() function.
To configure "unsubscribe" operation use cc_mqttsn_client_unsubscribe_config() function.
When there is a need to use a predefined topic id instead of the topic string use m_topicId member instead of m_topic.
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.
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.
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.
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.
While the handle returned by the cc_mqttsn_client_unsubscribe_prepare() is still valid it is possible to cancel / discard the 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.
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:
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.
To publish messages to the gateway use publish operation.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_publish_get_retry_period() function.
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.
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.
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.
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.
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.
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.
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.
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.
While the handle returned by the cc_mqttsn_client_publish_prepare() is still valid it is possible to cancel / discard the 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.
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:
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.
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
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().
To update will after the connection established use will operation.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_will_get_retry_period() function.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_will_get_retry_count() function.
To configure "will" operation use cc_mqttsn_client_will_config() function.
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).
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.
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:
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.
While the handle returned by the cc_mqttsn_client_will_prepare() is still valid it is possible to cancel / discard the 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.
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:
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.
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.
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.
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
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().
To enter the "sleep" mode use sleep operation.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_sleep_get_retry_period() function.
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.
To retrieve the configured response timeout use the cc_mqttsn_client_sleep_get_retry_count() function.
To configure "sleep" operation use cc_mqttsn_client_sleep_config() function.
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).
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.
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.
While the handle returned by the cc_mqttsn_client_sleep_prepare() is still valid it is possible to cancel / discard the operation.
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:
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.
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:
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:
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.
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.
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.