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.
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.
- Netduino Plus 3
- 2 x Grove One-Wire Temperature Sensor (USD 7.50)
- Base Shield V1.3 (USD 8.90)
Pingback: Netduino 3 Wifi Azure Service Bus client certificate issue | devMobile's blog
Pingback: My first AMQPNetLite program | devMobile's blog
Great project, do you have it on github so I can fork it?
Regards,
-ed
I can put the code on github no problem but before I do have a look at
https://blog.devmobile.co.nz/2015/11/14/netduino-3-wifi-queued-azure-event-hub-field-gateway-v1-0/
https://blog.devmobile.co.nz/2015/09/27/netduino-3-wifi-azure-event-hub-field-gateway-v2-0/
Depending on your application they maybe more useful…
@KiwiBryn
Thanks for the references, I just wanted to connect ASAP a Netduino Wifi 3 that I just bought with Azure EventHub.
Currently I am getting a System.Net.WebException at the request.GetRequestStream() line, I have been checking the request that I am building and it’s very much what MS says here: https://msdn.microsoft.com/en-us/library/azure/dn790664.aspx
By looking closely to the stackTrace I see that the error has something to do with the SSL layer
Microsoft.SPOT.Net.Security.SslStream::Authenticate
Microsoft.SPOT.Net.Security.SslStream::AuthenticateAsClient
System.Net.HttpWebRequest::EstablishConnection
System.Net.HttpWebRequest::SubmitRequest
System.Net.HttpWebRequest::GetResponse
But I can’t seem to find the answer, I just read about reseting the SSL Seed and did it, but did’t help much (https://blogs.msdn.microsoft.com/benjaminperkins/2014/11/04/how-i-got-https-to-work-with-the-net-micro-framework-and-my-gadgeteer/)
Tha’s why I wanted to check woth your full code, in order to see if I missed something.
P.S. I was able to get a valid Http request without using secure sockets, pointing straight at http://www.gooe.com, so that means my Netduino has a valid network configuration.
Regards
-ed
Hi
I found that accessing an Azure EventHub using HTTPS was broken when Microsoft updated the SSL certificates. See this postfor more info.
I now use Paolo Patierno’s AzureSBLite library or AMQPNetLite
I have some sample code which you could use as a starting point here and then the code for the different Azure gateways has examples of how I made the recovery from failures more robust.
@KiwiBryn
Pingback: Azure IoT Hub with MQTTnet | devMobile's blog
Pingback: Azure IoT Hub SAS Keys revisited | devMobile's blog