AdafruitMQTT

The Adafruit WICED Feather API includes an internal MQTT client that allows you perform basic MQTT operations directly with any MQTT broker.

AdafruitMQTT inherits from AdafruitTCP and also has access to all of the functions defined in the parent class.

Note: You are also free to use an external Client based MQTT library (for example Adafruit_MQTT_Library) if you prefer or need something fully under your control. AdafruitMQTT is provided for convenience sake, and to avoid external dependencies, but isn't the only option at your disposal.

Constructors

Some MQTT brokers require a username and password to connect.  If necessary, the two values should be provided in the constructor when declaring an instance of AdafruitMQTT.

If no username and password are required, simply use the default empty constructor.

AdafruitMQTT()
AdafruitMQTT(const char* username, const char* password)

Functions

bool connected  ( void );

bool connect    ( IPAddress ip, 
                  uint16_t    port          = 1883, 
                  bool        cleanSession  = true, 
                  uint16_t    keepalive_sec = MQTT_KEEPALIVE_DEFAULT);
bool connect    ( const char* host, 
                  uint16_t    port          = 1883,
                  bool        cleanSession  = true,
                  uint16_t    keepalive_sec = MQTT_KEEPALIVE_DEFAULT);

bool connectSSL ( IPAddress   ip, 
                  uint16_t    port          = 8883,
                  bool        cleanSession  = true, 
                  uint16_t    keepalive_sec = MQTT_KEEPALIVE_DEFAULT);
bool connectSSL ( const char* host, 
                  uint16_t    port          = 8883, 
                  bool        cleanSession  = true, 
                  uint16_t    keepalive_sec = MQTT_KEEPALIVE_DEFAULT);

bool disconnect ( void );

bool publish    ( UTF8String  topic, 
                  UTF8String  message, 
                  uint8_t     qos           = MQTT_QOS_AT_MOST_ONCE,
                  bool        retained      = false );

bool subscribe  ( const char* topicFilter, 
                  uint8_t     qos, 
                  messageHandler mh);

bool unsubscribe( const char* topicFilter );

void will       ( const char* topic,
                  UTF8String  message, 
                  uint8_t     qos           = MQTT_QOS_AT_MOST_ONCE,
                  uint8_t     retained      = 0);

void clientID   ( const char* client)
  
void setDisconnectCallback  ( void (*fp) (void) )

Connection Management

AdafruitMQTT can connect to an MQTT broker using both 'open' (unencrypted) or 'secure' (TLS/SSL encrypted) connections.

bool connected(void)

Indicates if we are currently connected to the MQTT broker or not.

Parameters: None

Returns: 'True' (1) if we are connected to the MQTT broker, otherwise 'false' (0).

bool connect ( IPAddress ip, uint16_t port = 1883, bool cleanSession = true, uint16_t keepalive_sec = MQTT_KEEPALIVE_DEFAULT);

Establishes an open connection with the specified MQTT broker.

Parameters:

  • ip: The IP address for the MQTT broker
  • port: The port to use (default = 1883)
  • cleanSession: Indicates whether the client and broker should remember 'state' across restarts and reconnects (based on the 'Client ID' value set in the constructor):
    • If set to false (0) both the client and server will maintain state across restarts of the client, the server and the connection. As state is maintained:
      • Message delivery will be reliable meeting the specified QOS even if the client, server or connection are restarted.
      • The server will treat a subscription as durable.
    • If set to true (1) the client and server will not maintain state across restarts of the client, the server or the connection. This means:
      • Message delivery to the specified QOS cannot be maintained if the client, server or connection are restarted
      • The server will treat a subscription as non-durable
  • keepalive_sec: This value defines the maximum interval (in seconds) between messages being sent or received. Setting a value here ensures that at least one message is sent between the client and the broker within every 'keep alive' period. If no data was sent within 'keepalive_sec' seconds, the Client will send a simple ping to the broker to keep the connection alive. Setting this value to '0' disables the keep alive feature. The default value is 60 seconds.

