Buttonsmatrix ghosting despite diodes

Hello guys,

its my first post in this forum and i hope i did everything right.

My Problem:
I’m trying to build a 8x8 Matrix with buttons with an Arduino pro micro.
I build and wired everything up, but although I used diodes, I have a problem with ghosting.
I checked every switch and every circuit twice now, everything is wired up properly.
Just to make it clear, I added two diodes to every switch, because theoretically those are two buttons(ON/OFF/ON). I hope this is understandable.

I orientated myself at this post and changed the code a bit: https://forum.arduino.cc/index.php?topic=632617.0

That’s the code I used:
Because windows is dump, i had to simulate two joysticks, but the problem also occurs when I’m just using one, so that shouldn’t be a problem(I think?).

#include <Keypad.h>
#include <Joystick.h>

#define ENABLE_PULLUPS

#define NUMBUTTONS0 32
#define NUMBUTTONS 32
#define NUMROWS0 4
#define NUMCOLS0 8
#define NUMROWS1 4
#define NUMCOLS1 8

#define JOYSTICK_COUNT 2

byte buttons0[NUMROWS0][NUMCOLS0] = {
  {0, 1, 2, 3, 4, 5, 6, 7},
  {8, 9, 10, 11, 12, 13, 14, 15},
  {16, 17, 18, 19, 20, 21, 22, 23},
  {24, 25, 26, 27, 28, 29, 30, 31}
};

byte buttons1[NUMROWS1][NUMCOLS1] = {
  {0, 1, 2, 3, 4, 5, 6, 7},
  {8, 9, 10, 11, 12, 13, 14, 15},
  {16, 17, 18, 19, 20, 21, 22, 23},
  {24, 25, 26, 27, 28, 29, 30, 31}
};

byte rowPins0[NUMROWS0] = {0, 1, 10, 14};
byte colPins0[NUMCOLS0] = {2, 3, 4, 5, 6, 7, 8, 9};

byte rowPins1[NUMROWS1] = {A0, A1, A2, A3};
byte colPins1[NUMCOLS1] = {2, 3, 4, 5, 6, 7, 8, 9};

Keypad buttbx = Keypad( makeKeymap(buttons0), rowPins0, colPins0, NUMROWS0, NUMCOLS0);
Keypad buttbx1 = Keypad( makeKeymap(buttons1), rowPins1, colPins1, NUMROWS1, NUMCOLS1);

Joystick_ Joystick[JOYSTICK_COUNT] = {
  Joystick_ (0x08, JOYSTICK_TYPE_JOYSTICK, 32, 0, false, false, false, false, false, false, false, false, false, false, false),
  Joystick_ (0x09, JOYSTICK_TYPE_JOYSTICK, 32, 0, false, false, false, false, false, false, false, false, false, false, false)
};

void setup() {
  Joystick[0].begin();
  Joystick[1].begin();

}

void loop() {
  CheckAllButtons0();

  CheckAllButtons1();

}

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

I hope someone can help me!
Many thanks in advance!

If its useful for you, here I also have a schematic:

Schematic_pro micro knopfe_2020-08-22_17-26-01.pdf (101 KB)

You say that it's ghosting but you don't say what switch does what. I would start by switching from pins 0 and 1 to some other pins (11, 12, 13, A4, A5) so you can use Serial. Then output the switch changes to Serial so you can see what is detected when. Perhaps with a list of changes detected and comments of what was actually switched it would become clearer what is happening.

I switched the cables from 0 and 1 to 15 and 16, but the Problem obviously still occurs.
I'm pretty sure that it is ghosting, as its a game controller and i can visualize the switches on my pc. And there i can see the typical ghosting things.

My last thought are the Diodes(IN4007), I read somewhere that those could be to slow for the arduino matrix. What do you think about that?

Thank you very much for your answer and I hope you can still help me.

I look at that schematic and see that you connected what should be simple open or closed switches into 2-way switches.

There is no need to do that. You only need paths from rows to columns where pressing a button completes a connection or not. A single diode per button is all you need to wire. If you add a 1 uF cap across each switch you won't have to debounce the buttons in software.

What you have seems overly-complicated, it's going to be harder to debug.

marcelinjo:
If its useful for you, here I also have a schematic:

It's not just "useful", it is absolutely critical if anyone is ever to be able to help you. For example, using the possibly inappropriate digital pins 0 and 1.

But you post a useless "PDF" instead of a proper image!

Really!
Matrix01.png
Expand!

One funny thing here is using twice as many diodes as necessary. :grinning:

johnwasser:
I would start by switching from pins 0 and 1 to some other pins (11, 12, 13, A4, A5) so you can use Serial. Then output the switch changes to Serial so you can see what is detected when.

Mmmm. How would you do that on a Pro Micro?


GoForSmoke:
If you add a 1 uF cap across each switch you won’t have to debounce the buttons in software.

