Mikrobus.Net Quail and EthClick

In my second batch of MikroElektronika Mikrobus clicks I had purchased an EthClick to explore the robustness and reliability of the Mikrobus.Net IP Stack.

My first trial application uses the Internet Chuck Norris database (ICNBD) to look up useful “facts” about the movie star.

public static void Main()
{
   EthClick ethClick = new EthClick(Hardware.SocketTwo);

   ethClick.Start(ethClick.GenerateUniqueMacAddress("devMobileSoftware"), "QuailDevice");

   while (true)
   {
      if (ethClick.ConnectedToInternet)
      {
         Debug.Print("Connected to Internet");
         break;
      }
   Debug.Print("Waiting on Internet connection");
   }

   while (true)
   {
      var r = new HttpRequest(@"http://api.icndb.com/jokes/random");

      r.Headers.Add("Accept", "*/*");

      var response = r.Send();
      if (response != null)
      {
         if (response.Status == "HTTP/1.1 200 OK")
         {
            Debug.Print(response.Message);
         }

      }
      else
      {
         Debug.Print("No response");
      }
      Thread.Sleep(10000);
   }
}

The ran first time and returned the following text

7c
{ "type": "success, "value": { "id": 496, "joke": "Chuck Norris went out of an infinite loop.", "categories": ["nerdy"]}}
0

85
{ "type": "success", "value": { "id": 518, "joke": "Chuck Norris doesn't cheat death. He wins fair and square.", "categories": []}}
0

It looks like the HTTP response parsing is not quite right as each message starts with the length of the message in bytes in hex and the terminating “0”.

Azure Event Hub Updates from a NetMF Device

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.

FEZ Spider, GPS and PulseOximeter

Fez spider and sensors for exercise monitoring device

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)

ServiceBusExplorerEventHub

Xively GPS Location data upload V2

In the previous post I assembled the xively request XML using a StringBuilder rather than using the XML support available in the NetMF. To use the NetMF XML library I needed to add a reference to the DPWS extensions (MFDpwsExtensions) and change the using statement at the top of the module from System.Text to System.Ext.Xml

static void xivelyFeedUpdate(string ApiKey, string feedId, string channel, double latitude, double longitude, double altitude)
{
byte[] buffer;

using (XmlMemoryWriter xmlwriter = XmlMemoryWriter.Create())
{
xmlwriter.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\"");
xmlwriter.WriteStartElement("eeml");
xmlwriter.WriteStartElement("environment");
xmlwriter.WriteStartElement("location");

xmlwriter.WriteStartAttribute("domain");
xmlwriter.WriteString("physical");
xmlwriter.WriteEndAttribute();

xmlwriter.WriteStartAttribute("exposure");
xmlwriter.WriteString("outdoor");
xmlwriter.WriteEndAttribute();

xmlwriter.WriteStartAttribute("disposition");
xmlwriter.WriteString("mobile");
xmlwriter.WriteEndAttribute();

xmlwriter.WriteStartElement("name");
xmlwriter.WriteString("Location");
xmlwriter.WriteEndElement();

xmlwriter.WriteStartElement("lat");
xmlwriter.WriteString(latitude.ToString("F5"));
xmlwriter.WriteEndElement();

xmlwriter.WriteStartElement("lon");
xmlwriter.WriteString(longitude.ToString("F5"));
xmlwriter.WriteEndElement();

xmlwriter.WriteStartElement("ele");
xmlwriter.WriteString(altitude.ToString("F1"));
xmlwriter.WriteEndElement();

xmlwriter.WriteEndElement();
xmlwriter.WriteEndElement();
xmlwriter.WriteEndElement();

buffer = xmlwriter.ToArray();
}

try
{
using (HttpWebRequest request = (HttpWebRequest)WebRequest.Create(xivelyApiBaseUrl + feedId + ".xml"))
{
request.Method = "PUT";
request.ContentLength = buffer.Length;
request.ContentType = "text/xml";
request.Headers.Add("X-ApiKey", xivelyApiKey);
request.KeepAlive = false;
request.Timeout = 5000;
request.ReadWriteTimeout = 5000;

// request body
using (Stream stream = request.GetRequestStream())
{
stream.Write(buffer, 0, buffer.Length);                }
using (var response = (HttpWebResponse)request.GetResponse())
{
Debug.Print("HTTP Status:" + response.StatusCode + " : " + response.StatusDescription);
}
}
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
}

