MQTT-SN Gateway Library
Library that allows implementation of MQTT-SN gateway.
Session

The Session object is responsible to manage and forward traffic of messages between single MQTT-SN client and the broker. When new message is received over the I/O link, the driving code must check whether any Session object has already been created for the client that sent the message (the clients can be differentiated by their origin address). If such Session does NOT exist, it must be created.

The received data needs to be forwarded to appropriate Session object for processing. And when the Session object requests to send message back, it needs to be sent to the right address. It is a responsibility of the driving code to maintain a valid map between the client's address and appropriate Session object.

Allocation

When using C++ interface, just instantiate object of cc_mqttsn_gateway::Session class. The destruction of the object will clean up all acquired resources.

cc_mqttsn_gateway::Session* session = new cc_mqttsn_gateway::Session(); // make sure to use smart pointer in production code
Interface for Session entity.
Definition: Session.h:29

When using C interface, the allocation is performed using cc_mqttsn_gw_session_alloc()

CC_MqttsnSessionHandle cc_mqttsn_gw_session_alloc(void)
Allocate Session object.
Handle for session object used in all cc_mqttsn_gw_session_* functions.
Definition: gateway_all.h:130

and de-allocation is performed using cc_mqttsn_gw_session_free() functions.

void cc_mqttsn_gw_session_free(CC_MqttsnSessionHandle session)
Free allocated Session object.

Receiving Data

The MQTT-SN gateway Session serves as message translator and forwarder between MQTT-SN client and MQTT broker. The driver code is responsible to manage these two independent communication links. Once a message is received on any of them, it must be provided the the Session object for processing.

Receiving Data from Client

C++ interface:

std::size_t consumed = session->dataFromClient(buf, bufLen);
std::size_t dataFromClient(const std::uint8_t *buf, std::size_t len)
Provide data received from the client for processing.

C interface:

unsigned consumed = cc_mqttsn_gw_session_data_from_client(handle, buf, bufLen);
unsigned cc_mqttsn_gw_session_data_from_client(CC_MqttsnSessionHandle session, const unsigned char *buf, unsigned bufLen)
Provide data received from the client for processing.

NOTE, that functions return number of bytes that were actually processed. If number of processed bytes is less that number of bytes residing in the buffer, then probably some unexpected protocol error has occurred.

The call to this function may cause invocation of various callback functions that have been set.

Receiving Data from Broker

C++ interface:

std::size_t consumed = session->dataFromBroker(buf, bufLen);
std::size_t dataFromBroker(const std::uint8_t *buf, std::size_t len)
Provide data received from the broker for processing.

C interface:

unsigned consumed = cc_mqttsn_gw_session_data_from_broker(handle, buf, bufLen);
unsigned cc_mqttsn_gw_session_data_from_broker(CC_MqttsnSessionHandle session, const unsigned char *buf, unsigned bufLen)
Provide data received from the broker for processing.

NOTE, that functions return number of bytes that were actually processed. If number of processed bytes is less that number of bytes residing in the buffer, then probably some unexpected protocol error has occurred.

The call to this function may cause invocation of various callback functions that have been set.

Sending Data

The Session object may require to send a message to either client or broker. The driving code has to provide appropriate callbacks for this purpose. When the callback is invoked, it is provided with pointer to data buffer. This buffer resides in internal data structures of the Session object. After the callback returns, this buffer may be updated. It means, that the driving code may require to copy the buffer to its internal data structures to preserve the data intact until send over I/O link operation is complete.

Sending Data to Client

C++ interface:

[](const std::uint8_t* buf, std::size_t bufLen)
{
...
});
void setSendDataClientReqCb(SendDataReqCb &&func)
Set the callback to be invoked when new data needs to be sent to the client.

C interface:

void my_send_to_client(void* userData, const unsigned char* buf, unsigned bufLen)
{
...
}
cc_mqttsn_gw_session_set_send_data_to_client_cb(handle, &my_send_to_client, someUserData);
void cc_mqttsn_gw_session_set_send_data_to_client_cb(CC_MqttsnSessionHandle session, CC_MqttsnSessionSendDataReqCb cb, void *data)
Set the callback to be invoked when new data needs to be sent to the client.

Sending Data to Broker

C++ interface:

