March 2014

Volume 29 Number 3

Windows 8.1 : Building a Netduino-Based HID Sensor for WinRT

Donn Morse

The Human Interface Device (HID) protocol was originally intended to simplify the use of devices such as mice, keyboards and joysticks. However, because of its unique features—including its self-descriptive nature—device manufacturers use the protocol to support medical devices, health and fitness devices, and custom sensors. If you’re new to the HID API, refer to the USB HID Information site (bit.ly/1mbtyTz) to find more information. Another great resource is Jan Axelson’s book, “USB Complete: The Devloper’s Guide, Fourth Edition” (Lakeview Research LLC, 2009).

Prior to Windows 8.1, if you were writing an application for a HID device you wrote a native Win32 app. But if you were a Web or a .NET developer, the ramp was steep. To address this, Microsoft introduced the HID Windows Runtime (WinRT) API with Windows 8.1 (bit.ly/1aot1by). This new API lets you write Windows Store apps for your device using JavaScript, Visual Basic, C# or C++.

In addition, Microsoft recently added support for several new transports, so you aren’t limited to a USB cable. Today, you can create a HID device that transmits and receives packets over USB, Bluetooth, Bluetooth LE, and I2C. (For more information, see “HID Transports” at bit.ly/1asvwg6.)

In this article, I’ll show how you can build a simple temperature sensor that’s compatible with the HID protocol. Then I’ll describe a sample Windows Store app that can display temperature data from the device.

Constructing the Temperature Sensor

The sample device is based on the Netduino development board (netduino.com). This open source board is used by hobbyists, academics and industrial engineers to build working prototype devices. And, because the Netduino is pin-compatible with the Arduino, you can attach your Arduino shields to quickly add functionality. (A shield is a board with specific functionality, such as wireless communication, motor control, Ethernet, RS232, LCD display and so on.) My sample device uses an RS232 shield to download the firmware. It uses the onboard USB connector to transmit and receive data.

The Netduino supports the .NET Micro Framework and its firmware is created with a free copy of Visual C# Express.

To obtain temperature data, the sample device uses the Texas Instruments LM35 sensor. The sensor takes 5 volts of input from the Netduino and converts it into an output voltage proportional to the current Celsius temperature.

Here are the parts you need to build your own HID sensor:

  • Netduino 1 or Netduino Plus 1 (Amazon, amzn.to/1dvTeLh): A development board with programmable microcontroller that supports the .NET Micro Framework.
  • RS232 shield (CuteDigi, bit.ly/1j7uaMR): The RS232 module for downloading and debugging the firmware. (This shield is required for the beta version of the firmware being used.)
  • LM35 Sensor (DigiKey, bit.ly/KjbQkN): The temperature sensor that converts input voltage to output voltage based on the current temperature.
  • RS232-to-USB converter cable (Parallax, bit.ly/1iVmP0a): The cable for downloading the temperature-sensor firmware via the RS232 shield. (Note that an FTDI chipset is required for compatibility with the shield.)
  • 9V 650mA power supply (Amazon, amzn.to/1d6R8LH): The power supply for the Netduino board.
  • USB to Micro-USB cable (Amazon, amzn.to/Kjc8Ii): The cable for sending HID packets from the Netduino to your Windows 8.1 tablet or laptop.

Figure 1 shows the complete HID temperature sensor setup.

The Complete HID Temperature Sensor Setup
Figure 1 The Complete HID Temperature Sensor Setup

The RS232 shield is attached to the top of the Netduino. The breadboard contains the LM35 sensor, which is attached to 5V, ground and Pin 0. (Pin 0 is one of six analog-to-digital [ADC] pins on the board). So, let’s get started.

The firmware your Netduino 1 (or Netduino Plus 1) comes with doesn’t support the HID protocol. You’ll need to configure your development board by installing version 4.1.1 of the beta firmware, which includes support for HID. You’ll find a zip folder containing the beta firmware at bit.ly/1a7f6MB. (You’ll need to create an account by registering with Secret Labs in order to download the file.)