Returns: 'True' (1) if the connection was successful, otherwise 'false' (0).

bool connect ( const char* host, uint16_t port = 1883, bool cleanSession = true, uint16_t keepalive_sec = MQTT_KEEPALIVE_DEFAULT);

Establishes an open connection with the specified MQTT broker.

Parameters:

  • host: The domain name for the MQTT broker
  • port: The port to use (default = 1883)
  • cleanSession: Indicates whether the client and broker should remember 'state' across restarts and reconnects (based on the 'Client ID' value set in the constructor):
    • If set to false (0) both the client and server will maintain state across restarts of the client, the server and the connection. As state is maintained:
      • Message delivery will be reliable meeting the specified QOS even if the client, server or connection are restarted.
      • The server will treat a subscription as durable.
    • If set to true (1) the client and server will not maintain state across restarts of the client, the server or the connection. This means:
      • Message delivery to the specified QOS cannot be maintained if the client, server or connection are restarted
      • The server will treat a subscription as non-durable
  • keepalive_sec: This value defines the maximum interval (in seconds) between messages being sent or received. Setting a value here ensures that at least one message is sent between the client and the broker within every 'keep alive' period. If no data was sent within 'keepalive_sec' seconds, the Client will send a simple ping to the broker to keep the connection alive. Setting this value to '0' disables the keep alive feature. The default value is 60 seconds.

Returns: 'True' (1) if the connection was successful, otherwise 'false' (0).

bool connectSSL ( IPAddress ip, uint16_t port = 8883, bool cleanSession = true, uint16_t keepalive_sec = MQTT_KEEPALIVE_DEFAULT)

Establishes a secure connection with the specified MQTT broker.

Parameters:

  • ip: The IP address of the MQTT broker
  • port: The port to use (default = 8883)
  • cleanSession: Indicates whether the client and broker should remember 'state' across restarts and reconnects (based on the 'Client ID' value set in the constructor):
    • If set to false (0) both the client and server will maintain state across restarts of the client, the server and the connection. As state is maintained:
      • Message delivery will be reliable meeting the specified QOS even if the client, server or connection are restarted.
      • The server will treat a subscription as durable.
    • If set to true (1) the client and server will not maintain state across restarts of the client, the server or the connection. This means:
      • Message delivery to the specified QOS cannot be maintained if the client, server or connection are restarted
      • The server will treat a subscription as non-durable
  • keepalive_sec: This value defines the maximum interval (in seconds) between messages being sent or received. Setting a value here ensures that at least one message is sent between the client and the broker within every 'keep alive' period. If no data was sent within 'keepalive_sec' seconds, the Client will send a simple ping to the broker to keep the connection alive. Setting this value to '0' disables the keep alive feature. The default value is 60 seconds.

Returns: 'True' (1) if the connection was successful, otherwise 'false' (0).

bool connectSSL ( const char* host, uint16_t port = 8883, bool cleanSession = true, uint16_t keepalive_sec = MQTT_KEEPALIVE_DEFAULT)

Establishes a secure connection with the specified MQTT broker.

Parameters:

  • host: The domain name of the MQTT broker
  • port: The port to use (default = 8883)
  • cleanSession: Indicates whether the client and broker should remember 'state' across restarts and reconnects (based on the 'Client ID' value set in the constructor):
    • If set to false (0) both the client and server will maintain state across restarts of the client, the server and the connection. As state is maintained:
      • Message delivery will be reliable meeting the specified QOS even if the client, server or connection are restarted.
      • The server will treat a subscription as durable.
    • If set to true (1) the client and server will not maintain state across restarts of the client, the server or the connection. This means:
      • Message delivery to the specified QOS cannot be maintained if the client, server or connection are restarted
      • The server will treat a subscription as non-durable
  • keepalive_sec: This value defines the maximum interval (in seconds) between messages being sent or received. Setting a value here ensures that at least one message is sent between the client and the broker within every 'keep alive' period. If no data was sent within 'keepalive_sec' seconds, the Client will send a simple ping to the broker to keep the connection alive. Setting this value to '0' disables the keep alive feature. The default value is 60 seconds.

