MQTT

Publish/Subscribe model #

MQTT is a lightweight publish/subscribe messaging protocol designed for machine to machine telemetry in low bandwidth environments. It is useful for use with low power sensors, but is applicable to many scenarios. The MQTT protocol is based on the principle of publishing messages and subscribing to topics. Multiple clients connect to a server often termed as a “broker”. The broker is a central communication point that transmits the messages between senders and the clients who subscribed to receive the messages on a certain topic. Clients include the topic in the message, when publishing to the broker. The topic is the routing information for the broker. The broker delivers all the messages on a specific topic to the client, if the client has subscribed to it. The broker and MQTT act as a simple, common interface for everything to connect to. The data producers and the data consumers are independant of each other.

publish-subscribe

Subscription to MQTT topics #

Messages in MQTT are published on topics. There is no need to configure a topic, publishing on it is enough. Topics are simple strings treated as a hierarchy, using a slash (/) as a separator.

Clients can receive messages by subscribing to a topic.

In figure above, we see Client 1 (the Publisher, a temperature sensor) sending temperature to the broker. Client 2 and Client 3 subscribed to the topic and will receive all the messages on that topic. In this case, a sample topic for sending temperature of room S25 in Duboue building of UPPA could be UPPA/Duboue/S25/temp.

mqtt

A subscription can be to an exact topic, in which case the client will receive all the messages on that exact topic, or it may include wildcards. Two wildcards are available, + or #. The + sign can be used as a wildcard for a single level of hierarchy. The subscription to UPPA/Duboue/+/temp would result in all the messages sent to UPPA/Duboue/S25/temp as well as any topic with an arbitrary value in the place of S25, for example, UPPA/Duboue/S24/temp. The # sign would then replace several level of hierarchy and is very useful when the detailed hierarchy is not known. For instance UPPA/# would get all data, and topics, from UPPA university. A detailed example is shown in figure above.

Multiple applications have been developed on MQTT which include Amazon Web Services, EVERYTHING IoT platform, Facebook Messenger and many others which are available on PlayStore for Android and AppStore for iOS. An example of Google PlayStore is shown in figure 3 below.

mqtt-app

Further documentation on MQTT is available on their official website http://mqtt.org/.

Brief description of the code #

We will briefly explain the steps used in the example below to use the MQTT protocol.

Include the publish/subscribe library

#include <PubSubClient.h>

Test parameters for MQTT: topic, temperature and MQTT server address

char *topicin = "UPPA/test"; 
char *topicout = "UPPA/Duboue/S25/temp"; 
char *msgTemp  = "22.5"; 
char* mqtt_server = "test.mosquitto.org";

Define the publish/subscribe client

WiFiClient espClient; 
PubSubClient client(espClient);

Define a function callback() to process incoming message from a given subscribed topic. This is not needed if you only publish, which is more typical of an end-device.

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]);
  }

Define a function reconnect() to keep trying to connect to the MQTT broket until it is successful. Here the client id can be random because the MQTT broker we use does not require authentication.

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");

    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
    // Wait 5 seconds before retrying  
      delay(5000);
    }
  }
}

In function setup(), setup the MQTT server

client.setServer(mqtt_server, 1883);

In function loop(), check if client is connected, if not then reconnect

void loop() {

  if (!client.connected()) {
    reconnect();
  }

At the end, publish the message on specified topic

int e=client.publish(topicout, msgTemp);

Complete working example #

// if you have an ESP8266 based board
#define ESP8266

#if defined ESP8266 || defined ARDUINO_ESP8266_ESP01
#include <ESP8266WiFi.h>
#else
#include <WiFi.h>
#endif

#include <PubSubClient.h>

// Update these with values suitable for your network.

char* ssid = "iPhoneD";
char* password = "345hello";

char *topicin = "UPPA/test"; 
char *topicout = "UPPA/Duboue/S25/temp"; 
char *msgTemp  = "22.5"; 
char* mqtt_server = "test.mosquitto.org";

WiFiClient espClient;
PubSubClient client(espClient);

int WiFi_status = WL_IDLE_STATUS;

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

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();
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Create a random client ID
    String clientId = "ESP8266Client-";
    clientId += String(random(0xffff), HEX);
    // Attempt to connect
    if (client.connect(clientId.c_str())) {
      Serial.println("connected");

    } 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() {
  delay(3000);
  Serial.begin(38400);

// Print a start message  
  Serial.println(F("Simple MQTT demo")); 
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  //client.setCallback(callback);
}

void loop() {

  if (!client.connected()) {
    reconnect();
  }
  
  //client.loop();  
  WiFi_status = WiFi.status();
  
  if ( WiFi_status != WL_CONNECTED) {
  while ( WiFi_status != WL_CONNECTED) {
      Serial.print("Attempting to connect to WPA SSID: ");
      Serial.println(ssid);
      // Connect to WPA/WPA2 network
      WiFi_status = WiFiWiFi.begin(ssid, password);
      delay(500);
    }
    Serial.println("Connected to AP");   
  } 
  Serial.print("Publishing: status=");

  int e=client.publish(topicout, msgTemp);
  Serial.println(e); 
  delay(7500);   
 } 
The raw source of the sketch example is visible here.

The code is ready for an Heltec WiFi LoRa 32 board so OLED is activated.

Testing and receiving the published MQTT message #

To test the MQTT publishing from the IoT device, we will use a computer with an MQTT client to listen for a specific topic.

IMPORTANT. In all the following examples and assignments, if you do not have access to a computer with a terminal window to use mosquitto_sub and mosquitto_pub command line, you can use the HiveMQ MQTT web client to subscribe and publish in place of mosquitto_sub and mosquitto_pub. In this case, also replace the MQTT broker test.mosquitto.org by broker.hivemq.com in the Arduino example.

char* mqtt_server = "broker.hivemq.com";

mosquitto_sub takes at least 2 parameters: -h and -t to indicate respectively the MQTT broker and the topic to subscribe to. In the Arduino example, the MQTT broker used was test.mosquitto.org and the published topic was "UPPA/Duboue/S25/temp". The -v option will display information in verbose mode to get the complete topic in presence of a wildcard.

The command would then look like:

mosquitto_sub -v -h test.mosquitto.org -t UPPA/#

which means subscribe to all topics under UPPA/. Each time that the IoT device is publishing, you should see on your computer terminal an output similar to:

> mosquitto_sub -v -h test.mosquitto.org -t UPPA/#
UPPA/Duboue/S25/temp 22.5
UPPA/Duboue/S25/temp 22.5
UPPA/Duboue/S25/temp 22.5
...

Going a step further: use Node-RED to create complex data workflows #

See Node-RED

Enjoy!

2021 - Congduc Pham