The download page on the Web site includes instructions for updating the firmware. However, these instructions are fairly complex, particularly if you’re new to the Netduino. The video at bit.ly/1d73P9x is a helpful, concise description of the firmware upgrade process.

After you’ve upgraded the firmware on your board, you’re ready to begin constructing the temperature-sensor circuit. The first step requires you to attach the RS232 shield to your board. (As I already mentioned, the Netduino is pin-compatible with the Arduino, so if you’ve been working with the Arduino and have an RS232 shield handy, you can use it.) Snap the RS232 shield onto the Netduino as shown in Figure 2.

Attaching the RS232 Shield to the Netduino
Figure 2 Attaching the RS232 Shield to the Netduino

After you’ve attached the RS232 shield, the next step is to attach the temperature sensor to the 5V power source, ground and pin 0 of the Netduino. Figure 3, from the TI datasheet for the sensor, shows the pin-outs.

The Sensor Pin-Outs
Figure 3 The Sensor Pin-Outs

Installing the Sensor Firmware

There are two layers, or instances, of firmware on the Netduino. The first is the manufacturer’s firmware, which includes the .NET Micro Framework; the second is your device’s firmware. The manufacturer’s firmware processes requests from the device firmware. The manufacturer’s firmware is loaded once onto the development board and executes each time you power up the device. In contrast, you typically refresh your device firmware multiple times during the development and prototyping process.

In order to install any device firmware, you first need to install an instance of Visual C# Express 2010 on your development machine. You’ll find a link to the download at bit.ly/1eRBed1.

For most Netduino projects, you can download and debug your firmware using the native USB connection. However, the beta version of the manufacturer’s firmware requires an RS232 connection (which is why the RS232 shield is required).

Once Visual C# Express is installed, attach the RS232-to-USB cable and open Windows Device Manager to determine which COM port Windows assigned to that cable.

When I attached the Parallax RS232-to-USB converter to my development machine, Windows mapped it to COM6, as Figure 4 shows.

The COM Port Assigned to the RS232-to-USB Cable
Figure 4 The COM Port Assigned to the RS232-to-USB Cable

Now that I know the COM port associated with the converter, I can power up my Netduino Plus 1, attach the RS232-to-USB cable, and start an instance of Visual C# Express to complete the download.

The first thing to do after starting Visual C# Express is to identify the correct transport and COM port. You do this by right-clicking on the project name in the Solution Explorer pane and choosing the Properties menu.

When the Properties dialog appears, choose the .NET Micro Framework tab and make the necessary selections, as shown in Figure 5.

Configuring the .NET Micro Framework Properties
Figure 5 Configuring the .NET Micro Framework Properties

After specifying the Transport and Device, you can deploy the firmware. Again, right-click the project name in the Solution Explorer pane and, this time, choose Deploy.

When the deployment completes, Visual C# Express will report the success in the Output pane.

You’re now ready to attach your device to a Windows 8.1 tablet or laptop and test it with the Custom Temperature Sensor sample app.

First, detach the RS232 cable, power down the Netduino, and then restart it with the auxiliary power supply. Give the device several seconds to power up and then attach the USB cable to the Netduino. After doing this, you should see your device added to the collection of HID devices in Device Manager. (The VID and PID in Figure 6 correspond to the VID and PID of the sample device; these are the vendor and product IDs.)

The Vendor and Product IDs of the Sample Device
Figure 6 The Vendor and Product IDs of the Sample Device

Once the device is installed on your Windows 8.1 machine, you’ll want to install and build the sample app. When the app starts, you can select the sensor and begin monitoring the ambient temperature in your office.

The Device Firmware

