I had read about how Azure Event Hubs supported both Advanced Message Queuing Protocol(AMQP) & Hypertext Transfer Protocol (HTTP) access and was keen to see how easy the REST API was to use from a .Net Microframework (NetMF) device.
My initial concept was an exercise monitoring system with a Global Positioning System (GPS) unit and a pulse oximeter connected to a FEZ Spider device. My posting GPS Tracker Azure Service Bus has more info about GPS Drivers and Azure Service Bus connectivity.
The software was inspired by the Service Bus Event Hubs Getting started, Scale Out Event Processing with Event Hubs,Service Bus Event Hubs Large Scale Secure Publishing and OBD Recorder for .Net Micro Framework with ServiceBus, AMQP (for IoT) samples. I created an Event Hub and associated device access keys and fired up Service Bus Explorer so I could monitor and tweak the configuration.
I started by porting the REST API SendMessage implementation of Service Bus Event Hubs Large Scale Secure Publishing sample to NetMF. My approach was to get the application into my local source control and then cut ‘n’ paste the code into a NetMF project and see what breaks. I then modified the code over several iterations so it ran on both the desktop and NetMF clients.
The next step was to download the HTTPS certificates and add them to the project as resources so the requests could be secured. See this post for more detail.
For the connection to be secured you need to set the local time (so the certificate valid to/from can be checked) and load the certificates so they can be attached to the HTTP requests
void ProgramStarted() { ... Microsoft.SPOT.Hardware.Utility.SetLocalTime(NtpClient.GetNetworkTime()); caCerts = new X509Certificate[] { new X509Certificate(Resources.GetBytes(Resources.BinaryResources.Baltimore)) };
I used the Network Time Protocol (NTP) library from the OBD Recorder for .Net Micro Framework sample to get the current time.
The Service Bus Event Hubs Large Scale Secure Publishing uses an asynchronous HTTP request which is not available on the NetMF platform. So I had to replace it with a synchronous version.
static void EventHubSendMessage(string eventHubAddressHttps, string token, string messageBody) { try { HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(eventHubAddressHttps + "/messages" + "?timeout=60" + ApiVersion); { ... request.Headers.Add("Authorization", token); request.Headers.Add("ContentType", "application/atom+xml;type=entry;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); } }
The code to generate the SAS Token also required some modification as string.format, timespan, and SHA256 functionality are not natively available on the .NetMF platform. The GetExpiry, and SHA256 implementations were part of the OBD Recorder for .Net Micro Framework sample.
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; } static uint GetExpiry(uint tokenLifetimeInSeconds) { const long ticksPerSecond = 1000000000 / 100; // 1 tick = 100 nano seconds</code> DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0); TimeSpan diff = DateTime.Now.ToUniversalTime() - origin; return ((uint)(diff.Ticks / ticksPerSecond)) + tokenLifetimeInSeconds; } 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; }
The HttpUtility class came from the OBD Recorder for .Net Micro Framework sample. The Base64NetMf42ToRfc4648 functionality is still necessary on NetMF 4.3.
After a couple of hours I had data upload working.(No GPS data as the device was running on my desk where GPS coverage is poor)