RobTillaart / I2CKeyPad8x8

Arduino library for 8x8 or smaller KeyPad connected to an I2C PCF8575.
MIT License
11 stars 1 forks source link

Multiple keys pressed #3

Closed Assamita81 closed 1 year ago

Assamita81 commented 1 year ago

Any chance of including the option of having multiple keys pressed at the same time?

RobTillaart commented 1 year ago

Thanks for the issue, will look into it asap.

Assamita81 commented 1 year ago

If it's of any use, the standard Keypad library has a switch that allows different behaviors depending on the state of the button. I use it together with a Joystick library and an arduino pro micro for a windows HID device, and it has these 4 different button states:

if (MFD.getKeys())
{
  for (int j=0; j<LIST_MAX; j++)   
  {
    if ( MFD.key[j].stateChanged )   
    {
      switch (MFD.key[j].kstate) 
      {  
        case PRESSED:
        case HOLD:
                  Joystick.setButton(MFD.key[j].kchar, 1);
                  break;
        case RELEASED:
                  break;
        case IDLE:
                  Joystick.setButton(MFD.key[j].kchar, 0);                              
                  break;
      }
    }   
  }
}

(sorry, I don't know how to make the code to display properly)

(updated the post to show code better, click edit to learn how)

RobTillaart commented 1 year ago

Current code

The algorithm scans an 8 by 8 matrix. It does this quite efficient by scanning 8 rows in one read followed by 8 columns in one read. I2C_KEYPAD8x8_NOKEY is easy to detect as no row / column has changed. The current code does 8 checks (bitmask match) to see if a single bit has changed. If this is not the case, I2C_KEYPAD8x8_FAIL is set.

Multiple Key detect.

Technically it is easy to detect that multiple keys are pressed. This is the point where I2C_KEYPAD8x8_FAIL is set in the current code. The real problem is to detect which keys are pressed, this might be deducible but not always. Furthermore if deducible it is not always possible to detect in which order (first, next, next, ... last)

Imagine a 2 by 2 grid with 4 keys.

       C0     C1
R0     A       B
R1     C       D

(assuming A is always pressed, think rotational symmetry) Pressing 2 keys can be done in 3 ways (A, B), (A,C) or (A, D). Pressing 3 keys can be done in 3 ways (A, B, C), (A,B,D) or (A, C, D). Pressing 4 keys can be done in 1 way (A, B, C, D).

So from the 7 multiple key presses, only 2 are deducible and 5 are not. The order of keypress is unknown.

3 by 3 and larger matrices have same problem (and several more complex ones).

In short there is no easy way to detect multiple keys.

Alternatives

In the above analysis the time axis was left out. It here is a substantial time difference between the two keypresses, one does not do one scan, but two (every change will generate an interrupt). In the first scan one sees that only key A is pressed. In the second scan one can see that multiple keys are pressed. Assuming that there is only one key extra pressed the (A,D) scenario above becomes deducible too.

This alternative scenario can be implemented under the assumption that keypresses are "much" slower than handling the interrupt and the keypad scan. Never done that as I never had the need, feel free to create a PR for this.

Another alternative is - https://github.com/adafruit/Adafruit_TCA8418

RobTillaart commented 1 year ago

If it's of any use, the standard Keypad library has a switch that allows different behaviors depending on the state of the button.

Very interesting to learn how it does the trick. Note: https://github.com/adafruit/Adafruit_TCA8418 uses a dedicated keypad processor for it. I know there are electronic tricks using diodes and or analog voltages in combination to keypad scanning to handle multiple keys.

Q: What is the standard KeyPad Library? There are a lot of keypad libraries out there. Can you provide an URL?

RobTillaart commented 1 year ago

Update

(A, D)

Note that the combination (B, C) would also give the same pattern as (A, D).

Assamita81 commented 1 year ago

Sorry, I called it standard because I thought it came installed by default with Arduino IDE, but I think that might actually not be the case, I might have installed it a very long time ago. It's this one: https://github.com/Chris--A/Keypad Yes, when there is a need of multiple keys being pressed, diodes need to be added to the button matrix.

RobTillaart commented 1 year ago

I know that one by name from the past, it is indeed from the early days when libraries were posted on the playground.

First impression is that I am not going to implement this.

Assamita81 commented 1 year ago

Yes, it's fully tested multiple times, it works like a charm as long as you know how to build a diode matrix (which is very easy, there are multiple images about it on google). I believe it can handle up to 10 keys being pressed at the same time, though the library could be easily modified to handle more if needed. I understand, it's a lot of work. I tried doing it myself, but my programming skills are not very advanced, I'm just a hobbist that knows just a little bit of programming. I had to try! but thank you anyway. I think for my current project, the easiest way to go is to just use 4 PCF8575 boards instead of just one and building a matrix.

RobTillaart commented 1 year ago

it works like a charm as long as you know how to build a diode matrix (which is very easy, there are multiple images about it on google).

Please provide the URL's that you know are good (there are incorrect drawings too - seen too many on the forum). Think this provide a good explanation - https://www.gammon.com.au/forum/?id=14175

I believe it can handle up to 10 keys being pressed at the same time

Its definitely configurable, but as the state management is O(n^2) I expect it to be very slow.

I think for my current project, the easiest way to go is to just use 4 PCF8575 boards instead of just one and building a matrix.

How much keys do you need to handle? with four PCF8575 you can handle up to 1024 (32x32) keys would be quite a "keyboard"

Assamita81 commented 1 year ago

Please provide the URL's that you know are good (there are incorrect drawings too - seen too many on the forum). Think this provide a good explanation - https://www.gammon.com.au/forum/?id=14175

I wire the cathode of the diode to one leg of the pushbutton, the annode to the row, and the other leg of the pushbutton to the column. In this way, multiple buttons being pressed at the same time are properly registered with the above keypad library.

Its definitely configurable, but as the state management is O(n^2) I expect it to be very slow.

Maybe, I don't know much about that. In any case, I do not need that many pressed at the same time. I have estimated a max number of buttons/switches being pressed at the same time of 6 to 9.

How much keys do you need to handle? with four PCF8575 you can handle up to 1024 (32x32) keys would be quite a "keyboard"

I need just about 64, but some of the inputs are latching switches, meaning they will stay on in certain positions. So without the possibility of having multiple presses at the same time, I cannot use an 8x8 matrix. I will need to wire individual pins to each button, hence the 4 boards (4 x 16).

RobTillaart commented 1 year ago

The other keypad libraries scans effectively one key at a time where this library scans 8 rows/colums in one read. Scanning per key makes detecting multiple keypresses quite well possible (using diodes).

This would mean that I had to start with a rewrite of the core and also do a single key scan at a time. As this library needs to read over I2C with a getkey() takes 2 reads. Scanning per key needs at least 16 reads so at least a factor 8 slower.

RobTillaart commented 1 year ago

I need just about 64, but some of the inputs are latching switches, meaning they will stay on in certain positions. So without the possibility of having multiple presses at the same time, I cannot use an 8x8 matrix. I will need to wire individual pins to each button, hence the 4 boards (4 x 16).

OK that makes perfect sense (and you do not need to solder the 64 diodes)

RobTillaart commented 1 year ago

Did a small test with a minimal patched version of HelloKeypad.pde (of the keypad lib) on an Arduino UNO (no keypad connected, just scanning)

columns rows keys micros us/key
3 4 12 324 27
6 6 36 892 25
8 8 64 1500 24

So pretty fast per key, as I2C read takes maybe 500 us (no PCF8575 nearby to test) 2 reads for an 8x8 matrix would be equally fast (order of magnitude, but the individual key scan would be way slower.

RobTillaart commented 1 year ago

I need just about 64, but some of the inputs are latching switches, meaning they will stay on in certain positions. So without the possibility of having multiple presses at the same time, I cannot use an 8x8 matrix. I will need to wire individual pins to each button, hence the 4 boards (4 x 16).

OK that makes perfect sense (and you do not need to solder the 64 diodes)

Q: How many hold keys do you have?

Assamita81 commented 1 year ago

In my project in particular, there are only 3 latching switches, but there are a number of buttons that might be kept pressed for a while. It's sort of a Joystick (you can google Apache Tedac and you'll see what I'm building), and it has two two-stage triggers. One of the triggers controls a laser that needs to be kept on when you shoot a missile all the way until the missile hits the target, which is done with the other trigger. That means that the right trigger first and second detents, which are momentary push buttons are pressed when you push the left trigger second detent to fire the missile, and they need to stay like that until the missile hits the target. So when firing a missile, there's a split second when all 4 switches (two triggers) are pressed at the same time. To that, you need to add the possibility that one of the other 3 latching switches are on.

RobTillaart commented 1 year ago

That is a Serious controller (with capital S)!

If performance is an issue, you might think of using - https://github.com/RobTillaart/MCP23S17 These are 16 channel SPI based and it reads 16 channels in 40 us (Arduino UNO) or less, way faster than I2C PCF8575.

Might be worth a look.

RobTillaart commented 1 year ago

@Assamita81 As there will be no multi-key support - could be a new library in a distant future - I close this issue. Feel free to reopen if needed.