Now let’s take a detailed look at the device firmware for the temperature sensor. At the outset, I’d like to thank the folks at Secret Labs (the manufacturers of the Netduino) for the work they’ve done to support HID over USB on the Netduino platform. The starting point for this firmware was a sample on the forum, the UsbHidEchoNetduinoApp, available at bit.ly/1eUYxAM.

Supporting the USB Transport As I noted earlier, Microsoft supports HID devices running over USB, Bluetooth, Bluetooth LE and I2C. However, the sample device described in this article uses the USB transport. What this actually means is that USB drivers will be moving packets in both directions: packets originating with the device are passed up to the HID driver (which passes them on to the API if there are interested apps); packets originating with the HID driver are passed back down to the device.

Windows uses specific data issued by the device upon connection to identify which USB drivers it should load.

Defining the Firmware Classes The firmware for the temperature-sensor device is built around two classes: Program and Sensor. The Program class supports a single Main routine that’s invoked at startup. The Sensor class defines the USB and HID settings for the temperature sensor. In addition, it supports the methods that send input reports and read output reports.

The Sensor class contains all of the code required to configure the USB transport. This includes the code that:

  • Configures a read endpoint
  • Configures a write endpoint
  • Specifies the vendor ID (VID)
  • Specifies the product ID (PID)
  • Specifies friendly names (manufacturer name, product name and so on)
  • Specifies other required USB settings for a HID device

Most of the USB configuration code is found in the Configure­HID method in the Sensors.cs module. This method, in turn, creates and initializes a Configuration object (bit.ly/1i1IcQ3) that contains the device’s USB settings (endpoints, VID, PID and so on).

The read endpoint allows the device to receive packets from the API and the HID driver. The write endpoint allows the driver to send packets up through the driver stack to the API.

Windows uses the vendor ID, product ID, and other USB settings (which were specified in the ConfigureHID method) to determine whether the device is a valid USB device and then to load the appropriate drivers.

Opening the Device Connection The Sensor class includes an Open method that’s called from within the Main routine of the Program class. As you can see in Figure 7, the Open method:

  • Retrieves the available USB controllers
  • Invokes the ConfigureHID method to establish the device’s USB and HID settings
  • Invokes the Start method on the first available controller
  • Creates a USB stream object with read and write endpoints

Figure 7 The Open Method

public bool Open()
{
  bool succeed = true;
  started = false;
  UsbController[] usbControllers = UsbController.GetControllers();
  if (usbControllers.Length < 1)
  {
    succeed = false;
  }
  if (succeed)
  {
    usbController = usbControllers[0];
    try
    {
      succeed = ConfigureHID();
      if (succeed)
      {
        succeed = usbController.Start();
      }
      if (succeed)
      {
        stream = usbController.CreateUsbStream(WRITE_ENDPOINT,
           READ_ENDPOINT);
      }
    }
    catch (Exception)
    {
      succeed = false;
    }
  }
  started = true;
  return succeed;
}

The Sensor class also includes a Close method, which is called when the device is detached from the host laptop or tablet.

Supporting the HID Protocol

The HID protocol is based on reports: feature reports, input reports and output reports. Feature reports can be sent by either the host (that is, a connected laptop or tablet) or the device. Input reports are sent by the device to the host. Output reports are sent by the host to the device.

In the case of our sample temperature sensor, the input report is a very simple two-byte packet. The first byte specifies the current temperature in degrees Fahrenheit; the second byte indicates the current report interval in millisec­onds. (The sensor firmware issues an input report at the frequency specified by the report interval.)

The output report for the sample device is even simpler—it’s a single byte that specifies the report interval. (This is an integer value that repre­sents the interval in milliseconds.)

Creating the Report Descriptor As I mentioned earlier, one of the features of a HID device is its self-­reporting nature: Upon connecting to a host, the device provides a description of its purpose, capabilities and packet format in what’s called a report descriptor. This descriptor indicates where the device fits in the HID universe (is it a mouse, a keyboard, a vendor-defined device?). The descriptor also specifies the format of the individual feature reports, input reports and output reports.

