Teensy 3.2 Keyboard // detect multiple keys pressed simultaneously

Status
Not open for further replies.

annama

Member
Hello there,

Just to clarify, I am not very experienced in coding, so bear with me.

In my project I'm building a new keyboard from scratch, new layout and everything. So I am using multiple simple pushbuttons to act as keys, and a Teensy 3.2 as "Keyboard" connected to my Mac (coding in Arduino app).
The buttons are not connected in rows and columns, but every button is individually connected to the Teensy, which might seem laborious, but it was easiest for me to do.
Every button gives out a character when pressed, except for 4 buttons (SPACE, BACKSPACE, NUM and COMBINATION). Every character-button is working, as well as space and backspace. (For this I used the instructions on the pjcr website https://www.pjrc.com/teensy/td_keyboard.html.)

What I want now is for the teensy to detect, if multiple buttons are pressed at the same time, therefore changing the output.
FOR EXAMPLE: If you press the "q"-button, a "q" is written. If you press the NUM-button + "q"-button, the output should change from "q" to "1".
So for the NUM-button, every character should be able to change to another printed output.

Then we have the COMBINATION-button (COMB), which should do a similar thing, but instead of just printing another output, it should be able to perform shortcuts.
FOR EXAMPLE: If you press the "a"-button, a "a" is written. If you press the COMB-button + "a"-button, the output should be similar to CMD+A (for Apple), so Select All.

The thing is, I know, that it is possible to use MODIFIER_KEYs, but with them you only press one button, and it will act as if two (or multiple) buttons were pressed simultaneously. But what I want, is that you actually have to press two buttons (just like on normal keyboards).

Here is my code so far:

Code:
#include <Bounce.h>
#include <keyboard.h>


//Bounce NameOfKey = Bounce (PIN, Time);
Bounce BCKSP = Bounce (18, 10);
Bounce NUM = Bounce (14, 10);
Bounce q = Bounce (3, 10);
Bounce d = Bounce (1, 10);
Bounce a = Bounce (2, 10);
Bounce z = Bounce (0, 10);
Bounce r = Bounce (6, 10);
Bounce s = Bounce (4, 10);
Bounce x = Bounce (5, 10);
Bounce w = Bounce (9, 10);
Bounce h = Bounce (8, 10);
Bounce m = Bounce (7, 10);
Bounce b = Bounce (17, 10);
Bounce t = Bounce (15, 10);
Bounce c = Bounce (16, 10);


void setup() {
  // put your setup code here, to run once:
  
// Set INPUT_PULLUP to have signal high when not pressed
//and low when connected to ground (pressed).

pinMode (18, INPUT_PULLUP);
pinMode (14, INPUT_PULLUP);
pinMode (3, INPUT_PULLUP);
pinMode (1, INPUT_PULLUP);
pinMode (2, INPUT_PULLUP);
pinMode (0, INPUT_PULLUP);
pinMode (6, INPUT_PULLUP);
pinMode (4, INPUT_PULLUP);
pinMode (5, INPUT_PULLUP);
pinMode (9, INPUT_PULLUP);
pinMode (8, INPUT_PULLUP);
pinMode (7, INPUT_PULLUP);
pinMode (17, INPUT_PULLUP);
pinMode (15, INPUT_PULLUP);
pinMode (16, INPUT_PULLUP);

}

void loop() {
  // put your main code here, to run repeatedly:
BCKSP.update();
NUM.update();
q.update();
d.update();
a.update();
z.update();
r.update();
s.update();
x.update();
w.update();
h.update();
m.update();
b.update();
t.update();
c.update();

if (BCKSP.fallingEdge()) {
  Keyboard.press(KEY_BACKSPACE);
  Keyboard.release(KEY_BACKSPACE);
}

if (q.fallingEdge()) {
  Keyboard.print("q");
}
if (d.fallingEdge()) {
  Keyboard.print("d");
}
if (a.fallingEdge()) {
  Keyboard.print("a");
}
if (z.fallingEdge()) {
  Keyboard.print("z");
}
if (r.fallingEdge()) {
  Keyboard.print("r");
}
if (s.fallingEdge()) {
  Keyboard.print("s");
}
if (x.fallingEdge()) {
  Keyboard.print("x");
}
if (w.fallingEdge()) {
  Keyboard.print("w");
}
if (h.fallingEdge()) {
  Keyboard.print("h");
}
if (m.fallingEdge()) {
  Keyboard.print("m");
}
if (b.fallingEdge()) {
  Keyboard.print("b");
}
if (t.fallingEdge()) {
  Keyboard.print("t");
}
if (c.fallingEdge()) {
  Keyboard.print("c");
}


}

I hope maybe someone gets what I mean and has an idea.
Thanks for your help, anyways!

Cheers,
Anna
 
I may not fully understand what you are asking, but my guess is you want the ability to have multiple buttons pressed and have that reflected on the other computer...

My guess is you need to look at using Keyboard.press instead of Keyboard.print.

That is if both Q and A are pressed, you would see:
Keyboard.press(KEY_Q);
Keyboard.press(KEY_A);
When either of these buttons are released you should then send a release, like...
Keyboard.release(KEY_Q);

That is you will also want to look at risingEdge() and to do the Release...
 
The important part for me at the moment is the NUM thing.

I want to have a different output, when two buttons are pressed at the same time (NUM + any other key with a letter).
So it's not like A + Q (two characters) are pressed at the same time, but a function key, for example NUM + Q (NUM + letter), so that the output changes from the letter Q to something else (like "1").

So it would be NUM + (whichever letter) giving a different output than the normal letter-button would give pressed alone.
I used Keyboard.press() and Keyboard.release() for my Space and Backspace keys, but I have no idea how to use it for combining two buttons, that's why for now I just used the Keyboard.print.
 
If you wish to have the NUM thing... you can do stuff in your code like:

Code:
if (q.fallingEdge()) {
  if (NUM.read() {
    // Num is not pressed.
    Keyboard.print("q");
  } else {
    // NUM is pressed. 
    Keyboard.print("1");
  }
}
 
Now I have one last question for a problem, which might not be solvable.

My keyboard is splittet into two parts, both using their own teensy, so I would not have to connect them with a wire. I can plug in both teensys and write just as if I'm using one keyboard (both work fine).
Both have their own codes, of course.

Now, is there a possibility, to have for example the NUM key work for both keyboards, even though it's just connected to one teensy?
Because now of course, I can only program the buttons for one half of my keyboard to work with the NUM key.

I don't know if one even can solve that...
 
If both keyboards plug into your PC, but there's no wire between them, you're limited to what the PC's keyboard driver gives you. Maybe you could read the LED state the PC has set? But keep in mind, you're depending on whatever the PC's driver decides to send. Windows and Macintosh might do this differently...
 
Maybe you could read the LED state the PC has set?

I'm sorry, dummy alarm, but I have really no idea what this means...



Additionally:
If it's getting really hairy and nothing will work, I would have to connect the two after all (but I would hate to do that, as it is kind of defeating the purpose). Would it be possible to add wires to (for example) the NUM button, and connect it to the second teensy as well?
Like this: ( [O] is the button, | are the wires )
IMG_0854.jpg
 
I think, if I don't want to go too deep and as I need this working in time, I will have to connect both sides (at least two buttons, one from the left hand for the NUM key and one from the right for a "SHIFT" key, to make these work on both hands' character keys).

So my question is, would it be possible to connect one button with two teensys?
I tried to google a bit, but I did not really find anything.

I would connect the buttons just like the drawing above, using the same way of wiring I used for all the other keys, but two times (one to one teensy and one to the other).

I just don't know if I could get into trouble doing this, and before I do I would like to verify, that I will not burn anything by doing it.
 
Again I am no EE type...

But I would think you should be able to connect same button to both boards... Obviously best if the switch has two sets of contacts, like I think you are showing.

My guess is you could probably also get away with connecting the connection of Teensy 1 and Teensy 2 together, have both of them configured as INPUT_PULLUP and have a common ground.

But hopefully a more EE type person could answer better.
 
The other direction that might be a possibility, but don't know, which is what Paul was mentioning, about LEDS.

That is I know with the T3.6 USB hosting code, that there are probably ways to set and query the state of the LEDS (Capslock, NumLock, Scroll Lock).

Now if your button actually output using the modifier keys like NumLock, and you could query it. Then once you sent the numlock command, and the other side could detect it then you could probably set it up to work.

BUT:

I don't see anything in the current USB code to be able to query it. Also it may be different for different types of processors.

And in the Host code, typically it only maybe did it once when doing a startup to get the initial state. So even if you could get it, you would need to probably do a query each time you detected a change in any key state.... Might not work great.

Another possible option. Have one Teensy slave off of the other one... That is only plug one into USB, maybe connect Serial1 of both Teensy boards to each other. When the slave one detects something it sends the data to the Primary Teensy who generates the appropriate USB Keyboard command.
 
I connected the buttons to two teensys and combined the grounds.
It works fine, I can now use the NUM key for both hands (yippieh!).

Now I have to figure out one last thing, and as I'm still bad at this, I will ask here again, because you were able to help me so well:

I am using your code now KurtE,

Code:
if (q.fallingEdge()) {
    if (NUM.read()) {
    Keyboard.print("q");
  }else {
  Keyboard.print("1");
}}

just like this and it works fine (now for both Hands).

Now I have a kind of similar button to the NUM, where I want to change the output again, but I do not quite know how to implement it into the loop:
Additional to the normal output (in this case "q") and the NUM-changed output (in this case "1"), I want to have a third output, if another key is pressed at the same time as another key (like "q") (I call this other button KOMB key [for "combination", as the purpose it will serve now is just temporary], which is the capital letter (in this case "Q").

So what I would love to have is:
Character button pressed : letter is printed ("q")
NUM + Charter button pressed at the same time : number or punctuation mark is printed ("1")
KOMB + Charter button pressed at the same time : capital letter is printed ("Q")

Could you help me one more time?
 
NUM + Charter button pressed at the same time : number or punctuation mark is printed ("1")
KOMB + Charter button pressed at the same time : capital letter is printed ("Q")

I just saw my typo, and maybe it's confusing, so I will correct it (for some reason I can't just edit my post):

Charter of course means "Character", so a button, whose output is a letter (for example "q").
 
Sorry, I assume you can find the solution to these. Probably just another test or two in your code to say: if (CHARACTER.read()) ...
Also as I am busy with other projects I probably won't have time to do it for you...

But each section could look something like:
Code:
if (q.fallingEdge()) {
    if (NUM.read()) {
        // Not Num 
        if (CHAR.read()) {
            Keyboard.print("q"); // neither num or CHR
        } else {
            Keyboard.print("q"); // Character pressed ? not sure what you want here
        }
  }else {
        if (CHAR.read()) {
            Keyboard.print("1"); // Only Num pressed
        } else {
            Keyboard.print("!"); // num and Char pressed
        }
}}

And again if you wish to add KOMB this would add another set of if/else within each of these groups... Starts to get unwieldy pretty quick

So if you have multiple modifiers, that is when I would have tendency to go to a more table driven approach... I would probably go completely table driven where I have the bounce objects as part of an array (or pointers to them in an array).

I would probably setup a table for each key or more likely a two dimensional table for all the keys. But start simple with an array for each character... I would then at the start look at the value of the modifiers to generate an index value...

Something like:
Code:
#include <Bounce.h>
#include <keyboard.h>


//Bounce NameOfKey = Bounce (PIN, Time);
[COLOR="#FF0000"]Bounce CHAR = Bounce (19, 10);  // guessing... [/COLOR]
Bounce BCKSP = Bounce (18, 10);
Bounce NUM = Bounce (14, 10);
Bounce q = Bounce (3, 10);
Bounce d = Bounce (1, 10);
Bounce a = Bounce (2, 10);
Bounce z = Bounce (0, 10);
Bounce r = Bounce (6, 10);
Bounce s = Bounce (4, 10);
Bounce x = Bounce (5, 10);
Bounce w = Bounce (9, 10);
Bounce h = Bounce (8, 10);
Bounce m = Bounce (7, 10);
Bounce b = Bounce (17, 10);
Bounce t = Bounce (15, 10);
Bounce c = Bounce (16, 10);

[COLOR="#FF0000"]// Tables for what to output for each character
// entries in array are in the order: No modifier, NUM modifier, CHAR modifier, NUM+CHAR modier                    
const char q_keys[] = {'q','1','!','?'};[/COLOR]

void setup() {
  // put your setup code here, to run once:

  // Set INPUT_PULLUP to have signal high when not pressed
  //and low when connected to ground (pressed).

  pinMode (18, INPUT_PULLUP);
  pinMode (14, INPUT_PULLUP);
  pinMode (3, INPUT_PULLUP);
  pinMode (1, INPUT_PULLUP);
  pinMode (2, INPUT_PULLUP);
  pinMode (0, INPUT_PULLUP);
  pinMode (6, INPUT_PULLUP);
  pinMode (4, INPUT_PULLUP);
  pinMode (5, INPUT_PULLUP);
  pinMode (9, INPUT_PULLUP);
  pinMode (8, INPUT_PULLUP);
  pinMode (7, INPUT_PULLUP);
  pinMode (17, INPUT_PULLUP);
  pinMode (15, INPUT_PULLUP);
  pinMode (16, INPUT_PULLUP);

}

void loop() {
  // put your main code here, to run repeatedly:
  [COLOR="#FF0000"]CHAR.update(); [/COLOR]
  BCKSP.update();
  NUM.update();
  q.update();
  d.update();
  a.update();
  z.update();
  r.update();
  s.update();
  x.update();
  w.update();
  h.update();
  m.update();
  b.update();
  t.update();
  c.update();

  if (BCKSP.fallingEdge()) {
    Keyboard.press(KEY_BACKSPACE);
    Keyboard.release(KEY_BACKSPACE);
  }

 [COLOR="#FF0000"] // Calculate our modifier index
  uint8_t modifier_index = 0;
  if (NUM.read() == 0) modifier_index |= 1;  // bit 1 says the NUM modifier was set
  if (CHAR.read() == 0) modifier_index |= 2;  // bit 2 says the CHAR modifer was set
  [/COLOR]

  [COLOR="#FF0000"]if (q.fallingEdge()) {
    // output the char that reflects current state of modifiers.
    Keyboard.print(q_keys[modifier_index]);
  }[/COLOR]
  if (d.fallingEdge()) {
    Keyboard.print("d");
  }
  if (a.fallingEdge()) {
    Keyboard.print("a");
  }
  if (z.fallingEdge()) {
    Keyboard.print("z");
  }
  if (r.fallingEdge()) {
    Keyboard.print("r");
  }
  if (s.fallingEdge()) {
    Keyboard.print("s");
  }
  if (x.fallingEdge()) {
    Keyboard.print("x");
  }
  if (w.fallingEdge()) {
    Keyboard.print("w");
  }
  if (h.fallingEdge()) {
    Keyboard.print("h");
  }
  if (m.fallingEdge()) {
    Keyboard.print("m");
  }
  if (b.fallingEdge()) {
    Keyboard.print("b");
  }
  if (t.fallingEdge()) {
    Keyboard.print("t");
  }
  if (c.fallingEdge()) {
    Keyboard.print("c");
  }


}
Again not sure of all of your mappings, you would need to expand table to handle more modifiers, also not sure of all of your desired values for Q...
I only show updating of Q, obviously you would need to setup tables for each of the other characters as well.

Good luck
 
Status
Not open for further replies.
Back
Top