MQTT: Why and How

MQTT is a protocol for message handling across networks. An MQTT server or Broker allows multiple Clients to send and receive short messages while they are attached (Subscribed).

In this diagram, a client with a thermometer attached will publish data to a specified topic.
Any other client can subscribe to that topic and be updated the message contents in real time.

  • The broker generally doesn’t need much horsepower, and Raspberry Pi are frequently used.
  • Clients can be anything from ESP8266, ESP32, indeed any hardware that can be programmed with C, Arduino, Python etc.
    The example also shows a solar inverter client publishing stats to the broker. An ESP32 is subscribed to that topic on the broker and kept up to date.

Since the data are being communicated very quickly, MQTT is also suitable in providing control signals that would tell an ESP to activate a relay, or provide data for it to analyse and respond.

Setting up an MQTT broker

There are several public brokers available. While this is easy to work with and a good way to get started, you may prefer to keep your data in house, and to avoid the need to expose your devices to the outside world.

Mosquitto can be installed on just about any raspberry pi.

https://mosquitto.org/blog/2013/01/mosquitto-debian-repository/
It is a popular broker, easy to install, lightweight and configurable.

Optional Websockets:
Once installed, activate the “websockets” port.
Edit: /etc/mosquitto/mosquitto.conf

user pi
port 1883
listener 8000
protocol websockets

This will allow javascript functions to dynamically update any web page to reflect changing mqtt messages (see below).

Example part 1: ESP8266 publishing to MQTT broker

A simple example showing how data from a sensor connected to the ESP can be published to MQTT.
This gadget samples the temperature and humidity, publishes the data to two topics on the MQTT broker and then goes to sleep for ten minutes. With an ESP8266 Wemos / Nodemcu or an 01 with a reset wire soldered on, it will reboot itself after sleep and do it again.
(Power consumption while asleep is down to several microamps.)

/* DHTClient - ESP8266 client with a DHT sensor as an input.
   Every "inverval" minutes - gets data, publishes to mqtt and sleeps.
 Set up: 
     identity
     wifi credentials
     mqtt server
     topics
     DHT pin 
     DHT type
     sleep cycle
  Routine draws 60mA on duty about 2 seconds
  Draws 20uA while sleeping
  Set the PIN, cycle and identity for ESP type, logging and cycle time
  Set also the topic names for MQTT
*/
String swVersion = __FILE__;
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#define DHTTYPE DHT22  //or DHT11 or DHT22 
#define DHTPIN  12  // 5 for Wemos , 2 for ESP-01

DHT dht(DHTPIN, DHTTYPE, 11); // 11 works fine for ESP8266
float humidity, temperature;  // Values read from sensor
const char* ssid = "SSID";
const char* password = "password";
const char* mqtt_server = "nnn.nnn.nnn.nnn";
const char* humTopic = "lounge/Humidity";
const char* tempTopic = "lounge/Temperature";
const char* identity = "ESP8266";
int cycle = 10; //  = x minutes interval at which to sleep/read sensor

WiFiClient espClient;
PubSubClient mclient(espClient);

void setup(void)
{
  Serial.begin(115200);
  Serial.println("Sensor loading.. ");
  Serial.println (swVersion);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  int i;
  while (WiFi.status() != WL_CONNECTED) { // Wait for the Wi-Fi to connect
    Serial.print(".");    
    delay(800);
  }
  Serial.println(WiFi.localIP());
  mclient.setServer(mqtt_server, 1883);
  mclient.connect(identity + WiFi.localIP()[3]);
  dht.begin();           // initialize temperature sensor
  Serial.println("DHT initialised");
  delay(2000);
}

void loop(void)
{
  if (!mclient.connected()) {
    reconnect();
  }
  getData();
  Serial.println("DHT Weather Reading Client");
  Serial.println("Temperature: "+ String(temperature));
  Serial.println("Humidity: " +String(humidity));
  publishData();
  Serial.println("Going to sleep for " + String(cycle) + " minutes.");
  delay(500);
  ESP.deepSleep(cycle * 60000000); // cycle * seconds
}

