October 2010

Volume 25 Number 10

Embedded Programming - Connected Devices Using the .NET Micro Framework

By Colin Miller | October 2010

Today, we see a proliferation of applications that include connected devices. In fact, the “Internet of Things” (devices connected over the Internet) is already estimated to be larger than the World Wide Web in terms of endpoints and is projected to grow to an order of magnitude larger in the next few years.

In the near future, we’ll be interacting with more intelligent devices instead of recognizable computers. Just look around your house. The things that can be usefully connected include your appliances (for energy management, software updates and maintenance); your car (for coordination on recharging your new electric vehicle with the grid, automatic testing and maintenance and software updates); your irrigation system (for scheduling based on weather reports and water management); your pets (to determine their location and configure invisible fencing); your thermostat (for remote control); and much more.

These devices are connected to one another, to smart controllers, to the router and to the cloud. What does this mean for Microsoft .NET Framework developers? Right now, .NET developers can produce applications for all parts of the system to which small devices connect. And with the .NET Micro Framework, .NET developers can develop the full system right down to the small devices.

The .NET Micro Framework is an implementation of the .NET Framework specifically targeting the smallest devices possible with extensions for embedded programming needs. It runs directly on the hardware without an underlying OS in order to minimize its footprint. General information is available at microsoft.com/netmf and the project’s open source community site: netmf.com.

Several months ago, I started a series of articles on the .NET Micro Framework blog (blogs.msdn.com/b/netmfteam) that describes the process of building one of these small devices—a bicycle computer—from the ground up using only the .NET Micro Framework, Visual Studio and a minimal amount of electronics. I wanted to demonstrate how .NET Framework developers can build rich applications on small devices. (The first article in this series can be found at: tinyurl.com/2dpy6rx.)  Figure 1 shows the actual computer. I picked a bicycle computer because everyone is a domain expert when it comes to bicycles. The application includes a gesture-based UI, supports a number of sensors and addresses issues like managing the battery power supply. 

image: The NETMF Bicycle Computer

Figure 1 The NETMF Bicycle Computer

The blog discusses each feature’s implementation, and the code for the project is available on CodePlex at netmfbikecomputer.codeplex.com/. However, I saved the best for last—in this article, I’m going to connect this device to a Microsoft Azure-hosted Web service over Wi-Fi. Here’s the scenario: you’ve just finished your ride and brought your bike into the garage. Your computer contains data it collected during your ride: distance, speed, cadence, incline, time and so on. You flip over to the data view and press the Upload button. The data for your ride is uploaded to the cloud, where it can be aggregated with all your other rides and shared with your friends.

In this article, I’ll focus on making the connection and uploading the data rather than discussing the form of the cloud service to which you might connect. Check out examples like bikejournal.com and cyclistats.com to see what the possibilities are for tracking your bicycling progress and competing with your friends. I’ll discuss how easy it is to make that connection.

First, a Little Background

The Web services model is great for connecting devices because it supports a full range of device and service interactions that may not be fully known at the time we create an application. On the device side, we use a subset of the full Web service infrastructure called Devices Profile for Web Services (DPWS)—see en.wikipedia.org/wiki/Devices_Profile_for_Web_Services to learn more about it. DPWS is described as Universal Plug and Play (UPNP) for networked devices. In the .NET Micro Framework, DPWS supports the WS-Addressing, WS-Discovery, WS-MetaDataExchange and WS-Eventing interfaces, and is built on the underlying technologies of SOAP, XML, HTTP, MTOM and Base64 encoding.

With DPWS, you can connect to devices that are clients (devices that consume services from others), or servers (devices that provide services for others) or both. You can negotiate what you provide and what you can consume through the metadata, and you can publish and subscribe to notifications of changes in other entities. Figure 2 shows the DPWS stack.

image: The DPWS Stack

Figure 2 The DPWS Stack

The implementation in the .NET Micro Framework supports DPWS version 1.0—which is compatible with Windows 7—and DPWS version 1.1—which is compatible with Windows Communication Foundation (WCF) 4. You can specify the binding you use for your connection: for example, SOAP over HTTP (ws2007HttpBinding) or a custom binding that you want to support.