Returns: 'True' (1) if the connection was successful, otherwise 'false' (0).

bool disconnect (void)

Disconnects from the remote MQTT broker.

Parameters: None

Returns: 'True' (1) if the disconnect was successful, otherwise 'false' (0) if an error occured (check .errno.errstr, etc.).

Messaging

The following functions allow you to publish, subscribe and unsubscribe to MQTT topics:

bool publish ( UTF8String topic, UTF8String message, uint8_t qos = MQTT_QOS_AT_MOST_ONCE, bool retained = false );

Published the supplied 'message' to the specified 'topic'.

Parameters:

  • topic: The topic where the data should be published (ex: "adafruit/data" or "home/rooms/bedroom/temp").  UTF8String is used to make it easier to work with UTF8 data.
  • message: The string of data to write to the specified 'topic'. UTF8String is used to make it easier to work with UTF8 data.
  • qos: The quality of service level (see the MQTT spec for details).  Default = 'At Most Once', meaning the message tries to send once but isn't persisted if the send fails.  Possible values are:
    • MQTT_QOS_AT_MOST_ONCE
    • MQTT_QOS_AT_LEAST_ONCE
    • MQTT_QOS_EXACTLY_ONCE
  • retained: Whether or not the published message should be 'retained' by the MQTT broker. Sending a message with the retained bool set to 'false' (0) will clear any previously retained message from the broker. The default value is false.

Returns: 'True' (1) if the publish was successful, otherwise 'false' (0) if an error occured (check .errno.errstr, etc.).

bool subscribe ( const char* topicFilter, uint8_t qos, messageHandler mh);

Subscribes to a specific topic, using a callback mechanism to alert you when new data is available on the specific topicFilter.

Parameters:

  • topicFilter: The topic name or topic 'filter' to subscribe to.  This can be either a single topic ("home/kitchen/fridge/temp") or make use of a standard MQTT wildcard like "home/+", which will subscribe to changes to any topic above the 'home/' level.
  • qos: A subscribing client can set the maximum quality of service a broker uses to send messages that match the client subscriptions. The QoS of a message forwarded to a subscriber thus might be different to the QoS given to the message by the original publisher. The lower of the two values is used to forward a message. The value of qos can be one of:
    • MQTT_QOS_AT_MOST_ONCE
    • MQTT_QOS_AT_LEAST_ONCE
    • MQTT_QOS_EXACTLY_ONCE
  • mh: The MQTT subscribe callback function that will handle callback events (see Subscribe Callback Handler below for details).

Returns: 'True' (1) if the subscribe was successful, otherwise 'false' (0) if an error occured (check .errno.errstr, etc.).

Subscribe Callback Handler(s)

When you subscribe to a specific topic or topic filter, you also need to pass in a callback function that will be used to handle any subscribe matches or events.

You can subscribe to up to EIGHT (8) topics with the internal MQTT client.
The same callback handler can be used for multiple subscriptions, or you can use individual callbacks for each subscribe.  The choice will depend on your specific project.

MQTT subscribe callback functions must have the following format:

/**************************************************************************/
/*!
    @brief  MQTT subscribe event callback handler

    @param  topic      The topic causing this callback to fire
    @param  message    The new value associated with 'topic'

    @note   'topic' and 'message' are UTF8Strings (byte array), which means
            they are not null-terminated like C-style strings. You can
            access its data and len using .data & .len, although there is
            also a Serial.print override to handle UTF8String data types.
*/
/**************************************************************************/
void subscribed_callback(UTF8String topic, UTF8String message)
{
  // Print out topic name and message
  Serial.print("[Subscribed] ");
  Serial.print(topic);
  Serial.print(" : ") ;
  Serial.println(message);

  // Echo back
  Serial.print("Echo back to " TOPIC_ECHO " ... ");
  mqtt.publish(TOPIC_ECHO, message); // Will halt if an error occurs
  Serial.println("OK");

  // Unsubscribe from SUBSCRIBED_TOPIC2 if we received an "stop" message
  // Won't be able to echo anymore
  if ( message == "stop" )
  {
    Serial.print("Unsubscribing from " TOPIC_SUBSCRIBE " ... ");
    mqtt.unsubscribe(TOPIC_SUBSCRIBE); // Will halt if fails
    Serial.println("OK");
  }
}

