Where are we with MQTT-SN?

This question was posed to me recently, with the added observation that it seemed no progress had been made in the last two years.  From my perspective this isn’t quite true.  Although the specification is still maintained by IBM, no movement has been made to standardize it at OASIS like MQTT has been. That is not to say that the subject of MQTT-SN has not arisen in the MQTT OASIS Technical Committee (TC) discussions, it has. But amongst the numerous improvements that we wanted to make to MQTT 3.1.1, and an ambitious timescale — publishing the new version MQTT 5.0 this year (2017) — addressing non TCP networks in MQTT was put to one side.

There is a continual wavering in my mind as to the importance of MQTT-SN. On one hand, edge computing platforms are getting ever more powerful, on the other, low power consumption is very important for battery power. The powerful edge processors are likely to have TCP stacks for which MQTT is appropriate: but there are still use cases for which low power use is the crucial factor. UDP, Bluetooth Low Energy (BLE) are typical transports used in this case.

If there is enough interest in a standardized MQTT-SN, its features could be addressed by the MQTT OASIS TC. There are a number of options. Incorporating MQTT-SN or its features into the main MQTT specification is one. However MQTT currently requires a reliable underlying network transport (TCP). To change that assumption we would have to consider very carefully the implications, which could take a significant amount of time. It has been suggested that MQTT-SN could be dealt with as a committee note: I’m not sure what form that would take.

In the meantime, I’ve been trying to get more MQTT-SN capabilities added to the Eclipse® Paho project. After I contributed an initial MQTT-SN packet library a couple of years ago, and the RSMB broker with MQTT-SN support to the Eclipse Mosquitto project at its inception, things stalled for a while (but not for a lack of interest on my part). There are forks of RSMB with fixes to the MQTT-SN support, and various gateways and clients around. One of those, written by Tomoaki Yamaguchi, had gained some support and I forget exactly how it happened, but Tomoaki has now contributed an MQTT-SN transparent gateway to Paho.

Over the past months this gateway has matured nicely. I recently used it to replace the use of RSMB in this experiment of Benjamin Cabé’s and it worked well. If we add BLE support to the gateway, then the Node.js BLE to UDP MQTT-SN forwarder in that experiment would not be required either. The gateway is written with the intention of allowing other transports to be added, so this should be eminently feasible.

I do remember seeing an email or post or something recently describing some other MQTT-SN components written by the poster. But now I can’t find or remember where I saw it. If this wasn’t a dream, and you know of such a thing, or it was you, please do let me know.

Where do we go from here?  Two avenues: practical implementations and the specification.  After the MQTT 5.0 standard is published, we can see if there is any interest in the OASIS TC in pursuing the application of MQTT to non-TCP networks.  In the meantime, I will continue working on and encouraging MQTT-SN contributions of the current specification to Paho and elsewhere.

If you are interested in MQTT-SN and would like to see it considered by the OASIS MQTT TC, or make any other comments to the TC, then you can use the mailing list.  If you know of any other implementations or have any questions, or suggestions for Paho, then I will be happy to hear of them

Using the Eclipse Paho “Test” Broker to Help Test MQTT

I may not have finished all my goals for the test material in the Eclipse Paho project, as outlined in this blog post of mine, but some components are still useful in their current state.

Recently my IBM and Paho colleague, James Sutton, needed to check the behaviour of the Java and Android clients when receiving an error code in response to an MQTT subscribe request. This error code, returned in the MQTT suback packet, was introduced in the last, and current, version of MQTT, 3.1.1. It takes the form of an 0x80 value in the granted QoS (Quality of Service) field, for which only 0, 1 or 2 are valid values – the set of integers which QoS can take in MQTT.

Now you could fire up a broker like Eclipse Mosquitto and configure it to disallow a subscription to a certain topic. If you know the broker well enough, this may be quick and easy. It’s possible you might have to do some fishing around, and figure out whether that broker is actually returning 0x80 as you wanted.