[](const std::uint8_t* buf, std::size_t bufLen)
{
...
});
void setSendDataBrokerReqCb(SendDataReqCb &&func)
Set the callback to be invoked when new data needs to be sent to the broker.

C interface:

void my_send_to_broker(void* userData, const unsigned char* buf, unsigned bufLen)
{
...
}
cc_mqttsn_gw_session_set_send_data_to_broker_cb(handle, &my_send_to_broker, someUserData);
void cc_mqttsn_gw_session_set_send_data_to_broker_cb(CC_MqttsnSessionHandle session, CC_MqttsnSessionSendDataReqCb cb, void *data)
Set the callback to be invoked when new data needs to be sent to the broker.

Time Measurement

The Session object may require to measure time to identify message delivery timeouts. It relies on the driving code to provide such service. There is a need to set appropriate callback:

C++ interface:

[](unsigned duration)
{
... // Set timer to expire after duration milliseconds
// After expiry call session.tick()
};
void setNextTickProgramReqCb(NextTickProgramReqCb &&func)
Set the callback to be invoked when new time measurement is required.

C interface:

void my_tick_req(void* userData, unsigned duration)
{
... /* Set timer to expire after duration milliseconds */
... /* After expiry call cc_mqttsn_gw_session_tick() */
}
cc_mqttsn_gw_session_set_tick_req_cb(handle, &my_tick_req, someUserData);
void cc_mqttsn_gw_session_set_tick_req_cb(CC_MqttsnSessionHandle session, CC_MqttsnSessionTickReqCb cb, void *data)
Set the callback to be invoked when new time measurement is required.

After the requested time expires, the driving code needs to notify the Session object. It must call the appropriate tick() function.

C++ interface:

session->tick();
void tick()
Notify the Session object about requested time period expiry.

C interface:

void cc_mqttsn_gw_session_tick(CC_MqttsnSessionHandle session)
Notify the Session object about requested time period expiry.

Based on some events, the Session object may require knowledge of elapsed time since last tick programming request. For this purpose the driving code must set a callback to cancel the existing time measurement and return number of elapsed milliseconds.

C++ interface:

[]()
{
... // cancel timer
return ...; // return number of elapsed milliseconds
});
void setCancelTickWaitReqCb(CancelTickWaitReqCb &&func)
Set the callback to be invoked when previously requested time measurement needs to be cancelled.

C interface:

unsigned my_cancel_timer_req(void* userData)
{
... /* cancel timer */
return ...; /* return number of elapsed milliseconds */
}
cc_mqttsn_gw_session_set_cancel_tick_cb(handle, &my_cancel_timer_req, someUserData);
void cc_mqttsn_gw_session_set_cancel_tick_cb(CC_MqttsnSessionHandle session, CC_MqttsnSessionCancelTickReqCb cb, void *data)
Set the callback to be invoked when previously requested time measurement needs to be cancelled.

Session Termination

The Session object may recognise disconnection of MQTT-SN client and/or MQTT broker. As the result the session object must be destructed immediately and new one is created once the client renews its connection. The request to terminate the session is performed via callback, which must be set by the driving code.

C++ interface:

[]()
{
... // Destruct session object.
});
void setTerminationReqCb(TerminationReqCb &&func)
Set the callback to be invoked when the session needs to be terminated and this Session object delete...

C interface:

void my_session_term(void* userData)
{
... /* Remove reference to session object from internal data structures */
}
cc_mqttsn_gw_session_set_term_req_cb(handle, &my_session_term, someUserData);
void cc_mqttsn_gw_session_set_term_req_cb(CC_MqttsnSessionHandle session, CC_MqttsnSessionTermReqCb cb, void *data)
Set the callback to be invoked when the Session needs to be terminated and the calling Session object...

Re-Connection to Broker

The MQTT-SN protocol specification defines messages and operations, that are not properly supported by the MQTT protocol, such as will information update. The Session object supports these kind of operations by sending updated CONNECT request to the broker on behalf of the client. However, MQTT protocol disallows multiple CONNECT messages in the single connection session. As the result the gateway must perform the following steps:

  1. send DISCONNECT message
  2. close existing TCP/IP connection
  3. open new TCP/IP connection
  4. send new CONNECT message with updated connection information.

