MKR1000 and MQTT over TLS

In the previous section, we learned how to connect to an Azure Function over TLS. We will now apply that knowledge to connect to an MQTT server securely. The code will use the same CloudMQTT server that was discussed in the MQTT section, where we connected to the server with authentication but without TLS.

There are many MQTT libraries available but I will use PubSubClient. PubSubClient itself does not natively support TLS. For connectivity however, it sits on top of EthernetClient or WifiClient. Since WifiSSLClient also uses TLS in its connect() method, we can use WifiSSLClient in combination with PubSubClient like below (first install the PubSubClient library in the IDE):

#include <SPI.h> 
#include <WiFi101.h>

#include <PubSubClient.h>

WiFiSSLClient wifiClient;
PubSubClient client(mqtt_server, mqtt_port, callback, wifiClient);

The PubSubClient instance client takes four parameters in its constructor:

  • mqtt_server: a char array with the hostname of your CloudMQTT instance
  • mqtt_port: an int with the TLS port of the CloudMQTT instance
  • callback: refers to a callback function that is called when a message arrives on a subscribed topic
  • wifiClient: WiFiSSLClient instance that will use TLS when PubSubClient calls its connect() method

The MQTT code is fairly similar to the MQTT library we used with the Particle Photon. First we set some MQTT connectivity variables (from the CloudMQTT dashboard) :

// MQTT connectivity
char mqtt_client[] = "MQTTCLIENT";
char mqtt_server[] = "INSTANCE.cloudmqtt.com";
int mqtt_port = CLOUDMQTTPORT;
char mqtt_user[] = "CLOUDMQTT USER";
char mqtt_pass[] = "CLOUDMQTT PASSWORD";

The callback function just prints the payload to Serial. Remember that the callback function is called when a message arrives on a subscribed topic.

void callback(char* topic, byte* payload, unsigned int length) {

  Serial.print("MQTT message arrived: ");
  for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

In loop() we start with:

// connect or reconnect to MQTT server
  if (!client.connected()) {
    reconnect(); // blocking
  }

If the MQTT client is not connected, the reconnect() function is run. That function keeps on trying to connect:

void reconnect() {
  // Loop until we're reconnected

  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(mqtt_client, mqtt_user, mqtt_pass)) {
      Serial.println("connected");
      // resubscribe
      client.subscribe("gebaCommand");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");

      // Wait 5 seconds before retrying (blocking)
      delay(5000);
    }
  }
}

Publishing the Json payload is very simple:

// build Json string and convert to char array for client.publish
String postData = "{\"temperature\":" + String(t) + ",\"humidity\":" + String(h) +"}";
char postBuffer[postData.length()+1];
postData.toCharArray(postBuffer, postData.length()+1);

Serial.println(postBuffer);

// publish message to MQTT server
client.publish("gebaTopic", postBuffer);

client.loop();

Note that client.publish() requires a char array instead of a String so some code is included to convert the String using the toCharArray method. Note that it is generally preferred to work exclusively with a char array in such a case.

For reference, the full sketch is included below:

#include <DHT.h>

#include <SPI.h> 
#include <WiFi101.h>

#include <PubSubClient.h>

#define DHTPIN            2         // Pin which is connected to the DHT sensor.
#define DHTTYPE           DHT22     // DHT 22 (AM2302)

DHT dht(DHTPIN, DHTTYPE);

// ssid and password of WiFi network
char ssid[] = "SSID";    
char pass[] = "WIFIPASSWORD";

// initial WiFi status
int status = WL_IDLE_STATUS;

// MQTT connectivity
char mqtt_client[] = "MQTTCLIENT";
char mqtt_server[] = "CLOUDMQTTINSTANCE.cloudmqtt.com";
int mqtt_port = CLOUDMQTTPORT;
char mqtt_user[] = "CLOUDMQTTUSER";
char mqtt_pass[] = "CLOUDMQTTPASS";

// MQTT callback
void callback(char* topic, byte* payload, unsigned int length) {

  Serial.print("MQTT message arrived: ");
  for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

// use the generic SSL client in combination with PubSubClient
WiFiSSLClient wifiClient;
PubSubClient client(mqtt_server, mqtt_port, callback, wifiClient);


void setup() {
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for the serial port to not miss messages
  }


  Serial.print("Checking for WiFi: ");
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("NOT AVAILABLE!");
    return;
  }
  Serial.println("PRESENT!");

  // try to connect to WiFi network
  while ( status != WL_CONNECTED) {
    Serial.print("Connecting to: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    // wait 10 seconds for connection:
    delay(10000);
  }

  // we are connected now
  Serial.println("Connected to WiFi!");
  printWiFiStatus();

  dht.begin();

}

void loop() {
  // connect or reconnect to MQTT server
  if (!client.connected()) {
    reconnect(); // blocking
  }

  // wait between measurement
  delay(5000);

  // post temperature and humidity from DHT22 to Azure Function
  float h = dht.readHumidity();
  // Read temperature as Celsius
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return;
  }

  // build Json string and convert to char array for client.publish
  String postData = "{\"temperature\":" + String(t) + ",\"humidity\":" + String(h) +"}";
  char postBuffer[postData.length()+1];
  postData.toCharArray(postBuffer, postData.length()+1);

  Serial.println(postBuffer);

  // publish message to MQTT server
  client.publish("PUBLISHTOPIC", postBuffer);

  client.loop();

}

void printWiFiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void reconnect() {

  // Loop until we're reconnected

  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(mqtt_client, mqtt_user, mqtt_pass)) {
      Serial.println("connected");
      // resubscribe
      client.subscribe("SUBSCRIBETOPIC");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");

      // Wait 5 seconds before retrying (blocking)
      delay(5000);

    }

  }

}

Do not forget to configure the root certificate for the WiFi chip using the procedure described in Arduino toolchain. Use api.cloudmqtt.com:443 to obtain the certificate used by CloudMQTT.

results matching ""

    No results matching ""