So James turned to the Paho test broker (startbroker.py in this repo.) (You must use Python 3 to run it, not Python 2). As it stands, this broker will return 0x80 if you try to subscribe to the topic “test/nosubscribe” (see line 322 in MQTTBrokers.py). That’s pretty easy. If you checked out that file, you will notice another two topics: “test/QoS 1 only” and “test/QoS 0 only”, which will return granted QoSs of a maximum of 1 and 0 respectively. These behaviours can be hard to elicit out of a standard MQTT broker.

Ultimately I guess I should make the specific topics which these responses are attached to configurable, but they aren’t right now.

There are some other characteristics of this broker, which single it out as a “test” broker rather than a product:

  • the goal of the coding is clarity rather than performance. You can tell me whether I succeeded
  • there is no persistence: if you want to simulate stopping and restarting a broker, this broker can just remain running, and disconnect all clients
  • MQTT specification conformance statements are embedded in the code, so that when a test suite is run against the broker, it can tell you which statements were encountered, and which weren’t.
  • the broker has parameters to choose behaviours which can vary but still conform to the MQTT specification:
    1. whether to publish QoS 2 messages on PUBREL or not
    2. whether multiple matching subscriptions result in one publication, or more than one
    3. whether queued QoS 0 messages are dropped if the client is disconnected, or not
    4. whether zero_length_clientids are allowed

    it’s possible more might be added in the future.

The main missing feature of this broker is TLS – but it does have WebSocket support. TLS is on my to do list, as will be updates for the next version of MQTT, 5, which we are working on, and is tentatively scheduled for completion next year.

As outlined in the blog post, this broker is meant to help with broker testing as well, as an oracle.

For some more information, see the Eclipse Paho website for the test tools.

You can use issues on this project to ask for new features or identify bugs, or pull requests to offer your own contributions.

Paho 1.1 Released

Finally I have produced the remaining pieces, and am pleased to be able to say that Paho 1.1 is released.

What’s new:

1) Paolo Patierno’s .Net and WinRT MQTT client, M2Mqtt, version 4.0.0
2) An Android service – a wrapper around the existing Java client. Version 1.0.2
3) Embedded C and C++ MQTT client libraries, version 1.0.0

Updated versions of the existing client libraries:

1) Roger Light’s Python MQTT client, version 1.1
2) The full fat Posix/Windows/MacOS C client, updated to version 1.0.3
3) The Java MQTT client, updated to version 1.0.2
4) The JavaScript MQTT client, updated to version 1.0.1

The downloads page is updated, and the formal release documentation is available.

Thanks to everyone who has contributed, with code, bugs, suggestions or advice — anything 🙂

I’ve updated some essentials on the web site, including references to the MQTT 3.1.1 standard, but there are updates that are still needed. If you find anything, please let me know.

An Example of Using TLS with the Paho MQTT Embedded C++ Client

Understandably, I’ve been asked a few times whether the Paho embedded client library will work with TLS. It will, but the only platform where I’ve written the code to do it so far is on mbed mbed. The reason why it hasn’t been widely publicised is that it uses the CyaSSL TLS library, which is licensed under the GPL. This means binaries linked with CyaSSL also have to be GPL, rather than the Apache license usually used on mbed, or the Eclipse licenses used for Paho.

But I can show you the code I did write. Now the client library is structured to be portable to any network library without changing the core code. It seems I need to explain this better, and further, certainly on the main Paho website as well as this blog. (Soon, I promise.) The following module is one I wrote to create a network class for CyaSSL which can be used by the embedded client class, MQTTClient. The two core methods which are needed are read and write; these are called whenever the library needs data, or has data to send. This is a basic connect — no client certificates are involved, but should serve as a model for what can be done. Here is the complete module — skip afterwards to see how to use it.


#if !defined(MQTTSSL_H)
#define MQTTSSL_H

#include "MQTT_mbed.h"
#include "mbed.h"
#include "EthernetInterface.h"

#include 


static TCPSocketConnection mysock; 

static int SocketReceive(CYASSL* ssl, char *buf, int len, void *ctx)
{
    int rc = mysock.receive(buf, len);
    if (rc == -1)
        rc = -2;  // -2 is WANT_READ
    return rc;
}
 
static int SocketSend(CYASSL* ssl, char *buf, int len, void *ctx)
{
    int rc = mysock.send(buf, len);
    return rc;
}