Due to the reason, that TCP/IP connection to the broker is managed by the driving code, it must implement steps 2 and 3 in the list above.

The Session object issues the reconnection request via callback, which must be provided by the driving code.

C++ interface

[]()
{
... // Close existing TCP/IP connection to broker and open a new one
});
void setBrokerReconnectReqCb(BrokerReconnectReqCb &&func)
Set the callback to be invoked when the session needs to close existing TCP/IP connection to the brok...

C interface

void my_broker_reconnect(void* userData)
{
... /* Close existing TCP/IP connection to broker and open a new one */
}
cc_mqttsn_gw_session_set_broker_reconnect_req_cb(handle, &my_broker_reconnect, someUserData);
void cc_mqttsn_gw_session_set_broker_reconnect_req_cb(CC_MqttsnSessionHandle session, CC_MqttsnSessionBrokerReconnectReqCb cb, void *data)
Set the callback to be invoked when the Session needs to close existing TCP/IP connection to the brok...

NOTE, that the updated connection statuses of the broker (first disconnected and then connected) must be reported to the Session object when they happen. Details are in Connection to Broker section below.

Gateway ID

The MQTT-SN client may broadcast SEARCHGW message in an attempt to discover existing gateways on the network. The Session object is responsible to send GWINFO message as a response. Such message contains numeric gateway ID. Hence, the Session object must also be aware of the gateway ID information:

C++ interface:

session->setGatewayId(5);
void setGatewayId(std::uint8_t value)
Set gateway numeric ID to be reported when requested.

C interface:

void cc_mqttsn_gw_session_set_id(CC_MqttsnSessionHandle session, unsigned char id)
Set gateway numeric ID to be reported when requested.

Retry Attempts

There are cases when the communication to either client or broker is driven by the Session object itself. Such communication may involve messages that require acknowledgement by the other side. If acknowledgement is not received in time, the message must be re-sent. The Session object may be configured with duration (in seconds) between resend attempts and total number of attempts to perform.

C++ interface:

session->setRetryPeriod(5U); // 5 seconds between resend attempts
session->setRetryCount(4U); // up to 4 attempts to resend the same message
void setRetryPeriod(unsigned value)
Set retry period to wait between resending unacknowledged message to the client and/or broker.
void setRetryCount(unsigned value)
Set number of retry attempts to perform before abandoning attempt to send unacknowledged message.

C interface:

cc_mqttsn_gw_session_set_retry_period(handle, 5U); /* 5 seconds between resend attempts */
cc_mqttsn_gw_session_set_retry_count(handle, 4U); /* up to 4 attempts to resend the same message */
void cc_mqttsn_gw_session_set_retry_count(CC_MqttsnSessionHandle session, unsigned value)
Set number of retry attempts to perform before abandoning attempt to send unacknowledged message.
void cc_mqttsn_gw_session_set_retry_period(CC_MqttsnSessionHandle session, unsigned value)
Set retry period to wait between resending unacknowledged message to the client and/or broker.

If not configured, the default values of 10 seconds and 3 attempts apply.

Predefined Topics

The messages in MQTT-SN protocol are published with numeric topic IDs instead of strings (like in original MQTT). The protocol also allows bypassing the topic strings registration and using predefined IDs directly. In this case the Session object must be configured with all known and applicable topic string and topic numeric ID pairs. It will use this information to convert between numeric topic ID topic topic string when forwarding PUBLISH messages to both directions.

C++ interface:

if (!session->addPredefinedTopic("some/predefined/topic", 123)) {
... // report error
}
if (!session->addPredefinedTopic("other/predefined/topic", 1111)) {
... // report error
}
bool addPredefinedTopic(const std::string &topic, std::uint16_t topicId)
Add predefined topic string and ID information.

C interface:

if (!cc_mqttsn_gw_session_add_predefined_topic(handle, "some/predefined/topic", 123)) {
... /* report error */
}
if (!cc_mqttsn_gw_session_add_predefined_topic(handle, "other/predefined/topic", 1111)) {
... /* report error */
}
bool cc_mqttsn_gw_session_add_predefined_topic(CC_MqttsnSessionHandle session, const char *topic, unsigned short topicId)
Add predefined topic string and ID information.

Allocating Topic IDs

