Friday, July 13, 2012

Arduino HID Gamepad, part 1

Note: This post mostly outlines my shield design and a quick test of the hardware using the Mouse example included with the Arduino library.  If you're looking for the actual HID gamepad implementation, have a look at part 2

I have been thinking for quite some time about a project which I hope to shape into a Senior Project for my Embedded Systems and Software Engineering degrees, but I've gotten a bit impatient and have kind of jumped the gun to start working on it.  The first step in this project requires implementing a generic HID gamepad.  The Arduino Leonardo recently launched with USB HID profile support, thanks to the on-chip USB support of the on-board Atmega32U4 microcontroller.  This makes prototyping much simpler, and it actually reduces the cost of the Leonardo compared to the Uno--which is otherwise the most comparable Arduino model out there--due to not needing a separate microcontroller to handle the USB-serial connection.  Now, I know there are a lot of Arduino haters out there, but the fact is, it is fantastic for prototyping.  I get as annoyed as anybody when I see somebody enclose an entire Arduino into a finished product rather than redesigning for the bare microcontroller (which I fully intend to do), but it's a nice prototyping platform with great community support.

Anyway, I got my Leonardo today and immediately loaded up the button mouse example sketch to try it out and lo and behold, it just worked.  Well, it would have just worked if I had built the circuit the way I was supposed to.  As it was, I didn't feel like breadboarding it with pull-down resistors, so I changed the code around to enable the internal pull-up resistors and then changed the logic to be low-triggered rather than high-triggered:


const int upButton = 2;     
const int downButton = 3;        
const int leftButton = 4;
const int rightButton = 5;
const int mouseButton = 6;

int range = 5;     // output range of X or Y movement; affects movement speed
int responseDelay = 10;     // response delay of the mouse, in ms

void setup() {
  // initialize the buttons' inputs:
  pinMode(upButton, INPUT);       
  pinMode(downButton, INPUT);       
  pinMode(leftButton, INPUT);       
  pinMode(rightButton, INPUT);       
  pinMode(mouseButton, INPUT);
  
  digitalWrite(upButton, HIGH);
  digitalWrite(downButton, HIGH);
  digitalWrite(leftButton, HIGH);
  digitalWrite(rightButton, HIGH);
  digitalWrite(mouseButton, HIGH);
  
  // initialize mouse control:
  Mouse.begin();
}

void loop() {
  // read the buttons:
  int upState = digitalRead(upButton);
  int downState = digitalRead(downButton);
  int rightState = digitalRead(rightButton);
  int leftState = digitalRead(leftButton);
  int clickState = digitalRead(mouseButton);

  // calculate the movement distance based on the button states:
  int  xDistance = (leftState - rightState)*range;
  int  yDistance = (upState - downState)*range;

  // if X or Y is zero, move:
  if ((xDistance == 0) || (yDistance == 0)) {
    Mouse.move(xDistance, yDistance, 0);
  }

  // if the mouse button is pressed:
  if (clickState == LOW) {
    // if the mouse is not pressed, press it:
    if (!Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.press(MOUSE_LEFT); 
    }
  } 
  // else the mouse button is not pressed:
  else {
    // if the mouse is pressed, release it:
    if (Mouse.isPressed(MOUSE_LEFT)) {
      Mouse.release(MOUSE_LEFT); 
    }
  }

  // a delay so the mouse doesn't move too fast:
  delay(responseDelay);
}


Anyway, the next thing to do was to build myself a gamepad that I could plug in to the Leonardo for testing.  I plan to use the SNES controller in my final project, but for prototyping, I decided to go with NES instead.  The NES uses the exact same control protocol as the SNES, just with fewer buttons, so that meant fewer parts, as well as fewer IO pins for my debug setup.  I had a few empty protoshields lying around, so I figured that would be perfect for this build.  I also had a CD4021 shift register in my parts drawer from an NES controller I cannibalized awhile back.  If you're looking to build a DIY NES controller, please, for heaven's sake, just go out and buy a 4021 and a few resistors and do it from scratch.  Don't destroy an NES controller just for its IC.  I've seen so many awesome mods that just piss me off because after all that hard work, they have this wiring on the inside.  Seriously, this is all you need (plus a couple of resistors).  Or, if you want to get really fancy, hit me up for one of these (scroll down to my last post for a picture with a size comparison to an SD card.  Anyway, despite that rant, I used the IC from an original controller, because I had already used the rest of the controller for various other stuff, and happened to have the IC left over.  I have a stash of the SMD 4021's, but alas, the protoshield's SOIC footprint is only 14 pins, so I needed the DIP version, and this one was the only one I had.

Ok, now that I'm done with that rant, time for pictures :)

I ran out of solder, so nothing is connected yet.  However, all of the parts are mounted, so I just need to get more solder (and perhaps some finer gauge wire than what I have on hand at the moment would be good too).  Apparently, RadioShack no longer carries through-hole mounted tact switches, but thankfully, the spacing on the SMD feet lined up well enough with the board holes.  The plan is to hook each button up directly to a digital I/O, as well as hooking them up to the 4021 and connecting its latch, clock, and data lines to the remaining digital IO's in order to implement the NES protocol as well.  This way, I can read the buttons either directly or via the NES protocol all from the same shield configuration.


Also, in case you're looking for the correct stacking headers for the Arduino shields, I've managed to track down a manufacturer that makes them.  Unfortunately, the stackable header kits on SparkFun and elsewhere don't actually have the right pin counts.  They sell kits with 2x6-pin headers and 2x8-pin headers.  Arduino shields actually use 1x6, 2x8, and 1x10.  You don't, strictly speaking, need the other four pins, but all of the official shields have them.  Digikey carries these headers, but they don't seem to carry them with tails quite as long as the ones on official shields.  There's not a lot of clearance with the ones I have.  So anyway, here are part numbers for the headers that you'll want, they aren't quite the same as the ones Digikey has (DK carries the -03 variety rather than the -04, which I explain below).


Samtec
1x10 pins: SSQ-110-04-T-S
1x8 pins: SSQ-108-04-T-S
1x6 pins: SSQ-106-04-T-S


Some variations on these part numbers:
-04 is the length of the tail.  -03 or shorter will make for very little clearance above the barrel jack.  On the older boards with full-sized USB-B plugs, you won't be able to clear the USB jack.  However, Digikey doesn't seem to carry anything longer than -03.  If you can find another distributor that stocks the longer tails, let me know in the comments


-T is tin plated pins, replace it with -G for gold plated.
The -110, -108, and -106 are the pin count.  If you want dual row, the numbering is -2XX (i.e. -205 would be 2x5 pins, etc.).

Anyway, that's it for now.  I'm super busy working two jobs this summer, so hopefully I'll get some free time now and then to work on some of this stuff.  Once I get USB HID down, I plan to move on to bluetooth HID (which is just a simple wrapper around the USB HID protocol, so it should be simple enough).  Then I'll be working on building the host module.  Fun times.

No comments:

Post a Comment