Our application for the bicycle computer is really pretty simple—we’ll just be uploading data to the cloud. DPWS can actually do so much more. For example, let’s say my utility company installs a smart service meter that limits the resources I can use at any specific time. Along with that, I install a local energy management service that gives me control over how I use those limited resources. I can set priorities and heuristics that allow the system to make decisions about how to limit consumption—for example, hot water for showers has a high priority.

Now I go out and buy a new dishwasher. I bring it home and plug it in. In the background, the dishwasher finds the local network and “discovers” the management service. It informs the service of its power consumption in various states and rules for how it can be used. Later, when the dishwasher is running, I jump in the shower and the hot water heater comes on. But once I start the dishwasher, I don’t want it to just turn off and have the food harden on the plates. To cut total consumption, the management service tells the dishwasher to just rinse the dishes every 15 minutes to keep them from drying out, and to resume where it left off in the washing cycle when I’m done with the shower. As you can see, this entire scenario can support an arbitrary set of endpoints with the functionality defined in DPWS.

Enough Background—Let’s Make It Work

Configuring the Wi-Fi radio is easy enough. There are two ways to do this—one uses the MFDeploy.exe utility, and the other is a programmatic interface supported by the GHI SDK (from GHI Electronics). Let’s start with MFDeploy.exe, which is a tool that comes with the .NET Micro Framework and can be found in the tools section of the SDK installation. In the Target | Configuration | Network Configuration dialog box (see Figure 3), I enable DHCP, select the security mechanism and enter the pass phrase and other configuration aspects of my home network.

image: The MFDeploy Network Configuration Dialog

Figure 3 The MFDeploy Network Configuration Dialog

DHCP will take care of filling in the Gateway and DNS field of the network settings. This information is available to the managed application through the NetworkInterface type and its child Wireless80211. The HTTP stack will implicitly use this information when sending and receiving bytes, but it might need an additional piece of information to enable using a proxy—something your network might require. To help the HTTP stack use the proxy correctly, it’s advisable to add the following to a program as a further hint as to where to connect:

WebRequest.DefaultWebProxy = 
  new WebProxy("<router IP Adress>");

If there’s no explicit proxy role in your network, you can usually default to using the Gateway address. With the GHI programmatic interface, you use some derivative of the code shown in Figure 4.

Figure 4 Wi-Fi Configuration with the GHI Programmatic Interface

// -- Set up the network connection -- //
WiFi.Enable(SPI.SPI_module.SPI2, (Cpu.Pin)2, (Cpu.Pin)26);
NetworkInterface[] networks = NetworkInterface.GetAllNetworkInterfaces();
Wireless80211 WiFiSettings = null;
for (int index = 0; index < networks.Length; ++index)
{
  if (networks[index] is Wireless80211)
  {
    WiFiSettings = (Wireless80211)networks[index];
    Debug.Print("Found network: " + WiFiSettings.Ssid.ToString());
  }
}
WiFiSettings.Ssid = "yourSSID";
WiFiSettings.PassPhrase = "yourPassphrase";
WiFiSettings.Encryption = Wireless80211.EncryptionType.WPA;
Wireless80211.SaveConfiguration(
  new Wireless80211[] { WiFiSettings }, false);
_networkAvailabilityBlocking = new ManualResetEvent(false);
if (!WiFi.IsLinkConnected)
{
  _networkAvailabilityBlocking.Reset();
  while (!_networkAvailabilityBlocking.WaitOne(5000, false))
 {
    if (!WiFi.IsLinkConnected)
    {
      Debug.Print("Waiting for Network");
    }
    else
    break;
  }
}
Debug.Print("Enable DHCP");
try
{
  if (!WiFiSettings.IsDhcpEnabled)
    WiFiSettings.EnableDhcp(); // This function is blocking
  else
  {
    WiFiSettings.RenewDhcpLease(); // This function is blocking
  }
}
catch
{
  Debug.Print("DHCP Failed");
}

The Figure 4 example assumes that you’re using WPA or WPA2 security. WEP is also supported. The first thing you do is identify the SPI port and control lines used by the radio. This configuration represents the connections for the hardware, a FEZ Cobra board from GHI. All that’s required is to set up the WiFiSettings, save the configuration and call EnableDHCP. Notice that some of these calls are blocking, and this can take some time, so you’ll need to ensure that the user knows what’s happening. Also, there’s no way in this programmatic interface to enumerate the available networks so you can select from them. I hard-coded the network information in the Figure 4 example. 