Callback Handler Parameters

  • topic: The topic that caused the subscribe callback to fire (UTF8-encoded)
  • message: The UTF8 encoded message associated with topic_data
Note the use of UTF8String for 'topic' and 'message' since the strings that are returned are UTF8 encoded and NOT NULL terminated, so we need to use this helper to convert them to something we can safely print.

bool unsubscribe( const char* topicFilter );

Unsubscribes from a specific topic or topic filter.

Parameters: The topic or topic filter to unsubscribe from

Returns: 'True' (1) is the unsubscribe was successful, otherwise 'false' (0).

Last Will

MQTT has a concept called a 'Last Will' message.  The optional 'Last Will' message can be set using a user-define topic, and this message will be sent if the server/broker is unable to contact the client for a specific amount of time.

This functionality isn't a mandatory part of MQTT, but can be used to detect when nodes are online and offline.  When you connect, you can for example set a string like "Online" to a specific topic, and then set a last will message of "Offline" to that same topic.  If the node goes offline (battery failure, disconnect, etc.), the broker will use the last will to set the topic to "Offline" once the server/client timeout occurs.

Be sure to set the last will BEFORE calling the .connect function since the last will is set during the connect phase!

void will ( const char* topic, UTF8String message, uint8_t qos = MQTT_QOS_AT_MOST_ONCE, uint8_t retained = 0);

Sets the last will message.

Parameters:

  • topic: The topic where the data should be published (ex: "adafruit/data" or "home/rooms/bedroom/temp").
  • message: The string of data to write to the specified 'topic' (UTF8String is used to make it easier to work with UTF8 data).
  • qos: The quality of service level (see the MQTT spec for details).  Default = 'At Most Once', meaning the message tries to send once but isn't persisted if the send fails.  Possible values are:
    • MQTT_QOS_AT_MOST_ONCE
    • MQTT_QOS_AT_LEAST_ONCE
    • MQTT_QOS_EXACTLY_ONCE
  • retained: Whether or not the published message should be 'retained' by the MQTT broker. Sending a message with the retained bool set to 'false' (0) will clear any previously retained message from the broker. The default value is false.

Returns: 'True' (1) is the last will message was successfully set, otherwise 'false' (0).

Client ID

The client identifier (Client ID) is an string that identifies each MQTT client connecting to an MQTT broker.

This value should be unique on the broker since the broker uses it for identifying the client and the client's current 'state' of the client (subscriptions, QoS, etc.).

By default, a random 10-23 character string will be generated for the unique Client ID that gets passed to the broker during the connection process. If you wish to maintain a consistent client ID across connections, however, you can override the random client ID by using the .clientID function below:

void clientID(const char* client)

Sets a manual Client ID, overriding the default random value.

Parameters:

  • client: A null-terminated string representing the client ID to pass to the MQTT broker.

Returns: Nothing 

Disconnect Callback

An optional disconnect callback is available in AdafruitMQTT.  This callback handler will fire when you are disconnected from the remote MQTT broker.

To use the callback, add the following function to your sketch (the function name and the contents of the function can change depending on your project requirements):

void disconnect_callback(void)
{
  Serial.println();
  Serial.println("-----------------------------");
  Serial.println("DISCONNECTED FROM MQTT BROKER");
  Serial.println("-----------------------------");
  Serial.println();
}

