12 key usb keypad with Sparkfun ProMicro

About a year ago I finished building the new photo booth for my photography small business. I wanted to use push button arcade buttons to begin the photo booths automation. I found some tutorials using a Teensy to act as a usb controller. In my first attempt at soldering I connected the arcade buttons to the Teensy.

The Teensy works great and really adds a lot to the photo booth.

From two buttons to twelve, connecting the usb keypad

We’re busy planning for a big Harry Potter Halloween Party and we wanted to use our photo booth. With Breeze Softwear’s DSLR Remote Pro I found the ability use keyboard shortcuts to change profiles. I also found that you can use the same print overlay in your live view so users can position themselves to match whatever overlay you’re using. Their example was a headless sumo wrestler so users could put their head on top of the wrestler. I thought of using cutouts of different characters from the Harry Potter movies so people could pose with their favorites.

The idea is great but I needed a way to allow people to quickly switch between the profiles. We don’t use a touch screen for our booth so I started looking for programable mini keyboards. I came across this tutorial on SparkFun for making your own usb keypad. The ProMicro is like the Teensy so I felt confidant I could make this work. After getting the ProMicro and Usb Keypad it was time to put them together.

Under the Microscope, a usb keypad

My work has a nice soldering station with a lighted magnifying glass making it easier to solder.

Soldering wires onto the usb keypad

Having not soldered anything in a year my skills clearly did not improve. They may not be clean but they did work. I did miss a little and got some solder on the keypad, so it’s a good thing this project is just for me.

Beginning the ProMicro connection to the usb keypad

I followed the tutorials suggested pins to use.

Finishing the connections to the usb keypad

Almost done!

Final Product, promicro usb keypad

All connected and the wire lengths were perfect to fold the Pro Micro up against the usb keypad.

Next step, coding

After getting all my wires connected I ran into a little bit of a problem. Using Arduino I pushed my code to the ProMicro but nothing worked. I realized I hadn’t tested the ProMicro when it first arrived so I tried the basic Blink program and that didn’t work either.

Now I needed to figure out if my ProMicro even worked. I found in the troubleshooting section a way to fix a “bricked” board by manually connecting the GRD and RST pins. This would load the Arduino Leonardo bootloader. If you tap the reset pin twice it will pause eight seconds before starting. This is just enough time to upload a new sketch. For more instructions on bricked boards read the full faq.

Doing so I was able to upload the Blink program so I knew my board wasn’t broken. Yea!

I’m not sure if it’s just my board, but any time I want to upload a new sketch I have to go through this procedure. I have the correct board selected in Arduino so I’m not sure why it keeps happening.

Back on track

Now that I could load new sketches onto my ProMicro I had to re-solder my connections. I had removed them to do my troubleshooting of the ProMicro. On the second time around I used different pins so my code below wont match the pictures above.

I began using the code from the tutorial but only a few of the buttons were working, and half that did were printing the wrong number. I looked at the datasheet for the usb keypad and found that the pins were updated since the writing of the tutorial.

12key usb keypad datasheet

All I had to do was rewrite a small part of the code to change the watched pins.

