Table of Contents

U3: Sending MQTT messages

In this scenario, you will send MQTT message exposing temperature and humidity to the MQTT server available on the internal.IOT network. By sending sensor data, you will learn, how to expose the device's state and how to inform others that your device stopped working uncontrolled way.

Target group

Undergraduate / Bachelor / Engineering Students

Prerequisites

We assume you already know how to:

MQTT broker present in the internal.IOT network is also visible under public address. So whenever you publish an MQTT message using VREL node that is connected to the internal.IOT network, you may subscribe to it using other devices connected to the internal.IOT, i.e. other VREL node or if you're physically present at SUT in the IOT laboratory room 320, then you can connect your mobile and laptop to the internal.IOT network and use “internal” IP address. However, if you're in a remote location, you can access the same broker under public IP as stated in the node description. Same messages published on the internal network are also visible on the public side. Mind, to access MQTT broker you need to use IP, user and password (applies to both public and private IPs of the MQTT Broker). Refer to the node documentation for the latest information.

Note - information present in source code samples can be not up-to-date - remember to refer to the VREL node documentation for the latest information on IPs, users and passwords for both internal.IOT network access and for the MQTT Broker.

Scenario

In this scenario, you will connect to the infrastructure as a client (STA) and use the MQTT server to publish information about temperature, humidity. Also, you will expose device state using MQTT “last will” mechanism: on startup, you will configure MQTT broker to send an MQTT message with payload automatically off when connection breaks (i.e. your device goes down) and your starting code will send the same message with payload on to notify subscribers that you're (back) on-line. Parallelly, you will present temperature and humidity data on the LCD screen.

In this scenario, you do not subscribe your device to any messages, you only publish them.

Result

You should be able to see connection status, temperature and humidity on the screen while your MQTT subscriber should be able to present you readings delivered via MQTT. It should be equal to the data presented in the LCD screen you observe locally, via video stream.

Start

Define some identifiers to separate and update AP's SSID and passphrase easily. To format lines for the LCD, we suggest using a char buffer of 20 characters (one full line) and some 2-3 integers for iterators. Remember to declare the LCD control class in your code. You do not need to instantiate the WiFi communication class - as you have only one interface here, it is a singleton class you can refer to directly using WiFi. Note - in this scenario, you connect only once. If your connection breaks, you will have no information about it here. To handle such a situation, there are multiple solutions: you can check in the loop() if the connection is OK and in case it is down, you can call your re-connecting function, or you can use one of the asynchronous handlers, triggered automatically via the WiFi manager. To use the latter approach, refer to the ESP8266 WiFi implementation for Arduino documentation.

Steps

Following steps do not present full code - you need to supply missing parts on your own! We do not present here how to connect to the WiFi AP. If you're in doubt, rever to the U1 scenario. We also do not present in details how to organise and print DHT11 sensor data on the LCD screen. Please refer to scenario B2 if you need a recall.

Step 1

Include all necessary libraries. We use PubSubClient library to contact MQTT broker. The minimum set here is:

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
...

Declare some identifiers to let you easier handle necessary modifications and keep code clear:

#define wifi_ssid "internal.IOT"
#define wifi_password "IoTlab32768"
#define mqtt_server "192.168.90.5"
#define mqtt_user "vrel"
#define mqtt_password "vrel2018"
...
Step 2

Declare some identifiers, here MQTT messages' topics, MQTT client ID and payloads for the status notification (on / off).

Use unique names for topics and for the MQTT client, do some random, use your MAC as part of it. It is important because MQTT broker identifies client using its name thus if your device shares name with some other that is already working, you may not get information about connection lost because another device with the same name is still active on the network. Unique topics are also essential: if you accidentally overlap, you may get an invalid reading with someone that is using the same topic but different payload.
#define MQTTClientName ...<your client name>...
#define tempTopic  ...<some topic for temperature>... // give it some unique topic
                                                      // i.e. including your name
#define humTopic   ...<come topic for humidity>...    // as above
 
//MQTT last will
#define lastWillTopic ...<some topic for exposing state and last will>...   
                                                      // give it some unique topic 
                                                      // i.e. including your name
#define lastWillMessage "off"
#define mqttWelcomeMessage "on"
Step 3

By the regular variables related to you WiFi Esp network client, DHT sensor, buffer for string processing and so on, here you need to configure an object additionally to handle communication with MQTT broker. As you may use many brokers in your app, you need to instantiate it yourself. The constructor accepts WiFiClient as a parameter, so here you need explicitly declare one:

// WiFi & MQTT
WiFiClient espClient;
PubSubClient client(espClient);

Configure your network client, remember to set ESP.mode(WIFI_SFA). Once you're done with starting, initialise MQTT broker client. Once you're done with connecting to the WiFi, configure your MQTT PubSubClient:

client.setServer(mqtt_server, 1883);

You can call it i.e. in the setup() function, after a successful connection to the WiFi AP but before you execute step 4.

Step 4

If your WiFi client is working, your MQTT client is configured it is time to connect to the MQTT broker. It is a good idea to have this procedure separated to call as needed if your connection with MQTT broker goes down for any reason. Here we encapsulate it in the reconnect() function:

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    if (client.connect(MQTTClientName, mqtt_user, mqtt_password, 
                       lastWillTopic, 0, true, lastWillMessage)) 
    {
      client.publish(lastWillTopic, mqttWelcomeMessage, true);
    } else 
    {
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
  lcd.setCursor(0,1);
  lcd.print("MQTT Connected!");
}

Function retries every 5 seconds, in case it is unable to connect to the MQTT broker.
You can call it in the very beginning of the loop() function, checking in advance if your client is connected or not, i.e. this way:

...
if (!client.connected()) {
    reconnect(); //reconnect MQTT
  }
...
Step 5

Prepare a code that publishes MQTT messages. You will call it periodically within loop() section. Do not try to send data too frequently. Once every 10-20s is pretty enough as nor humidity nor temperature will change rapidly. On the other hand, sending too frequently causes network bottlenecks and heavy load on the MQTT broker.

Your publishing routine may look somehow like this:

void mqttPublish()
{
  hum = dht.readHumidity();
  temp = dht.readTemperature();
  if(client.connected())
  {
    if (!(isnan(temp)||isnan(hum)))
    {
      client.publish(tempTopic, String(temp).c_str(), false);  // Do not retain
                                                               // messages
      client.publish(humTopic, String(hum).c_str(), false);
    }
  }
}
If you choose to implement this routine as a ticker/timer, you will experience ESP crash every execution of this loop (you will observe it as your device may freeze or restart every execution). The reason is if you use timer/ticker, those routines are not giving extra time for the WiFi handling routine (while main loop() indeed does). reading from the DHT sensor takes so much time, blocking here device to handle WiFi communication, so hardware watchdog will trigger an exception and break your code execution!
To handle this issue you must read DHT sensor in the main loop() and insert them into the variables then limit MQTT publish procedure just to only prepare and send data over the network (this is legit). Anyway, we do not consider this kind of scenario here as we call publishing code directly from within the loop() function so it is safe.

Finally your loop() may look like this:

void loop()
{
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  sprintf(buffer,"Temp is %2.1f C",temp);
  lcd.setCursor(0,2);
  lcd.print(buffer);
  sprintf(buffer,"Humidity is %2.1f%% Rh",hum);
  lcd.setCursor(0,3);
  lcd.print(buffer);
  mqttPublish();
  delay(5000);  
}

the client.loop() is to handle incoming MQTT messages. There are none here but it is good practice to have it in your code.

Result validation

Observe connection progress on the LCD via video stream. Once WiFi and MQTT are connected you should be able to see temperature and humidity readings on the LCD and additionally those should be sent over the MQTT messages to the MQTT broker. Connect your MQTT client and subscribe to your messages (you may do it using a wildcard character) i.e. with means of the MQTT spy application. Remember to connect MQTT spy to public IP address unless you're a student physically present in our laboratory room and you have direct access to the internal.IOT network. Observe data on the LCD screen the same as over MQTT messages (note, there may be a delay because of the network bottlenecks and MQTT broker load).

FAQ

What topic should I choose?: Up to you. Anyway, we suggest using non-trivial ones just not to overlap with other users.
Why MQTT broker uses two IP addresses?: As programming ESP826 devices may issue many hacking scenarios, we decided to use separated WiFi network. On the other hand, it should be a way to exchange some data with the external world. Here comes the solution: our MQTT broker is connected one leg to the separated internal.IOT network while another interface connects to the public Internet. This way any message you publish from within the private network is visible (you can subscribe to it) on both sides: public and private one. This way you can send information to the i.e. IBM Watson cloud or Microsoft Azure to store it and visualise. Opposide, you can drive the node device connected to the internal.IOT solely from the Internet, i.e. implementing some interface using dashboard. All you need is to publish to our MQTT broker on its public interface and write a code that subscribes to those messages on the private side. Simple and safe.