Really? :roll_eyes:

To be honest, I soldered before I thought, and then i realized, i made a mistake and used too much diodes. But despite some euros wasted this should work or have i made a dump mistake again?

To be honest again, I never heard of a capacitor in a matrix. How should that work and how should I connect those?

Wait... an 8x8 matrix for only 32 buttons? Why?

Windows can only handle 32 buttons per controller but i want more, so i start 2 controllers with 32 buttons each on one Arduino and then i have the 8x8=64 Buttons.
You can see this in the code shown above.

Paul__B:
Mmmm. How would you do that on a Pro Micro?


Really? :roll_eyes:

Not the way he has them wired. No. But... context...

marcelinjo:
To be honest again, I never heard of a capacitor in a matrix. How should that work and how should I connect those?

On this page: https://gammon.com.au/forum/?id=11955

About half-way down is Hardware debounce.

You only wire up the Normally Open side of the switch, the button down closes it.

One button per matrix intersection to connect crossing wires, row and column. I make 1 row pin INPUT_PULLUP and 1 column pin OUTPUT LOW, and all the other row and column pins are made INPUT (electrically neutral when not read). If the button at that row and column is pressed, the row pin goes LOW and that gets read as button pressed at known row and column. Then the column pin is set INPUT (which is LOW) and the next column pin made OUTPUT (still LOW) to read the row pin and see if pressed and when the columns are done, the row pin is made INPUT LOW and the next row pin made INPUT HIGH (same as INPUT_PULLUP) then all the columns checked, then repeat, over and over.

GoForSmoke:
Not the way he has them wired. No. But... context...

And the context is a matrix being scanned. Do you think a capacitor would work in that situation?

Ok, i haven't quiet got it yet. Thanks for the website with the debounce, but is that my problem?
If I add capacitors will it work or do I also have to do something else.

And I'm sorry but it's a bit complicated for me to understand:

GoForSmoke:
One button per matrix intersection to connect crossing wires, row and column. I make 1 row pin INPUT_PULLUP and 1 column pin OUTPUT LOW, and all the other row and column pins are made INPUT (electrically neutral when not read). If the button at that row and column is pressed, the row pin goes LOW and that gets read as button pressed at known row and column. Then the column pin is set INPUT (which is LOW) and the next column pin made OUTPUT (still LOW) to read the row pin and see if pressed and when the columns are done, the row pin is made INPUT LOW and the next row pin made INPUT HIGH (same as INPUT_PULLUP) then all the columns checked, then repeat, over and over.

You don't have to explain it again(if you don't want to), just tell me if i have to change something in the code there.

And if I have to add those capacitors, could you give me a little schematic of it?
Would be super nice, and you don't have to make the full matrix, maybe just the first 2 or 3 rows?

Again thanks to everyone who is trying to help me, means a lot to me!

Paul__B:
And the context is a matrix being scanned. Do you think a capacitor would work in that situation?

When every time a row is read all the caps in that row get weak 5V on one side.
Every time a column is read, that side of all column caps gets LOW.
Every pin not the current row and column is set INPUT LOW.

But the caps don’t charge so quick with INPUT_PULLUP current, by magnitudes.
If it would work, the time required is a killer.

Oh well, debounce in software. I just happen to have an example only tested with jumpers placed in Uno holes.

// add-a-sketch_matrix_buttons 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, May 9/2018 by GFS. Compiled on Arduino IDE 1.6.9.
/*  Button Debounce Example

  This example requires a diode per button in the row&column matrix.
  Each row and column pin has its own wire. Where they cross a button and diode
  connect in series allowing current to flow from the row wire to the column wire
  only. The diode is to allow multiple buttons pressed at once detected correctly.

  OTOH for this test, jumper a row pin (2 or 3) to a column pin (4 or 5) while
  watching serial monitor.

  Yes I'm using a 16 bit micros timer to time fractions of millis as micros.
  The button reader only reads 1 button per call so as to not block void loop().
  Each button has a history byte that holds the last 8 reads with 256 possible
  states but only 4 of them being significant.
  0 is the button held down
  255 is the button left up
  127 is the buton transitioning from up to down, button just released.
  128 is the button transititioning from down to up, button just pressed.
  everything else is to be ignored as bounce.

  For multiple buttons the between-reads time is reduced and each pin is read in
  turn.
*/

// matrix_buttons vars --- 2D matrix
const byte rows = 2; // test with 4 buttons or jumper
const byte cols = 2;
byte rowIdx, colIdx = 255;
byte buttonRowPin[ rows ] = { 6, 7 };
byte buttonColPin[ cols ] = { 4, 5 };
byte buttonHistory[ rows ][ cols ];
word markButtonTime;        // 16-bit micros timers
const word waitButtonTime = 50; // micros, 50 us
// type word as micros can time across 65.535 millis before rollover, can be a few late