class MQTTSSL
{
public:
    MQTTSSL() : eth()
    {
        ssl = 0;
        ctx = 0;
        
        eth.init();                          // Use DHCP
        eth.connect();
        
        CyaSSL_Init();
        CyaSSL_Debugging_ON();
        method = CyaTLSv1_2_client_method();
    }
    
    int connect(char* hostname, int port, int timeout=1000)
    {
        int rc = -1;
        
        /* Initialize CyaSSL Context */
        if ( (ctx = CyaSSL_CTX_new(method)) == NULL)
        {
            WARN("unable to get ctx");
            goto exit;
        }
        CyaSSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
        CyaSSL_SetIORecv(ctx, SocketReceive); 
        CyaSSL_SetIOSend(ctx, SocketSend);
        
        mysock.set_blocking(false, timeout);    
        if ( (rc = mysock.connect(hostname, port)) != 0)
            goto exit;
        
        if ( (ssl = CyaSSL_new(ctx)) == NULL)
        {
            ERROR("unable to get SSL object");
            rc = -1; goto exit;
        }   
        CyaSSL_set_using_nonblock(ssl, 1);
        if ( (rc = CyaSSL_connect(ssl)) != SSL_SUCCESS)
        {    
            rc = CyaSSL_get_error(ssl, 0);
            WARN("err = %d, %s\n", rc, CyaSSL_ERR_error_string(rc, "\n"));
            WARN("SSL Connection Error\n");
            rc = -1;
        }
        else
        {
            LOG("SSL Connected\n") ;
            rc = 0;
        }
    exit:
        return rc;
    }

    int read(unsigned char* buffer, int len, int timeout)
    {
        int rc = 0;
                
        mysock.set_blocking(false, timeout);  
        rc = CyaSSL_read(ssl, buffer, len);
        DEBUG("called CyaSSL_read len %d rc %d\n", len, rc);
        return rc;
    }
    
    int write(unsigned char* buffer, int len, int timeout)
    {
        int rc = 0;
        mysock.set_blocking(false, timeout);  
        rc = CyaSSL_write(ssl, buffer, len);
        DEBUG("called CyaSSL_write len %d rc %d\n", len, rc);
        return rc;
    }
    
    int disconnect()
    {
        CyaSSL_free(ssl);
        int rc = mysock.close();
 
        CyaSSL_CTX_free(ctx) ;
        eth.disconnect();
        return rc;
    }
    
    EthernetInterface& getEth()
    {
        return eth;
    }
    
private:

    EthernetInterface eth;

    CYASSL_METHOD*  method;
    CYASSL_CTX*     ctx;
    CYASSL*         ssl;
    
};

#endif

To use it, you need to create an instance of MQTTSSL, and pass the class as a template parameter when you create an MQTT client instance.


MQTTSSL ipstack;
MQTT::Client client(ipstack);

Now follows the sort of function you might use to connect. You have to make the network connection first, before calling the MQTT connect.


int connect(MQTT::Client* client, MQTTSSL* ipstack)
{   
    const char* iot_ibm = ".messaging.internetofthings.ibmcloud.com";
    
    // Network connect first
    char hostname[strlen(org) + strlen(iot_ibm) + 1];
    sprintf(hostname, "%s%s", org, iot_ibm);
    int rc = ipstack->connect(hostname, IBM_IOT_PORT);
    if (rc != 0)
        return rc;
     
    // Construct clientId - d:org:type:id
    char clientId[strlen(org) + strlen(type) + strlen(id) + 5];
    sprintf(clientId, "d:%s:%s:%s", org, type, id);
    
    // Now MQTT Connect
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    data.clientID.cstring = clientId;    
    if ( (rc = client->connect(&data)) == 0) 
        displayMessage("Connected");
    return rc;
}

If this is successful, you can now make other MQTT calls, like subscribe and publish.


if ( (rc = client.subscribe("iot-2/cmd/+/fmt/json", MQTT::QOS1, messageArrived)) != 0)
           WARN("rc from MQTT subscribe is %d\n", rc); 

