USB Input Devices

I have an interesting piece of hardware on my desk.

DSC_2014

This is a Novation LaunchPad S.  It is a midi device that is used mostly for music production.  It has a grid of 9X9 buttons (one button is missing in the upper right corner).  Each button has a red and a green LED under it.  The electronic music community has done some fun and interesting things with it.

I have just started to play Elite Dangerous and the amount of controller customization that the game allows is stunning.  While attempting to figure out some of the key mappings I look over and see the launchpad sitting there.  Why am I using the keyboard  when I could map the buttons on the launchpad to the functions I want to perform in the game?  How cool would it be to have multi colored, blinking, and animated soft buttons to control my spaceship?  My next two weeks of free time are now consumed.

automap settings
Novation Automap

DSC_2015

Novation has a software package called Automap.  This application allows you to map any key on the launchpad to a keystroke in the OS.  It is a quick way to do what I wanted and soon I was back playing the game.

 

I quickly realized that some keys would work and others would not.  Keys like WASD would not work while “J” was just fine.  Others like “1” and “2” would only work intermittently.  Automap was not going to work for what I wanted.

 

I start looking around and I find the launchpad.py project that allows me to control the launchpad directly (in Python!).  This project does a great job of documenting all of the controls inside the launchpad and includes different examples of how to read all the buttons at once, or one at a time.  There is also multiple ways to set the colors of LEDs under the buttons.

I’m done with Automap.  My project is now going to be a fully customized input device for Elite.  After creating some different button types (toggle, input, flashing, etc.) it is now time for input into the game.  I find another python project that allows me to send keystrokes to the OS.

With my new project and new mapping for the launchpad off I go into space.

Nothing works.

I increase the delay between KeyDown() and KeyUp().  It is now a little better.  I attempt an integration with Autoit3;  nope.   A little more searching reveals that DirectInput does not use the OS level keyboard functions.   There is some type of USB polling going on below the level I am programming at.  At this point I am unsure if Elite is using DirectInput but this is my best guess as to what the problem is.

 

Software emulation of hardware is everywhere.  You would be hard pressed to find a website that is not running on a VM.  You would also have a hard time finding a popular device that has not had a software emulation made of it.  I attempt to find a project that has emulated a USB keyboard.  I find a proof of concept by Yaron Shani that is using another well known technology USB/IP.

USB/IP is a project that allows USB devices connected to a host to be connected to another computer by routing the USB device’s communication channels over the network and broadcasting them to the client computer.  It then rebroadcasts those channels, like the device is connected to a USB hub, plugged into the client computer.  The proof of concept that Yaron performed, emulated the host software and reported back a mouse device that only existed in his software.  He was then able to send back mouse movement data when the device was queried.

Starting with this example, I created an HID keyboard device and reported it back instead of the software mouse.  After at least a week of messing with USB channels, data packets, and USB HID device information, I was never able to get the computer to recognize a valid keyboard device.  Each time I tried, the computer would attempt to install a device driver and fail.  The device would then never be connected to the computer.  I believe that I failed to get the interrupt channel that the USB device communicates events over created correctly.  Therefore, I could never get it attached to the USB/IP client.

During this time I was still searching for some sort of USB keyboard emulation.  I was stunned to find how little information there is about the subject and that the subject is almost taboo.  Turns out when you figure out how to send keystrokes this way you can bypass the physical security of a Windows machine and send CTRL-ALT-DEL keystrokes like you are sitting at the machine.

While searching for a solution I noticed that an Arduino device can emulate a USB input device.

I then found that USB keyboard support is available in the 32U4 devices.

Say hello to the Arduino Due.

This little device has two USB ports.  One for programming the board and a native USB port with the SAM3x MCU chip driving it.  One more nice thing is;  I already have one of these devices in the house running another project that I will write about later.

After plugging this device into a USB port on my computer  I now get the successful driver installation that had eluded me while working with USB/IP.

Now the project gets simple again.  I now plug the programming port into the computer and connect to it with my Python project.   I decide to send my messages to the Due as a two byte message.  One byte to represent a key was released and one to represent a key was pressed.  Reading the data in the Due is as simple as handling a serial event and using the Keyboard library to send Keyboard.release() and Keyboard.press().

Back to the game again I realize that the Due is using ASCII key values to determine what key is being pressed.  What I didn’t realize was the library would send a “Shift+k” for uppercase “K”.  After being down so long in the guts of USB I learned that the raw keyboard data always sent modifiers (Shift, Ctrl, & Alt) in a separate byte other than the byte of the key being pressed.  An uppercase “K” was the same value as a lowercase “k”.

Okay, I have to map the key press values to the lowercase ASCII characters. After a little more remapping and then…

Success.

This input is extremely fast and responsive.  I do still have issues with holding a key down that I hope to work out in the future.

This has been a fun little integration and I hope to expand on it in for other games.  I am also hoping that there will be an API in Elite so I can read the status from the game and change the look or state of the launchpad based on that information.

I have posted the code here:

https://github.com/bobhelander/launchpad_mapper