HID Gamepad - Send repeating keypresses to PC while button is held.

Hi all. Novice Arduino using working on a HID gamepad project. I have the code below working with my custom gamepad. I just want to know if I can add a feature into the code.

Desired response: If a key is held down it actually sends a signal to the PC that looks like the key is being pressed>released>pressed>released cycling every 200ms until the key is actually released.

Basically make the computer read that the user is repeatedly pressing the key when it is actually held down.

This seems to be a pretty unique feature and I am not sure if it is possible. I haven’t found any other posts that match what I am looking for.

#include <Key.h>
#include <Keypad.h>
#include <Keyboard.h>

const byte ROWS = 4; //two rows
const byte COLS = 6; //two columns

char keys[ROWS][COLS] = {
{‘D’, ‘H’, ‘L’, ‘P’, ‘T’, ‘X’},
{‘C’, ‘G’, ‘K’, ‘O’, ‘S’, ‘W’},
{‘B’, ‘F’, ‘J’, ‘N’, ‘R’, ‘V’},
{‘A’, ‘E’, ‘I’, ‘M’, ‘Q’, ‘U’}
};
byte rowPins[ROWS] = {1, 2, 3, 4}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 6, 7, 8, 9, 10}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

void setup() {
Serial.begin(9600);
}
void loop() {
char customKey = customKeypad.getKey();
if (customKey) {
//Keyboard.write(“key”);
Serial.println(customKey);
}

if (customKey == ‘A’) {
Keyboard.write (‘A’);
}

if (customKey == ‘B’) {
Keyboard.write (‘B’);
}

if (customKey == ‘C’) {
Keyboard.write (‘C’);

}

if (customKey == ‘D’) {
Keyboard.write (‘D’);

}

if (customKey == ‘E’) {
Keyboard.write (‘E’);

}
if (customKey == ‘F’) {
Keyboard.write (‘F’);

}
if (customKey == ‘G’) {
Keyboard.write (‘G’);

}
if (customKey == ‘H’) {
Keyboard.write (‘H’);

}
if (customKey == ‘I’) {
Keyboard.write (‘I’);

}
if (customKey == ‘J’) {
Keyboard.write (‘J’);

}
if (customKey == ‘K’) {
Keyboard.write (‘K’);

}
if (customKey == ‘L’) {
Keyboard.write (‘L’);

}
if (customKey == ‘M’) {
Keyboard.write (‘M’);

}
if (customKey == ‘N’) {
Keyboard.write (‘N’);

}
if (customKey == ‘O’) {
Keyboard.write (‘O’);

}
if (customKey == ‘P’) {
Keyboard.write (‘P’);

}
if (customKey == ‘Q’) {
Keyboard.write (‘Q’);

}
if (customKey == ‘R’) {
Keyboard.write (‘R’);

}
if (customKey == ‘S’) {
Keyboard.write (‘S’);

}
if (customKey == ‘T’) {
Keyboard.write (‘T’);

}
if (customKey == ‘U’) {
Keyboard.write (‘U’);

}
if (customKey == ‘V’) {
Keyboard.write (‘V’);

}
if (customKey == ‘W’) {
Keyboard.write (‘W’);

}
if (customKey == ‘X’) {
Keyboard.write (‘X’);

}
}

You could try this (compiles, not tested…)

(This assumes only one key pressed at a time. I’m not sure if the Keypad library supports multiple keys pressed at once; if it does and you need that support this code would need significant changes.)

#include <Key.h>
#include <Keypad.h>
#include <Keyboard.h>

#define IS_RELEASED     false
#define IS_PRESSED      true

const byte ROWS = 4; //two rows
const byte COLS = 6; //two columns

char keys[ROWS][COLS] = {
  {'D', 'H', 'L', 'P', 'T', 'X'},
  {'C', 'G', 'K', 'O', 'S', 'W'},
  {'B', 'F', 'J', 'N', 'R', 'V'},
  {'A', 'E', 'I', 'M', 'Q', 'U'}
};
byte rowPins[ROWS] = {1, 2, 3, 4}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 6, 7, 8, 9, 10}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

char
    keyPressed,
    lastKey;

void setup() 
{
    Serial.begin(9600);
    lastKey = customKeypad.getKey();
    
}//setup

void loop() 
{
    static uint8_t
        stateKey = IS_RELEASED;
    static uint32_t
        timeKey;
    uint32_t        
        timeNow = millis();
    char 
        customKey = customKeypad.getKey();

    if( customKey != lastKey )
    {
        if( customKey == NO_KEY )
        {
            //transitioned from something to NO_KEY, so stop auto-repeat
            stateKey = IS_RELEASED;
            
        }//if
        else if( lastKey == NO_KEY )
        {
            //transition was from NO_KEY to something pressed...
            keyPressed = customKey;
            timeKey = millis();
            Keyboard.write( customKey );
            Serial.println( customKey );
            stateKey = IS_PRESSED;
            
        }//if
        
        lastKey = customKey;        
    
    }//if

    switch( stateKey )
    {
        case    IS_RELEASED:
            //do nothing
        break;

        case    IS_PRESSED:
            //while in this state, every 200mS send another press of the key
            //that got us into this state
            if( timeNow - timeKey >= 200ul )
            {
                timeKey = timeNow;
                Keyboard.write( customKey );
                Serial.println( customKey );
                
            }//if
            
        break;
        
    }//switch
    
}//loop
byte rowPins[ROWS] = {1, 2, 3, 4}; //connect to the row pinouts of the keypad

  Serial.begin(9600);

