MQTT Client Library Encyclopedia – Arduino PubSubClient

Guest post by Nick O’Leary

Arduino PubSubClient
Language C++ / Arduino
Website http://pubsubclient.knolleary.net/
API-Style Blocking
License MIT

Description

The PubSubClient for the Arduino open-source electronics platform has been available since 2009. At the time, Arduino had recently released its first Ethernet Shield and it seemed a natural fit to run use MQTT.

With such a constrained environment, it was important to keep the library as small as possible. This could be achieved by only implementing the features of the protocol that made sense. In particular, it only supports Clean Sessions and does not support QoS 2 messages as there is such limited memory and no standard persistence mechanism.

The Arduino platform defines a standard api for network client libraries to implement. By allowing sketches to pass in any implementation of the API, the PubSubClient is able to support a wide range of Arduino-compatible hardware out of the box.

It has been used in a number of production systems and has recently been updated to support MQTT 3.1.1.

There are other constants defined it that file to control the version of MQTT used and the keepalive time, as well as constants that reflect the different connection states the client can be in.

Features

MQTT 3.1
MQTT 3.1.1 ok
LWT ok
SSL/TLS ok
Automatic Reconnect nok
QoS 0 ok
QoS 1 ok
QoS 2 ok
Authentication ok
Throttling nok

Usage

Installation

The library can be installed into the Arduino IDE using the built-in Library Manager.

Open the Library Manager by selecting `Sketch -> Include Library -> Manage Libraries…`
Search for “PubSubClient”
Click the “Install” button

The latest release can also be downloaded directly from GitHub

Maximum Message Size
As part of minimising its footprint, it limits the size of any MQTT packet it can send or receive to 128 bytes. If you want to send or receive messages larger than this, you must change the value of `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h`. The library allocates this much memory in its internal buffer, which reduces the memory available to the sketch itself.

Connect

The following example assumes you are using the standard Ethernet Shield. For other hardware types, refer to its documentation on how to initialise the appropriate network client.

