AdafruitMQTT includes an OPTIONAL helper class called AdafruitMQTTTopic that can be used to publish data to a single topic on an MQTT broker.

This helper class inherits from Print, which allows you to write data to MQTT topics similarly to how you would write data to the 'Serial Monitor', using .print statements.

See 'MQTT/MqttTopicClass' in the examples folder for an example of how to use AdafruitMQTTTopic.

Constructor

AdafruitMQTTTopic(AdafruitMQTT& mqtt, 
                  const char* topic, 
                  uint8_t qos = MQTT_QOS_AT_MOST_ONCE,
                  bool retain = false)

Parameters:

  • mqtt: A reference to the AdafruitMQTT instance associated with this helper (since the connection to the MQTT broker is defined and managed there).
  • topic: A null-terminated string containing the topic to publish to
  • qos: An optional quality of server (QoS) level to use when publishing.  If left empty, this argument will default to 'At Most Once', meaning it will try to publish the data but if the operation fails it won't persist the attempt and retry again later.
  • retain: Sets the 'retain' bit to indicate if any messages published to the MQTT broker should be retained on the broker for the next client(s) that access that topic.

The following example shows how to properly declare an instance of the AdafruitMQTTTopic class (note that the default QoS and retain values are used):

#define CLIENTID          "Adafruit Feather"
#define TOPIC             "adafruit/feather"

AdafruitMQTT      mqtt      (CLIENTID);
AdafruitMQTTTopic publisher (mqtt, TOPIC);

Functions

In addition to the functions defined in the Print base class (see the Print.h source as well), the following functions are defined as part of AdafruitMQTTTopic:

void retain(bool on)

void retain (bool on)

Enables or disabled the 'retain' feature when publishing messages. This indicates whether the published message should be maintained on the broker when a message is written to the topic.

The default value for 'retain' is false, unless it is modified using this function.

Parameters:

  • on: Whether or not the published message should be 'retained' by the MQTT broker. Sending a message with the this set to 'false' (0) will clear any previously retained message from the broker.

Returns: Nothing

Subscribe Callbacks

You can also subscribe or unsubcribe to publications on the topic using the following functions:

bool subscribe   (messageHandler_t mh);
bool unsubscribe (void);
bool subscribed  (void);

bool subscribe (messageHandler_t mh)

This function will subscribe to the topic and any changes will be sent to the specified callback handler.

Parameters

  • mh: The callback handler where the subscription event should be redirected to.

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

Subscription callback handlers 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.printf("["); Serial.print(topic); Serial.printf("]");
  Serial.print(" : message = ") ;
  Serial.println(message);

  // Unsubscribe if message = "stop"
  if ( message == "stop" )
  {
    Serial.print("Unsubscribing ... ");
    mqttTopic.unsubscribe(); // Will halt if fails
    Serial.println("OK");
  }
}

bool unsubscribe (void)

Unsubscribes to the topic if you previously called .subscribe.

Parameters: None

Returns: 'True' (1) if the operation succeeded, otherwise 'false' (0).

bool subscribed (void)

Indicates whether you are currently susbcripted to this topic or not.

Parameters: None

Returns: 'True' (1) if you are subscribed, otherwise 'false' (0).

Publishing Data via 'Print'

One important thing to keep in mind with AdafruitMQTTTopic is that every .print* function corresponds to an MQTT publication request.

The following code will result in three different MQTT publications:

int   number_of_days = 7;
char* place = "somewhere";

pub.print(number_of_days);
pub.print(" days since something happened ");
pub.print(place);

You can work around this '1 print = 1 publication' restriction by using the printf function, as shown in the example below:

int   number_of_days = 7;
char* place = "somewhere";

pub.printf("%d days since something happened %s", number_of_days, place);

For a full list of printf modifiers (the special '%' character sequences that get replaced with variables after the main string) see printf here.

The most common modifiers are described below though (all preceded by '%' so '%d' for a signed decimal value, etc.) :

  • d or i: Signed decimal value ('int', 'int16_t', etc.)
  • u: unsigned decimal value ('uint32_t', etc.)
  • x: lower-case hexadecimal integer (ex. 'a12b' for 0xA12B)
  • X: upper-case hexadecimal integer (ex. 'A12B' for 0xA12B)
  • f: floating point value ('float', etc.)
  • s: null-terminated string of characters (ex. "sample")
  • c: A single characters (ex. 'a')

Example

The following sketch shows how you might use AdafruitMQTTTopic in the real world.  The latest source can be found in the MQTT/MqttTopicClass folder in 'examples'.

/*********************************************************************
 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 connects to a public MQTT server (with/without TLS)
 * and publishes a message to a topic every 5 seconds.
 *
 * For server details 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 or
 * - A full desktop client like MQTT.fx https://learn.adafruit.com/desktop-mqtt-client-for-adafruit-io/installing-software
 *
 * To run this demo
 * 1. Change WLAN_SSID/WLAN_PASS
 * 2. Decide whether you want to use TLS/SSL or not (USE_TLS)
 * 3. Change CLIENTID, TOPIC, PUBLISH_MESSAGE, WILL_MESSAGE if you want
 * 4. Compile and run
 * 5. Use your MQTT desktop client to connect to the same sever and subscribe
 *    to the defined topic to monitor the published message(s).
 */

#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             "adafruit/feather"
#define WILL_MESSAGE      "Goodbye!!"

AdafruitMQTT      mqtt;
AdafruitMQTTTopic mqttTopic(&mqtt, TOPIC, MQTT_QOS_EXACTLY_ONCE);

char old_value = '0';
char value = '0';

/**************************************************************************/
/*!
    @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 Publish using Publisher 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(TOPIC, WILL_MESSAGE, MQTT_QOS_AT_LEAST_ONCE);

  // Connect to broker
  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");

  // Subscribe with callback
  mqttTopic.subscribe(subscribed_callback);

  Serial.println("Please use desktop client to subcribe to \'" TOPIC "\' to monitor");

  // Inital publish
  Serial.printf("Publishing \'%d\' ... ", value);
  mqttTopic.print( value ); // use .write to send in binary format
  Serial.println("OK");
}

/**************************************************************************/
/*!
    @brief  This loop function runs over and over again
*/
/**************************************************************************/
void loop()
{
  // value changed due to subscribed callback
  if (old_value != value)
  {
    // check if still subscribed
    if ( mqttTopic.subscribed() )
    {
      old_value = value;
      Serial.println();
      Serial.printf("Publishing \'%c\' ... \r\n", value);
      mqttTopic.print( value ); // use .write to send in binary 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)
{
  // Copy received data to 'value'
  memcpy(&value, message.data, 1);

  // Print out topic name and message
  Serial.printf("["); Serial.print(topic); Serial.printf("]");
  Serial.print(" : value = ") ;
  Serial.println(value);

  // Increase value by 1
  value++;

  // wrap around
  if (value > '9') value = '0';

  // Unsubscribe if we received an "stop" message
  // Won't be able to echo anymore
  if ( message == "stop" )
  {
    Serial.print("Unsubscribing ... ");
    mqttTopic.unsubscribe(); // 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();
}

This guide was first published on Mar 23, 2016. It was last updated on Mar 26, 2024.

This page (AdafruitMQTTTopic) was last updated on Mar 01, 2016.

Text editor powered by tinymce.