I was expecting the XML libraries to be quite chunky, but on my Netduino Plus 2 there wasn’t a huge size difference, the StringBuilder download was 49K8 bytes and the XMLWiter download was 56K1 bytes.

When I ran the StringBuilder and XMLWriter versions they both had roughly 92K6 bytes of free memory.

Realistically there was little to separate the two implementations

Xively GPS Location data upload V1

For one of the code club projects we looked at the National Marine Electronics Association (NMEA) 0183 output of my iteadStudio GPS Shield + Active Antenna. We used the NetMF Toolbox NMEA GPS processing code with a couple of modifications detailed here.

IteadStudio GPS

IteadStudio GPS shield and Antenna

For another project we had used Xively a “Public Cloud for the Internet of Things”. The Xively API has support for storing the position of a “thing” and it didn’t look like it would take much effort to extend the original GPS demo to trial this. The xively Location & waypoints API is RESTful and supports JSON & XML

void xivelyFeedUpdate(string ApiKey, string feedId, string channel, double latitude, double longitude, double altitude)
{
try
{
using (HttpWebRequest request = (HttpWebRequest)WebRequest.Create(xivelyApiBaseUrl + feedId + ".xml"))
{
StringBuilder payload = new StringBuilder();
payload.Append(@"<?xml version=""1.0"" encoding=""UTF-8""?><eeml><environment><location domain=""physical"" exposure=""outdoor"" disposition=""mobile""><name>Location</name><lat>");
payload.Append(latitude.ToString("F5"));
payload.Append("</lat><lon>");
payload.Append(longitude.ToString("F5"));
payload.Append("</lon><ele>");
payload.Append(altitude.ToString("F1"));
payload.Append("</ele></location></environment></eeml>");

byte[] buffer = Encoding.UTF8.GetBytes(payload.ToString());

request.Method = "PUT";
request.ContentLength = buffer.Length;
request.ContentType = "text/xml";
request.Headers.Add("X-ApiKey", xivelyApiKey);
request.KeepAlive = false;
request.Timeout = 5000;
request.ReadWriteTimeout = 5000;

// request body
using (Stream stream = request.GetRequestStream())
{
stream.Write(buffer, 0, buffer.Length);
}
using (var response = (HttpWebResponse)request.GetResponse())
{
Debug.Print("HTTP Status:" + response.StatusCode + " : " + response.StatusDescription);
}
}
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
}

The position of the “thing” is displayed like this

Xively poisition

The position of my car

The XML was constructed using a stringbuilder (NetMF 4.2) as this appeared easier/smaller than using the baked in XML functionality.

Netduino Plus PulseRate Monitor V2

In the final couple of code club sessions we built a pulse rate monitor to show a practical application for the NetMF InterruptPort, and communication between threads using the Interlocked class (Increment & exchange). This was then enhanced to display the data locally and upload it to the cloud to illustrate a basic HTTP interaction and serial communications.

The application displays the approximate pulse rate in Beats Per Minute (BPM) on a 16×2 character LCD display and also uploads the information to a free developer account at Xively a “Public Cloud for the Internet of Things”.

Netduino Plus 2 rate monitor

The xively trial account has a limit of 25 calls a minute, rolling 3 minute average (Dec 2013) which was more than adequate for our application and many other educational projects.

The xively API supports managing products, managing devices,  reading & writing data, reading & wiring metadata, querying historical data and searching for data feeds, using a RESTful approach.

The NetduinoPlus2 has full support for the NetMF system.http and sufficient memory so that there is plenty of room left for an application. If you are using a Netduino Plus (or other NetMF device with limited memory) an approach which reduces memory consumption is detailed here.

The xively data API supports JSON, XML and CSV formats for upload of data and for the pulse rate monitor we used CSV. The following code was called roughly every 20 seconds.

static void xivelyFeedUpdate( string ApiKey, string feedId, string channel, string value )
{
try
{
using (HttpWebRequest request = (HttpWebRequest)WebRequest.Create(xivelyApiBaseUrl+ feedId + ".csv"))
{
byte[] buffer = Encoding.UTF8.GetBytes(channel + "," + value);


request.Method = "PUT";
request.ContentLength = buffer.Length;
request.ContentType = "text/csv";
request.Headers.Add("X-ApiKey", ApiKey);
request.KeepAlive = false;
request.Timeout = 5000;
request.ReadWriteTimeout = 5000;


// request body
using (Stream stream = request.GetRequestStream())
{
stream.Write(buffer, 0, buffer.Length);
}


using (var response = (HttpWebResponse)request.GetResponse())
{
Debug.Print("HTTP Status:" + response.StatusCode + " : " + response.StatusDescription);
}
}
}
catch (Exception ex)
{
Debug.Print(ex.Message);
}
}

The pulse rate information can then displayed by xively in tables and graphs.

Pulse Rate data gaph at xively

Pulse Rate data display

At the end of term presentation several parents and family members were doing press-ups and other exercises to see how high their pulse rate went and how quickly it recovered.

Bill of materials (Prices as at Dec 2013)

HTTPS with NetMF calling an Azure Service Bus endpoint

Back in Q1 of 2013 I posted a sample application which called an Azure Service Bus end point just to confirm that the certificate configuration etc. was good.

Since I published that sample the Azure Root certificate has been migrated so I have created a new project which uses the Baltimore Cyber Trust Root certificate.

The sample Azure ServiceBus client uses a wired LAN connection (original one used wifi module) and to run it locally you will have to update the Date information around line 24.

NetMF HTTP Client Possible Memory Leak

As part of another project I was using the .NetMF HTTPClient functionality to initiate an HTTP GET.

I was having problems with the application crashing after a while so fired up the Visual Studio debugger. Due to a configuration problem the application was kicking of a WebRequest every 10 secs which was significantly faster than intended.

After some poking around I found the application was crashing due to a memory leak of roughly 45K per https call (http is okay). I tried several different ways of declaring/loading the x509 certificate to see if that impacted what the GC could collect but it made little or no difference. I tried forcing a GC after each webrequest but this also appeared to make little or no difference. Basically the BINARY_BLOB_HEAD value increases until the application runs out of memory and then crashes.

To help identify the problem I built a small test harness which reliably reproduces the problem with minimum overhead. The code is available here my HTTP Client Sample.

HTTPS + certificate loaded into a global variable

7120164 bytes
7074420
7029252
6984036
6938952
6893736
6848520
6803304
6758088
6712740

HTTP + certificate loaded into a global variable

7165980 bytes
7167840
7167840
7167708
7167840
7167840
7167840
7167708
7167708
7167840

HTTP – no certificate

7165980 bytes
7167840
7167840
7167840
7167840
7167840
7167840
7167708
7167840
7167840

HTTPS – certificate loaded for every webrequest

7118640 bytes
7073028
7027860
6982644
6937296
6892080
6846864
6801780
6756432
6711348

HTTPS + certificate loaded into a global readonly variable

7118664 bytes
7072920
7027752
6982668
6937452
6892236
6846888
6801804
6756588
6711372

HTTPS Memory info – Start

Type 0F (STRING ): 3084 bytes
Type 11 (CLASS ): 14352 bytes
Type 12 (VALUETYPE ): 1512 bytes
Type 13 (SZARRAY ): 7860 bytes
Type 03 (U1 ): 3192 bytes
Type 04 (CHAR ): 852 bytes
Type 07 (I4 ): 1044 bytes
Type 0F (STRING ): 72 bytes
Type 11 (CLASS ): 2616 bytes
Type 12 (VALUETYPE ): 84 bytes
Type 15 (FREEBLOCK ): 7118664 bytes
Type 16 (CACHEDBLOCK ): 216 bytes
Type 17 (ASSEMBLY ): 32688 bytes
Type 18 (WEAKCLASS ): 96 bytes
Type 19 (REFLECTION ): 192 bytes
Type 1B (DELEGATE_HEAD ): 864 bytes
Type 1D (OBJECT_TO_EVENT ): 240 bytes
Type 1E (BINARY_BLOB_HEAD ): 152496 bytes
Type 1F (THREAD ): 1536 bytes
Type 20 (SUBTHREAD ): 144 bytes
Type 21 (STACK_FRAME ): 1632 bytes
Type 22 (TIMER_HEAD ): 216 bytes
Type 27 (FINALIZER_HEAD ): 144 bytes
Type 31 (IO_PORT ): 108 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 3552 bytes

HTTPS Memory info – Finish

Type 0F (STRING ): 3084 bytes
Type 11 (CLASS ): 14364 bytes
Type 12 (VALUETYPE ): 1512 bytes
Type 13 (SZARRAY ): 7860 bytes
Type 03 (U1 ): 3192 bytes
Type 04 (CHAR ): 852 bytes
Type 07 (I4 ): 1044 bytes
Type 0F (STRING ): 72 bytes
Type 11 (CLASS ): 2616 bytes
Type 12 (VALUETYPE ): 84 bytes
Type 15 (FREEBLOCK ): 6711372 bytes
Type 16 (CACHEDBLOCK ): 96 bytes
Type 17 (ASSEMBLY ): 32688 bytes
Type 18 (WEAKCLASS ): 96 bytes
Type 19 (REFLECTION ): 192 bytes
Type 1B (DELEGATE_HEAD ): 828 bytes
Type 1D (OBJECT_TO_EVENT ): 240 bytes
Type 1E (BINARY_BLOB_HEAD ): 559476 bytes
Type 1F (THREAD ): 1920 bytes
Type 20 (SUBTHREAD ): 192 bytes
Type 21 (STACK_FRAME ): 1632 bytes
Type 22 (TIMER_HEAD ): 216 bytes
Type 27 (FINALIZER_HEAD ): 168 bytes
Type 31 (IO_PORT ): 108 bytes
Type 34 (APPDOMAIN_HEAD ): 72 bytes
Type 36 (APPDOMAIN_ASSEMBLY ): 3552 bytes


...
using (HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(@"https://www.google.co.nz"))
{
request.Method = "GET";

request.HttpsAuthentCerts = caCerts;

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
PulseDebugLED();
}
Debug.Print(response.StatusDescription);
}
}
Debug.Print("Memory: " + Microsoft.SPOT.Debug.GC(true).ToString());
...

