Ever since the 2013 BUILD conference, a huge amount of people have attempted to integrate peripherals into their modern applications – and of all the devices we can plug
into our computers only one has stood out as the prized device people want to connect?
The Dream Cheeky Thunder
Missile Launcher is a $35 novelty toy available from various retailers
around the world. The Thunder is simple in its constructions – its a USB based air-powered missile launcher that allows you to sight and fire foam darts at unsuspecting people
hovering in your immediate vicinity. The device has been so popular with geeks globally that it has even featured in the Big Bang theory.
Modern apps –> HID Device
Aside from its creditability with those of us that frequent code, one of the other exciting features of the Thunder is its driver stack. Instead of using a proprietary
driver in a similar fashion to the OWI-535 robotic arm the device
itself actually leverages an existing Windows standard as a HID (Human interface device) in much the same way as a keyboard and mouse. So why would
this be existing for us a developers? Firstly because we don’t need to do anything with the OS to install or configure the device, and also as it uses
a standard Windows driver which has been ported to the ARM stack the device will also work on a Windows RT device such as a Surface too!
Plugging in the device and Windows recognizing it is only a small part of our solution. As app developers we want to leverage this device from our modern application,
and allow our users to start shooting each other. Luckily Windows 8.1 provides us with the support for such a
dilemma by introducing the Windows.Device.HumanInterfaceDevices
namespace.
So lets spend a few minutes looking at the code required to start firing our missiles.
Windows.Device.HumanInterfaceDevice
As with any modern application that wants to go beyond some standard UI prompts, we need to ask permission from our user to access the device.
This is a security feature that prevents apps from becoming malware. In Windows 8.1, as per Windows 8 we do this via the package.appxmanifest file,
and we leverage the namespace extensions that allows us to reference the new 8.1 capabilities. Currently Visual Studio manifest editor doesn’t support these changes,
so open up your file in a text editor and add the following lines:
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/2010/manifest"
xmlns:m2="http://schemas.microsoft.com/appx/2013/manifest">
<Capabilities> ...
<m2:DeviceCapability Name="humaninterfacedevice">
<m2:Device Id="any">
<m2:Function Type="usage:0001 0010"/>
</m2:Device>
</m2:DeviceCapability></Capabilities>
</Package>
In this example I’ve been a little bit broader in the request I am making to the user. Specifically instead of defining a product, I am asking for access to any HID
device which match the usage page and usage ID included. I’ve done this deliberately to show the difference between specifying an app looking for one particular devices vs. a range of devices.
If you did want to be a little granular you could specify the VID and PID as per my USB example in stead of the any argument.
Once we have declared our intention to access, we need to wire up some code to find if the device is attached to our machine. Again just to show some contrast to my last post,
I’m going to do this using a DeviceWatcher
. If I wanted a moment in-time view of if the device is connected I can use the
Windows.Devices.Enumeration
technique I demonstrated
in my robot arm article. Instead I have opted to use a watcher, using a method on the same object
Windows.Devices.Enumeration.DeviceInformation
.
A watcher however monitors the device manager on the machine for devices to be added, and will raise an event if it then finds a device. This provides you with functionality
within your app to deal with devices to be added or removed at anytime in the life cycle.
The code we need to create a watcher is :
const ushort vid = 8483;
const ushort pid = 4112;
const ushort uid = 16;
const ushort uPage = 1;
var deviceWatcher = DeviceInformation.CreateWatcher(
HidDevice.GetDeviceSelector(uPage, uid, vid, pid));
deviceWatcher.Added += (s, a) => Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, async () => { .. do something here.. });
deviceWatcher.Start();
This is very similar to any device connectivity connection. We are creating an AQS (query statement) string using a helper method on
Windows.Devices.HumanInterfaceDevice.HidDevice
by passing in the Vendor ID, Product Id, Usage ID, and Usage Page values. The AQS is then used by the watcher to find any devices connected to the machine, which causes the event
to be fired. One important piece of code to note is the dispatcher in the event handler. This is present because when the device is added and you try to connect a Windows appears
in the UI asking for permission from the user. Therefore any code used to connect to the device needs to be on the UI thread.
To actually open up a connection to the device, it’s a matter of calling one async method on the
Windows.Devices.HumanInterfaceDevice.HidDevice
object.
_hidDevice = await HidDevice.FromIdAsync(a.Id,FileAccessMode.ReadWrite);
The FromIdAsync
method takes two arguments, which is the device ID (returned as part of the
DeviceInformation
from the watcher event), and also an enumerated
FileAccessMode
to specify the connection type – which in our instance is ReadWrite
due to our wanting to pass commands to the device.
Now we have an active connection to the device, so we can start sending data to it. HID devices have fairly small and easy payloads to transmit – if you imagine
a keyboard HID device it simply sends the corresponding keypress data, which are all relatively small. Being of a similar device type the Thunder expects simple small
payloads (especially in comparison to the USB robotic arm), which correspond to up, down, left, right, fire.
To send such data, we simply use a byte[] and add it out an OutputReport
object. Luckily another helper class creates the object for us, so we just need to append
our payload and send the data using code similar to this:
private async Task SendOutputMessage(byte[] message) {
if (_hidDevice != null) {
var report = _hidDevice.CreateOutputReport();
report.Data = message.AsBuffer();
await _hidDevice.SendOutputReportAsync(report);
}
}
And that is it! Using those few lines of code we have an active connection to the USB Thunder missile launcher and can start sending commands.
Here is how we would toggle the LED on:
var LED_ON = new byte[] { 0, 3, 1, 0, 0, 0, 0, 0, 0 };
await SendOutputMessage(LED_ON);
In the meantime have fun with your hardware hacking!