void getData() {
  dht.begin();
  humidity = dht.readHumidity();          // Read humidity (percent)
  temperature = dht.readTemperature() - 1;   // Read temperature as *C  ??????? Take off 6 for error !!!!
  if (isnan(humidity) || isnan(temperature)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }
}
void publishData() {
  Serial.println("Publishing to " + String(mqtt_server) );
  uint16_t publish1 = mclient.publish(tempTopic, String(temperature).c_str(), true);
  uint16_t publish2 = mclient.publish(humTopic, String(humidity).c_str(), true);
}

void reconnect() {
  while (!mclient.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (mclient.connect(identity + WiFi.localIP()[3])) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(mclient.state());
      Serial.println(" try again in 2 seconds");
      delay(2000);
    }
  }
}


Example part 2: Websockets Dynamic reading MQTT data on a webpage

Using HTML and some Javascript, it’s possible to for defined <id> containers on a page to be subscribed to the topic(s) and automatically updated when there are any changes.
A couple of external scripts (mqttws31.js and jquery-latest.js) will be needed.

References:
https://www.eclipse.org/paho/index.php?page=clients/js/index.php
https://jquery.com/download/

<head>
<title>Goes WHERE</title>

    <script src="mqttws31.js" type="text/javascript"></script>
    <script src="scripts/jquery-latest.js"></script>
    <script type="text/javascript">

    host = 'xxx.xxx.xxx.xxx';	// Broker hostname or IP address
    port = 8000;
    topic = '#';		// topic to subscribe to
    useTLS = false;
    username = null;
    password = null;
    cleansession = true;
    var mqtt;
    var reconnectTimeout = 4000;
    var mqttid = 'This client';  /////// Important to set the name of the mqtt toopics for this client////
    function MQTTconnect() {
	if (typeof path == "undefined") {
		path = '/mqtt';
	}
	mqtt = new Paho.MQTT.Client(
			host,
			port,
			path,
			"web_" + parseInt(Math.random() * 100, 10)
	);
        var options = {
            timeout: 3,
            useSSL: useTLS,
            cleanSession: cleansession,
            onSuccess: onConnect,
            onFailure: function (message) {
                $('#status').val("Connection failed: " + message.errorMessage + "Retrying");
                setTimeout(MQTTconnect, reconnectTimeout);
            }
        };
        mqtt.onConnectionLost = onConnectionLost;
        mqtt.onMessageArrived = onMessageArrived;
        if (username != null) {
            options.userName = username;
            options.password = password;
        }
        console.log("Host="+ host + ", port=" + port + ", path=" + path + " TLS = " + useTLS + " username=" + username + " password=" + password);
        mqtt.connect(options);
    }
    function onConnect() {
        $('#status').val('Connected to ' + host + ':' + port + path);
        mqtt.subscribe("Lounge_Humidity", {qos: 1});
        mqtt.subscribe("Lounge_Temp", {qos: 1});
        $('#topic').val(topic);
    }
    function onConnectionLost(response) {
        setTimeout(MQTTconnect, reconnectTimeout);
        $('#status').val("connection lost: " + responseObject.errorMessage + ". Reconnecting");
    };
    function onMessageArrived(message) {
        var topic = message.destinationName;
        var payload = message.payloadString;
        if (topic === "Lounge_Humidity") {
        document.getElementById("LoungeHumidity").innerHTML = payload;
        }
        if (topic === "Lounge_Temp") {
        document.getElementById("LoungeTemp").innerHTML = payload ;
        }
    };

    $(document).ready(function() {
        MQTTconnect();
    });
    </script>
  </head>
  <body>
<h2>Demonstration of MQTT with Websockets</h2>

    <topleft><h3 div id='LoungeHumidity'></div></h3></topleft>
    <topright><h3 div id='LoungeTemp'></div></h3></topright>


</body>
</html>

Use css to style the page into sections as you wish….

Leave a comment