Over the last couple of weeks I have been beta testing a Netduino 3 Wifi board. One of the great features of the new board is baked in support for SSL 3.0 and TLS 1.2 which enables direct connection to services which require https.
EDIT: The device has been running under my desk powered by a wall wart for a week. It has been monitoring the temperature of my office and the air gap between the curtains and the glass. My ADSL has gone down a couple of times but the N3 has recovered all by itself and kept on going.
In a couple of previous blog posts I have shown how to upload data to an Azure Event Hub from other NetMFdevices. I built an application that runs on a FEZ Spider and a lightweight Service Gateway so I was keen to see how well a Netduino 3 Wifi based solution worked.
To get something working on my Netduino 3 device I started with the application I had written for the FEZ spider. The code is based on OBD Recorder for .Net Micro Framework with ServiceBus, AMQP (for IoT) samples. The test client used a couple of DS18B20 temperature sensors to monitor the temperature of my fridge and freezer.

Netduino 3 device with Seeedstudio Shield and two temperature sensors
I created an Event Hub and associated device access keys and fired up Service Bus Explorer so I could see what was happening.
In the application the first step was to add code to wait for the device to acquire an IP address. (Will replace this code with a more efficient approach)
// Wait for Network address if DHCP
NetworkInterface networkInterface = NetworkInterface.GetAllNetworkInterfaces()[0];
if (networkInterface.IsDhcpEnabled)
{
Debug.Print(" Waiting for IP address " );
while (NetworkInterface.GetAllNetworkInterfaces()[0].IPAddress == IPAddress.Any.ToString()) ;
}
// Display network config for debugging
Debug.Print("Network configuration");
Debug.Print(" Network interface type: " + networkInterface.NetworkInterfaceType.ToString());
Debug.Print(" MAC Address: " + BytesToHexString(networkInterface.PhysicalAddress));
Debug.Print(" DHCP enabled: " + networkInterface.IsDhcpEnabled.ToString());
Debug.Print(" Dynamic DNS enabled: " + networkInterface.IsDynamicDnsEnabled.ToString());
Debug.Print(" IP Address: " + networkInterface.IPAddress.ToString());
Debug.Print(" Subnet Mask: " + networkInterface.SubnetMask.ToString());
Debug.Print(" Gateway: " + networkInterface.GatewayAddress.ToString());
foreach (string dnsAddress in networkInterface.DnsAddresses)
{
Debug.Print(" DNS Server: " + dnsAddress.ToString());
}
deviceId = BytesToHexString(networkInterface.PhysicalAddress);
Then to send the message to the event hub the request has to have a authorisation token attached
private void EventHubSendMessage(string eventHubAddressHttps, string messageBody)
{
string token = CreateSasToken(eventHubAddressHttps + "/messages", sasKeyName, sasKeyText);
try
{
using( HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(eventHubAddressHttps + "/messages" + "?timeout=60" + ApiVersion))
{
request.Timeout = 2500;
request.Method = "POST";
// Enable these options to suit your environment
//request.Proxy = new WebProxy("myproxy.myorganisation.com", true);
//request.Credentials = new NetworkCredential("myusername", "mytopsecretpassword");
request.Headers.Add("Authorization", token);
request.Headers.Add("ContentType", "application/json;charset=utf-8");
byte[] buffer = Encoding.UTF8.GetBytes(messageBody);
request.ContentLength = buffer.Length;
// request body
using (Stream stream = request.GetRequestStream())
{
stream.Write(buffer, 0, buffer.Length);
}
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
Debug.Print("HTTP Status:" + response.StatusCode + " : " + response.StatusDescription);
}
}
}
catch (WebException we)
{
Debug.Print(we.Message);
}
}
// Create a SAS token for a specified scope. SAS tokens are described in http://msdn.microsoft.com/en-us/library/windowsazure/dn170477.aspx.
private static string CreateSasToken(string uri, string keyName, string key)
{
// Set token lifetime to 20 minutes. When supplying a device with a token, you might want to use a longer expiration time.
uint tokenExpirationTime = GetExpiry(20 * 60);
string stringToSign = HttpUtility.UrlEncode(uri) + "\n" + tokenExpirationTime;
var hmac = SHA.computeHMAC_SHA256(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(stringToSign));
string signature = Convert.ToBase64String(hmac);
signature = Base64NetMf42ToRfc4648(signature);
string token = "SharedAccessSignature sr=" + HttpUtility.UrlEncode(uri) + "&sig=" + HttpUtility.UrlEncode(signature) + "&se=" + tokenExpirationTime.ToString() + "&skn=" + keyName;
return token;
}
private static string Base64NetMf42ToRfc4648(string base64netMf)
{
var base64Rfc = string.Empty;
for (var i = 0; i < base64netMf.Length; i++)
{
if (base64netMf[i] == '!')
{
base64Rfc += '+';
}
else if (base64netMf[i] == '*')
{
base64Rfc += '/';
}
else
{
base64Rfc += base64netMf[i];
}
}
return base64Rfc;
}
static uint GetExpiry(uint tokenLifetimeInSeconds)
{
const long ticksPerSecond = 1000000000 / 100; // 1 tick = 100 nano seconds
DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
TimeSpan diff = DateTime.Now.ToUniversalTime() - origin;
return ((uint)(diff.Ticks / ticksPerSecond)) + tokenLifetimeInSeconds;
}
The initial version of the Netduino TI CC3100 driver has some limitations e.g. no server certificate validation but these should be attended to in future releases.
The software was based on Brad’s One-Wire and DS18B20 library with fixes from here.