USB Joystick Device

The USBJoystick interface is used to emulate a joystick (gamecontroller) over the USB port. You can send throttle, rudder, and X, Y co-ordinates as well as button and hatswitch states.

Wiring

You can use an existing USB data cable and cut off one end. This typically exposes four wires, Red, Black, White and Green. On the mbed LPC1768 the USB connector should be attached as follows:

  • Green USB D+ to p31
  • White USB D- to p32
  • Red USB 5V to Vin
  • Black USB GND to GND

You can connect the USB power to VIN to power the mbed board from the Host PC when connected.

Identify the correct D+ and D- pins on other mbed boards, the ST Nucleo F411RE for example uses these pins:

  • Green USB D+ to PA_12
  • White USB D- to PA_11
  • Red USB 5V to E5V (with jumper JP5 set to E5V)
  • Black USB GND to GND
  1. Note that a 1k5 pullup resistor is needed between the 3v3 pin and the D+ pin or the host PC (Windows) will not recognise your board as a USB device.
  2. Note that on some boards the pullup is actually switched on or off by another processor pin. That allows enabling or disabling the USB communication under software control. Here is an example schematic from the mbed LPC1768 USB section.

/media/uploads/wim/usb_mbed_target.jpg

In this case the pullup is switched by T2 which is controlled by the LPC1768 chip. The Nucleo doesnt have the pullup by default. Check the hardware schematic of your (non-)mbed boards and modify accordingly.

  1. Note that the filtering networks 33R/18pF on D+/D- are recommended, but sometimes not present on a board.

SystemCoreClock and USB clock

  1. Note that the USB hardware engine needs an accurate internal clock of 48 MHz. Some (non-mbed) boards may default to a not so accurate internal oscillator that is not stable enough for reliable USB communications
  2. Note that the Nucleo's need a specific USB device lib to work with the Joystick lib that is presented here. The reference at the end of this page should help you get started. Some other ST F103 examples are here and here.

Hello World

USBJoystick Hello World

 #include "mbed.h"
 #include "USBJoystick.h"
 
 USBJoystick joystick;
 
 int main(void)
 {
   uint16_t i = 0;
   int16_t throttle = 0;
   int16_t rudder = 0;    
   int16_t x = 0;
   int16_t y = 0;
   int32_t radius = 120;
   int32_t angle = 0;
   uint32_t buttons = 0;    
   int8_t hat = 0;    
   
   while (1) {
       // Basic Joystick
       throttle = (i >> 8) & 0xFF; // value -127 .. 128
       rudder = (i >> 8) & 0xFF;   // value -127 .. 128        
       buttons = (i >> 8) & 0x0F;   // value    0 .. 15, one bit per button     
       hat    = (i >> 8) & 0x07;   // value 0..7 or 8 for neutral         
       i++;        
       
       x = cos((double)angle*3.14/180.0)*radius;  // value -127 .. 128
       y = sin((double)angle*3.14/180.0)*radius;  // value -127 .. 128
       angle += 3;        

       joystick.update(throttle, rudder, x, y, buttons, hat);

       wait(0.001);
   }
}

Windows Device Driver Installation

Windows will automatically install device drivers when the USB joystick device is first connected to your PC. The proper functioning of the joystick can be checked by opening the Windows Control Panel on your PC and inspecting the USB HID device list. See figure below.

/media/uploads/wim/_scaled_hid_installed.jpg

Note that you should see a new generic HID device and a specific Game controller device.

When all is well you can now find the game controller by clicking on the special gamecontroller icon in the Windows Control Panel. There should be a new Game controller. Click on its properties button and voila....

/media/uploads/wim/_scaled_joystick_installed.jpg

Throttle, Rudder, X,Y and the buttons/hatswitch should be constantly changing through all possible values.

Using mbed as a USB Joystick Device

You could for example use mbed analog inputs to send X, Y, throttle or rudder values to a game running on your PC. Buttons or Hat switches can reflect mbed DigitalIn pins or could be read out from I2C or SPI portexpanders. Any regular PC game that can use joysticks should be able to run with the mbed joystick emulator. You could also develop your own PC applications and access the joystick data through DirectX calls.

Developing More features

The current implementation represents a simple joystick. Additional buttons and functions can be added by modifying the USB Descriptor and adapting the 'report' structure inside the Joystick::update() method. Suppose you need 8 buttons for example. This means changing the descriptor as shown below:

... first part of descriptor goes here

// 8 Position Hat Switch
               USAGE(1), 0x39,                 // Usage (Hat switch)
               LOGICAL_MINIMUM(1), 0x00,       // 0
               LOGICAL_MAXIMUM(1), 0x07,       // 7
               PHYSICAL_MINIMUM(1), 0x00,      // Physical_Minimum (0)
               PHYSICAL_MAXIMUM(2), 0x3B, 0x01, // Physical_Maximum (315)
               UNIT(1), 0x14,                  // Unit (Eng Rot:Angular Pos)                            
               REPORT_SIZE(1), 0x04,
               REPORT_COUNT(1), 0x01,
               INPUT(1), 0x02,                 // Data, Variable, Absolute               

#if (BUTTONS4 == 1)
// 4 Buttons
               USAGE_PAGE(1), 0x09,            // Buttons
               USAGE_MINIMUM(1), 0x01,         // 1
               USAGE_MAXIMUM(1), 0x04,         // 4
               LOGICAL_MINIMUM(1), 0x00,       // 0
               LOGICAL_MAXIMUM(1), 0x01,       // 1
               REPORT_SIZE(1), 0x01,
               REPORT_COUNT(1), 0x04,
               UNIT_EXPONENT(1), 0x00,         // Unit_Exponent (0)
               UNIT(1), 0x00,                  // Unit (None)                                           
               INPUT(1), 0x02,                 // Data, Variable, Absolute
#endif

#if (BUTTONS8 == 1)
// 8 Buttons
               USAGE_PAGE(1), 0x09,            // Buttons
               USAGE_MINIMUM(1), 0x01,         // 1
               USAGE_MAXIMUM(1), 0x08,         // 8
               LOGICAL_MINIMUM(1), 0x00,       // 0
               LOGICAL_MAXIMUM(1), 0x01,       // 1
               REPORT_SIZE(1), 0x01,
               REPORT_COUNT(1), 0x08,
               UNIT_EXPONENT(1), 0x00,         // Unit_Exponent (0)
               UNIT(1), 0x00,                  // Unit (None)                                           
               INPUT(1), 0x02,                 // Data, Variable, Absolute

// Padding 4 bits
               REPORT_SIZE(1), 0x01,
               REPORT_COUNT(1), 0x04,
               INPUT(1), 0x01,                 // Constant

... rest of descriptor

Note that all USB message packets from the USB device (i.e. Joystick) to the USB Host (i.e. PC) consist of a number of bytes. The bits inside the bytes are filled according to the information in the USB descriptor. There should be no 'holes' left in the descriptor. All bits must be defined. You may add padding bits if needed. The example above uses 4 bits for the hatswitch and the remaining 4 bits in the same byte for the first 4 buttons. The next byte uses 4 bits for the remaining 4 buttons and therefore also needs 4 padding bits to fill the gap. You could also modify the descriptor to instead use 4 bits for the hatswitch, then insert 4 padding bits and then use the next byte for all 8 buttons.

Windows Driver Troubleshooting!

Note that changing the descriptor also requires a new Vendor ID and/or Product ID (see Joystick constructor). That is needed because Windows links the VID/PID to a specific descriptor so that it knows which device driver should be loaded. When you change the descriptor and windows detects a mistake (eg missing or wrong bit padding) it will fail to install the USB HID driver and you probably need to use another Product ID to retry after fixing your code....

Once you have modified the descriptor, you obviously also need to modify the update method to fill the correct bits in the report message structure and set the correct report length. For example:

bool USBJoystick::update(int16_t t, int16_t r, int16_t x, int16_t y, uint32_t button, uint8_t hat) {
             HID_REPORT report;
   _t = t;
   _r = r;   
   _x = x;
   _y = y;
   _button = button;     
   _hat = hat;

   // Fill the report according to the Joystick Descriptor
   report.data[0] = _t & 0xff;            
   report.data[1] = _r & 0xff;               
   report.data[2] = _x & 0xff;            
   report.data[3] = _y & 0xff;
#if (BUTTONS4  == 1)             
   report.data[4] = ((_buttons & 0x0f) << 4) | (_hat & 0x0f) ;    // report byte is filled with 4 bits for buttons and 4 bits for hat
   report.length = 5;                                                               // message length is 5 bytes
#endif
#if (BUTTONS8  == 1)
   report.data[4] = ((_buttons & 0x0f) << 4) | (_hat & 0x0f) ;    // report byte is filled with 4 bits for buttons and 4 bits for hat                                  
   report.data[5] =  (_buttons & 0xf0) >> 4;                             // report byte is filled with 4 bits for remaining buttons
   report.length = 6;                                                               // message length is now 6 bytes
#endif
           
   return send(&report);
}