The report descriptor for the temperature sensor is found in Sensors.cs, as shown in Figure 8.

Figure 8 The Report Descriptor for the Temperature Sensor

hidGenericReportDescriptorPayload = new byte[]
  {
    0x06,0x55,0xFF,     //HID_USAGE_PAGE_VENDOR_DEFINED
    0x09,0xA5,          //HID_USAGE (vendor_defined)
    0xA1,0x01,          //HID_COLLECTION(Application),
    // Input report (device-transmits)
    0x09,0xA7,          //HID_USAGE (vendor_defined)
    0x15,0x00,          //HID_LOGICAL_MIN_8(0), // Minimum temp is 0 degrees F
    0x25,0x96,          //HID_LOGICAL_MAX_8(150), // Max supported temp is
                                                  // 150 degrees F
    0x75,0x08,          //HID_REPORT_SIZE(8),
    0x95,0x01,          //HID_REPORT_COUNT(1),
    0x81,0x02,          //HID_INPUT(Data_Var_Abs),
    0x09,0xA8,          //HID_USAGE (vendor_defined)
    0x15,0x4B,          //HID_LOGICAL_MIN_8(75),   // minimum 75 ms
    0x25,0xFF,          //HID_LOGICAL_MAX_8(255),  // maximum 255 ms
    0x75,0x08,          //HID_REPORT_SIZE(8),
    0x95,0x01,          //HID_REPORT_COUNT(1),
    0x81,0x02,          //HID_INPUT(Data_Var_Abs),
    // Output report (device-receives)
    0x09,0xA9,          //HID_USAGE (vendor_defined)
    0x15,0x4B,          //HID_LOGICAL_MIN_8(75),   // minimum 75 ms
    0x25,0xFF,          //HID_LOGICAL_MAX_8(255),  // maximum 255 ms
    0x75,0x08,          //HID_REPORT_SIZE(8),
    0x95,0x01,          //HID_REPORT_COUNT(1),
    0x91,0x02,          //HID_OUTPUT(Data_Var_Abs),
    0xC0                //HID_END_COLLECTION
   };

The first two lines of the descriptor inform the host that this particular device is vendor-defined:

0x06,0x55,0xFF,     //HID_USAGE_PAGE_VENDOR_DEFINED
0x09,0xA5,          //HID_USAGE (vendor_defined)

Lines four through 15 indicate the format of the two-byte input report. Lines four through nine describe the first byte of the input report, which specifies the temperature reading:

0x09,0xA7,          //HID_USAGE (vendor_defined)
0x15,0x00,          //HID_LOGICAL_MIN_8(0), // Minimum temp is 0 degrees F
0x25,0x96,          //HID_LOGICAL_MAX_8(150), // Max supported temp is  
                                              // 150 degrees F
0x75,0x08,          //HID_REPORT_SIZE(8),
0x95,0x01,          //HID_REPORT_COUNT(1),
0x81,0x02,          //HID_INPUT(Data_Var_Abs),

The 10th through 15th lines describe the second byte of the input report, which specifies the report interval (in milliseconds):

0x09,0xA8,          //HID_USAGE (vendor_defined)
0x15,0x4B,          //HID_LOGICAL_MIN_8(75),   // minimum 75 ms
0x25,0xFF,          //HID_LOGICAL_MAX_8(255),  // maximum 255 ms
0x75,0x08,          //HID_REPORT_SIZE(8),
0x95,0x01,          //HID_REPORT_COUNT(1),
0x81,0x02,          //HID_INPUT(Data_Var_Abs),

The report descriptor for the sample device is included as part of the UsbController.Configuration object (bit.ly/1cvcq5G) that’s created within the ConfigureHID method in Sensor.cs.

Supporting the HID Input Report The input report is defined as a structure in the Sensor.cs module:

struct InputReport
{
  public byte Temperature; // Temperature in degrees Fahrenheit
  public byte Interval;    // Report interval (or frequency) in seconds
}

The firmware issues input reports using the UsbStream object (bit.ly/1kElfUZ) it created in the Open method. These input reports are issued from the SendInputReport method when the firmware invokes the stream.Write method:

protected void SendInputReport(InputReport report)
{
  byte[] inputReport = new byte[2];
  inputReport[0] = (byte)report.Temperature;
  inputReport[1] = (byte)report.Interval;
  stream.Write(inputReport, 0, 2);
}

Issuing Temperature Data with Input Reports The Update method in the Sensor class issues an input report to the connected host:

public int Update(int iTemperature, int iInterval)
{
  InputReport inputReport = new InputReport();
  byte Interval = 0;
  inputReport.Temperature = (byte)iTemperature;
  inputReport.Interval = (byte)iInterval;
  SendInputReport(inputReport);
  Interval = GetOutputReport();
  return (int)Interval;
}

The Update method is invoked from within an infinite while loop, shown in Figure 9, which executes in the firmware’s Main routine (found in Program.cs).

Figure 9 The While Loop That Invokes the Update Method

while (true)
{
  // Retrieve the current temperature reading
  milliVolts = (double)voltsPin.Read();  // Read returns a value in the
                                         // specified range
  tempC = milliVolts / 10.0;             // Sensor returns 10mV per  
                                         // degree Centigrade
  tempF = 1.8 * tempC + 32;              // Convert to degrees Fahrenheit
  simpleTemp = (int)tempF;
  // Because there are voltage fluctuations when the external
  // power supply is connected to the Netduino, use a running
  // average to "smooth" the values
  if (firstReading)
  {
    firstReading = false;
    currentTemp = simpleTemp;
    for (i = 0; i < 12; i++)
      tempArray[i] = simpleTemp;
    }
  else
  {
    tempArray = Shift(simpleTemp, tempArray);  // Shift the array elements and
                                               // insert the new temp
    currentTemp = Average(tempArray);          // Compute a running average of
                                               // the last 12 readings
  }
  RequestedInterval = sensor.Update(currentTemp, CurrentInterval);
  // Check for a possible new interval requested via an
  // output report
  if (RequestedInterval != 0)
  {
    CurrentInterval = RequestedInterval;
  }
  led.Write(true);
  Thread.Sleep(CurrentInterval);
  led.Write(false);
  }
}

Supporting the HID Output Report The output report is defined as a structure in the Sensor.cs module:

struct OutputReport
{
  public byte Interval; // Report interval (or frequency) in seconds
}

The firmware receives output reports via the same UsbStream object it created in the Open method. These output reports are received within the GetOutputReport method:

protected byte GetOutputReport()
{
  byte[] outputReport = new byte[1];
  int bytesRead = 0;
  if (stream.CanRead)
  {
  bytesRead = stream.Read(outputReport, 0, 1);
  }
  if (bytesRead > 0)
  return outputReport[0];
  else
  return 0;
}

Adjusting the Report Interval with Output Reports The firmware supports a report interval specified in milliseconds. The minimum supported interval is 75 ms; the maximum interval is 255 ms. An app requests a new interval by sending an output report to the device. The device, in turn, reports the current interval in each input report that it sends to any connected app.

The firmware applies the current interval by invoking the Thread.Sleep method (bit.ly/LaSYVF) for the number of seconds specified by the current interval:

led.Write(true);
Thread.Sleep(CurrentInterval);
led.Write(false);

By pausing the while loop for this duration, registered apps receive input reports at the specified interval.

The HID Temperature-Sensor App

The sample app demonstrates how you can display temperature data from an attached HID temperature sensor using the new HID WinRT API for Windows 8.1.  This new API lets your app retrieve data from HID devices, and control them as well.

The sample is designed to work with an attached HID device that detects temperatures from 0 to 150 degrees Fahrenheit. The app monitors and then displays the temperature sensor’s current reading.