WARNING: Pin 1 is one of the Serial pins. You should not use it both for your keypad AND for Serial.

I shifted the inputs to pins 2-11.

The repeat isn’t working correctly yet. With this sketch I get two outputs per key press. (AA, BB and so on) instead of a continuous repeat when pressed.

#include <Key.h>
#include <Keypad.h>
#include <Keyboard.h>

#define IS_RELEASED false
#define IS_PRESSED true

const byte ROWS = 4; //two rows
const byte COLS = 6; //two columns

char keys[ROWS][COLS] = {
{‘D’, ‘H’, ‘L’, ‘P’, ‘T’, ‘X’},
{‘C’, ‘G’, ‘K’, ‘O’, ‘S’, ‘W’},
{‘B’, ‘F’, ‘J’, ‘N’, ‘R’, ‘V’},
{‘A’, ‘E’, ‘I’, ‘M’, ‘Q’, ‘U’}
};
byte rowPins[ROWS] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 7, 8, 9, 10, 11}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

char
keyPressed,
lastKey;

void setup()
{
Serial.begin(9600);
lastKey = customKeypad.getKey();

}//setup

void loop()
{
static uint8_t
stateKey = IS_RELEASED;
static uint32_t
timeKey;
uint32_t
timeNow = millis();
char
customKey = customKeypad.getKey();

if( customKey != lastKey )
{
if( customKey == NO_KEY )
{
//transitioned from something to NO_KEY, so stop auto-repeat
stateKey = IS_RELEASED;

}//if
else if( lastKey == NO_KEY )
{
//transition was from NO_KEY to something pressed…
keyPressed = customKey;
timeKey = millis();
Keyboard.write( customKey );
Serial.println( customKey );
stateKey = IS_PRESSED;

}//if

lastKey = customKey;

}//if

switch( stateKey )
{
case IS_RELEASED:
//do nothing
break;

case IS_PRESSED:
//while in this state, every 200mS send another press of the key
//that got us into this state
if( timeNow - timeKey >= 200ul )
{
timeKey = timeNow;
Keyboard.write( customKey );
Serial.println( customKey );

}//if

break;

}//switch

}//loop

dougn0782:
I shifted the inputs to pins 2-11.

The repeat isn’t working correctly yet. With this sketch I get two outputs per key press. (AA, BB and so on) instead of a continuous repeat when pressed.

After looking at the sourcecode for the Keypad library it looks like getKey() doesn’t function as I thought; it actually looks for a state change rather than returning the currently pressed key. That means that subsequent calls to getKey aren’t going to return the value of the key being pressed.

I think you’d need to include getState(…) calls to know if a key is in the HOLD state.

Here’s another untested example. It compiles but I’ve never used Keypad before so it’s probably still a mess.

#include <Key.h>
#include <Keypad.h>
#include <Keyboard.h>

#define IS_RELEASED     false
#define IS_PRESSED      true

const byte ROWS = 4; //two rows
const byte COLS = 6; //two columns

char keys[ROWS][COLS] = {
  {'D', 'H', 'L', 'P', 'T', 'X'},
  {'C', 'G', 'K', 'O', 'S', 'W'},
  {'B', 'F', 'J', 'N', 'R', 'V'},
  {'A', 'E', 'I', 'M', 'Q', 'U'}
};

byte rowPins[ROWS] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 7, 8, 9, 10, 11}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

char
    keyPressed,
    lastKey;

void setup() 
{
    Serial.begin(9600);
    customKeypad.setHoldTime( 200 );
    lastKey = customKeypad.getKey();
    
}//setup

void loop() 
{
    static uint8_t
        stateKey = IS_RELEASED;
    static uint32_t
        timeKey;
    uint32_t        
        timeNow = millis();
    KeyState
        keypadState;
    char 
        customKey = customKeypad.getKey();
    
    switch( stateKey )
    {
        case    IS_RELEASED:
            customKey = customKeypad.getKey();
            if( customKey != NO_KEY )
            {
                //got the first instance of the key press
                Keyboard.write( customKey );
                Serial.println( customKey );
                timeKey = timeNow;
                stateKey = IS_PRESSED;
                
            }//if
            
        break;

        case    IS_PRESSED:            
            switch( customKeypad.getState() )
            {
                case    HOLD:
                    //if key transitions to HOLD issue repeats every 200mS
                    if( timeNow - timeKey >= 200ul )
                    {
                        timeKey = timeNow;
                        Keyboard.write( customKey );
                        Serial.println( customKey );
                        
                    }//if
                
                break;

                case    RELEASED:
                    //when state shows key is released, go back to IS_RELEASED state
                    stateKey = IS_RELEASED;
                    
                break;

                default:
                    //don't care about PRESSED and IDLE states here
                    
                break;
                
                    
            }//switch
            
        break;
        
    }//switch
    
}//loop