The 8 button version will look like this in the Game controller properties panel. /media/uploads/wim/joy8but.jpg

Wanna go over the top? Here's the 32 button version for you:

/media/uploads/wim/joy32but.jpg

Information about the Descriptor structure can be found on http://www.usb.org/developers In particular on http://www.usb.org/developers/hidpage#HID_Usage where the HID Usage page data can be found. A usefull application note by Silicon Laboratories that explains the descriptor is here:/media/uploads/wim/hid_usb_intro_an249.pdf

The http://www.usb.org/developers page also has a USB Descriptor Tool available for download (dt2_4.zip). This tool allows you to create, edit and validate HID Report Descriptors. The tool also supports a variety of output formats (.txt, .inc, .h, etc.). (Note: I have not yet used that tool myself). When you want (ehhm.. need) to do some debugging on USB communications and device drivers the Microsoft tool USBView may be useful. It can be found for example on the FTDI site (http://www.ftdichip.com/Support/Utilities.htm). With this tool you can inspect the descriptor data that Windows received from a device.

Note that the USB HID interface can also support Mouse or Keyboard emulation (even at the same time). Obviously that requires a more compound USB Descriptor and a new update method.

Software Library and Example code

You can find the USB Joystick software here. An older (deprecated) version is here.

You can find an example of the Joystick application for an ST F411 here

Have fun!


Report

14 comments on USB Joystick Device:

09 Jul 2013

Hey Wim,

sorry about the post on the wiki, for some reason I seemed to have missed this page! Anyways, the 16bit joystick works just fine with some small modifications to the class and descriptor.

cheers, Ries

10 Sep 2013

Do you know if this will work as a controller for an XBOX, with standard functions, 2 Joy sticks, 3 sets of 4 buttons etc..

Cheers

Ceri

10 Sep 2013

ceri clatworthy wrote:

Do you know if this will work as a controller for an XBOX, with standard functions, 2 Joy sticks, 3 sets of 4 buttons etc..

