Aegidius
 Plüss   Aplulogo
     
 www.aplu.ch      Print Text 
© 2021, V10.4
 
 
 
Examples 3: MQTT Communication
Arduino
 

 

MQTT (Message Queuing Telemetry Transport) is a widely used communication protocol that provides data exchange over the Internet. MQTT is a useful alternative to HTTP communication, especially for communication with microcontrollers and mobile devices where resources are scarce. MQTT also communicates via TCP, but very economically. In contrast to HTTP requests, which require a lot of data for the protocol header, the MQTT overhead is only a few bytes. In addition, clients do not need a fixed IP address and can be both sender and receiver. An MQTT client can publish information on a freely chosen topic. Other clients can subscribe to these topics. The central component of MQTT is a so-called MQTT broker, which manages the communication. The publisher sends his information to the broker, who forwards it to all subscribers. There is no automatic feedback to the publisher as to whether and who has actually read the information. The data is sent to the broker in real time and the broker forwards it immediately to the subscribers. This means that the data is not stored by the broker, as is the case with most cloud application.

Typically, the publisher is a microcontroller that collects measurement data, such as air temperature or other parameters of a system. It publishes the data periodically or triggered by specific event and the information can be retrieved by any number of subscribers. MQTT brokers can be found on the Internet, providing their services free of charge. For example, you can use the following hosts:

test.mosquitto.orgm2m.eclipse.org, broker.hivemq.com

The connection to one of these servers is established via a WLAN access point (SSID/password required)).

ex3_1

In this first example the Arduino publishes sensor data from an environment sensor (temperature/humidity). Here one of the the precise Sensirion sensors SHT31, SHT35 or SHT85 is used, but any other sensor data (luminosity, vibration, PIR motion detection, etc.) could be published. The information is retrieved from the MQTT broker with a smartphone or a PC.

ex3_3

PublishEnviro.ino creates the connection to the broker "broker.hivemq.com" via an access point and publishes the measured temperature and humidity under the topic /hb9abh/home/temp and /hb9abh/home/humi about every 10 seconds.

// PublishEnviro.ino

#include <linkup.h>
#include <sht.h>

const char host[] = "broker.hivemq.com";

void setup()
{
  Serial.begin(9600);
  Wire.begin(); 

  while (true)
  {
    Serial.println("Start delaying...");
    delay(5000); 
    
    Serial.println("Connecting to WLAN...");
    char reply[40];
    connectAP("myssid", "mypassword", reply);
    if (strlen(reply) > 0)
      Serial.println("Connection to WLAN successful");
    else
    {
      Serial.println("Connection to WLAN failed");
      continue;
    }
  
    mqtt_broker(host, 1883, "", "", 0);
    Serial.println("Connecting to MQTT broker...");
    if (mqtt_connect(true))
      Serial.println("Connection to broker established");
    else
    {  
      Serial.println("Connection to broker failed");
      continue;
    }

    while (true)
    {
      char buf[64];
      float temp;
      float humi;
      getTempHumi(temp, humi);
      char temp_s[20];
      dtostrf(temp, 4, 1, temp_s);
      strcat(temp_s, " C");
      char humi_s[20];
      dtostrf(humi, 4, 0, humi_s);
      strcat(humi_s, " %");
      sprintf(buf, "temp: %s - humi: %s\n", temp_s, humi_s);
      Serial.print(buf);
  
      if (mqtt_publish("/hb9abh/home/temp", temp_s, false, 0) &&
            mqtt_publish("/hb9abh/home/humi", humi_s, false, 0))
        Serial.println("Publish successful");
      else
      {       
        Serial.println("Publish failed");
        break;
      }
      Serial.println("Wait for next publishing");
      delay(10000);    // publish interval
    }  
  }
}

void loop()
{}
 

As you see, the program is robust, e.g. runtime errors are detected by the return values of the mqtt functions and the connection is restarted at the top level

You can use an MQTT client application as a subscriber, which is installed on a PC or smartphone, e.g.
  ex3_4

To retrieve the data with a smartphone or PC, select the same broker and subscribe to the /hb9abh/home/temp and /hb9abh/home/temp topics with Add Subscriber.

ex3_5 ex3_6
Subscription with MQTTBox
with MQTTDash

In the next example MQTT is used to create a remote control system where the Arduino receives commands to turn on a device. This can be a 12V LED lamp switched on by a relay such as the Grove device that uses very little actuating current and can be driven directly from a GPIO port. The Arduino also publishes its state in regular intervals. As good side effect this keeps the connection to the broker open without the need to send "PING" requests.

wy3_7

