using Keypad library for a full QWERTY keyboard

Hi all. I'm working on a project that includes wiring an old Apple IIc keyboard to a teensy board to act as a USB keyboard.

Simplifying the multiplexing of keys, i've been using the keypad library from the playground (http://arduino.cc/playground/Code/Keypad). After some fiddling with the keyboard itself and working around some dead pins, I have it working, mostly. It's a big matrix - 14 columns by 10 rows, which uses up almost all of the inputs on the teensy.

My sketch works in detecting one key at a time, so I can type with it, but it's not very useful without modifier keys. I'm not sure if I'm running into a limitation of the keypad library (detecting multiple keys at a time isn't necessarily required on a 3x4 keypad) or if my implementation is lacking.

So... is it possible to detect multiple, simultaneous keypresses in the keypad library? If so, can someone point me to an example sketch I can look at for guidance? If not, is there a better way to attempt what i'm doing - a matrix keyboard library I haven't found?

My sketch works in detecting one key at a time, so I can type with it, but it's not very useful without modifier keys. I'm not sure if I'm running into a limitation of the keypad library or if my implementation is lacking.

I can tell you with certainty that it's definitely the library. I was one of the people who wrote it. ;) In fact I made absolutely sure that it would only return one key press at a time. Give me a little time (no later than this weekend) to think about it and I'll see what I can do with the library.

Can you tell me what kind of modifiers you'd like to have/use?

modifiers? the usual - shift, control, apple key...

this is an old-school apple II keyboard i'm working on. it still has the apples on it - not the command squiggly. and no alt/option, though that would probably be necessary with a more modern counterpart. the apple keys should map to MODIFIER_GUI, right?

my thought while driving home tonight was that i could do it with two keypad objects, the second one only managing the pins for the modifier keys. luckily, they're separated from the alpha/num keys on the keyboard itself.

any thoughts?

[thought] i could do it with two keypad objects, the second one only managing the pins for the modifier keys. luckily, they’re separated from the alpha/num keys on the keyboard itself.

I was thinking the same thing but I haven’t been able to test it. I’m not sure just how well a micro can support separate C++ objects in memory. Even if it works there would still be the problem that you could only use one modifier at a time. Control+Shift+ wouldn’t be available. Still it would be interesting to see what happens. Are you willing to try?

i'll give it a shot. i'm more interested in being able to type in mixed case and use symbols than anything else - this is a hack, not going to be my daily driver :)

using the second keypad object doesn’t seem to work. i’m not sure what’s happening, though, since even when i explicitly set a modifier key, it has no effect.

here’s my complete sketch (you can see the IIc matrix map commented at the top:

#include <Keypad.h>


/*

	31	24	22	8	14	10	6	1	4	2	26	16	34	28

12	ESC	1	2	3	4	5	6	7	8	9

3						\		=	0	-

33	TAB	Q	W	E	R	T	Y	U	I	O

5						`		P	[	]

18	A	D	S	H	F	J	G	K	;	L

20	Z	X	C	V	B	M	N	,	.	/

23											CMD	CMD

21	CTL												SHFT	CAPS

9						DEL		DN	LA	RA	

7						RTN		UP	SPC	'



14 columns
10 rows

24 input pins


*/


const byte ROWS = 8; // rows
const byte COLS = 10; // columns
char keys[ROWS][COLS] = {

{	KEY_ESC,	KEY_1,	KEY_2,	KEY_3,	KEY_4,	KEY_5,	KEY_6,	KEY_7,	KEY_8,	KEY_9}  ,

{	0,	0,	0,	0,	0,	KEY_BACKSLASH,	0,	KEY_EQUAL,	KEY_0,	KEY_MINUS} ,

{	KEY_TAB,	KEY_Q,	KEY_W,	KEY_E,	KEY_R,	KEY_T,	KEY_Y,	KEY_U,	KEY_I,	KEY_O},

{	0,	0,	0,	0,	0,	KEY_TILDE,	0,	KEY_P,	KEY_LEFT_BRACE,	KEY_RIGHT_BRACE},

{	KEY_A,	KEY_D,	KEY_S,	KEY_H,	KEY_F,	KEY_J,	KEY_G,	KEY_K,	KEY_SEMICOLON,	KEY_L},

{	KEY_Z	,KEY_X,	KEY_C,	KEY_V	,KEY_B,	KEY_M,	KEY_N,	KEY_COMMA,	KEY_PERIOD,	KEY_SLASH},

{	0 , 	0	,0	,0,	0,	KEY_BACKSPACE,	0	,KEY_DOWN,	KEY_LEFT,	KEY_RIGHT},

{	0,	0,	0,	0,	0	,KEY_ENTER,	0	,KEY_UP,	KEY_SPACE	,KEY_QUOTE}

};


byte rowPins[ROWS] = {
  0 ,1,2,3,4,5,6,9}; //connect to the row pinouts of the keypad  

byte colPins[COLS] = {
  10,11,12,13,14,15,16,17,18,19}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );




const byte ROWS2 = 2; // rows
const byte COLS2 = 4; // columns
char keys2[ROWS2][COLS2] = {
{ 0 , 0 ,MODIFIERKEY_SHIFT	,KEY_CAPS_LOCK },
{MODIFIERKEY_GUI,	MODIFIERKEY_GUI,  0 ,0 }
};
byte rowPins2[ROWS2] = {
  7,8}; //connect to the row pinouts of the keypad  

byte colPins2[COLS2] = {
  20,21,22,23}; //connect to the column pinouts of the keypad

Keypad keypad2 = Keypad( makeKeymap(keys2), rowPins2, colPins2, ROWS2, COLS2 );




void setup(){
}

void loop(){
  char key = keypad.getKey();  
  char key2 = keypad2.getKey();

  if (key){
    Keyboard.set_key1(key);
  } else {
    Keyboard.set_key1(0);
  }

    
  if (key2){
    Keyboard.set_key2(key2);
  } else {
    Keyboard.set_key2(0);
  }

 // Keyboard.set_key3(MODIFIERKEY_SHIFT);

  Keyboard.send_now();

}

i should probably mention that i'm using the Teensy with Teensyduino and sending the keys via the keyboard library - see here:

http://www.pjrc.com/teensy/td_keyboard.html

oops... forgot the syntax for adding a modifier key. my loop is now thus:

void loop(){
  char key = keypad.getKey();  
  char key2 = keypad2.getKey();

    
  if (key2){
    Keyboard.set_modifier(key2);
  }

  if (key){
    Keyboard.set_key1(key);
  } 

  Keyboard.send_now();
  Keyboard.set_modifier(0);
  Keyboard.set_key1(0);   
    
}

okay. i think i have an idea of what's going on.

initially, i thought the modifier keypad (keypad2) wasn't working, but it turns out that it was, only very intermittently.

so i swapped my order of the getKey() lines - now keypad2 is getting updated first. suddenly, my modifiers are working!

but the other keys are all only working intermittently... so i think it's a speed problem. the teensy's not able to update both matrices in one pass of the loop. maybe a delay?

using the second keypad object doesn't seem to work. i'm not sure what's happening, though, since even when i explicitly set a modifier key, it has no effect.

Thanks for the link to the teensy keyboard code. It was a big help and I can tell you that you are not using the functions to correctly send modifier keys. (Sorry if I sound a bit blunt but I'm racing to post this from work.)

I can go into it more when I get home tonight but just a quick example:

I see that you commented it out but you used Keyboard.set_key3(MODIFIERKEY_SHIFT); to send a modifier key when you should have used

Keyboard.set_modifier(MODIFIERKEY_SHIFT);

From the teensy link you also need to do:

Keyboard.send_now() After you have set the normal and modifier keys, use Keyboard.send_now() to communicate that combination to the PC or Mac.

so i swapped my order of the getKey() lines - now keypad2 is getting updated first. suddenly, my modifiers are working!

but the other keys are all only working intermittently... so i think it's a speed problem. the teensy's not able to update both matrices in one pass of the loop. maybe a delay?

No, the keypad was built to work better without any delay()s in your code. I think there might be some interference between the keypad objects keypad and keypad2. I'll take a look and see if I can identify what it might be.

What is the keyboard object? Browsed through your code and didn't find a definition. Is it in keypad.h? Just trying to understand your code to see if I can offer any help.

BTW, do you know if the debounce time in the keypad library is static variable or not?

What is the keyboard object? Browsed through your code and didn't find a definition. Is it in keypad.h? Just trying to understand your code to see if I can offer any help.

Edit: I should have paid closer attention to your question. Sorry about the mis-answer. Yeah, sorry about that, I was using C++ jargon to describe when the 'object' was created in the users code. For example the keypad2 object is created when he called:

Keypad keypad2 = Keypad( makeKeymap(keys2), rowPins2, colPins2, ROWS2, COLS2 );

BTW, do you know if the debounce time in the keypad library is static variable or not?

Debounce time is variable and can be set by the user, for example:

void setup() {
    Serial.begin(9600);
    keypad.addEventListener(keypadEvent);
    keypad.setDebounceTime(500);
}

Although, I doubt you'll ever need a debounce time as high as 500 milliseconds. ;)

liudr:
What is the keyboard object? Browsed through your code and didn’t find a definition. Is it in keypad.h? Just trying to understand your code to see if I can offer any help.

the keyboard object comes from the teensyduino libraries. sorry, i didn’t make that clear in the OP.

see:
http://www.pjrc.com/teensy/td_keyboard.html

my current and semi-working sketch:

const byte ROWS = 8; // rows
const byte COLS = 10; // columns
char keys[ROWS][COLS] = {

{   KEY_ESC,    KEY_1,  KEY_2,  KEY_3,  KEY_4,  KEY_5,  KEY_6,  KEY_7,  KEY_8,  KEY_9}  ,

{   0,  0,  0,  0,  0,  KEY_BACKSLASH,  0,  KEY_EQUAL,  KEY_0,  KEY_MINUS} ,

{   KEY_TAB,    KEY_Q,  KEY_W,  KEY_E,  KEY_R,  KEY_T,  KEY_Y,  KEY_U,  KEY_I,  KEY_O},

{   0,  0,  0,  0,  0,  KEY_TILDE,  0,  KEY_P,  KEY_LEFT_BRACE, KEY_RIGHT_BRACE},

{   KEY_A,  KEY_D,  KEY_S,  KEY_H,  KEY_F,  KEY_J,  KEY_G,  KEY_K,  KEY_SEMICOLON,  KEY_L},

{   KEY_Z   ,KEY_X, KEY_C,  KEY_V   ,KEY_B, KEY_M,  KEY_N,  KEY_COMMA,  KEY_PERIOD, KEY_SLASH},

{   0 ,     0   ,0  ,0, 0,  KEY_BACKSPACE,  0   ,KEY_DOWN,  KEY_LEFT,   KEY_RIGHT},

{   0,  0,  0,  0,  0   ,KEY_ENTER, 0   ,KEY_UP,    KEY_SPACE   ,KEY_QUOTE}

};


byte rowPins[ROWS] = {
  0 ,1,2,3,4,5,8,9}; //connect to the row pinouts of the keypad  

byte colPins[COLS] = {
  10,11,12,13,14,15,16,17,18,19}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );




const byte ROWS2 = 1; // rows
const byte COLS2 = 4; // columns
char keys2[ROWS2][COLS2] = {
{MODIFIERKEY_SHIFT, MODIFIERKEY_SHIFT, 0 ,0 }
//{ 0 , 0 ,MODIFIERKEY_SHIFT, MODIFIERKEY_SHIFT }
};
byte rowPins2[ROWS2] = {
  6}; //connect to the row pinouts of the keypad  

byte colPins2[COLS2] = {
  20,21,22,23}; //connect to the column pinouts of the keypad

Keypad keypad2 = Keypad( makeKeymap(keys2), rowPins2, colPins2, ROWS2, COLS2 );




void setup(){

}

void loop(){
  
  
  
  char key = keypad.getKey();  
  char key2 = keypad2.getKey();


  KeyState modifier = keypad2.getState();  

    
  if (key2){
    Keyboard.set_modifier(key2);
  }


  KeyState alpha = keypad.getState();
  if (key){
    Keyboard.set_key1(key);
  } 

  Keyboard.send_now(); 
  
  if(modifier == RELEASED) {
    Keyboard.set_modifier(0);
  }
  if(alpha == RELEASED) {
    Keyboard.set_key1(0);   
  }


}

notice two getKey() lines. the first "alpha" keypad is responsive, and the second "modifier" works only occasionally. if i swap those lines, the opposite is true, which leads me to think that i'm asking too much of the teensy in each pass of the matrix scan.

would it be more likely to work if i used waitForKey() instead, and only read the modifier keypad if there's an alpha key pressed to modify?

which leads me to think that i'm asking too much of the teensy in each pass of the matrix scan.

I don't think the teensy is having any problem handling even this many rows/columns. One reason I say that is because when you swap the getKey()'s the non-working keys work. And a second is because I had to add a bit of code to slow down the scanning of the pins. The teensy is capable of scanning the entire keyboard hundreds, or even thousands of times faster than any human could press a key.

would it be more likely to work if i used waitForKey() instead,

Not really because getKey() and waitForKey() call the same exact code. Also, waitForKey() blocks which prevents any and all other code from running.

mstanley: I don't think the teensy is having any problem handling even this many rows/columns. One reason I say that is because when you swap the getKey()'s the non-working keys work. And a second is because I had to add a bit of code to slow down the scanning of the pins.

in that case, what's the likelihood that i could get a modified version of keypad library from you? :)

Very good, actually. If you are up for it I am using you and your project to improve the library. ;)

Last night I made a couple of changes that will fill an array with every key that is being pressed. Once the array is filled (happens each time the keyboard is scanned) you can first check to see if a modifier was pressed and any other keys after that.

There are issues with matrix style keyboards called shadowing and jamming but careful arrangement of the rows and columns will support modifier keys. Unless your keyboard has built in series diodes for each key.(?) That would eliminate those problems.

nope. no diodes. just big fat copper traces and lumpy '80s solder joints. probably lots of lead, too.

thanks for taking the time to update your library for my edge case. i'll be glad to help debug, if i'm able.

option8: i'll be glad to help debug, if i'm able.

Works for me. I can talk you through any debugging that we'll need to do. It's simple stuff as you'll see.