MQTT

Disclaimer

This is a copy of Victor and Guillem’s session on MDEF program. Complete version here

Do it yourself

If you want to make your own Raspberry PI - MQTT/Node-red server, go here! https://gitlab.com/fablabbcn-projects/learning/multinetworking/-/tree/master

MQTT was developed by Andy Stanford-Clark (IBM) and Arlen Nipper (Eurotech; now Cirrus Link) in 1999 for the monitoring of an oil pipeline through the desert.

The goals were to have a protocol, which is bandwidth-efficient and uses little battery power, because the devices were connected via satellite link and this was extremely expensive at that time.

The protocol uses a publish/subscribe architecture wich is event-driven and enables messages to be pushed to clients. The central communication point is the MQTT broker, it is in charge of dispatching all messages between the senders and the rightful receivers. Each client that publishes a message to the broker, includes a topic into the message.

The topic is the routing information for the broker. Each client that wants to receive messages subscribes to a certain topic and the broker delivers all messages with the matching topic to the client. Therefore the clients don’t have to know each other, they only communicate over the topic.

This architecture enables highly scalable solutions without dependencies between the data producers and the data consumers. source

A topic is a simple string defined by the user that can have more hierarchy levels, which are separated by a slash.

input/antonio/temperature
ouput/pepito/motor

Wilcards can also be used in sigle leves ej. input/+/temperature will return temperatures of all users. Or in multilevels: output/# will return all outputs from all users.

MQTT on Arduino IDE

You can find the full API documentation for the PubSubClient library here

For the first test you can copy/paste the code example in this document.

Setup WiFi

Change WiFi settings and mqtt_server address.

const char* ssid = "YOURWIFIHERE";
const char* password = "YOURPASSWORDHERE";
const char* mqtt_server = "SERVERADDRESS";
const char* mqtt_user = "MQTTUSER";
const char* mqtt_pass = "MQTTPASS";

Receiving data

// SET THE TOPIC TO SUBSCRIBE HERE
client.subscribe("testing");
// USE RECEIVED DATA HERE
if (strPayload.toInt() > 5) digitalWrite(LED_BUILTIN, LOW);
else digitalWrite(LED_BUILTIN, HIGH);

Publishing data

// READ YOUR SENSOR DATA HERE
float value = analogRead(A0);

// Send value as characters
char msg[50];
snprintf (msg, 50, "%f", value);
Serial.print("Publish message: ");
Serial.println(msg);

// SET THE TOPIC TO PUBLISH HERE
client.publish("outTopic", msg);

Code example

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// SETUP WIFI CREDENTIALS
const char* ssid = "YOURWIFIHERE";
const char* password = "YOURPASSWORDHERE";
const char* mqtt_server = "SERVERADDRESS";
const char* mqtt_user = "MQTTUSER";
const char* mqtt_pass = "MQTTPASS";

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  pinMode(BUILTIN_LED, OUTPUT);
  Serial.begin(9600);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  // PUT THE CODE TO START YOUR SENSORS HERE

}

void loop() {

  if (!client.connected()) reconnect();
  client.loop();

  // Publish every 1000 milliseconds
  if (millis() % 1000 == 0) {

    // READ YOUR SENSOR DATA HERE
    float value = analogRead(A0);

    // Send value as characters
    char msg[50];
    snprintf (msg, 50, "%f", value);
    Serial.print("Publish message: ");
    Serial.println(msg);

    // SET THE TOPIC TO PUBLISH HERE
    client.publish("outTopic", msg);
  }
}

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

  String strPayload = String((char*)payload);

  // Serial.println(strPayload.toFloat());
  // Serial.println(strPayload.toInt());

  // USE RECEIVED DATA HERE
  if (strPayload.toInt() > 5) digitalWrite(LED_BUILTIN, LOW);
  else digitalWrite(LED_BUILTIN, HIGH);

}

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(), mqtt_user, mqtt_pass)) {
      Serial.println("connected");

      // SET THE TOPIC TO SUBSCRIBE HERE
      client.subscribe("testing");

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

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

MQTT with python

We can use Python and paho-mqtt client. Some examples here. This option can be used if you don’t have a wifi capable module and you have a normal arduino that can send data over serial to your computer. This code example will go in your computer (we don’t provide code for the arduino here)

Read serial data from your arduino

import serial

PORT = '/dev/cu.usbmodem1421'
BAUDRATE = 115200

ser = serial.Serial(PORT, BAUDRATE)

while True:
    # Read the data 
    value = ser.readline().replace("\r\n", "")

    # Print it
    print (value)

Publish it to the MQTT broker

import paho.mqtt.client as mqtt

mqtt_broker = "SERVERADDRESS"
mqtt_user = "MQTTUSER"
mqtt_pass = "MQTTPASS"
broker_port = 1883

def on_connect(client, userdata, flags, rc):
   print("Connected With Result Code "+rc)

def on_message(client, userdata, message):
   print("Message Recieved: "+message.payload.decode())

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect(mqtt_broker, broker_port)
client.username_pw_set(username = mqtt_user, password = mqtt_pass)

client.subscribe("output", qos=1)

client.publish(topic="input", payload="Hello from python", qos = 1, retain = False)

client.loop_forever()

Code example

import paho.mqtt.client as mqtt
import time
import serial

# MQTT Stuff
mqtt_broker = "SERVERADDRESS"
mqtt_user = "MQTTUSER"
mqtt_pass = "MQTTPASS"
broker_port = 1883

# Serial stuff
PORT = '/dev/cu.usbmodem1421'
BAUDRATE = 115200

ser = serial.Serial(PORT, BAUDRATE)

def on_connect(client, userdata, flags, rc):
   print(f"Connected With Result Code: {rc}")

def on_message(client, userdata, message):
   print(f"Message Recieved: {message.payload.decode()}")
   # Do something here with the message

def on_log(client, obj, level, string):
    print (string)

def read_sensor():
    sensor_reading = str(ser.readline().replace("\r\n", ""))
    return sensor_reading

client = mqtt.Client(clean_session = True)
client.on_connect = on_connect
client.on_message = on_message
client.on_log = on_log
client.username_pw_set(username = mqtt_user, password = mqtt_pass)
client.connect(mqtt_broker, broker_port)

# Subscribe to your topic here
client.subscribe("output/yourname", qos=1)
client.publish(topic="input", payload="Hello from python", qos = 1, retain = False)

# Start looping (non-blocking)
client.loop_start()

while True:
    # Read data here
    sensor_reading = read_sensor()
    # Publish data here
    client.publish(topic="input", payload=sensor_reading, qos = 1, retain = False)
    time.sleep(5)

References