Blackfin:
After looking at the sourcecode for the Keypad library it looks like getKey() doesn't function as I thought; it actually looks for a state change rather than returning the currently pressed key. That means that subsequent calls to getKey aren't going to return the value of the key being pressed.

I think you'd need to include getState(...) calls to know if a key is in the HOLD state.

Hmm, still not working. Can the basic keypad sketch be used with a delay at the end of the loop? Then have it reset with it loops again? Would that cause the output keypresses to repeat?

One way to get separate press and release events for each key is to use the MultiKey example from the Keypad library.

#include <Keypad.h>
#include <Keyboard.h>


const byte ROWS = 4;
const byte COLS = 6;


char keys[ROWS][COLS] =
{
  {'D', 'H', 'L', 'P', 'T', 'X'},
  {'C', 'G', 'K', 'O', 'S', 'W'},
  {'B', 'F', 'J', 'N', 'R', 'V'},
  {'A', 'E', 'I', 'M', 'Q', 'U'}
};


byte rowPins[ROWS] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 7, 8, 9, 10, 11}; //connect to the column pinouts of the keypad


//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);


void setup()
{
  Serial.begin(9600);
  customKeypad.setHoldTime(200);
} //setup


void loop()
{
  // Fills kpd.key[ ] array with up-to 10 active keys.
  // Returns true if there are ANY active keys.
  if (customKeypad.getKeys())
  {
    for (int i = 0; i < LIST_MAX; i++) // Scan the whole key list.
    {
      if (customKeypad.key[i].stateChanged)   // Only find keys that have changed state.
      {
        switch (customKeypad.key[i].kstate)    // Report active key state : IDLE, PRESSED, HOLD, or RELEASED
        {
          case PRESSED:
            Keyboard.press(customKeypad.key[i].kchar);
            break;
          case HOLD:
            break;
          case RELEASED:
            Keyboard.release(customKeypad.key[i].kchar);
            break;
          case IDLE:
            break;
        }
      }
    }
  }
}//loop

Note: Another option is the EventKeypad example.

OK, you made me get my keypad out and actually debug it. Try this:

#include <Key.h>
#include <Keypad.h>
#include <Keyboard.h>

#define IS_RELEASED     false
#define IS_PRESSED      true

const byte ROWS = 4; //two rows
const byte COLS = 6; //two columns

char keys[ROWS][COLS] = {
  {'D', 'H', 'L', 'P', 'T', 'X'},
  {'C', 'G', 'K', 'O', 'S', 'W'},
  {'B', 'F', 'J', 'N', 'R', 'V'},
  {'A', 'E', 'I', 'M', 'Q', 'U'}
};

byte rowPins[ROWS] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {6, 7, 8, 9, 10, 11}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

char
    keyPressed,
    lastKey;

void setup()
{
    Serial.begin(9600);
    customKeypad.setHoldTime( 200 );
    lastKey = customKeypad.getKey();
   
}//setup

void loop()
{
    static uint8_t
        stateKey = IS_RELEASED;
    static uint32_t
        timeKey;
    uint32_t       
        timeNow = millis();
    KeyState
        keypadState;
    static char 
        customKey;
   
    switch( stateKey )
    {
        case    IS_RELEASED:
            customKey = customKeypad.getKey();
            if( customKey != NO_KEY )
            {
                //got the first instance of the key press
                Keyboard.write( customKey );
                Serial.println( customKey );
                timeKey = timeNow;
                stateKey = IS_PRESSED;
               
            }//if
           
        break;

        case    IS_PRESSED:           
            char tempchar = customKeypad.getKey();
            keypadState = customKeypad.getState();
            switch( keypadState )
            {
                case    HOLD:
                    //if key transitions to HOLD issue repeats every 200mS
                    if( timeNow - timeKey >= 200ul )
                    {
                        timeKey = timeNow;
                        Keyboard.write( customKey );
                        Serial.println( customKey );
                       
                    }//if
               
                break;

                case    RELEASED:
                    //when state shows key is released, go back to IS_RELEASED state
                    stateKey = IS_RELEASED;
                   
                break;

                default:
                    //don't care about PRESSED and IDLE states here
                   
                break;
               
                   
            }//switch
           
        break;
       
    }//switch
   
}//loop

Awesome! Thanks to both of you. This is perfect.

What does the "200ul" stand for? I assume 200ms but don't know what ul is.
if( timeNow - timeKey >= 200ul )

dougn0782:
Awesome! Thanks to both of you. This is perfect.

What does the “200ul” stand for? I assume 200ms but don’t know what ul is.
if( timeNow - timeKey >= 200ul )

It denotes to the compiler to consider the value – 200 – as an “unsigned long”, important when dealing with millis() or micros() which return 32-bit values.

Note: I was looking at the Keypad sources and noticed that the KeyboardEventListener feature only works for one key at a time. If you want to support simultaneous keys, use the .getKeys() function as demonstrated above.