The program sets Port 13 to 0 or 1 and shows the current state on the on-board LED. The Arduino subscribes to the topic /hb9abh/lamp and listens for the payload "on" or "off" sent by any publisher (PC, tablet, smartphone, etc.). You can run it, even if you don't have Port 13 connected to an external device such as a relay.

The Arduino periodically calls the function mqtt.receive() in a loop (here about every second), which returns the MQTT message in char array parameters topic and payload. Both are empty if no message has arrived. (If several messages have arrived since the last call, they are stored in a receive buffer and the "oldest" message is returned for each call.). If mqtt.receive() returns false, the connection to the broker is lost.

// SubscribeSwitch.ino

#include <linkup.h>
#include <sht.h>

char host[] = "broker.hivemq.com"; 
char topic_switch[] = "/hb9abh/lamp";
char topic_state[] = "/hb9abh/lamp/state";
char state[10];
char buf[100];
char topic[64];
char payload[64];

void setup()
{
  Serial.begin(9600);
  Wire.begin(); 
  pinMode(13, OUTPUT);
  char reply[20];

  while (true)
  {
    digitalWrite(13, LOW);
    Serial.println("Start delaying...");
    delay(5000); 
  
    Serial.println("Connecting to WLAN...");
    connectAP("myssid", "mypassword", reply);
    if (strlen(reply) > 0)
      Serial.println("Connection to WLAN successful");
    else
    {
      Serial.println("Connection to WLAN failed");
      continue;
    }
    
    mqtt_broker(host, 1883, "", "", 0);
    Serial.println("Connecting to MQTT broker...");
    if (mqtt_connect(true))
      Serial.println("Connection to broker established");
    else
    {  
      Serial.println("Connection to broker failed");
      continue;
    }
  
    if (mqtt_subscribe(topic_switch, 0))
      Serial.println("Subscription successful");
    else
    {
      Serial.println("Subscription failed");
      continue;
    }
    int count = 0;
    strcpy(state, "OFF");
    while (true)
    {
      count += 1;
      if (count % 10 == 0)
      {
        sprintf(buf, "Pubishing topic: %s - payload: %s", topic_state, state);
        Serial.println(buf);
        if (!mqtt_publish(topic_state, state, false, 0))
        {
          Serial.println("Error. Publish failed");
          break;
        }
      }  
      // Checking for message
      if (!mqtt_receive(topic, payload))
      {
        Serial.println("Error. Connection to broker lost");
        break;
      }
      // payload empty if no message received    

      if (strcmp(payload, "on") == 0)
      {
          digitalWrite(13, HIGH);
          Serial.println("Switched on");
          strcpy(state, "ON");
      }
      if (strcmp(payload, "off") == 0)
      {
          digitalWrite(13, LOW);
          Serial.println("Switched off");
          strcpy(state, "OFF");
      }
      delay(1000);
    }  
  }
}

void loop()
{}

The program is quite long because it also writes state information to the console. It is robustly written, i.e. if an error occurs somewhere, the connection is restarted. If you use MQTTTDash on your smartphone, you can switch your device from anywhere in the world. The system has been successfully used to water the home garden during stays abroad. For such applications it is safer to use a non-public broker such as cloudmqtt.com.

ex3_8

To demonstrate the program logic, the error handling can be removed. The result is a highly simplified program.

// SubscribeSwitchSimple.ino

#include <linkup.h>
#include <sht.h>

char host[] = "broker.hivemq.com"; 
char topic_switch[] = "/hb9abh/lamp";
char topic_state[] = "/hb9abh/lamp/state";
char state[10];
char buf[100];
char topic[64];
char payload[64];

void setup()
{
  Serial.begin(9600);
  Wire.begin(); 
  pinMode(13, OUTPUT);
  char reply[20];

  while (true)
  {
    digitalWrite(13, LOW);
    delay(5000); 
  
    connectAP("myssid", "mypassword", reply);
    mqtt_broker(host, 1883, "", "", 0);
    mqtt_connect(true);
    mqtt_subscribe(topic_switch, 0);
    int count = 0;
    strcpy(state, "OFF");
    while (true)
    {
      count += 1;
      if (count % 10 == 0)
        mqtt_publish(topic_state, state, false, 0)
      mqtt_receive(topic, payload);
      if (strcmp(payload, "on") == 0)
      {
          digitalWrite(13, HIGH);
          strcpy(state, "ON");
      }
      if (strcmp(payload, "off") == 0)
      {
          digitalWrite(13, LOW);
          strcpy(state, "OFF");
      }
      delay(1000);
    }  
  }
}

void loop()
{}