MQTT::Message message;
char* pubTopic = "iot-2/evt/status/fmt/json";
            
char buf[250];
sprintf(buf,
     "{\"d\":{\"myName\":\"IoT mbed\",\"accelX\":%0.4f,\"accelY\":%0.4f,\"accelZ\":%0.4f,\"temp\":%0.4f,\"joystick\":\"%s\",\"potentiometer1\":%0.4f,\"potentiometer2\":%0.4f}}",
            MMA.x(), MMA.y(), MMA.z(), sensor.temp(), joystickPos, ain1.read(), ain2.read());
message.qos = MQTT::QOS0;
message.retained = false;
message.dup = false;
message.payload = (void*)buf;
message.payloadlen = strlen(buf);
client.publish(pubTopic, &message);

I will write this up more fully soon. With any luck, this has also given you an idea of what needs to be done to port the client to a different network API.

Paho Embedded C++ MQTT Client on mbed and for Arduino

I’ve written previously about the embedded client libraries in Paho. There’s
MQTTPacket, and then the start of the design for the higher level APIs.

I started the first C++ API in ARM mbed as IBM and ARM were working together on an Internet of Things (IoT) kit. The ARM mbed online tools make it very easy to build and load applications onto an mbed-enabled micro controller. Just download the built application, drag to the USB-connected filesystem, then reset the micro controller. Easy as that!

To fit into mbed API conventions, the API:

  • is written in C++
  • uses templates
  • does not use the STL (uses too much resources)
  • is blocking, for ease of use

and uses no dynamic memory allocations. It’s called MQTTClient, and you can read the API description here.

The definitive source for the library is still in Paho, but both MQTTPacket and MQTTClient are also available directly in the mbed online environment. You can find them in the code for the MQTT team along with a sample application HelloMQTT, which uses the mbed Ethernet library.

If you want to use these APIs to connect to the IBM IoT service with MQTT, you can find applications that allow you to do just that in the IBM IoT team code library.

It’s easy to port the MQTTClient library to other networking libraries and other operating systems. So far, we’ve done that for Linux and Arduino.  The Linux port makes it easy to build and test the code in the normal Eclipse build environments.  The Arduino port was started to see if it would work.  It does, and the resulting API is actually quite close to the Arduino conventions too.

The prebuilt Arduino client library is available here. After downloading it, in the Arduino IDE use Sketch -> Import Library… -> Add Library… selecting the downloaded zip file. A sample sketch demonstrating the client is included in the download. I intend to add an entry on the Arduino libraries page sometime soon.

We’ll make ports for other operating systems available as we create them.  But if anyone gets around to a port before we do, we’d love that to be contributed to Paho.  Operating systems such as Contiki, TinyOS, FreeRTOS and RIOT.  If you have a favourite embedded OS that I’ve not mentioned, please suggest it.

Porting the Paho synchronous embedded C++ client

The MQTT library on mbed.org also now exists in Eclipse Paho. I intend Paho to be the master copy.

There are two APIs in this library, the largely complete synchronous API in MQTTClient.h, and the unfinished asynchronous API in MQTTAsync.h. In this post I’ll be talking about the synchronous API. I have discussed the major design points previous posts, now I’ll describe how to use the API. The documentation page on mbed can be found here.

The API implementation is intended to avoid system calls to allow it to be easily ported. Its function is limited purely to MQTT rather than networking setup for instance. This does mean that there is some work to use the API on a new OS, but we plan to collect examples in Paho for as many OSes as we can — contributions welcome!

The API is defined as a class template with these parameters:


template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = 100, int MAX_MESSAGE_HANDLERS = 5>

There are two class parameters, Network and Timer which are needed to provide platform specific implementations of sending and receiving data from the network, and timing, respectively. The third and fourth parameters are limits on the maximum MQTT packet size to be handled and the number of message handling callbacks. For each subscription, a message handling callback is needed, so you can think of this second limit as the maximum number of subscriptions you can have active at one time. Increasing these limits will increase the storage used when allocating the MQTT client object.

The file mbed.h contains an mbed specific implmentation of the Timer class. The file MQTTSocket.h contains an mbed specific network implementation for the Ethernet interface. The only requirements for the network class that you need to supply are the two methods:


int read(unsigned char* buffer, int len, int timeout);
int write(unsigned char* buffer, int len, int timeout);

Where

buffer
is the location to write data to, or read data from
len

is the number of bytes of that datatimeoutis the maximum number of milliseconds for the operation to complete.

The timer class counts down from a value to zero. I chose this way because it is always used for timeouts, so it checks to see whether the time has expired:


bool expired();                 // has the time expired?
void countdown_ms(int ms);      // start the timer for ms milliseconds
void countdown(int seconds);    // start the timer in seconds
int left_ms();                  // return the time left in milliseconds

Looking at the mbed example HelloMQTT, you see we have to connect the network interface before calling the MQTT connect:


MQTTEthernet ipstack = MQTTEthernet();
              
MQTT::Client<MQTTEthernet, Countdown> client = MQTT::Client<MQTTEthernet, Countdown>(ipstack);

int rc = ipstack.connect("m2m.eclipse.org", 1883);
if (rc != 0)
   lcd.printf("rc from TCP connect is %d\n", rc);
 
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;       
data.MQTTVersion = 3;
data.clientID.cstring = "mbed-sample";
data.username.cstring = "testuser";
data.password.cstring = "testpassword";
if ( (rc = client.connect(&data)) != 0)
   lcd.printf("rc from MQTT connect is %d\n", rc);

and then we can call subscribe, publish, unsubscribe and disconnect methods on the client in the usual sort of way for MQTT APIs.

Examples of how to use this API on Linux is in this
directory on Paho, in the hello.cpp and stdoutsub.cpp programs. The API is already running on Arduino, for which we’ll add an example, and for other platforms and operating systems later.

Portable MQTT C/C++ clients for resource-limited platforms

When I started writing the Paho MQTT C client as long ago as 2008, it looked to me like the world of embedded operating systems was converging on Linux and Windows variants. This was based on my (limited) experience with embedded systems such as the Arcom Viper and various WinCE hand held devices. Consequently I wrote the Paho MQTT C client using Posix and Windows system APIs.

Now that decision doesn’t look so appropriate. There is a proliferation of cheap and small embedded platforms – some can run Linux, some can’t. In my last two posts I starting describing a new, more portable MQTT C client at its simplest. That first layer simply takes C data and serializes it into a buffer ready for sending across the network using any network APIs you choose:


len = MQTTSerialize_connect(buf, buflen, &data); /* 1 */
len += MQTTSerialize_publish(buf + len, buflen - len, 0, 0, 0, 0, topicString, payload, payloadlen); /* 2 */
rc = write(mysock, buf, len);

– there are corresponding API calls for deserializing buffers received from the network into C data structures.

API for mbed

This works fine, but I was also asked to write an API that would work well in the mbed world (The mbed development platform is the fastest way to create products based on ARM microcontrollers). C++ is the standard language here, but non-Posix networking APIs. What I’ve come up with so far (after some discussion) is an API that has an outline like this:


template<class Network, class Timer, class Thread, class Mutex>
class Client
{
public:    
    /* connectionLost and default messageArrived callbacks to be added */
    struct Result
    {
        /* success or failure result data */
        Client<Network, Timer, Thread, Mutex>* client;
        int connack_rc;
    };
    typedef void (*resultHandler)(Result*); 

    Client(Network* network, const Limits limits = Limits()); 

    int connect(MQTTPacket_connectData* options = 0, resultHandler fn = 0);     
    int publish(const char* topic, Message* message, resultHandler rh = 0);
    int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh, resultHandler rh = 0);
    int unsubscribe(const char* topicFilter, resultHandler rh = 0);
    int disconnect(int timeout, resultHandler rh = 0);

    void yield(int timeout);
}

This API will look familiar to anyone who is acquainted with the Paho MQTT APIs for C and Java. The resultHandler parameters are optional callback functions which give the outcome of the standard calls. If these are not set, then the MQTT operation calls will block until complete, using no background thread. If resultHandler callback functions are set, then a background thread will be started and will handle MQTT networking while the application can continue with other business.