#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.
byte mac[]    = {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
IPAddress ip(172, 16, 0, 100);
const char* server = "broker.example.com";

EthernetClient ethClient;
PubSubClient mqttClient(ethClient);

void setup()
{
  Ethernet.begin(mac, ip);
  // Allow the hardware to sort itself out
  delay(1500);
  mqttClient.setServer(server, 1883);

  if (mqttClient.connect("myClientID")) {
    // connection succeeded
  } else {
    // connection failed
    // mqttClient.state() will provide more information
    // on why it failed.
}

void loop()
{
  mqttClient.loop();
}

The client connects with a default keepalive timer of 15 seconds. This can be configured by changing the value of MQTT_KEEPALIVE in PubSubClient.h.

If the call to mqttClient.connect returns false, the connection has failed for some reason. A call to `mqttClient.state()` will provide more information. PubSubClient.h defines a number of constants that can be used to determine why the connection failed – for example, whether it was a network issue or the server rejected the connection with a known reason code.

Once connected, the `mqttClient.loop()` function must be called regularly. This allows the client to maintain the connection and check for any incoming messages.

Connect with MQTT 3.1 or MQTT 3.1.1

In order to minimise the size of the library, the choice of MQTT version must be done at compile time. The version is chosen by changing the value of the MQTT_VERSION in PubSubClient.h – it defaults to MQTT 3.1.1:

// MQTT_VERSION : Pick the version
//#define MQTT_VERSION MQTT_VERSION_3_1
#define MQTT_VERSION MQTT_VERSION_3_1_1

Connect with LWT

byte willQoS = 0;
const char* willTopic = "willTopic";
const char* willMessage = "My Will Message";
boolean willRetain = false;

boolean rc = mqttClient.connect("myClientID", willTopic, willQoS, willRetain, willMessage);

Connect with Username / Password

boolean rc = mqttClient.connect("myClientID", "myUsername", "myPassword");

Publish

The client only supports publishing at QoS 0:

boolean rc = mqttClient.publish("myTopic", "myMessage");

The function will return true if the message was successfully published to the server.
It will return false if:

  • the client was not currently connected to the server, or
  • the resulting MQTT packet to exceeded the libraries maximum packet size

Publish a retained Message

char* message = "Hello World";
int length = strlen(message);
boolean retained = true;
mqttClient.publish("myTopic",(byte*)message,length,retained);

Subscribe

In order to subscribe to messages, a callback function must be set on the client. This is done using the setCallback function:

void callback(char* topic, byte* payload, unsigned int length)
{
    // handle received message
}

mqttClient.setCallback(callback);

Then, once the client is connected, it can subscribe to a topic:

// Defaults to QoS 0 subscription:
boolean rc = mqttClient.subscribe("myTopic");

// Specify the QoS to subscribe at. Only supports QoS 0 or 1:
boolean rc = mqttClient.subscribe("myOtherTopic",1);

The call to subscribe will return true if the subscribe packet was successfully sent to the server – it does not block until the acknowledgment is received from the server.

It will return false if:

  • the client was not currently connected to the server,
  • an invalid qos was specified, or
  • the topic was too long and caused the MQTT packet to exceed the libraries maximum packet size

If you want to publish a message from within the message callback function, it is necessary to make a copy of the topic and payload values as the client uses the same internal buffer for inbound and outbound messages:

void callback(char* topic, byte* payload, unsigned int length) {
  // Allocate the correct amount of memory for the payload copy
  byte* p = (byte*)malloc(length);
  // Copy the payload to the new buffer
  memcpy(p,payload,length);
  // Republish the received message
  mqttClient.publish("outTopic", p, length);
  // Free the memory
  free(p);
}

Unsubscribe

boolean rc = mqttClient.unsubscribe("myTopic");

As with the call to subscribe, this function will return true if the unsubscribe packet was successfully sent to the server – it does not block until the acknowledgment is received from the server.

It will return false if:

  • the client was not currently connected to the server, or
  • the topic was too long and caused the MQTT packet to exceed the libraries maximum packet size

Disconnect

mqttClient.disconnect();

This will disconnect from the broker and close the network connection. The client can be reconnected with a subsequent call to `mqttClient.connect()`

Full example application

The library provides a number of examples when added to the Arduino IDE. They can be accessed by selecting “File” -> “Examples” -> “PubSubClient”

The following is a basic example that connects to a broker, publishes a message and then subscribes to a given topic. Whenever a message is received it is printed to the Serial console.

#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.
byte mac[]    = {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
IPAddress ip(172, 16, 0, 100);
IPAddress server(172, 16, 0, 2);

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

EthernetClient ethClient;
PubSubClient mqttClient(ethClient);

void reconnect() {
  // Loop until we're reconnected
  while (!mqttClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (mqttClient.connect("arduinoClient")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      mqttClient.publish("outTopic","hello world");
      // ... and resubscribe
      mqttClient.subscribe("inTopic");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup()
{
  Serial.begin(57600);

  mqttClient.setServer(server, 1883);
  mqttClient.setCallback(callback);

  Ethernet.begin(mac, ip);
  // Allow the hardware to sort itself out
  delay(1500);
}

void loop()
{
  if (!mqttClient.connected()) {
    reconnect();
  }
  mqttClient.loop();
}

Author Information

Nick O’Leary | IBM
Nick is an Emerging Technology Specialist at IBM where he gets to do interesting things with interesting technologies. He has been involved with MQTT for 10 years, working on both client and server implementations, as well as production solutions. He is a member of the OASIS MQTT Technical Committee.
  Website

14 comments

  1. jaydeep patel says:

    Hello Nick O’Leary,

    thia is jaydeep.. i am developing one embedded system using esp8266 and rabbitMQ… sometime i get error rc=-1 sometime i get rc=-2 and sometime rc=-4…. so i have to know what i should have to configer in my program? like server, username, password… etc…..

  2. Juan Carlos Gamboa says:

    Thanks for this excellent post

  3. Omkar says:

    is there any way to publish message with QoS = 2 if possible please reply..
    Thank You

    1. Hi Omkar,

      unfortunately the Arduino MQTT Client does not support QoS 2.

      Regards,
      Florian from the HiveMQ Team

  4. beppe says:

    I used the example with esp8266 and everything works perfectly.
    There is however a problem that I could not solve.
    At the start of the program I need to know the status of the topic I subscribed; unfortunately I get a callback only when the topic changes its value
    It is possible to request the sending of the status of a topic?
    Regards,

    1. Hi beppe,

      glad to see, you’re taking an interest in MQTT.
      That’s a good question and there are two ways to achieve, what you are looking for.

      You can either use QoS greater than 0 and set your cleanSession flag to false.
      See our chapter on persistent session and message queueing .

      Or if you don’t want persistent sessions, the behavior you are describing is being covered with the retained message functionality.

      Hope that helps,
      Florian from the HiveMQ Team.

  5. Simon says:

    It is great to see this example. But I’m wondering if it is at all possible to give an example of publishing a JSON message to the broker?

    1. Hallo Simon,

      MQTT allows for any payload content. So publishing a JSON message should not cause any difficulties. Just write the JSON into the payload.

      Hope that was helpful.
      Florian, from the HiveMQ Team.

  6. Miguel says:

    I want to publish sensor data to HiveMQ server using MQTT protocol via GPRS/GSM modem, it’s possible ? What client do you recommend?. I googled MQTT for Arduino, MQTT Arduino available only for Ethernet shield or Wifi shield.

  7. Umair says:

    how to generate “myClientID” ?

    1. Hi Umair,

      You can choose any client identifier, that is conform with the MQTT specification.

      The Server MUST allow ClientIds which are between 1 and 23 UTF-8 encoded bytes in length, and that contain only the characters
      “0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ”

  8. Victor says:

    Have any examples for using MQTT with arduino and GPRS network modem?

  9. rogier says:

    Great tutorial Nick, thanks!
    Only question i have is how to control an output pin of the arduino.
    Because a simple if topic = xxxx is not working with me.
    Do you have a simple example how to do this?

    Thanks!

  10. Chris says:

    Hi,
    I have been using your example with an ethernet shield and arduino, and all works fine until I connect wifi sensors. It seems that when a wifi sensor reboots and access the network, therer is a “collision”of some kind and ethernet client disconects from mosquitto client, and reconnects. In that time, data that i sbeing published is lost.
    any ideas

Leave a Reply

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