Arduino USB joystick to iCade converter

  • 2014/03/16 Update: The source code to this is now on GitHub. Check the Arduino link at the top of each page of this site.
  • 2013/02/22 Update: WordPress seems to love to chew up source code, so the sources pasted in are not working properly. I will try to find a solution. Sometimes it works great, other times not at all.
IMG_0649

Arduino Leonardo and USB Host shield acting as USB Joystick to iCade converter.

Today, I picked up my Circuits@Home USB Host adapter from the post office. Now I could finally try to put together a converter that would read from a standard USB joystick and send out iCade formatted keyboard characters to my iPad. Since I had never worked with USB before, I was amazed it only took about 30 minutes to figure out. This is a testament to the work done by the folks responsible for the USB Host library code. The inclusion of functioning sample code allowed me to make quick modifications to do what I wanted.

I will post full details soon, but right now, here are some highlights.

For this project, I am using an Arduino Leonardo. Unlike the UNO and most other Arduinos, the Leonardo is capable of acting as a USB HID (human interface device) so it can appear to be a mouse or keyboard to the computer it is plugged in to. In my case, the computer would be an iPad via Apple’s Camera Connector Kit USB port adapter, and the Leonardo would be acting like a keyboard to send iCade keys,

I had previously downloaded the Arduino USB library from GitHub and extracted it in to my Arduino IDE library folder. I discussed my first experience with Arduino libraries in an earlier article when I was trying to get the iTead Studios USB shield to work.

I was able to test the USB by running various example programs that came with the library, specifically USBHidJoystick. Here is an example of that program’s output as I moved the joystick and pressed some buttons:

This example program shows values coming back from the USB joystick. The joystick and buttons seem to be contained in the Z bytes (Z1 and Z2).

This example program shows values coming back from the USB joystick. The joystick and buttons seem to be contained in the Z bytes (Z1 and Z2).

The joystick I am using is a “Playtech Pro Arcade Fighting Stick” sold on Amazon.com. (As of this posting, the price is $32, but it was about $26 back in December.) Moving the joystick around seemed to change bits in the Z1 value. The four arcade buttons changed bits in Z2. This joystick has a row of seven small control buttons on the top, and four of those buttons (marked Select, Start, L3 and R3) toggled the remaining four bits in Z1.

I do not know how standard USB joysticks are, but I would hope at least the joystick directions and primary buttons would be standardized. The four buttons that were different colors and labeled like Playstation controllers (triangle, circle, X, square) were the high bits of Z2, and the grey surrounding buttons (L1, L2, R1 and R2) were the lower four bits. Perhaps these grey buttons map to the front edge buttons found on console gamepads?

The iCade has a joystick and eight buttons, so I would need to figure out which of the USB buttons I would need to use.

This sample program would be the basis of my experiment. It was made of an Arduino sketch called USBHIDJoystick.pde, and two C++ files — hidjoystickrptparser.cpp and hidjoystickrptparser.h. It seemed the sketch would initialize the USB library, and specify the name of a custom function which would parse the USB data and pull out the joystick related bits. Inside the main loop() was just a call to a task handler function in the library, which I assume is the code responsible for polling and processing incoming USB data.

Inside the hidjoystickrptparser.cpp file, there was a function called OnGamePadChanged() which would print out various values. I planned to comment the print lines out, and just have it call my own button processing code which would parse the set bits rather than read digital input pins. I would just stick the Z1 and Z2 bytes together as a 16-bit integer:

void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
  // Call our joystick handler...
  // We are going to combine the two Z1 and Z2 bytes in to a 16-bit value
  // for easier parsing...
  handleJoystick( (unsigned int)(evt->Z1< <8)|(evt->Z2) );

The GamePadEventData structure, defined in the .h file, contains five byte variables:

uint8_t X, Y, Z1, Z2, Rz;

I would just take the Z1, shift it to the left 8 bits, and OR in the Z2 value, creating a new 16-bit value that held both. In hex, it would look like this: 0xAABB, where AA is Z1 and BB is Z1. Now, inside my new handleJoystick() routine, I could look for those bits rather than scan digital I/O pins.

Instead of using the example sketch, I planned to just merge the USB specific items in to my existing teensy_icade sketch. This would be including some header files, and declaring some variables. The following code was lifted directly from the sample sketch:

// Header files, taken from USBHIDJoystick example.
#include

#include
#include
#include
#include <usb_ch9 .h>
#include
#include
#include
#include</pre>
<address>#include
#include

#include "hidjoystickrptparser.h"