The app supports three “scenarios,” each of which maps to specific features in the app’s UI. In addition, each scenario maps to a corresponding XAML and C# source file. The following lists each scenario, its corresponding modules and its function:

Device Connect (Scenario1_ConnectToSensor.xaml; Scenario1_ConnectToSensor.xaml.cs)

  • Supports connecting a HID device to a Windows 8.1 PC.
  • Enumerates the connected temperature sensors so the user can select one.
  • Establishes a device watcher that monitors the status of the device. (The device watcher fires an event when the user disconnects or reconnects the selected HID device.)

Get Temperature Data (Scenario2_GetTemperatureData.xaml; Scenario2_GetTemperatureData.xaml.cs)

  • Monitors the selected temperature sensor.
  • Depicts a temperature gauge and renders the current reading using a slider control.

Set Report Interval (Scenario3_SetReportInterval.xaml; Scenario3_SetReportInterval.xaml.cs)

  • Allows the user to control the frequency at which the temperature sensor reports its status. (The default interval is 250 ms, but users can choose intervals from 75 ms to 250 ms.)

Supporting Device Connections

The device-connect scenario enables several aspects of connecting a HID device to a Windows 8.1 PC: enumerating connected devices, establishing a device watcher, handling device disconnection and handling device reconnection.

Establishing a Device Connection The code that handles the device connection is found in three modules: Scenario1_ConnectToSensor.xaml.cs, EventHandlerForDevices.cs and DeviceList.cs. (The first module contains the primary code for this scenario; the other two contain supporting functionality.)

The first phase of the connection occurs before the UI is visible. In this phase, the app creates a DeviceWatcher object that notifies the app when devices are added, removed or changed. The second phase occurs after the UI is displayed and the user is able to choose a specific device from the connected HID devices. The app displays a Device­InstanceId string for each connected device; the string includes the VID and PID for the given device. In the case of the sample temperature sensor, the DeviceInstanceId string has the form:

HID\VID_16C0&PID_0012\6&523698d&0&0000

Figure 10 shows the app as it appears after enumeration has completed and the user has connected to the device.

The Windows 8.1 App Connected to a HID Device
Figure 10 The Windows 8.1 App Connected to a HID Device

The First Stage of Device Connection Here are the methods called during the first stage of device connection (before the UI is displayed), along with tasks accomplished by each method:

DeviceConnect (Scenario1_DeviceConnect.xaml.cs) invokes the CustomTemperatureSensor.InitializeComponent method, which initializes the app’s UI components such as the text blocks and buttons.

InitializeDeviceWatchers(Scenario1_DeviceConnect.xaml.cs) invokes the HidDevice.GetDeviceSelector method to retrieve a device selector string. (The selector is required in order to create a device watcher.) Once the selector is obtained, the app invokes DeviceInformation.CreateWatcher to create the DeviceWatcher object and then EventHandler­ForDevice.Current.Add­DeviceWatcher. (This last method allows the app to monitor changes in device status.)

AddDeviceWatcher (EventHandlerForDevices.cs) creates the event handlers for three device events: Enumeration Completed, Device Added and Device Removed.

SelectDeviceInList(Scenario1_DeviceConnect.xaml.cs) checks to see if the user has selected a device and, if so, it saves the index for that device.

In terms of the HID API, the primary code of interest is found in the InitializeDeviceWatchers method, shown in Figure 11. This code invokes the HidDevice.GetDeviceSelector method (bit.ly/1eGQI1k) and passes the UsagePage, UsageId, VID and PID for the temperature sensor.

Figure 11 The InitializeDeviceWatchers Method