Time to run Reflector over the system.http assembly and see what is going on. I will also post on tinyclr.com to see if anyone else has encountered anything like this. Without my typo I most probably never have noticed this possible problem before deployment.

HTTPS with NetMF HTTP Client managing certificates

One of the services I needed to call from my Fez Spider required an HTTPS connection. The HTTP client sample located at

C:\Users\…\Documents\Microsoft .NET Micro Framework 4.2\Samples\HttpClient

shows how to load a certificate and use it when making a request.

There wasn’t a lot of information about getting the required certificate so I decided to document how I did it. On my Windows Server 2k8 box I use either a web browser or the Certificate Manager for exporting certificates. The easiest way is to use your preferred browser to access the service endpoint (To enable the export functionality you need to “Run as administrator”).

1.IECertificate

View the certificate

2.CertificateDetails

Select the root certificate

3.CertificatePath

View the root certificate information

4.CertificateRoot

View the root certificate details

5.CertificateRootDetails

Export the certificate

6.CertificateRootExport

Save CER file in the resources directory of your NetMF Project and then add it to the application resources.

If you know the Root Certification Authority you can export the certificate using Certificate Manager

Certificate Manager

Don’t forget to Update the SSL Seed using MF Deploy and ensure that the device clock is correct.

I use either an Network Time Protocol (NTP) Client or an RTC (Realtime Clock) module to set the device clock.

