Secure connection to an Azure Function
In this section, the MKR1000 will be configured to connect directly to an Azure Function with TLS. Unlike the Particle Photon, where WiFi connectivity is a system level configuration, the MKR1000 requires you to configure WiFi in your code. For those comfortable with the Arduino ecosystem, this is not surprising. The steps to connect to the Azure Function are:
- Configure WiFi on the device
- Configure the connection for TLS (requires the certificate configuration as shown in Arduino toolchain)
- Get temperature and humidity readings from a DHT22 sensor
- Send HTTP POST request to the Azure Function
Configure WiFi
Unlike the Particle Photon, you need to load a library to configure WiFi in your sketch. That library is the WiFi101 library. To install it, use the Library Manager (Sketch > Include Library > Manager Libraries) and search for WiFi. Then click the WiFi101 library and install the latest version.
In your code, you need to use it, together with the SPI.h library which is used for communicating with the WiFi chip:
#include <SPI.h>
#include <WiFi101.h>
To configure your WiFi network, define two char arrays to hold the SSID of your WiFi network and the WEP or "WPA2 personal" password. Hopefully you use one of the two and certainly the latter.
char ssid[] = "SSID";
char pass[] = "PASSWORD";
Now you need some code to connect to your WiFi network. The setup() function is mostly WiFi initialization, except for the DHT sensor setup. Before setup, we declare an integer variable called status to hold the WiFi status (connected or not).
Note that because we now write firmware that we flash over a USB cable, we can use the Serial object to log messages to the serial port. Use the Serial Monitor (CTRL-SHIFT-M) to view these messages.
int status = WL_IDLE_STATUS;
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();
}
The code mostly speaks for itself. Although not required in our case since the MKR1000 does not have a WiFi shield, the code first checks if the WiFi chip is present. When it is, we keep trying to connect to the network with WiFi.begin(ssid, pass). When that succeeds, status is set to WL_CONNECTED and we can continue.
When you run the sketch without a serial connection, remove the while (!Serial) { } loop or adjust it to break out of the loop after a couple of seconds.
When the connection to the WiFi network succeeds, some information about the connection is printed with printWiFiStatus(). That function, taken from the Arduino website, is defined as:
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");
}
WiFiSSLClient
The WiFiSSLClient class is part of the WiFi101 library and allows us to create a client that connects to the specified IP address or hostname in SSL. In our code, an instance of the class is declared after some other variables for hostname, uri and port:
char hostname[] = "FUNCNAME.azurewebsites.net"; // server address
char uri[] = "/api/HttpParticleTrigger?code=WFYE2oFZ1XnScqhbBRhcuB20OyUeDvTlqbaj8uF718EkYPytRtjcOg==";
int port = 443;
WiFiSSLClient client;
Because we will connect securely to an Azure Function, the hostname will be FUNCNAME.azurewebsites.net where FUNCNAME is the name you gave to your Function App. Because we use the same Azure Function as in the HTTP section, the uri is the same. Because the Azure Function requires authorization, include the code in your request.
To connect to the server, you will later use
client.connectSSL(hostname, port)
but you can also use client.connect(hostname, port) because the WiFiSSLClient will always connect with SSL!
HTTP POST to Azure Function
In loop() the code repeatedly posts temperature and humidity data from a DHT22 sensor to the Azure Function. To mix things up a little, two things are done differently:
- The Json string is built manually using the String class
- A raw HTTP POST is performed over the TLS connection created by client.connectSSL
Let's take a look at the code (after collecting the DHT22 readings in h and t which are discussed later):
String postData = "{\"temperature\":" + String(t) + ",\"humidity\":" + String(h) +"}";
Serial.println(postData);
client.stop();
Serial.println("Begin HTTP request");
if (client.connectSSL(hostname, port)) {
Serial.println("Client connected");
client.print("POST ");
client.print(uri);
client.println(" HTTP/1.1");
client.println("Host: FUNCNAME.azurewebsites.net");
client.println("Content-Type: application/json");
client.println("Connection: close");
client.print("Content-Length: ");
client.println(postData.length());
client.println();
client.println(postData);
client.println();
}
else {
Serial.println("Connection failed");
}
It should be clear that, when we successfully connect with client.connectSSL, the HTTP communication is done manually by sending the required commands over the connection. Your browser sends similar commands to a web server when it requests pages and other artifacts like images and .css files. The commands that are sent are similar to what is shown below:
POST /api/HttpParticleTrigger?code=WFYE2oFZ1XnScqhbBRhcuB20OyUeDvTlqbaj8uF718EkYPytRtjcOg== HTTP/1.1
Host: FUNCNAME.azurewebsites.net
Content-Type: application/json
Connection: close
Content-Length: POSTDATALENGTH
{"temperature":24.80,"humidity":33.40}
Because we set Content-Type to application/json, the body should contain Json and that is exactly what we send on the last line. Note that your need empty lines before and after the body!
DHT22 sensor
You should already by familiar with the DHT22 sensor because we used it before with the Particle Photon. To use it with the MKR1000, use the Library Manager to install the DHT sensor library:
In addition, use the Add .ZIP Library menu (from Sketch > Library Manager) to add the library inside the zip file downloaded from https://github.com/adafruit/Adafruit_Sensor/releases (download the last version). After adding these libraries, the code is identical to the Particle Photon code.
For reference, here is the full code to connect to an Azure Function at regular intervals:
#include <DHT.h>
#include <SPI.h>
#include <WiFi101.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[] = "WIFISSID";
char pass[] = "WIFIPASS";
// initial WiFi status
int status = WL_IDLE_STATUS;
// address to connect to
char hostname[] = "FUNCNAME.azurewebsites.net"; // server address
char uri[] = "/api/HttpParticleTrigger?code=WFYE2oFZ1XnScqhbBRhcuB20OyUeDvTlqbajw98418EkYPytRtjcOg==";
int port = 443;
WiFiSSLClient client;
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() {
// wait between measurement
delay(2000);
// 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
String postData = "{\"temperature\":" + String(t) + ",\"humidity\":" + String(h) +"}";
Serial.println(postData);
client.stop();
Serial.println("Begin HTTP request");
if (client.connectSSL(hostname, port)) {
Serial.println("Client connected");
client.print("POST ");
client.print(uri);
client.println(" HTTP/1.1");
client.println("Host: FUNCNAME.azurewebsites.net");
client.println("Content-Type: application/json");
client.println("Connection: close");
client.print("Content-Length: ");
client.println(postData.length());
client.println();
client.println(postData);
client.println();
}
else {
Serial.println("Connection failed");
}
}
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");
}
Although this code is pretty minimal, it demonstrates that using TLS from a microcontroller does not have to be very complicated as long as the hardware and libraries provide the necessary support. Let's hope MQTT over TLS is just as easy!