The code has only tested been on PCs. Dont have an Xbox to try it out myself. It should work if a regular USB joystick also works on the Xbox. You can easily change the joystick features (# axis, # buttons) by modifying the USB descriptor.

15 Dec 2013

I was planning to use port this code to create an arcade stick controller. But I'm not a mbed expert. I have imported the project and the library. but i didn't found any axis or buttons on the USB descriptor. Can you give me a little help? Where do I define whitch pins to use.

15 Dec 2013

Fernando Santos wrote:

I was planning to use port this code to create an arcade stick controller. But I'm not a mbed expert. I have imported the project and the library. but i didn't found any axis or buttons on the USB descriptor. Can you give me a little help? Where do I define whitch pins to use.

Take the example code shown above, add some DigitalIn pins to read your buttons and AnalogIn pins for analog values like the throttle. You then need to use these input values to set the bits in the variables for 'button' or to set the value of 'throttle'. So your own values replace the dummy value 'i' that is currently used for testing. You can read up on DigitalIn in the handbook here and on AnalogIn here. Obviously you need to connect external switches and potmeters to generate the inputs for the software to work.

16 Dec 2013

Yes I've found it. I've managed to change the activation of the 4 buttons . But I couldn't increase the number of buttons I've changed the define "#define REPORT_ID_JOYSTICK 16" I saw this command " report.data[4] = ((_button & 0x0f) << 4) | (_hat & 0x0f) ; "

So I think that the change needs to be more profound. Can you give me a hint what statement do I need to change to add this buttons?

But it wasn't enough.

Wim Huiskamp wrote:

Fernando Santos wrote:

I was planning to use port this code to create an arcade stick controller. But I'm not a mbed expert. I have imported the project and the library. but i didn't found any axis or buttons on the USB descriptor. Can you give me a little help? Where do I define whitch pins to use.

Take the example code shown above, add some DigitalIn pins to read your buttons and AnalogIn pins for analog values like the throttle. You then need to use these input values to set the bits in the variables for 'button' or to set the value of 'throttle'. So your own values replace the dummy value 'i' that is currently used for testing. You can read up on DigitalIn in the handbook here and on AnalogIn here. Obviously you need to connect external switches and potmeters to generate the inputs for the software to work.

17 Dec 2013

@fernando: see example above on how to use 8 buttons instead of 4. That should get you going.

03 Dec 2014

Hey, a little late to the party, so first let me say thanks, I've used your code to make an arcade controller, cheers!

Now, on to the question: has anybody got this to work with a PS3? My board is a Nucleo F401RE, which is detected fine as a HID device using this library, on windows. However, it is not detected on Mac OS X or a PS3. :(

Thanks in advance!

17 Dec 2016

From the screen shots of the USB Gamepad control panel, you can see numerous buttons lit up at once. Can you explain that? And maybe talk about connection of buttons to your board. Pullup vs pulldown? BTW the code does not compile with current versions of Mbed, even after updating. It seems the USBDevice library you are using does not support Nucleo F411RE boards.

25 Dec 2016

@mark The buttons are all independent. They take up one bit per button in the report.data[] bytes. In the testcode I just emulated most buttons. I think I used a simple counter and copied or inverted and copied the countervalue into the report.data[] fields. That's why you see the same values in the 2nd and 3rd row while the 1st and 4th are inverted patterns. In a real joystick you would readout DigitalIn pins, possibly declared as a BusIn. You could also use a simple I2C or SPI port expander (eg PCF8574) to save on DigitalIn pins. That should still be fast enough. You may also want to add some software or hardware debouncing on the pins. Pullup or Pulldown would both work. I prefer pullup resistors and a switch that closes the circuit to GND. This Joystick example is quite old. I will have a look at the latest release of the USBDevice lib and mbed lib to see where the warnings or errors come from. The F411 and other ST devices need an adapted USBDevice library as it does not yet support the ST devices. This link will get you started.

13 Jan 2017

I have mostly finished a fork of this program and changed it to 6 axes. I'm using it with the FRDM-KL25Z and get the joystick showing up properly in Windows 10 with the right number of controls, but nothing moves when I change the analog inputs. The buttons don't even cycle as they did before (same code). I have updated the product_id in the joystick constructor so it is installed fresh, but still no movement. Any ideas as to what's wrong? My code is here:

Import libraryUSBJoystick_Test

Testing 6DOF USB HID joystick device.

Thanks!

14 Jan 2017

Hi Nathan, I will try to find some time to look at your code. When nothing changes anymore there is typically something wrong with the descriptor. I have seen that happening even when only a single value is wrong. This could include a value that is out of min/max range. Try to make descriptor changes incrementally. Temporarily modify the update method to ignore the actual parameters and just fill the record with hardcoded data that can be recognised easily (eg 0xAA).

16 Jan 2017

Thank you, Wim.

16 Jan 2017

OK, so according to your descriptor you are using 16 bit values for each of the 6 Axes. That means 2 bytes must be used for each axis in the report.data record. The update method should set these 2 bytes to prepare the new report message. However you set only one byte per axis and consequently the report length does not match the descriptor. The windows HID driver doesnt like that incorrect message format and ignores it all (no change in buttons etc):

Your code:

   // Fill the report according to the Joystick Descriptor
   report.data[0] = _x & 0xffff;      // note that _x is 16 bit, but report data is 8 bit !      
   report.data[1] = _y & 0xffff;               
   report.data[2] = _z & 0xffff;            
   report.data[3] = _rx & 0xffff;
   report.data[4] = _ry & 0xffff;            
   report.data[5] = _rz & 0xffff;

// report.length will be wrong..

Should be:

   // Fill the report according to the Joystick Descriptor
   report.data[0] =  _x           & 0xff;            
   report.data[1] = (_x >> 8) & 0xff;               

   report.data[2] =  _y           & 0xff;            
   report.data[3] = (_y >> 8) & 0xff;               

etc...

I have tested X,Y plus 6 axes (with 8 bit per axis) and that works. Note that the 'HID DEVICE properties' dialog window only seems to show X,Y plus a maximum of 6 more axes. When you add more channels some will no longer show up although updating continued.

Please log in to post comments.