I am considering splitting the API into two completely separate units, one blocking and single-threaded, the second non-blocking and multi-threaded. This would make the intended use clearer, and reduce the size of both APIs (resultHandlers would be unnecessary in the single-threaded API for instance). Other points to note:

  • C++ Standard Template Library (STL) not used – too heavyweight
  • messageHandler callback per subscription (see discussion) – there will also be a default messageHandler
  • system APIs for networking, timing and threading passed in as template parameters (for the synchronous API threading and mutex classes are not needed)
  • heap memory allocation minimized (I hope to avoid it altogether)
  • memory use limited at object construction time by the limits parameter (see below)
  • network connection handled outside of this API
  • persistence, if/when added, as another template parameter

The limits parameter looks like this:

typedef struct limits
{
    int MAX_MQTT_PACKET_SIZE;       // two buffers, for sending and receiving, will be allocated of this size
    int MAX_MESSAGE_HANDLERS;       // each subscription requires a message handler
    int MAX_CONCURRENT_OPERATIONS;  // each command which runs concurrently can have a result handler, when we are in multi-threaded mode
    int command_timeout;            // how long to wait for any command to complete, in seconds
        
    limits()
    {
        MAX_MQTT_PACKET_SIZE = 100;
        MAX_MESSAGE_HANDLERS = 5;
        MAX_CONCURRENT_OPERATIONS = 1; // 1 indicates single-threaded mode - set to >1 for multithreaded mode
        command_timeout = 30;
    }
} Limits;

Here are example programs to use this API on mbed and Linux. These demonstrate the portability of the API – it’s not just for mbed, or for one networking API.

I’ve not finished this API yet, and it will be pulled back into Paho.

Three Layers

I’ve been thinking that a third layer supplying offline message buffering would complete the picture. (I’m hoping that I will never have to write another MQTT C client API after this effort 🙂 ) This simply means the ability to send and receive messages from the API when there is no connection to the MQTT server. This feature is frequently asked for, but does increase the internal complexity of an API and thus its memory requirements. Some of the questions that arise when considering a buffered client API are:

  • what if messages are “sent” by the application, but the client library never connects successfully?
  • how many messages should we buffer? what size?
  • should the messages be sent/received when the application is not running?
  • when and how often should the API try to (re-)connect to the server?
  • how should buffered messages be stored? for how long?

There are reasonable answers to these questions which will make such an API suitable for embedded platforms, and certainly for a portable C/C++ MQTT API. The family of MQTT C/C++ APIs for portability/embedded systems would then look like this:

  1. serialization/deserialization API – currently C only
  2. blocking and non-blocking unbuffered MQTT client in a style similar to current C and Java APIs, but portable across system libraries – currently C++ only (on top of library 1)
  3. buffered MQTT API, portable across system libraries (on top of library 2).

Whether each of these layers should be in both C and C++, I’m not sure. I think it depends on the demand. I am relishing using C++ again (without STL) and am reluctant to go back to C for layers 2 and 3 unless there’s a real need.

Comments or suggestions on any of this are welcome.

Receiving messages with the Paho embedded C client

In my previous post I described the motivation behind writing a new MQTT C client, and the first sample – how to publish a message with a small application program.

Now I want to go onto the second sample pub0sub1.c. This is meant to mimic the sort of program that subscribes to a command topic, and publishes a value from sensor at a regular interval – in this case 1 second.

The first part of the program is similar to the first, sending a connect packet. But now because we want to subscription, we should wait for the connack packet to be sent back from the server. First we set the timeout on the socket, so when we read from it, if no data arrives, it will return rather than block. Because we want to send a sensor reading every 1 second, we set the timeout to 1 second.

struct timeval tv;
tv.tv_sec = 1;  
tv.tv_usec = 0;  
setsockopt(mysock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));

A helper function, MQTTPacket_read is used to get the data for one packet into a supplied buffer buf. The buffer length is specified in buflen – to avoid overruns. MQTT packet lengths are defined in a specific variable-length field, which this function reads, and then attempts to read the rest of that packet.

MQTTPacket_read(buf, buflen, getdata);