When not using predefined topic IDs, there is a process of topic string registration and allocating relevant numeric topic ID. This allocation is performed by the Session object. By default the Session object will pick the first number available. However, it may be wise to reserve some range of topic IDs to be predefined ones, especially when there are some client specific predefined topic IDs, which added later when connected client ID is known (see Connected Client Report). The library provides a way to limit range of topic IDs allocated during the registration process.

C++ interface:

if (!session->setTopicIdAllocationRange(0x1000, 0xffff)) {
... // report error
}
bool setTopicIdAllocationRange(std::uint16_t minVal, std::uint16_t maxVal)
Limit range of topic IDs allocated for newly registered topics.

C interface:

if (!cc_mqttsn_gw_session_set_topic_id_alloc_range(handle, 0x1000, 0xffff)) {
... // report error
}
bool cc_mqttsn_gw_session_set_topic_id_alloc_range(CC_MqttsnSessionHandle session, unsigned short minTopicId, unsigned short maxTopicId)
Limit range of topic IDs allocated for newly registered topics.

Start Operation

After been properly configured the Session object needs to be started.

C++ interface:

if (!session->start()) {
... // The session hasn't been properly configured, report error
}
bool start()
Start this object's operation.

C interface:

... /* The session hasn't been properly configured, report error */
}
bool cc_mqttsn_gw_session_start(CC_MqttsnSessionHandle session)
Start the Session's object's operation.

If not properly started, the Session object will ignore any input from client and broker.

The operation of the Session object can also be stopped / paused for a while.

C++ interface:

session->stop();
void stop()
Stop the operation of the object.

C interface:

void cc_mqttsn_gw_session_stop(CC_MqttsnSessionHandle session)
Stop the operation of the Session object.

The operation of the stopped Session object may be restarted using the same cc_mqttsn_gateway::Session::start() and cc_mqttsn_gw_session_start() functions.

Connection to Broker

AFTER (important emphasis here) successfully starting operation of the Session object (see Start Operation) the driving code must initiate TCP/IP connection to the MQTT broker. The driving code must also constantly monitor the state of this connection and to the Session object any detected change. When started, the Session object assumes broker is disconnected.

C++ interface:

session->setBrokerConnected(true); // Reports broker being connected
void setBrokerConnected(bool connected)
Notify the Session object about broker being connected / disconnected.

C interface:

cc_mqttsn_gw_session_broker_connected(handle, true); // Reports broker being connected
void cc_mqttsn_gw_session_broker_connected(CC_MqttsnSessionHandle session, bool connected)
Notify the Session object about broker being connected / disconnected.

When issuing broker re-connection request (see Re-Connection to Broker), the Session object expects this call to happen twice: the first one for disconnection report and second one for new connection report.

The driving code also responsible to detect unsolicited disconnects of TCP/IP link to the broker and report it to the Session object using the same function call.

Client Authentication

The MQTT protocol supports client authentication, where the CONNECT message may contain username and password. The MQTT-SN protocol, on the other hand, lacks this feature. The Session object provides an ability to set a callback function, which will be invoked when MQTT-SN client's connection request is recognised. This callback may provide authentication information for this client, which is going to be filled in the forwarded CONNECT request.

C++ interface:

[](const std::string& clientId) -> cc_mqttsn_gateway::Session::AuthInfo
{
return std::make_pair(getUsernameFor(clientId), getPasswordFor(clientId));
});
std::pair< std::string, BinaryData > AuthInfo
Type of authentication information.
Definition: Session.h:38
void setAuthInfoReqCb(AuthInfoReqCb &&func)
Set the callback to be used to request authentication information for specific client.

C interface:

void my_auth_callback(
void* userData,
const char* clientId,
const char** username,
const unsigned char** password,
unsigned* passwordLen)
{
*username = ...; /* assign pointer to username for provided client ID */
*password = ...; /* assign pointer to password for provided client ID */
*passwordLen = ...; /* specify length of the password */
}
cc_mqttsn_gw_session_set_auth_info_req_cb(handle, &my_auth_callback, someUserData);
void cc_mqttsn_gw_session_set_auth_info_req_cb(CC_MqttsnSessionHandle session, CC_MqttsnSessionAuthInfoReqCb cb, void *data)
Set the callback to be used to request authentication information for specific client.

