USB Joystick

Published on 28 November, 2010

Introduction

With the phasing out of game, serial and parallel ports from modern computers and the ever increasing popularity of USB, it makes sense that hobbyists start getting to grips working with USB. Unfortunately, USB is not a simple protocol and can be daunting to implement. Luckily, there are several solutions on the market that can make implementing a USB device much easier.

This project focuses on the use of a USB PIC and the mikroC compiler to convert an old game port joystick to utilize USB. One of the advantages of the mikroC compiler are the built USB HID libraries that make creating a USB HID device a doddle. When writing USB code using the mikroC compiler, the USB device produced is a generic HID device that can be used to transfer data to and from the PIC. However, it is possible to modify the USB descriptor generated by mikroC so that it produces a specific USB HID device, like a keyboard, mouse, joystick or graphics tablet.

Compiler

This project uses the mikroC v8 compiler. However, the methods used will most probably be relevant to other compilers that can generate HID code.

Joystick

The joystick I used for this project is an old IBM 76H1571, photographed below:

IBM 76H1571 Joystick

The 76H1571 is a 4-button joystick with a throttle and POV hat. What’s interesting to note is that you can’t use the throttle and POV hat at the same time – you can only use one or neither. The two slide switches on the front of the joystick are used to switch the throttle and POV hat on and off, so that you could choose which one you wanted to use.

Joystick slide switches

Because there is no point for this restriction with USB, the converted joystick will be able to use both the throttle and POV hat at the same time. This then makes the two switches at the front redundant, so why not add those as two extra buttons?

In summary, the converted joystick will have the following specifications:

HID Report Descriptor

When the compiler generates the USB HID code, it creates a descriptor that is sent to the USB host that tells it what type of USB device it is. A HID device descriptor is slightly different as it has an extra descriptor embedded in it that specifies the type of HID device and how it is used. It is this section that will be modified to change our device into a joystick.

Creating the Descriptor

The USB IF website contains a useful tool that greatly facilitates the creation of HID report descriptors. It’s called the HID Descriptor Tool and can be downloaded for free from the HID Tools page. Once downloaded, extract the archive and run Dt.exe.

Using the tool, it is possible to create your own report descriptor for your joystick (or any other HID device), specifying the number of axis and buttons it has and any other features (rudder pedals, throttles etc). However, the tool also comes with some premade descriptors that you can immediately use or modify to suit your needs. They are found in the same folder as the executable and are distinguishable by their .hid extension. The premade joystick descriptor is called joystk.hid and this is the one I used. Once loaded, you’ll be presented with the following screen:

I’ve highlighted the important sections in red. From these sections you can deduce the following specs:

You’ll notice that the REPORT_SIZE property defines the width of the data used to represent the parameter and the REPORT_COUNT property determines how many reports are sent to represent the parameter.

Modifying the Descriptor

Have a look at Modifying the Joystick HID Descriptor to see how.

Adding the Descriptor to your code

With your report descriptor created you need to export it into C code. To do that, click on File->Save As in the HID Descriptor Tool. In the Save dialog that appears, change the file type to Header File (*.h).

This will create a C header file that you can then add into your project.

mikroC Integration

Adding the header file into the descriptor generated by mikroC requires a little bit of work. If you have a look at the mikroC descriptor, you’ll notice that every byte is followed by ‘,0’ (ignoring the quotes, that’s comma zero). You will need to modify the report descriptor you generated to include this padding. After doing that you’ll end up with a descriptor that look something like this:

0x05, 0,
0x01, 0,                   // USAGE_PAGE (Generic Desktop)
0x15, 0,
0x00, 0,                   // LOGICAL_MINIMUM (0)
0x09, 0,
0x04, 0,                   // USAGE (Joystick)
0xa1, 0,
0x01, 0,                   // COLLECTION (Application)
0x05, 0,
0x02, 0,                   //   USAGE_PAGE (Simulation Controls)
0x09, 0,
0xbb, 0,                   //   USAGE (Throttle)
0x15, 0,
0x81, 0,                   //   LOGICAL_MINIMUM (-127)
0x25, 0,
0x7f, 0,                   //   LOGICAL_MAXIMUM (127)
0x75, 0,
0x08, 0,                   //   REPORT_SIZE (8)
0x95, 0,
0x01, 0,                   //   REPORT_COUNT (1)
0x81, 0,
0x02, 0,                   //   INPUT (Data,Var,Abs)
0x05, 0,
0x01, 0,                   //   USAGE_PAGE (Generic Desktop)
0x09, 0,
0x01, 0,                   //   USAGE (Pointer)
0xa1, 0,
0x00, 0,                   //   COLLECTION (Physical)
0x09, 0,
0x30, 0,
/////////////////////////////////////////
//     USAGE (X)
0x09, 0,
0x31, 0,                   //     USAGE (Y)
0x95, 0,
0x02, 0,                   //     REPORT_COUNT (2)
0x81, 0,
0x02, 0,                   //     INPUT (Data,Var,Abs)
0xc0, 0,                   //   END_COLLECTION
0x09, 0,
/////////////////////////////////////////////
0x39, 0,                   //   USAGE (Hat switch)
0x15, 0,
0x00, 0,                   //   LOGICAL_MINIMUM (0)
0x25, 0,
0x03, 0,                   //   LOGICAL_MAXIMUM (3)
0x35, 0,
0x00, 0,                   //   PHYSICAL_MINIMUM (0)
0x46, 0,
0x0e, 0,
0x01, 0,                   //   PHYSICAL_MAXIMUM (270)
0x65, 0,
0x14, 0,                   //   UNIT (Eng Rot:Angular Pos)
0x75, 0,
0x04, 0,                   //   REPORT_SIZE (4)
0x95, 0,
0x01, 0,                   //   REPORT_COUNT (1)
0x81, 0,
0x02, 0,                   //   INPUT (Data,Var,Abs)
0x05, 0,
/////////////////////////////////////
0x09, 0,                   //   USAGE_PAGE (Button)
0x19, 0,
0x01, 0,                   //   USAGE_MINIMUM (Button 1)
0x29, 0,
0x04, 0,                   //   USAGE_MAXIMUM (Button 4)
0x15, 0,
0x00, 0,                   //   LOGICAL_MINIMUM (0)
0x25, 0,
0x01, 0,
//   LOGICAL_MAXIMUM (1)
0x75, 0,
0x01, 0,                   //   REPORT_SIZE (1)
0x95, 0,
0x04, 0,                   //   REPORT_COUNT (4)
0x55, 0,
0x00, 0,                   //   UNIT_EXPONENT (0)
0x65, 0,
0x00, 0,                   //   UNIT (None)
0x81, 0,
0x02, 0,                   //   INPUT (Data,Var,Abs)
0xc0, 0                    // END_COLLECTION

Now that the descriptor has been padded, the next step is to delete the report descriptor generated by mikroC and replace it with your one. To do that, first create the mikroC descriptor using the mikroC HID tool, then open it up in the editor.

The actual descriptor data itself is stored completely within the DescTables array. The bottom 50 or so lines in the array are the report descriptor (lines 109-160). Delete those lines, and then paste in the new descriptor in its place. Now, the following modifications need to be made to the USBdsc.c file:

And that’s it. The descriptor should now be correctly mofied to behave as a USB joystick. The easiest way to test this out is to compile the code on to a PIC, plug it into a USB port on your PC and make sure that it is recognized correctly by the PC. Then, go to Control Panel and open up the Game Controllers options dialog. You should be able to see your joystick listed.

Sending Data to the PC

With the PIC recognized as a USB joystick, the hard part is over. Sending joystick data to the PC is a simple matter. When we created the descriptor earlier, we could work out most of the data format from looking at the descriptor. Some extra experimentation has determined the following specification:

Value Range
Throttle -127 (min) to 127 (max)
X axis -127 to 127
Y axis -127 to 127
POV Hat Up 0
POV Hat Right 1
POV Hat Down 2
POV Hat Left 3
POV Hat Neutral 4
Button 1 0-1
Button 2 0-1
Button 3 0-1
Button 4 0-1

The throttle, X and Y values are all 8-bit values. However, the POV and button values are 4-bits each and so they are packed into one byte. The data format is shown below:

Bit position
7 6 5 4 3 2 1 0
Throttle
X-axis
Y-axis
Button 4 Button 3 Button 2 Button 1 POV Hat

With the data format determined, it is a simple matter to write some firmware that interfaces with the some buttons and potentiometers and sends the data to the PC, to confirm that the firmware is working correctly. The behaviour of the PIC joystick can be examined using the Game Controllers options dialog in Control Panel.

Hardware

With the USB firmware tested and running fine, it is time to move on to the actual joystick conversion. The first thing that needs to be done is to open up the joystick and remove the existing circuitry and the gameport cable:

Switch Setup

The next step is to work out how all the switches and potentiometers are wired together. The potentiometers for the joystick axes and throttle will probably be simple to spot and simple to wire as well as all they need is power, ground and a voltage out to the PIC. The switches might prove to be a little tricky, depending on the joystick. By tracing the tracks on the PCB in the stick, it has been determined that the switches are connected according to the following diagram:

The interesting one to note is the POV hat. Instead of being 4 separate switches, the POV hat is wired up as an analogue system, where the resistance across the green and orange wires determines which button was pressed. The following table shows the resistance of each switch:

Switch Position Resistance (Ohms)
Neutral 80k
Up 200
Right 20k
Down 40k
Left 60k

Therefore, in order to find out the direction of the POV hat, its wires will be connected in a potential divider circuit as shown below:

When wired to a 5V supply, this will give the following voltage outputs:

Switch Position Voltage (V)
Neutral 3.9
Up 0.045
Right 2.4
Down 3.2
Left 3.7

Circuit

With the joystick wiring determined, the next step is to design a circuit that can interface with all the buttons and pots and send the data to the PC. My circuit uses a PIC18F2550 running at 20MHz. The switches are all connected to PORTB and the potentiometers and POV hat are connected to the ADCs on PORTA. The schematic is shown below (click on the image to see the full-size version):

The stripboard layout is shown below (note the 3 wire links. Click for larger image):

With the circuit built and tested, it can finally be placed into the joystick base and the gameport cable replaced with a USB one. A photo of the converted joystick is shown below:

And that’s it! Put everything back together again and plug the joystick into a free USB port. With luck, the joystick will be correctly recognized by Windows. You can then calibrate it and start using it to play games that support a joystick.

Conclusion

This project kills two birds with one stone. On one hand, the project shows you how to create a USB HID Joystick device and convert your old joystick into a modern one or even create your own joystick. On the other hand, if you know how to create the firmware for a USB joystick, the steps to create the firmware for any other HID device are very similar and the only difference will be in creating the HID report descriptor and how you send the data to the PC.

Project Downloads

You can download the project files here, including the source code and schematics.

USB Joystick Project Files