The third parameter is a pointer to helper function which is called to read the data from whatever source is required. In this case, a socket:

int mysock = 0;

int getdata(char* buf, size_t count)
{
	return recv(mysock, buf, (size_t)count, 0);
}

The received data, of length count, is placed into the buffer, and the length of received data returned. MQTTPacket_read will return the code of the packet type received, or -1 if there was no data. So we check that the packet was a CONNACK, and then parse it with a call to MQTTDeserialize_connack. This allows us to check the connack return code.

if (MQTTPacket_read(buf, buflen, getdata) == CONNACK)
{
	int connack_rc;

	if (MQTTDeserialize_connack(&connack_rc, buf, buflen) != 1 || connack_rc != 0)
	{
		printf("Unable to connect, return code %d\n", connack_rc);
		goto exit;
	}
}
else
	goto exit;

Next we follow a similar process with sending a subscribe packet and waiting for the suback.

topicString.cstring = "substopic";
len = MQTTSerialize_subscribe(buf, buflen, 0, msgid, 1, &topicString, &req_qos);

rc = write(mysock, buf, len);
if (MQTTPacket_read(buf, buflen, getdata) == SUBACK) 	/* wait for suback */
{
	int submsgid;
	int subcount;
	int granted_qos;

	rc = MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, buf, buflen);
	if (granted_qos != 0)
	{
		printf("granted qos != 0, %d\n", granted_qos);
		goto exit;
	}
}
else
	goto exit;

Finally we create the main loop. It calls MQTTPacket_read to wait for incoming publish packets. The read times out after 1 second if no packet arrives. If a publish does arrive, we parse it into variables we can use, like payload_in.

while (!toStop)
{
	if (MQTTPacket_read(buf, buflen, getdata) == PUBLISH)
	{
		int dup, qos, retained, msgid, payloadlen_in, rc;
		char* payload_in;
		MQTTString receivedTopic;

		rc = MQTTDeserialize_publish(&dup, &qos, &retained, &msgid, &receivedTopic,
					&payload_in, &payloadlen_in, buf, buflen);
		printf("message arrived %.*s\n", payloadlen_in, payload_in);
	}

	printf("publishing reading\n");
	len = MQTTSerialize_publish(buf, buflen, 0, 0, 0, 0, topicString, payload, payloadlen);
	rc = write(mysock, buf, len);
}

Whether we receive an incoming message or not, we do send a reading from the sensor every time.

The structure MQTTString has two parts – a length delimited string and a C style null-delimited string.

typedef struct
{
	int len;
	char* data;
} MQTTLenString;

typedef struct
{
	char* cstring;
	MQTTLenString lenstring;
} MQTTString;

#define MQTTString_initializer {NULL, {0, NULL}}

MQTT strings are length delimited. To avoid unnecessary copying of received data, by default when a packet is parsed, or deserialized, the string data is not copied into an MQTTString. Instead, the length delimited type has its length set, along with a pointer to the appropriate place in the buffer into which MQTTPacket_read placed the data read from the socket.

Later, if we want to, we can convert the length delimited string into a null-delimited C string (and I will add a helper function for this). This will probably involve allocating some memory, and copying the data so we can add the null character. For efficiency, we were trying to avoid both actions, which is why C-style strings are not created by default.

All the possible error conditions are by no means catered for in this program. Adding that error handling would improve reliability at the cost of size. As it is, whereas the QoS 0 publisher came in at under 5k compiled (64-bit Linux), this program is less than 10k (more than a completely empty program). I’m reasonably happy with that.

These examples are using the standard socket calls. Next step is to try real embedded environments to see how well this MQTT client fits in.

New “Embedded” Paho MQTT C Client

Yesterday I added the first phase of a new C client for “embedded” platforms to the Paho project in a new repository. It might seem odd to start another C client library, but I think the logic is sound.

When I started writing the first version of the Paho C client it was to work with IBM MQTT servers like Microbroker, Message Broker (now IBM Integration Bus Advanced) and my own RSMB. In spite of trying to keep it as lightweight as I could, I had to respond to many requirements without which the library would not be ‘complete’. So it grew. The main priority was to make MQTT programming simple – size was not that important.