#include #include
#include
#include
// Define some C++ stuff.
USB Usb;
USBHub Hub(&Usb);
HIDUniversal Hid(&Usb);
JoystickEvents JoyEvents;
JoystickReportParser Joy(&JoyEvents);</address>
<pre>

Inside my setup(), I would need to include the USB specific items. Since I have not found any documentation on the USB library, I can only assume what these functions do. Usb.Init() seems clear enough (though this code still attempts to run even if it fails, which is bad), and the Hid.SetReportParser() seems to be where the calling program passes in a structure containing the handling functions that the USB code will call when it gets a joystick packet. Or something.

  // USB initialization stuff.
  if (Usb.Init() == -1)
      Serial.println("OSC did not start.");

  delay( 200 );

  if (!Hid.SetReportParser(0, &Joy))
      ErrorMessage<uint8_t>(PSTR("SetReportParser"), 1  );

After this, the only other bit of code I would be borrowing was to put a call to the USB task handler inside my loop():

  // Handle USB
  Usb.Task();

Now all I needed to do was pull out my pin reading code from the loop, and make it a separate function which would now be called, not by my loop, but when the USB handler had data to process.

Since my original code used an array of bytes, each representing what pin should be read to indicate the specific direction/button was active, I decided to use the same approach with this. I would use the same button mappings, but alter the defines so that instead of containing a byte value representing a pin number, each one would be a 16-bit value representing which bit was the one for the button. It looks like this:

// We will be treating joystick as a 16-bit value.
// Extra Buttons:
#define SELECT_USB  (1< <8)  // Z1:0x0100 - Select
#define L3_USB      (1<<9)  // Z1:0x0200 - L3
#define R3_USB      (1<<10) // Z1:0x0400 - R3
#define START_USB   (1<<11) // Z1:0x0800 - Start
// Joystick:
#define UP_USB      (1<<12) // Z1:0x1000 - Up
#define RIGHT_USB   (1<<13) // Z1:0x2000 - Right
#define DOWN_USB    (1<<14) // Z1:0x4000 - Down
#define LEFT_USB    (1<<15) // Z1:0x8000 - Left
// Grey/Front Buttons:
#define BTN1_USB    (1<<0)  // Z2:0x0001 - L2
#define BTN2_USB    (1<<1)  // Z2:0x0002 - R2
#define BTN3_USB    (1<<2)  // Z2:0x0004 - L1
#define BTN4_USB    (1<<3)  // Z2:0x0008 - R1
// Primary Buttons:
#define BTN5_USB    (1<<4)  // Z2:0x0010 - "Triangle"
#define BTN6_USB    (1<<5)  // Z2:0x0020 - "Circle"
#define BTN7_USB    (1<<6)  // Z2:0x0040 - "X"
#define BTN8_USB    (1<<7)  // Z2:0x0080 - "Square"

In my original code, I called them “BTN1_PIN” or “RIGHT_PIN”, but I wanted to change the names to be more clear, and also so a future version might mix both capabilities in the same source code. My array of these items would be updated to hold 16-bit values (instead of bytes), and use the renamed defines:
[/code]

// Each of these items is a 16-bit value, where the bits represent the 12
// iCade buttons.
unsigned int myPins[USB_BTN_COUNT] =
  {UP_USB, DOWN_USB, LEFT_USB, RIGHT_USB,
  BTN1_USB, BTN2_USB, BTN3_USB, BTN4_USB,
  BTN5_USB, BTN6_USB, BTN7_USB, BTN8_USB};

You can see that I also renamed a count #define to be “USB_BTN_COUNT” instead of “DI_PIN_COUNT”. The other defines I used in the original were removed, since they were just used to error check the user in case they tried to build a version using pins outside of the allowed range.

My iCade array remains the same, though it will need to be customized to map the eight USB buttons to the proper iCade buttons once I figure out what they should be.

Now the real work could begin. I ripped out the entire loop that went through the myPins[] array and made only a few changes. Instead of reading the status of a digital pin in the array, I already knew the status since I was being passed in the bit value. I would just set status to that specific bit:

void handleJoystick(unsigned int buttonMask)
{
  /*-------------------------------------------------------------------------*/
  // Loop through each Digital Input pin.
  for (int thisPin=0; thisPin < USB_BTN_COUNT; thisPin++ )
  {
    // Read the pin's current status.
    unsigned int status = (buttonMask & myPins[thisPin]);

For digital pins, you read each pin and got a LOW or HIGH value. For this, I assume multiple bits could be set at the same time (?), so I wanted to take the buttonMask passed in from the USB code and just test the specific bit pattern for the button in question. I was still looping through all 12 buttons, but now I would be checking the status of a bit pattern to a bit mask in an array, rather than reading a digital pin and storing the status of that pin.

Now status would no longer be LOW or HIGH (0 or 1), but instead would either be 0x0000 or have a specific bit set like 0x0200 or 0x8000. Instead of comparing “status==LOW” I needed to simply check against it being 0. (My Teensy wiring used “active low” so LOW meant the button was pressed, and here the bit pattern being HIGH mean it was pressed.) That change looked like this:
[/code]

            // If pin is Active LOW,
            if (status!=0)
            {

(I still need to clean up the comments.)

I believe those were the only changes I needed to make, but my first test did not work. It seems my debounce code was causing some kind of issue. Since I assumed the joystick should already be doing denounce before sending out a USB packet with the button status, I tried just commenting out my debounce check and that got everything working. Almost.

The Teensy 2.0 had to be told what kind of USB device it was at compile time. A menu setting would toggle it between USB Serial, or USB Keyboard. With the Leonardo, that option did not appear in the IDE. A quick search revealed I needed to turn on the USB keyboard support in setup():

   Keyboard.begin();

Once done, I could open up a text editor (so the “typing” from the Arduino had a place to show up) and move the joystick around and see the results on the screen:

iCade keys generated by the USB joystick.

iCade keys generated by the USB joystick.

“lv” represents BTN8 pressed then released. “hr” is BTN5. “we” is UP. It worked!

Testing on the iPad was next. Unlike the Teensy 2.0, which could run from the tiny 20mah of power the iPad USB port provided, the Leonardo and USB Host shield would be more demanding. I was not sure what the rules were with using an external power supply on an Arduino — didn’t I read somewhere that you had to change something to prevent problems if it was also connected to a computer’s USB port at the same time?

To be safe, I decided to use a powered USB hub. For testing, my setup looked like this:

JOYSTICK -> Leonardo -> USB Hub -> Mac

I figured all I would have to do is unplug the hub cable from the Mac, and plug it in to the iPad using the Apple Camera Connector kit.

It worked just fine, and I soon found myself testing it on Atari’s Greatest Hits (the first official app to support the iCade) as well as Gridlee (a free arcade game which runs on the MAME emulator).

The buttons were not where they needed to be, so I will need to fix that next.

But for now, I wanted to share my initial progress. I will clean up the code and post it soon.

6 thoughts on “Arduino USB joystick to iCade converter

  1. Sven "Thorwan" Haskel

    Wow! I’m stunned.

    I bought an Arduino Leonardo and USB host shield for pretty much this project in january, but couldn’t find time to work on it until a few days ago. I wanted to hook up a HORI Real Arcade Pro 3 to an iPad and make it put out iCade compatible Keyboard commands. Its output in the USBHidJoystick example is pretty similar to what you’ve got. I’ve read a lot about USB descriptors and device class definitions the last few days and couldn’t find out where to start with this… (Haven’t done any coding since DOS was a thing…) While I’m sure I’d get it working some day, I’m glad I stumbled upon your website before getting too frustrated.

    Please, could you sent me the full code? I probably need to make some minor adjustments to get it to work with my arcade stick, but it would save me a lot of work :)

    I’m also glad that you tackle the X-Arcade, as I’m having an old Dual Joystick model. Thanks for doing all of this,


    Sven

    Reply
    1. allenhuffman Post author

      I will be glad to share my test code. I have not had time to go back and rewrite it, but I envision making it support digital pins (like my Atar 2600 project) as well as various USB joysticks. I have learned quite a bit.

      Reply
      1. Sven "Thorwan" Haskel

        Sounds great, I’d really appreciate it!
        Regarding the test code, just drop me an email. If there’s anything I can help with (I.e. testing), let me know!


        Sven

        Reply
  2. Riaan L. Rai

    I have just completed a similar project , USB HID keyboard to Bluetooth HID keyboard adapter with mame to icade key map conversion. My project works with the X-Arcade BYO kit and ordinary USB Keyboards. The unique feature of my project is that it connects to ipad or iPhone via Bluetooth instead of usb. I will post links soon. Regards.

    Reply
    1. Allen Huffman Post author

      What did you use for the Bluetooth shield on Arduino? When I was searching, they were around $50, which would make it it more expensive than an actual iCade core or something. I still think having an X to iCade converter would be a nice product.

      Reply

Leave a Reply