void matrixButtonsTask()
{
  if ( word( micros()) - markButtonTime >= waitButtonTime ) // read occaisoinally
  {
    // iterate the row and column indexes
    if ( ++colIdx >= cols ) // ++rowIdx pre-increments rowIdx before comparing to rows
    {
      colIdx = 0;
      if ( ++rowIdx >= rows ) // ++rowIdx pre-increments rowIdx before comparing to rows
      {
        rowIdx = 0;
      }
    }

    pinMode( buttonRowPin[ rowIdx ], INPUT_PULLUP ); // supplies weak 5V
    pinMode( buttonColPin[ colIdx ], OUTPUT ); // is LOW, grounds pressed buttons

    buttonHistory[ rowIdx ][ colIdx ] <<= 1; // shift history bits up 1 for the new read
    buttonHistory[ rowIdx ][ colIdx ] += digitalRead( buttonRowPin[ rowIdx ] ); // read history streams through buttonHistory
    markButtonTime = micros(); // gets the low 16 bits of micros(), time to 60 ms + margin

//    digitalWrite( buttonRowPin[ rowIdx ], LOW ); // turn the weak 5V off
//    pinMode( buttonRowPin[ rowIdx ], OUTPUT );
//    delayMicroseconds( 10 ); // drain the wire
    pinMode( buttonRowPin[ rowIdx ], INPUT );
    pinMode( buttonColPin[ colIdx ], INPUT ); // was OUTPUT LOW
  }
}


void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Button Matrix Example, free by GoForSmoke\n" ));

  for ( byte i = 0; i < rows; i++ )
  {
    pinMode( buttonRowPin[ i ], INPUT ); // mode INPUT pin is electrically neutral
    for ( byte j = 0; j < cols; j++ )
    {
      if ( i == 0 ) // only set the column pins once
      {
        pinMode( buttonColPin[ j ], INPUT ); // mode INPUT pin is electrically neutral
      }

      buttonHistory[ i ][ j ] = 255; // all buttons start UP
    }
  }
};


void loop()
{
  matrixButtonsTask();
  /*
    0 is the button held down
    255 is the button left up
    127 is the buton changing from up to down, button just released.
    128 is the button changing from down to up, button just pressed.
    everything else is to be ignored as bounce.
  */
  static byte row, col;

  switch ( buttonHistory[ row ][ col ] )
  {
    case 128 : // pin is HIGH in bit 7, LOW for 7 reads, up to down detected
      buttonHistory[ row ][ col ] = 0; // change detected, make it into no change now
      Serial.print( F( "button " ));
      Serial.print( rowIdx );
      Serial.print( F( ", " ));
      Serial.print( colIdx );
      Serial.print( F( "  press detected     " ));
      Serial.println( millis());
      break;

    case 127 : // pin is LOW in bit 7, HIGH for 7 reads, down to up detected
      buttonHistory[ row ][ col ] = 255; // change detected, make it into no change now
      Serial.print( F( "button " ));
      Serial.print( rowIdx );
      Serial.print( F( ", " ));
      Serial.print( colIdx );
      Serial.print( F( "  release detected   " ));
      Serial.println( millis());
      break;
  }

  if ( ++col >= cols )
  {
    col = 0;;

    if ( ++row >= rows )
    {
      row = 0;;
    }
  }
}

PS – this could be sped up using port registers instead of Arduino functions,
but be less beginner friendly.

When you have the resources of a microcontroller to hand, I really cannot see why you would want to unnecessarily complicate the hardware when software debouncing is actually more reliable (because the algorithm is superior).

Sorry, but I haven't the time just now (bed!) to examine either the OP's code or Smoke's. :cold_sweat:

For people new to code, being able to put off debounce seems like a good idea to me.

Actually, the OP's redundant wiring allows a very simple de-bounce using the "R-S" latch hardware approach implemented in software!


johnwasser:
I believe they are not SPDT buttons but ON-OFF-ON three-position switches. See this in the original post:

OK, well spotted - I take back the above, but leave it there for general reference.

We may have been confused by the repeated reference to "buttons" when they apparently are not. Are they spring return to centre?

It is quite unclear as to what the purpose of this device is, which impacts on whether debouncing actually matters at all. If each switch is simply a status, then a few milliseconds of uncertainty quite likely does not even matter.

Also, the OP does not really seem to have explained the actual nature of the claimed "ghosting". We may be barking up the wrong tree. :cold_sweat:

Erik_Baas:
Wait... an 8x8 matrix for only 32 buttons? Why?

I believe they are not SPDT buttons but ON-OFF-ON three-position switches. See this in the original post:

marcelinjo:
Just to make it clear, I added two diodes to every switch, because theoretically those are two buttons(ON/OFF/ON). I hope this is understandable.

They could have contacts for NO and NC use.