38 Responses to USB Joystick

  1. i have a 8MHZ crystal, but i cant figure out how to get mikroC to work with this 8Mhz crystal, i have no 20mhz crystals

    do you know how i can set this up?

  2. OK, i sorted it, and YOU RULE! thank you for the code! at least i have something to go with now :)

  3. jos says:

    interesting explantion
    but i have one question. i am using
    psoc programmer & designer. so do
    you think (can it work?) if i program the
    PIC18F2550 using psoc prog.

    thank you in advance
    Jos

    • Amr Bekhit says:

      I doubt that you could program a Microchip device using a PSoC programmer. You could still build this project using a PSoC if you find some USB HID sample code: the changes to the descriptor will be very similar.

  4. aj says:

    #include “Definit.h”
    #include “VARs.h”
    Sir i am to submit project very soon so please reply.
    i am using mikroc and 18f4550 so what are these files for do they need to be changed in my case.

    • Amr Bekhit says:

      Definit.h and Vars.h can be found in the Examples directory in your MikroC installation folder. They contain various definitions that the HID Library needs to work. Just copy them to your project folder. You don’t need to edit them.

  5. Iron says:

    Hi Amr !
    I read your page one hundred time but my project don’t want to work :(

    What did you want to say by “unpadded size of the report descriptor”?

    how can I know it? :(
    Thanks for times you spend for us

  6. Matthew says:

    Hey, I was just wondering if you possibly had a larger picture of your circuit board. I’m trying to follow the protoboard diagram, but I’m not sure what the X’s are and it’s kind of confusing me :/

    Great guide though, I’ve had one of these sitting around forever!

    • Amr Bekhit says:

      Hi Matthew,

      The X’s on the stripboard layout indicate where you should break the tracks. Regarding a larger circuit diagram, if you click on the image of the schematic you will be taken to a full size version. If that’s not big enough then please get in touch via the Contact page and I’ll send you a larger version.

      Glad you like the guide!

      Amr

  7. Felipe says:

    how do I modify the unsigned char const ProductDescr?

  8. Felipe says:

    I already had changed the name, but it was necessary to change the product id to show the new name.
    I used the mikroC’s tool to create the name.

  9. ev says:

    Hi Amr Bekhit,

    I was trying to modify your code in to a wheel
    with but it doesnt work. i am new to this. The problem is that i don’t understand to change the hid description in mikroC and the connection between the program usbdsc and joystick part. The joystick part is done.
    the compiler doesnt have any errors zo the problem is in usbdsc.C. The part of the software DT.exe i understand but what do i need to change than in mikroC. Sorry for my bad english

    Regards,
    ev

  10. Steve says:

    Whe I “build” in MikroC Pro I get numerous error!? Why is this please?

  11. Steve says:

    Sorry it appears my quote did not post properly?
    I will post them again without quotes. My errors upon “builing”—>
    0 122 Compilation Started USBJoystick.c
    13 396 Invalid declarator expected’(‘ or identifier USBJoystick.c
    13 300 Syntax Error: ‘)’ expected, but ‘;’ found USBJoystick.c
    14 300 Syntax Error: ‘;’ expected, but ‘bit’ found USBJoystick.c
    14 398 Declarator error USBJoystick.c
    14 396 Invalid declarator expected’(‘ or identifier USBJoystick.c
    14 300 Syntax Error: ‘)’ expected, but ‘;’ found USBJoystick.c
    14 402 ; expected, but ‘}’ found USBJoystick.c
    15 371 Specifier needed USBJoystick.c
    15 396 Invalid declarator expected’(‘ or identifier USBJoystick.c
    15 312 Internal error ” USBJoystick.c
    0 102 Finished (with errors): 18 Sep 2011, 23:09:14 usbjoystick.mcppi

  12. Steve says:

    Oh and sorry to be a pain, but I forgot to mention I am building using a pic18f4550
    Many thanks
    Regards
    Steve

    • Steve says:

      It’s ok,
      I solved my own problem :)
      There’s obviously been a lot of changes to the library since this “tut” was written, and mikroc pro also handles specific bits differently. Now I’m just stuck with the HID desc’. A blank device will display if I use the desc’ made by mikroc (but obviously this is unusable), however if I modify the desc’ I get an exclamation mark next to the device (no joystick symbol) :(

      • Steve says:

        OK… That problem was solved. But now when I press a button nothing is actually happening!? I guess the wrong (or none at all) data is getting transmitted?? Gonna have to do some more research, unless anyone has pointers please??

  13. Setsuna says:

    Hi,

    I’m a newbie in USB programing.To learn the FUN way,i plan to convert my old joystick to USB joystick.I understand how the Descriptor works.I converted it to use in other compiler (not Mikro C).It works.It connects & shows in Game Controllers but..it didn’t work.It freezes there & nothing happen even when i tried to move the pots.What could went wrong.?Help

  14. suraj says:

    hi,

    i’m kinda new to these things and i would like to how would i recompile this project source after editing??? i doesn’t hav a MikroC project file

    plz help………..

  15. Daniel says:

    i am not able to understand what to do to program the pic ….. give me more tips? Some errors appear in the compiler …. i need it for may notes at school…

    error: 0 360 Unresolved extern ‘USB_Init_Desc’ __Lib_USB_genHID.c

    all using miKro compiler c pro 5,61

    thanks

    • Amr Bekhit says:

      It sounds like you haven’t enabled the USB library for your project. You can do so from the library manager.

      • daniel says:

        oh! Now I understood, i have compiled the program without erros! soo…. another problem…. the potenciometer…. you gave a table with the values of resistence… but, the nominal value is??? sorry for the english mistakes…. i am a student of english….

  16. daniel says:

    thanks, but i trying to know the values of the potenciometers that are in J8, J9….

    • Amr Bekhit says:

      Ah I see. Those potentiometers were the ones for the X, Y and throttle axes already fitted in the joystick. I didn’t measure what their resistances were, but that doesn’t really matter, as I’m just interested in the voltage on their wipers. I’m sure most potentiometers you can find will work just fine.

  17. Daniel says:

    Hi! Now I am having some problems with the code, some errors appear when I build the project… I want to modify something in your project… that are working perfectly… but only with your Hex file… not mine.. could you help me….? if you inform your e-mail… it would be easier..

    270 123 Compiled Successfully USBdsc.c
    0 122 Compilation Started USBJoystick.c
    13 305 Inconsistent type USBJoystick.c
    13 305 Inconsistent type USBJoystick.c
    13 396 Invalid declarator expected’(‘ or identifier USBJoystick.c
    22 324 Undeclared identifier ‘HID_InterruptProc’ in expression USBJoystick.c
    34 324 Undeclared identifier ‘HID_Enable’ in expression USBJoystick.c
    43 316 Identifier expected, but ‘bit’ found USBJoystick.c
    43 402 ; expected, but ‘bit’ found USBJoystick.c
    45 316 Identifier expected, but ‘bit’ found USBJoystick.c
    45 402 ; expected, but ‘bit’ found USBJoystick.c
    45 424 ‘}’ expected ‘;’ found USBJoystick.c
    91 424 ‘}’ expected ‘}’ found USBJoystick.c
    0 102 Finished (with errors): 08 jun 2012, 14:43:04 MyProject.mcppi

  18. bandicoot says:

    hello
    i want to replace the analog X-Y with an accelerometer do you think it’s possible in your code like this project
    http://www.starlino.com/usb_gamepad.html
    thanks

    • Amr Bekhit says:

      I’d imagine that would be relatively simple to do. All you would need to do is read the accelerometer data for both the x and y axes and transmit those values in place of the joystick’s x and y values.

  19. Abhishek says:

    Can anyone tell me the changes i’ll need to make to this code to make it work 18f4550…i’m fairly new to this…

  20. Valdis says:

    Hello!

    I just trying to adapt Your code for Mikroc for PIC v.5.8.0 and got only hid device in my hardware, but instead of joystick. can You help me to resolve this problem?

    Best regards,
    Valdis Bogdans.

  21. Bandicoot says:

    hello
    i try to add bootloader to your code but no luck do you have a idea to keep a bootlader like HID where code need to start at 0×01000
    THANKS

  22. Paul says:

    I modified the descriptor and I’m able to see the progress bar of the throttle and rudder in the joystck properties Windows, but have no reaction in the flight sim. Instead X and y works. What’s wrong? What I have to add again inot descriptor to see this 2 axis?

    Thanks

  23. Mike says:

    I use mikroC PRO for PIC cannot open this file.How to open it? thank

  24. Yeshitha Amarasekara says:

    I dont understand what to modify in the USBdsc.c file and when I build that c file I get an error, main function not defined although I included Definit.h and VARs.h in the project file. please help me???

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>