int getKeypadStatus()
{
  int rowPins[4] = {keypadPins[1], keypadPins[6], keypadPins[5], keypadPins[3]};  // row pins are 2, 7, 6, and 4 of the keypad
  int columnPins[3] = {keypadPins[2], keypadPins[0], keypadPins[4]};  // column pins are pins 3, 1, and 5 of the keypad

After all the buttons were functioning correctly on the usb keypad I changed the switch cases. The shortcuts for profiles are CTRL + SHIFT + #. Originally this wouldn’t have been possible but version 1.0.1 of Arduino allowed for non-ASCII characters. Sending this updated sketch to my ProMicro and loading up DSLR Remote Pro it was a success. Switching between profiles was instantaneous though the bottom row of buttons wouldn’t work. These pointed at F1, F2, and F3 respectively but only triggered the keys 1,2, and 3. I’ll continue to work on this but nine profiles will be plenty for our party so I’ll probably just leave it there.

Here is my final code, or check it out on github for the latest version.

/* keyPadHiduino Example Code
   by: Jim Lindblom
   date: January 5, 2012
   license: MIT license. If you find this code useful, please
   feel free to use this code however you'd like, commercially 
   or otherwise. Just keep this license on there whatever you do.
*/
// Pins 1-7 of the keypad connected to the Arduino respectively:
int keypadPins[7] = {2, 3, 6, 7, 10, 16, 14};
int keypadStatus;  // Used to monitor which buttons are pressed.
int timeout;  // timeout variable used in loop

void setup()
{
  for (int i=0; i<7; i++)
  {
    pinMode(keypadPins[i], INPUT);  // Set all keypad pins as inputs
    digitalWrite(keypadPins[i], HIGH);  // pull all keypad pins high
  }
}

void loop()
{
  keypadStatus = getKeypadStatus();  // read which buttons are pressed
  if (keypadStatus != 0)  // If a button is pressed go into here
  {
    sendKeyPress(keypadStatus);  // send the button over USB
    timeout = 2000;  // top of the repeat delay
    while ((getKeypadStatus() == keypadStatus) && (--timeout))  // Decrement timeout and check if key is being held down
      delayMicroseconds(1);
    while (getKeypadStatus() == keypadStatus)  // while the same button is held down
    {
      sendKeyPress(keypadStatus);  // continue to send the button over USB
      delay(50);  // 50ms repeat rate
    }
  }
}

/* sendKeyPress(int key): This function sends a single key over USB
   It requires an int, of which the 12 LSbs are used. Each bit in
   key represents a single button on the keypad.
   This function will only send a key press if a single button
   is being pressed */
void sendKeyPress(int key)
{
  switch(key)
  {
    case 1:  // 0x001
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('1');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 2:  // 0x002
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('2');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 4:  // 0x004
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('3');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 8:  // 0x008
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('4');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 16:  // 0x010
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('5');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 32:  // 0x020
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('6');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 64:  // 0x040
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('7');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 128:  // 0x080
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('8');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 256:  // 0x100
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('9');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 512:  // 0x200
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('KEY_F1');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 1024:  // 0x400
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('KEY_F2');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
    case 2048:  // 0x800
      Keyboard.press(KEY_LEFT_SHIFT);
      Keyboard.press(KEY_LEFT_CTRL);
      Keyboard.write('KEY_F3');
      Keyboard.release(KEY_LEFT_CTRL);
      Keyboard.release(KEY_LEFT_SHIFT);
      break;
  }
}

/* getKeypadStatus(): This function returns an int that represents
the status of the 12-button keypad. Only the 12 LSb's of the return
value hold any significange. Each bit represents the status of a single
key on the button pad. '1' is bit 0, '2' is bit 1, '3' is bit 2, ..., 
'#' is bit 11.
This function doesn't work for multitouch.
*/
int getKeypadStatus()
{
  int rowPins[4] = {keypadPins[1], keypadPins[6], keypadPins[5], keypadPins[3]};  // row pins are 2, 7, 6, and 4 of the keypad
  int columnPins[3] = {keypadPins[2], keypadPins[0], keypadPins[4]};  // column pins are pins 3, 1, and 5 of the keypad
  int keypadStatus = 0;  // this will be what's returned
  
  /* initialize all pins, inputs w/ pull-ups */
  for (int i=0; i<7; i++)
  {
    pinMode(keypadPins[i], INPUT);
    digitalWrite(keypadPins[i], HIGH);
  }
  
  for (int row=0; row<4; row++)
  {  // initial for loop to check all 4 rows
    pinMode(rowPins[row], OUTPUT);  // set the row pin as an output
    digitalWrite(rowPins[row], LOW);  // pull the row pins low
    for (int col=0; col<3; col++)
    {  // embedded for loop to check all 3 columns of each row
      if (!digitalRead(columnPins[col]))
      {
        keypadStatus |= 1 << ((row+1)*3 + (col+1) - 4);  // set the status bit of the keypad return value
      }
    }
    pinMode(rowPins[row], INPUT);  // reset the row pin as an input
    digitalWrite(rowPins[row], HIGH);  // pull the row pin high
  }
  
  return keypadStatus;
}

Leave a Reply