The end result is a library which builds to about 80k (if you strip the debugging symbols), and is not easily stripped down if you want something smaller. A later addition was the asynchronous client, in the same repository, which changed the programming model to be entirely non-blocking, but didn’t make the library any smaller.

Even though most devices are getting ever more capable, there are still the smallest, most frugal motes that have limited capabilities. MQTT was intended to work on these devices, which is why the protocol is public – you can write a bespoke client library if you want. The existing client libraries are too big and inflexible, so this is the first stage in attempting to fix that.

I didn’t want to destabilize the existing libraries, so decided to start afresh. These small devices might not use the usual libraries for networking, threads, timers or even memory allocation. So I thought I would start from the bottom up, with a basic lower layer that would omit all network or memory allocation calls. This lower layer merely serializes and deserializes MQTT packet data so that it can be written to or read from the data destination or source. The end goal is a totally asynchronous API – perhaps with an external definition akin to the existing asynchronous API.

The best way to see it in action is to look at some samples. The simplest sample qos0pub.c is to send a QoS 0 MQTT message. It connects, publishes and disconnects all in one go, making it one of the shortest MQTT applications you can write. The core of the program looks like:

MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
int rc = 0;
char buf[200];
MQTTString topicString = MQTTString_initializer;
char* payload = "mypayload";
int payloadlen = strlen(payload);int buflen = sizeof(buf);

data.clientID.cstring = "me";
data.keepAliveInterval = 20;
data.cleansession = 1;
len = MQTTSerialize_connect(buf, buflen, &data); /* 1 */

topicString.cstring = "mytopic";
len += MQTTSerialize_publish(buf + len, buflen - len, 0, 0, 0, 0, topicString, payload, payloadlen); /* 2 */

len += MQTTSerialize_disconnect(buf + len, buflen - len); /* 3 */

rc = Socket_new("127.0.0.1", 1883, &mysock);
rc = write(mysock, buf, len);
rc = close(mysock);

We set up the data we want to connect with, and add that to a buffer (1). Then we add the MQTT publish command to the end of the same buffer (2). Lastly we add the disconnect command to the same buffer (3). The disconnect is not strictly necessary – it’s an act of kindness to the server, for the cost of a few bytes.

We can write this buffer directly to a socket which is connected to an MQTT server, and then close the socket immediately, and we’ve published a message. The entire program comes in at about 5k over and above a completely empty one using RedHat and gcc – which is more like it, although I’d prefer smaller.

I’ll talk about receiving messages in a second post.

MQTT, QoS and Persistence

Over the years, every now and again I get asked “what is the point of supporting QoS 1 or 2 in MQTT without full persistence?” I drew up this table a while back to help answer that question.

First a reminder of MQTT definitions of reliability of QoS:

  • 0 – at most once (fire and forget)
  • 1 – at least once
  • 2 – once only
Error Situation No Persistence Persistence
Communications errors: broken connection from whatever cause (intermittent signals e.g. over wireless, hardware problem, etc) All QoSs apply with any persistence (will hold to their reliability criteria). All QoSs apply with any persistence (will hold to their reliability criteria).
Power failure to remote host machine (bridge connection) All QoSs apply with any persistence (will hold to their reliability criteria). All QoSs apply with any persistence (will hold to their reliability criteria).
Power failure to local host machine All in-progress messages lost. Retained messages and subscriptions lost Shutdown persistence: all state since last restart lost.
Full persistence: full protection for all QoSs, as long as all persistence store write caching is turned off, both in the operating system and in the hardware. Otherwise, data can still be lost
Filesystem corruption All state lost All state lost.
Normal persistence will not help. Raid or other disk mirroring needed
Hard drive failure All state lost All state lost.
Normal persistence will not help. Raid or other disk mirroring needed
Complete system destruction All state lost All state lost.
Normal persistence will not help. Mirroring to another system needed
Destruction of location All state lost All state lost.
Normal persistence will not help. Mirroring to system in remote location needed
End of the world or universe All state lost All state lost.
Normal persistence will not help. Remote location needs to be on another planet or in another universe