Depending on the application and device you might need to set the device clock every so often.

HTTP Headers GPRS Modem HTTP Post

All my initial deployments used a CAT5 cable and my ADSL connection which was great for testing and debugging but not really representative of the connectivity a mobile solution would experience.

For this test I used a seeedstudio GPRS shield on top of a Netduino Plus.

Netduino + SeeedStudio GPRS Modem

SeedStudio GPRS Modem on top of Netduino Plus

The shield is based on a SIM900 module from SIMCom Wireless and can also initiate HTTP requests. This functionality looked useful as it could make my code a bit simpler and reduce the load on the Netduino CPU.

I initially looked at a the Netduino driver for Seeeduino GSM shield on codeplex but it appeared to only support the sending of SMS messages. (Feb2012)

After some more searching I stumbled across the CodeFreak Out SeeedStudio GPRS Driver which is available as a Nuget package or source code. I had to modify the code to allow me to pass a list of HTTP headers to be added into the request

var gsm = new GPRSShield("www.vodafone.net.nz", SerialPorts.COM1);
gsm.Post(@"gpstrackerhttpheaders.cloudapp.net", 80, @"/posV4.aspx", httpHeaders, "application/html", "");

My simulated data used the same header format as in my earlier testing

x-Pos: 5C-86-4A-00-3F-63 20130218081228 F -43.00000 172.00000 31.4 1.31 0 0