Connected Client Report

The Session object provides an ability to get notified when a MQTT-SN client is successfully connected. The notification is performed using a callback. Inside this callback, the driving code may provide some client specific configurations, such as new predefined topic IDs relevant only to the connected client.

C++ interface:

[session](const std::string& clientId)
{
if (!session->addPredefinedTopic("client/specific/predefined/topic", 2222)) {
... /* report error */
}
});
void setClientConnectedReportCb(ClientConnectedReportCb &&func)
Set the callback to be invoked when MQTT-SN client is successfully connected to the broker.

C interface:

void my_client_connect_report(void* userData, const char* clientId)
{
if (!cc_mqttsn_gw_session_add_predefined_topic(handle, "client/specific/predefined/topic", 2222)) {
... /* report error */
}
}
cc_mqttsn_gw_session_set_client_connect_report_cb(handle, &my_client_connect_report, someUserData);
void cc_mqttsn_gw_session_set_client_connect_report_cb(CC_MqttsnSessionHandle session, CC_MqttsnSessionClientConnectReportCb cb, void *data)
Set the callback to be invoked when MQTT-SN client is successfully connected to the broker.

Default Client ID

The Session object interface allows to specify the default ID of the client, which is going to be used to connect to broker, if the client doesn't provide non-empty ID string in its connection attempt. This may be useful when client is connected over one-to-one I/O link such as RS-232. In order to simplify the client implementation and reduce amount of data transferred on the link, the Session object may be configured to substitute the empty client ID with other, non-empty one, when forwarding the connection request to the broker.

The default client ID configuration may also be used with "publish only" clients (see Publish Only Client below). The configured client ID will be used when connecting to the broker on behalf of the "publish only" client.

C++ interface:

session->setDefaultClientId("some_default_id");
void setDefaultClientId(const std::string &value)
Provide default client ID for clients that report empty one in their attempt to connect.

C interface:

cc_mqttsn_gw_session_set_default_client_id(handle, "some_default_id");
void cc_mqttsn_gw_session_set_default_client_id(CC_MqttsnSessionHandle session, const char *clientId)
Provide default client ID for clients that report empty one in their attempt to connect.

NOTE, that the Session object will make an attempt to substitute reported client ID if and only if the client ID is empty and configured default client ID is NOT.

Publish Only Client

The MQTT-SN protocol allows "publish only" clients, which don't make an attempt to connect to the gateway/broker, but allowed to publish messages with predefined topic IDs and QoS=-1. The gateway must connect to the broker on behalf of such client. The connection operation involves knowledge about client ID and "keep alive" period. The client ID, that is going to be used, is configured as Default Client ID. The "keep alive" period can be provided using separate function:

C++ interface:

session->setPubOnlyKeepAlive(100); // 100 seconds
void setPubOnlyKeepAlive(std::uint16_t value)
Provide default "keep alive" period for "publish only" clients, that do not make an attempt to connec...

C++ interface:

cc_mqttsn_gw_session_set_pub_only_keep_alive(handle, 100); /* 100 seconds */
void cc_mqttsn_gw_session_set_pub_only_keep_alive(CC_MqttsnSessionHandle session, unsigned value)
Provide default "keep alive" period for "publish only" clients, that do not make an attempt to connec...

If such configuration is not provided, the default value of 60 seconds is assumed.

Sleeping Client

The Session object supports client entering the SLEEP mode without any extra configuration. It will send the PINGREQ messages on behalf of the client to keep the connection to the broker alive, and will accumulate all the messages sent to the client until it wakes up.

However, it may be a good idea to limit the number of messages, the Session object is allowed to accumulate, to avoid consumption of too much memory.

C++ interface:

session->setSleepingClientMsgLimit(1000); // no more that 1000 messages
void setSleepingClientMsgLimit(std::size_t value)
Provide limit to number pending messages being accumulated for the sleeping client.

C++ interface:

cc_mqttsn_gw_session_set_sleeping_client_msg_limit(handle, 1000); /* no more that 1000 messages */
void cc_mqttsn_gw_session_set_sleeping_client_msg_limit(CC_MqttsnSessionHandle session, unsigned value)
Provide limit to number pending messages being accumulated for the sleeping client.