For a commercial implementation of the bicycle computer, I need to write an integrated Wi-Fi configuration UI that exposes the information entered into the dialog shown in Figure 3, and an onscreen keyboard to enter it. I may have time to write this up in another blog article before this article is published. I’m working on a Time Service article that shows how to use the Wi-Fi connection to get the date and time when I start up the computer so that I don’t have to keep the device running to maintain that information or have the user (me) enter it on startup.

Setting up the Service Connection

All that remains is the DPWS implementation. As a reminder, I’m going to focus on the device side. The Azure service I’m using provides a simple “Hello World” template. I start with this and extend the contract by adding the fields that I need to store and the operations by writing the UpLoad and Get operations. This creates a service that can accept my data, store it and give me back the last data stored. Obviously, a full service requires much more work, but that’s another article. Let’s look briefly at the contract created for this service. The ServiceContract contains the two operations and the DataContract contains the fields (see Figure 5).

Figure 5 The Service Contract

[ServiceContract]
  public interface IBikeComputerService
  {
    [OperationContract]
    BikeComputerData GetLastComputerData();
    [OperationContract]
    void UploadBikeComputerData(BikeComputerData rideData);
  }
  // Use a data contract as illustrated in the sample below 
  // to add composite types to service operations.
  [DataContract]
  public class BikeComputerData
  {
    DateTime _Date;
    TimeSpan _StartTime;
    TimeSpan _TotalTime;
    TimeSpan _RidingTime;
    float    _Distance;
    float    _AverageSpeed;
    float    _AverageCadence;
    float    _AverageIncline;
    float    _AverageTemperature;
    bool     _TempIsCelcius;
            
    [DataMember]
    public DateTime Date…
    [DataMember]
    public TimeSpan StartTime…
    [DataMember]
    public TimeSpan TotalTime…
    [DataMember]
    public TimeSpan RidingTime…
    [DataMember]
    public float Distance…
    [DataMember]
    public float AverageSpeed…
    [DataMember]
    public float AverageCadence…
    [DataMember]
    public float AverageIncline…
    [DataMember]
    public float AverageTemperature…
    [DataMember]
    public bool TemperatureIsInCelcius…
  }

The actual implementation of the contract is minimal to support the bicycle computer example (see Figure 6).

Figure 6 The Contract Implementation

public class BikeComputerService : IBikeComputerService
{
  static BikeComputerData _lastData = null;
  public BikeComputerData GetLastComputerData()
  {
    if (_lastData != null)
    {
      return _lastData;
    }
    return new BikeComputerData();
  }
  public void UploadBikeComputerData(BikeComputerData rideData)
  {
    _lastData = rideData;
  }
}

The WSDL Defines the Service

From the contract and schema that I created, the Azure service auto-generates a Web Service Definition Language (WSDL) file. It contains the Service Modeling Language (SML) definition of the service. The W3C specification for the WSDL document can be found at w3.org/TR/wsdl. It defines the operations and messages that the service supports. The WSDL is stored as an XML description on a Web site, where it’s accessible to anyone connecting to the service. Ours can be found at http://netmfbikecomputer.codeplex.com/. Figure 7 shows a small snapshot of the WSDL file so you can see what it looks like, but remember that this file is auto-generated and is only consumed by other programs. You don’t need to ever write this complex and touchy XML.

image: The WSDL File
(click image to zoom)

Figure 7 The WSDL File

You can see that the WSDL includes the definitions for messages for data input and output and operations for getting and uploading data. Now that we have our simple interface to the service defined and published, how do I program to that? That’s pretty easy as well.

Generating Code with MFSvcUtil.exe

On the desktop, there’s a utility called the ServiceModel MetadataUtility Tool (SvcUtil.exe) that can generate service model code from metadata documents (like our WSDL) and vice versa. The .NET Micro Framework has a similar utility called MFSvcUtil.exe. This is a command-line tool that’s best run in the project directory. So, we run it by pointing it at the published WSDL specification:

<SDK_TOOLS_PATH>\MFSvcUtil.exe http://netmfbikecomputerservice.cloudapp.net/BikeComputerService.svc?wsdl

The tool generates three files (see Figure 8).