private void InitializeDeviceWatchers()
{
  // Custom sensor
  var CustomSensorSelector =
    HidDevice.GetDeviceSelector(CustomSensor.Device.UsagePage,
    CustomSensor.Device.UsageId, CustomSensor.Device.Vid,
    CustomSensor.Device.Pid);
  // Create a device watcher to look for instances of the custom sensor
  var CustomSensorWatcher =
    DeviceInformation.CreateWatcher(CustomSensorSelector);
  // Allow EventHandlerForDevice to handle device watcher events that
  // relate or affect the device (device removal, addition, app
  // suspension/resume)
  AddDeviceWatcher(CustomSensorWatcher, CustomSensorSelector);
}

The UsagePage and UsageId values are defined in the file constants.cs:

public class Device
{
  public const UInt16 Vid = 0x16C0;
  public const UInt16 Pid = 0x0012;
  public const UInt16 UsagePage = 0xFF55;
  public const UInt16 UsageId = 0xA5;
}

These class members correspond to values specified in the HID report descriptor that’s defined in the device’s firmware:

hidGenericReportDescriptorPayload = new byte[]
{
  0x06,0x55,0xFF,     //HID_USAGE_PAGE_VENDOR_DEFINED
  0x09,0xA5,          //HID_USAGE (vendor_defined)

The GetDeviceSelector method returns an Advanced Query Syntax (AQS) string in the CustomSensorSelector variable. The app then uses this string when it creates a device watcher and when it enumerates the DeviceInformation objects.

The Second Stage of Device Connection The second stage of device connection allows the user to make a selection from the list of connected devices. This stage establishes the currently selected device. Here are the methods (all in EventHandlerForDevices.cs) called and what each one does.

OpenDeviceAsync opens the connection to the device.

RegisterForAppEventsregisters for app suspension and resume events.

RegisterForDeviceAccessStatusChange listens for changes in device-access permissions.

RegisterForDeviceWatcherEvents registers for the added and removed events.

StartDeviceWatcher starts the device watcher.

SelectDeviceInListchecks to see if the user has selected a device and, if so, saves the index for that device. It also writes a “Currently connected …” string to the output window if the connection is successful.

In terms of the HID API, the primary code of interest is in the OpenDeviceAsync method. This code invokes the HidDevice.From­IdAsync method (bit.ly/1hyhVpI), which returns a HidDevice object (bit.ly/1dsD2rR) that the app uses to access the device, retrieve input reports and send output reports:

public async Task<Boolean> OpenDeviceAsync(DeviceInformation deviceInfo,
  String deviceSelector)
  {
    // This sample uses FileAccessMode.ReadWrite to open the device
    // because you don’t want other apps opening the device and  
    // changing the state of the  device.
    // FileAccessMode.Read can be used instead.
    device = await HidDevice.FromIdAsync(deviceInfo.Id, 
        FileAccessMode.ReadWrite);
...

Supporting the Device Watcher Device enumeration occurs when the app is first started and begins even before the UI is displayed. After enumeration completes, the app monitors device status.

Device status is reported by a DeviceWatcher object (bit.ly/1dBPMPd). As the name implies, this object “watches” the connected devices—­if the user removes or connects his device, the watcher reports the event to the app.  (These events are only reported after the enumeration process is finished.)

Retrieving Input Reports

The temperature-retrieval scenario monitors the input reports issued by the temperature-sensor and uses a slider control to display the current temperature, as shown in Figure 12. (Note that this control is limited to displaying temperature data. The properties IsDoubleTapEnabled, IsHoldingEnabled, IsRightTapEnabled and IsTapEnabled have all been set to False.)

Displaying the Current Temperature
Figure 12 Displaying the Current Temperature

The primary method supporting this scenario is the OnInput­ReportEvent event handler, found in Scenario2_GetTemperature­Data.xaml.cs. The app registers this event handler when the user chooses the Get temperature data scenario and presses the Register for Temperature Detection button. The app registers the event handler within the RegisterForInputReportEvents method. In addition to registering the handler, this method saves an event token so it can unregister.

private void RegisterForInputReportEvents()
{
  if (!isRegisteredForInputReportEvents)
  {
    inputReportEventHandler = new TypedEventHandler<HidDevice,
      HidInputReportReceivedEventArgs>(this.OnInputReportEvent);
    registeredDevice = EventHandlerForDevice.Current.Device;
    registeredDevice.InputReportReceived += inputReportEventHandler;
    isRegisteredForInputReportEvents = true;
  }
}

Once the event handler is registered, it reads each input report issued by the sensor and uses the new temperature value to update the TemperatureSlider control. After it updates the control, this method writes the current temperature and report interval values to the Output section of the app, as shown in Figure 13.

Figure 13 Reading and Displaying Sensor Data

private async void OnInputReportEvent(HidDevice sender,
  HidInputReportReceivedEventArgs eventArgs)
{
  // Retrieve the sensor data
  HidInputReport inputReport = eventArgs.Report;
  IBuffer buffer = inputReport.Data;
  DataReader dr = DataReader.FromBuffer(buffer);
  byte[] bytes = new byte[inputReport.Data.Length];
  dr.ReadBytes(bytes);
  // Render the sensor data
  await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
  {
    CurrentReadingText.TextAlignment = TextAlignment.Center;
    CurrentReadingText.Text = bytes[1].ToString();
    TemperatureSlider.Value = (int)bytes[1];
    rootPage.NotifyUser(bytes[1].ToString() + 
     " degrees Fahrenheit, " +
      bytes[2].ToString() +
      " millisecond report-interval", NotifyType.StatusMessage);
  });
}

Sending Output Reports

The report-interval scenario sends an output report to the temperature sensor and writes the count of bytes as well as the value written to the Output area of the app’s window. The app sends an output report after the user chooses the Set report interval scenario, selects a value from the Value to Write dropdown, and then presses the Send Output Report button.

Figure 14 shows the Set report interval scenario and the dropdown that’s populated with the report interval options. (These values represent a report-interval in milliseconds; so, by selecting 100, the app will receive 10 readings every second.)

Setting the Report Interval
Figure 14 Setting the Report Interval

The primary method of the report-interval scenario is SetReportIntervalAsync, found in the Scenario3_SetReport­Interval.xaml.cs module (see Figure 15). This method invokes the HidDevice.SendOutputReportAsync method (bit.ly/1ad6unK) to send an output report to the device.

Figure 15 SetReportIntervalAsync

private async Task SetReportIntervalAsync(Byte valueToWrite)
{
  var outputReport =
    EventHandlerForDevice.Current.Device.CreateOutputReport();
  var dataWriter = new DataWriter();
  // First byte contains the report id
  dataWriter.WriteByte((Byte)outputReport.Id);
  dataWriter.WriteByte((Byte)valueToWrite);
  outputReport.Data = dataWriter.DetachBuffer();
  uint bytesWritten =
    await EventHandlerForDevice.Current.Device.
    SendOutputReportAsync(outputReport);
  rootPage.NotifyUser("Bytes written:  " + 
    bytesWritten.ToString() + ";
    Value Written: " + valueToWrite.ToString(), 
        NotifyType.StatusMessage);
}

Wrapping Up

First I gave you a quick look at building a HID device that monitors the voltage emitted by a simple sensor. For an example of how you could monitor a sensor that toggles a digital I/O pin (rather than emitting a range of voltages), see the motion-sensor sample on MSDN at bit.ly/1gWOlcC.

Then you took a quick look at writing a simple app that monitors and controls a HID device. For more information about the HID WinRT API, visit bit.ly/1aot1by.


Donn Morse is a senior content developer at Microsoft. Reach him at donnm@microsoft.com.

THANKS to the following technical expert for reviewing this article: Arvind Aiyar (Microsoft)
Arvind Aiyar is a Senior Software Development Engineer at Microsoft. Among other things, he’s the developer responsible for creating the new HID WinRT API.