Then pass this function name into the .setDisconnectCallback function BEFORE calling .connect or .connectSSL:

// Set the disconnect callback handler
mqtt.setDisconnectCallback(disconnect_callback);

AdafruitMQTT Example

The following example illustrates how to subscribe to topics, set the last will message, publish, and implement one or more subscribe callback handlers:

This example uses the freely accessible test MQTT broker at test.mosquitto.org. This server is publicly accessible, so be careful what data you push to it since anyone can see the publications!
/*********************************************************************
 This is an example for our Feather WIFI modules

 Pick one up today in the adafruit shop!

 Adafruit invests time and resources providing this open source code,
 please support Adafruit and open-source hardware by purchasing
 products from Adafruit!

 MIT license, check LICENSE for more information
 All text above, and the splash screen below must be included in
 any redistribution
*********************************************************************/

#include <adafruit_feather.h>
#include <adafruit_mqtt.h>
#include "certificate_mosquitto.h"

/* This sketch demonstrates subscribe/unsubscribe activity with
 * callbacks.
 * 
 * It will connect to a public MQTT server (with/without TLS)
 * and subscribe to TOPIC_SUBSCRIBE (defined below).
 * 
 * - When a message is received, it will echo back to TOPIC_ECHO
 * - If the received message is "stop", we will
 *   unsubscribe from TOPIC_SUBSCRIBE and you won't be able to
 *   echo content back to the broker any longer.
 * 
 * Note: TOPIC_SUBSCRIBE and TOPIC_ECHO must not be the same topic!
 * Ex. They must not be "adafruit/+" and "adafruit/echo", since this will
 * cause an infinite loop (received -> echo -> received -> ....)
 *
 * For details on the MQTT broker server see http://test.mosquitto.org/
 *  - Port 1883 : MQTT, unencrypted
 *  - Port 8883 : MQTT, encrypted (TLS)
 *
 * Note: may You need an MQTT desktop client such as the lightweight
 * Java client included in this repo: org.eclipse.paho.mqtt.utility-1.0.0.jar
 * 
 * For information on configuring your system to work with MQTT see:
 * - https://learn.adafruit.com/desktop-mqtt-client-for-adafruit-io/installing-software
 *
 * To run this demo
 * 1. Change the WLAN_SSID/WLAN_PASS to match your access point
 * 2. Decide whether you want to use TLS/SSL or not (USE_TLS)
 * 3. Change TOPIC*, WILL*, enable CLIENTID if needed
 * 4. Compile and run
 * 5. Use an MQTT desktop client to connect to the same MQTT broker and
 *    publish to any topic beginning with "adafruit/feather/" (depending
 *    on TOPIC_SUBSCRIBE). To be able to recieve the echo message, please
 *    also subcribe to "adafruit/feather_echo" (TOPIC_ECHO).
 */

#define WLAN_SSID         "yourSSID"
#define WLAN_PASS         "yourPass"

#define USE_TLS           0

#define BROKER_HOST       "test.mosquitto.org"
#define BROKER_PORT       (USE_TLS ? 8883 : 1883)

// Uncomment to set your own ClientID, otherwise a random ClientID is used
//#define CLIENTID          "Adafruit Feather"

#define TOPIC_SUBSCRIBE   "adafruit/feather/+"
#define TOPIC_ECHO        "adafruit/feather_echo"

#define WILL_TOPIC        "adafruit/feather"
#define WILL_MESSAGE      "Goodbye!!"

AdafruitMQTT mqtt;

/**************************************************************************/
/*!
    @brief  Disconnect handler for MQTT broker connection
*/
/**************************************************************************/
void disconnect_callback(void)
{
  Serial.println();
  Serial.println("-----------------------------");
  Serial.println("DISCONNECTED FROM MQTT BROKER");
  Serial.println("-----------------------------");
  Serial.println();
}