image: Executing the MFSvcUtil.exe Command
(click image to zoom)

Figure 8 Executing the MFSvcUtil.exe Command

The BikeComputerService.cs file contains the definition of the data in the messages, the classes defining the operations supported by the service (because our device is a client) and a number of helper functions for serializing and de-serializing the data (see Figure 9).

Figure 9 The BikeComputerService.cs File

namespace BikeComputer.org
{
  [DataContract(Namespace="http://tempuri.org/")]
  public class GetLastComputerData ...
    
  public class GetLastComputerDataDataContractSerializer : DataContractSerializer…
    
  [DataContract(Namespace="http://tempuri.org/")]
  public class GetLastComputerDataResponse ...
    
  public class GetLastComputerDataResponseDataContractSerializer : DataContractSerializer…
    
  [DataContract(Namespace="http://tempuri.org/")]
  public class UploadBikeComputerData ...
    
  public class UploadBikeComputerDataDataContractSerializer : DataContractSerializer…
    
  [ServiceContract(Namespace="http://tempuri.org/")]
  [PolicyAssertion(Namespace="http://schemas.xmlsoap.org/ws/2004/09/policy",
    Name="ExactlyOne",
    PolicyID="WSHttpBinding_IBikeComputerService_policy")]
  public interface IIBikeComputerService ...
}
namespace schemas.datacontract.org.BikeComputerServiceWebRole...

The BikeComputerClientProxy.cs file contains the proxy interfaces for the Web service:

namespace BikeComputer.org
{
  public class IBikeComputerServiceClientProxy : DpwsClient
  {
    private IRequestChannel m_requestChannel = null;
        
    public IBikeComputerServiceClientProxy(Binding binding,    
      ProtocolVersion version) : base(binding, version)...
        
    public virtual GetLastComputerDataResponse 
      GetLastComputerData(GetLastComputerData req) ...
        
    public virtual UploadBikeComputerDataResponse  
      UploadBikeComputerData(UploadBikeComputerData req) ...        
  }
}

The third file that MFSvcUtil.exe creates is the BikeComputerServiceHostedService.cs file; this contains the interface logic that runs on the service side of the interface. In our case, the WSDL was generated from the service and data contracts that were created, so this file is a “throw away” for us. It’s generated to cover scenarios where you get a published WSDL and you want to replicate a service from it or where you want to run a service on another device. Remember that devices can be clients, servers or both. The option to have devices provide services for other devices enables some interesting applications. This is what BikeComputerServiceHostedService contains:

namespace BikeComputer.org
{
  public class IBikeComputerServiceClientProxy : DpwsHostedService
  {
    private IIBikeComputerService m_service;
        
    public IBikeComputerService(IIBikeComputerService service, 
      ProtocolVersion version) : base(version) ...
    public IBikeComputerService(IIBikeComputerService service) :
      this(service, new ProtocolVersion10())...
    public virtual WsMessage GetLastComputerData(WsMessage request) ...
    public virtual WSMessage UploadBikeComputerData(WsMessage request) ...
  }
}

Uploading the Data

As you’ve seen, all the device application code so far has been auto-generated from the WSDL. What code do you actually have to write on the client to connect to the service and post your data and read it back just to be sure that it’s been received? Just a few lines of code are required. Here’s what I did in the bicycle computer project.

In the RideDataModel class, I added the following to the constructor to set up the DPWS connection:

public RideDataModel()
{
  _currentRideData = new CurrentRideData();
  _summaryRideData = new SummaryRideData();
  _today = DateTime.Now; //change this to the time service later.
  //--Setup the Web Service Connection
  WS2007HttpBinding binding = new WS2007HttpBinding(
    new HttpTransportBindingConfig(new Uri
      ("http://netmfbikecomputerservice.cloudapp.net/BikeComputerService.svc")
     ));
  m_proxy = new
    IBikeComputerServiceClientProxy(
    binding, new ProtocolVersion11());    
  _upload = new 
    UploadBikeComputerData();
  _upload.rideData = new
    schemas.datacontract.org. 
    BikeComputerServiceWebRole.
    BikeComputerData();
  }