I timed 10 requests

4789,3778,3793,3756,3796,3825,3806,3817,3795,3877

Average 3903 mSec

This was a bit slower than I was expecting so i’ll have to do some digging into the code and see if anything looks a bit odd.

HTTP Headers Payload encryption

So far the content of the messages has been sent as clear text which would not be acceptable for many applications. The requirement for data privacy causes a problem on the Netduino+ (Nov 2012) as the standard NetMF crypto libraries are not baked into the platform.

I then set about finding some crypto libraries which were NetMF friendly. RSA and Xtea are included in some of other NetMF platforms in the Microsoft.SPOT.Cryptography assembly so Xtea seemed like a reasonable choice to ensure interoperability.

When looking for crypto implementations one of the best sources is the Legion of the Bouncy Castle which was where I started. I downloaded the the V17.7 zip file had a look at the size of the Xtea code & all the associated support libraries and then parked that approach as I was worried about the size and performance of the resulting binaries.

I then looked at other possible tea, xtea & xxtea implementations (including porting the original C code to C#)

I am not a cryptographer so I can’t personally confirm the quality and correctness of an implementation. So after having interop problems I went back to the Legion of the Bouncy Castle which has been peer reviewed by experts and had another look. To get an Xtea implementation working on a .NetMF platform like the Netduino+ you need to include the following files…

  • CryptoException.cs
  • DataLengthException.cs
  • IBlockCipher.cs
  • ICipherParameters.cs
  • KeyParameter.cs
  • XTEAEngine.cs

On the Azure side of things where size is not so critical I just added a reference to the Bouncy Castle main project.

Xtea requires 128 bit blocks so you need to pad out the data on the client, then trim off the padding on the server.
// Pad out the data to a multiple of 8 bytes with spaces
if (xPosition.Length % 8 != 0)
{
xPosition += new string(' ', 8 - xPosition.Length % 8);
}

The key and the data need to be converted to byte arrarys, the Xtea engine initialised and a buffer for storing the encrypted data created.

byte[] dataBytes = Encoding.UTF8.GetBytes(xPosition);
byte[] keyBytes = Encoding.UTF8.GetBytes(key);

xteaEngine.Init(true, new KeyParameter(keyBytes));

Then the data can be encrypted in 8 byte chunks
byte[] cryptoBytes = new byte[dataBytes.Length];
for (int i = 0; i < dataBytes.Length; i += 8)
{
xteaEngine.ProcessBlock(dataBytes, i, cryptoBytes, i);
}

I hex encoded the encrypted data for transmission. Downside to this was it doubled the size of the payload
string hexEncodedCryptoBytes = ByteArrayToHex(cryptoBytes);

I added a StopWatch so I could measure the time taken to encrypt the position data (roughly 72 chars) on my Netduino+
285,342,277,345,282,345,342,350,278,343
Average 318mSec

The size of the payload had grown a bit
Request - Bytes Sent: 262
POST http://gpstrackerhttpheaders.cloudapp.net/posV7.aspx HTTP/1.1
Host: gpstrackerhttpheaders.cloudapp.net
x-Pos: 693A7AC6EBF4E5848CE8ABBA2BC6CAC1ED20574C1B2384E7E246A202C8A67E3DE14EE5231A5DF98C211F64F8402547F8BFDCC2241AAE3782A820086E5EF37AA2C50744941F588442
Content-Length: 0
Connection: Close

Response - Bytes Received: 132
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html
Date: Sun, 03 Feb 2013 04:53:30 GMT
Content-Length: 0

This increase in size had an impact on the time taken to send the message

1123,1144,1122,1142,1125,1125,1138,1111,1099,1141
Average 1127mSec

The binary downloaded to the Netduino+ had grown to 28K which still left plenty of space for additional functionality.