/**************************************************************************/
/*!
    @brief  The setup function runs once when the board comes out of reset
*/
/**************************************************************************/
void setup()
{
  Serial.begin(115200);

  // Wait for the USB serial port to connect. Needed for native USB port only
  while (!Serial) delay(1);

  Serial.println("MQTT Subscribe Example\r\n");

  // Print all software versions
  Feather.printVersions();

  while ( !connectAP() )
  {
    delay(500); // delay between each attempt
  }

  // Connected: Print network info
  Feather.printNetwork();

  // Tell the MQTT client to auto print error codes and halt on errors
  mqtt.err_actions(true, true);

  // Set ClientID if defined
  #ifdef CLIENTID
  mqtt.clientID(CLIENTID);
  #endif

  // Last will must be set before connecting since it is part of the connection data
  mqtt.will(WILL_TOPIC, WILL_MESSAGE, MQTT_QOS_AT_LEAST_ONCE);

  // Set the disconnect callback handler
  mqtt.setDisconnectCallback(disconnect_callback);

  Serial.printf("Connecting to " BROKER_HOST " port %d ... ", BROKER_PORT);
  if (USE_TLS)
  {  
    // Disable default RootCA to save SRAM since we don't need to
    // access any other site except test.mosquitto.org
    Feather.useDefaultRootCA(false);

    // mosquitto CA is pre-generated using pycert.py
    Feather.addRootCA(rootca_certs, ROOTCA_CERTS_LEN);

    // Connect with SSL/TLS
    mqtt.connectSSL(BROKER_HOST, BROKER_PORT);
  }else
  {
    mqtt.connect(BROKER_HOST, BROKER_PORT);
  }
  Serial.println("OK");

  Serial.print("Subscribing to " TOPIC_SUBSCRIBE " ... ");
  mqtt.subscribe(TOPIC_SUBSCRIBE, MQTT_QOS_AT_MOST_ONCE, subscribed_callback); // Will halted if an error occurs
  Serial.println("OK");
}

/**************************************************************************/
/*!
    @brief  This loop function runs over and over again
*/
/**************************************************************************/
void loop()
{

}

/**************************************************************************/
/*!
    @brief  MQTT subscribe event callback handler

    @param  topic      The topic causing this callback to fire
    @param  message    The new value associated with 'topic'

    @note   'topic' and 'message' are UTF8Strings (byte array), which means
            they are not null-terminated like C-style strings. You can
            access its data and len using .data & .len, although there is
            also a Serial.print override to handle UTF8String data types.
*/
/**************************************************************************/
void subscribed_callback(UTF8String topic, UTF8String message)
{
  // Print out topic name and message
  Serial.print("[Subscribed] ");
  Serial.print(topic);
  Serial.print(" : ") ;
  Serial.println(message);

  // Echo back
  Serial.print("Echo back to " TOPIC_ECHO " ... ");
  mqtt.publish(TOPIC_ECHO, message); // Will halt if an error occurs
  Serial.println("OK");

  // Unsubscribe from SUBSCRIBED_TOPIC2 if we received an "stop" message
  // Won't be able to echo anymore
  if ( message == "stop" )
  {
    Serial.print("Unsubscribing from " TOPIC_SUBSCRIBE " ... ");
    mqtt.unsubscribe(TOPIC_SUBSCRIBE); // Will halt if fails
    Serial.println("OK");
  }
}

/**************************************************************************/
/*!
    @brief  Connect to defined Access Point
*/
/**************************************************************************/
bool connectAP(void)
{
  // Attempt to connect to an AP
  Serial.print("Attempting to connect to: ");
  Serial.println(WLAN_SSID);

  if ( Feather.connect(WLAN_SSID, WLAN_PASS) )
  {
    Serial.println("Connected!");
  }
  else
  {
    Serial.printf("Failed! %s (%d)", Feather.errstr(), Feather.errno());
    Serial.println();
  }
  Serial.println();

  return Feather.connected();
}
Last updated on 2016-03-07 at 01.00.02 PM Published on 2016-03-23 at 01.29.37 PM