Next, I created a method in that class for uploading the data to the Web service. This routine puts my summary data from the ride into the fields for the Web service schema (referenced in the WSDL and reflected in BikeComputerService.cs), which is designed to match that data. Then I invoke the proxy’s UploadBikeComputerData method with the upload data and retrieve the date of the last ride on the Web service to validate that my data was received (see Figure 10).

Figure 10 Uploading Data to the Web Service

public bool postDataToWS()
{
  //-- Load the ride summary data into the upload fields --//
  _upload.rideData.AverageCadence = _summaryRideData.averageCadence;
  _upload.rideData.AverageIncline = _summaryRideData.averageIncline;
  _upload.rideData.AverageSpeed = _summaryRideData.averageSpeed;
  _upload.rideData.AverageTemperature = 
    _summaryRideData.averageTemperature;
  _upload.rideData.Date = _summaryRideData.rideDate;
  _upload.rideData.Distance = _summaryRideData.distance;
  _upload.rideData.RidingTime = _summaryRideData.ridingTime;
  _upload.rideData.StartTime = _summaryRideData.startTime;
  //-- Upload the data --//
  m_proxy.UploadBikeComputerData(_upload);
  //-- Validate the upload by retrieving the data and comparing --//
  GetLastComputerData req = new GetLastComputerData();
  GetLastComputerDataResponse back = m_proxy.GetLastComputerData(req);
  if (back.GetLastComputerDataResult.Date == _upload.rideData.Date)
  {
    return false;
  }
  return true;
}

This assumes that you take only one ride a day, so if you do take more rides, you need to change that comparison logic. I expect to use the “Pause” feature on the bicycle computer and treat all the rides in a day as one set of data. I already have the ability to store the data in a file on an SD card on the bike as I described in a previous article on the blog. I’ll add the ability to track which sets of ride data have been posted to the Web service and post any that are missing. This allows me to be out of range of my wireless connection for some time and still be able to update the Web service later. Another enhancement is to enable connecting to a PC as an intermediary when my home network isn’t available.

So, 17 lines of code that I wrote in the application (most of it mapping the summary data into the service schema fields) and I’m uploading data to a service and doing validity checks. Not bad.

Now We End Our Ride Connected

At the end of a ride, as I come into my garage, I can navigate to the Save Your Data screen with a simple gesture and post my data up to the cloud.

There’s still some work to do at that end to make the data more useful, but that’s another story.

Embedded devices have been islands of specialized technology where ease of programming and flexibility were traded off for small footprint/low-cost and high-performance requirements. More and more frequently, we’re seeing small devices connected to other devices and to networks to create compelling solutions. At the same time, the price of processors and memory continues to go down to the point where we no longer have to give up the productivity of desktop tools and languages to be able to make rich and price-competitive devices. The result is that .NET programmers find themselves with both the skills and the opportunities to work on small devices.

The Web services model provides a strong option for connecting these small devices, because discovering remote services, subscribing to remote events and exchanging information about services through metadata enables the flexible connection of a number of devices. The cost is that the connection—using SOAP and XML—is verbose, so it may not be appropriate in all cases.

The bicycle computer project demonstrates that .NET Framework programming skills enable you to write compelling UIs for small devices, write drivers for a variety of sensors and connect those devices with the cloud. As I showed you, the work involves defining the Web service, and the majority of the code that runs on the device is auto-generated by MFSvcUtil.exe. Just a few lines of additional code were required to upload the data. Other applications may require probing for the Web service (WS_Discovery), or subscribing to events on other endpoints (WS_Eventing), or dealing with devices and services of varying capabilities (WS_MetaDataExchange). All of these can be added to the basic data exchange model as needed.

As a friend of mine once commented, .NET Framework programmers can now add “Embedded Programmer” to their business cards. We’d love to hear about the devices that you build with .NET on the Discussions at netmf.com, where I can also answer any questions that you have on this article or on the blog site.

Happy riding!


Colin Miller started his affair with computing as a scientific programmer, building 8- and 16-bit experimental control systems. He strayed from small devices, working for 25 years (including 15 at Microsoft) on PC software including databases, desktop publishing, consumer products, Word, Internet Explorer, Passport (LiveID) and online services. As the product unit manager of the .NET Micro Framework, he has finally (and happily) been able to merge these disparate parts of his career.

Thanks to the following technical experts for reviewing this article: Jane Lawrence and Patrick Butler Monterde