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.

8 Replies to “Portable MQTT C/C++ clients for resource-limited platforms”

  1. At the moment it seems to me, that wildcarded topics are not supported.

    template int MQTT::Client::deliverMessage(MQTTString* topic, Message* message)
    {
    int rc = -1;

    // we have to find the right message handler – indexed by topic
    for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i)
    {
    if (messageHandlers[i].topic != 0 && MQTTPacket_equals(topic, (char*)messageHandlers[i].topic))
    {
    messageHandlers[i].fp(message);
    rc = 0;
    break;
    }
    }
    if (rc == -1)
    defaultMessageHandler(message);

    return rc;
    }

    The incoming topic and the subscribed topic are tested on equality within this passage:
    MQTTPacket_equals(topic, (char*)messageHandlers[i].topic)
    It does not consider wildcards like + or # in the subscription topic but only checks on exact equality. Therefore a subscribed topic with containing wildcards is ignored.

    1. Messages received for wildcard topic subscriptions are sent to the default message handler for the moment, while I think about how best to handle them. I am trying to keep the API as small as I can, and I have highlighted the penalties of supporting per-subscription message handlers for wildcard topic filters. To send messages to matching wildcard subscriptions will require topic-matching code, and could result in message “duplication” if more than one wildcard subscription matches. Whether or not you consider this to be “duplication” is probably a matter of opinion. I tend to consider it duplication because in implementing MQTT servers I have always sent a single message out to a subscriber regardless of the number of matching subscriptions (because that was what Andy Stanford-Clark wanted :-)).

      I would like to hear what you think about this.

      1. This behavior astonished me. If a mqtt-client subscribes to a topic and adds an handler to this topic, I’d expect this handler be called after a message published to this topic is received. Otherwise the default handler has to know about how to handle all the wildcarded subscriptions. I’m new to mqtt, and perhaps it is the correct way doing this, but it astonished me.

        1. That was one reason why initially I was not so keen on a handler per subscription. That model hides the fact that the MQTT protocol tells you which topic a message was published on, but not which subscription caused that message to be delivered. Of course I have worked with MQTT for many years so I know this. Thanks for your input. I’ll add the code to call the handler for wildcard subscriptions. This will result in multiple handlers being called when multiple subscriptions match the topic for a message. If the server sends out multiple messages for matching overlapping subscriptions (as allowed by the MQTT standard, an option which I think is wrong), this will result in the duplication of messages.

  2. Thanks for the very light weight MQTT serialization/deserialization API in C. We are considering running on a small PIC non-RTOS so the concept of serializing/de-serializing data works well. When I loaded “test1.c” to make sure that I could build and run the library, I had to modify MQTTPacket_checkVersion() to add a check for version 4 as follows:

    else if (version == 4 && !memcmp(protocol->lenstring.data, “MQTT”,
    min(4, protocol->lenstring.len)))
    rc = 1;

    After adding the above lines all tests passed.

    Thanks…

    1. Thanks for reporting this. This is a bug – the previous line should have have the check. I will fix.

  3. hi, i am learning MQTT in the recent days, but still get confused about it. i don’t know if it can be regard as a network server and it can also manage the data inside.( by using Mysql ?) . if that so, how can i use it in my program ?
    i’ve installed the MQTT server on windows, and launched it already, i could use a little .c file to check if the port( 1883) could be connected, and the answer was yes,but i didn’t know how to connect it with APIs, i knew the idea about pub/sub(similar to MSSQL), but i didn’t know the detail steps to use it. could you please tell me how can i use it in c++( vs or it’s variations)

    1. Hello. MQTT is a protocol designed to make it easy to send messages between programs, regardless of where they are running. That’s all it does. Because the protocol is open, you can write your own interface to MQTT if you like. But it’s easier to use an API already written: a good place to start is the Paho project. The software page on http://mqtt.org lists more options for client libraries and servers.

Leave a Reply

Your email address will not be published. Required fields are marked *