Button box/matrix: can only use one button at a time

This is a SimHub project, I do not have code information in this post.

I am having an issue with my button box only recognizing one button at a time. The code and wiring works if I only want to press one button at a time. I know about the possibility of ghosting when you press multiple buttons with a button matrix, but I do not think that is what is going on. When I press a button, it registers. When I press a second button at the same time, it over rides the first button. From what I understand, ghosting would not override another button, but it would be like confusing the circuit because it cant recognize which button(s) were pressed.

I am wondering if my understanding of ghosting is correct. And if anyone might have an idea as to what my issue is.

I am using simhub to create arduino code for my project, this is the configuration I am using... I do have the gamepad option turned on.

Here is a picture of the working wiring...

Here is somewhat of an easier diagram to see the circuit.

Here is a good explanation about a keyboard with diodes: https://www.gammon.com.au/forum/?id=14175

I think your diodes are okay.
There should be no problem to press multiple buttons. It could be a software problem, but good code would accept multiple button presses.

What SimHub ? How can I take the same steps as you for the sketch ? Can you show the sketch ?
I don't want to run a unknown exe file on my computer.

I hope the blue wire is soldered on the other side of the Pro Micro :thinking:
Breadboard can have bad contacts. The diodes can have glue on the wires from the strip you took them from.

Here is a note:

Matrix wiring is intended for momentary switches, you cant use toggle switches (non momentary) and won't support multiple buttons pressed at a time

Multiple button presses is not supported.
I can not find the sources of SimHub, so I suppose that it is not open-source ?
That makes it almost useless for us Arduino-enthusiasts.

Can you find a good button box sketch ? Maybe on a gamers forum they know good projects.

This Arduino forum should have a "Gamers" section :joystick:

I check out your example tomorrow after work, thanks!

Simhub is used for projects like this for gamers, it helps create the code and a dashboard for button boxes for various games.

I was looking into how to extract and post the code here, but it is not what I am typically used to seeing. There are several tabs and I do not know what all is for my project. I think it adds everything the software can do into the code and just do not "activate" what is not needed.

Huh... well I guess you found the ticket item... SimHub will not work for what I want to do. I need to create the code from scratch. I have looked for example code for a button box like this, but they include components my project does not use and to be honest... I would not know what I am doing to modify it.

Can you post the Arduino code that simhub created for your project?

Attached is a zipped folder of everything simhub puts into IDE
Simhub.zip (53.0 KB)

The file "SHButtonMatrix.h" is where they only allow one button at a time:

				if (pressedButton != lastPressedButton) {

					if (lastPressedButton != -1) {
						shButtonChangedCallback(lastPressedButton, 0);
					}
					if (pressedButton != -1) {
						shButtonChangedCallback(pressedButton, 1);
					}
					buttonLastStateChanged = millis();
				}

Someone would have to re-write that part to maintain separate state information for each button. Then the state change callbacks would be called as each button changed state.

I looked at it, and it is not so easy. I think that a object is needed for each button or use the Bounce2 library, which already has debounce and a state-change-detection. I also got a little lost when trying to find how the keys are passed on to a higher level.

Starting from scratch is easier, or a "gamer's" buttonbox that does it right.

@chevyguy, The Bounce2 and other libraries can debounce a button. Every button bounces and need a "debounce" in software. The Bounce2 can also detect the moment a button is pressed and the moment the button a released.
A keyboard sends a command when a button is pressed and another command when that button is released. So the Bounce2 library is a perfect match for a keyboard, where every button can be pressed as long as your want and as many buttons together as you want.

The problem is that it would take too much time and effort to put that into SimHub.

Here is a first try. It compiles without errors. There may be assumptions in higher levels that only one button is ever pressed at a time but at least this will allow you to attempt to press more than one at a time. You will still need diodes for full N-key rollover.

#ifndef __SHBUTTONMATRIX_H__
#define __SHBUTTONMATRIX_H__

#include <Arduino.h>
#include "SHDebouncer.h"

typedef void(*SHButtonMatrixChanged) (int, byte);

class SHButtonMatrix
{

  private:

    FastDigitalPin button;
    unsigned long buttonLastStateChanged;

    SHButtonMatrixChanged shButtonChangedCallback;
    SHDebouncer debouncer;

    byte rowCount;
    byte colCount;
    byte * colPins;
    byte * rowPins;
    bool * pressedState;
    
  public:

    void begin(byte cols, byte rows, byte * col, byte * row, SHButtonMatrixChanged changedcallback)
    {

      debouncer.begin(10);
      rowCount = rows;
      colCount = cols;
      colPins = col;
      rowPins = row;

      pressedState = (bool *) malloc(rowCount * colCount * sizeof (bool) + 1);
      memset(pressedState, 0, rowCount * colCount * sizeof (bool) + 1); // clear all to 'false'

      for (int x = 0; x < rowCount; x++)
      {
        pinMode(rowPins[x], INPUT);
      }

      for (int x = 0; x < colCount; x++)
      {
        pinMode(colPins[x], INPUT_PULLUP);
      }

      shButtonChangedCallback = changedcallback;
    }

    void read()
    {
      if (debouncer.Debounce())
      {
        if (buttonLastStateChanged - millis() > 50)
        {
          for (int colIndex = 0; colIndex < colCount; colIndex++)
          {

            byte curCol = colPins[colIndex];
            pinMode(curCol, OUTPUT);
            digitalWrite(curCol, LOW);

            for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
            {
              unsigned buttonNumber = rowIndex * colCount + colIndex + 1;

              byte curRow = rowPins[rowIndex];
              pinMode(curRow, INPUT_PULLUP);

              if (digitalRead(curRow) == LOW)
              {
                // This button is pressed
                if (!pressedState[buttonNumber])
                {
                  // This button is newly presed
                  shButtonChangedCallback(buttonNumber, 1);
                  pressedState[buttonNumber] = true;
                  buttonLastStateChanged = millis();
                }
              }
              else
              {
                // This button is NOT presed
                if (pressedState[buttonNumber])
                {
                  // This button is newly released
                  shButtonChangedCallback(buttonNumber, 0);
                  pressedState[buttonNumber] = false;
                  buttonLastStateChanged = millis();
                }
              }
              pinMode(curRow, INPUT);
            } // End for (rowIndex)

            pinMode(curCol, INPUT);
          } // End for (colIndex)

        }
      }
      return;
    }
};

#endif
1 Like

So from what I gather, I am better off not using simhub at all and just write the code myself…

Referring to your diagram, I would suggest adding 10K pullup resistors for signals A1, A2 and A3.

I've had no success trying to get simhub to accept multiple button presses at one time. I've tried a few things I found online, but nothing worked.

I think I am going to find some basic matrix code and manipulate that to make it work for my project.

Try this new way to connect buttons and save pins. In the Wokwi example, press any one button first (with the mouse), then try using the keyboard to press multiple keys as labelled on the buttons.

⦿ Built-in debounce for all buttons.
⦿ Multiple pressed button detection up to 8 per row (use keyboard and mouse).
⦿ Uses one clock pin to provide 3 input states and for debounce sample period.
⦿ No pullup or pulldown resistors required for buttons.
⦿ One pulsed pin current limiting resistor required per button pair (column).
⦿ Can be scaled for more or fewer buttons.
⦿ No external GPIO expander ICs required.
⦿ No diodes required.
⦿ No ghosting.

MultiKey example for pressing multiple keys which are organized in a matrix : https://github.com/Chris--A/Keypad/blob/master/examples/MultiKey/MultiKey.ino.
I have not tried it.

Tip: In Wokwi it is possible to keep a button pressed. Click on the button, move the mouse away while keeping the left mouse button pressed. It is hard to see, but it is visible which key is pressed.

@dlloyd If I press button A, B, I and J in your example, then button H does not work.

Yes, correct.

The buttons are organized 2 per column with limitation that both pressed in any column cannot be correctly detected.

Here, for any given column, the D4, D5 and D6 inputs can each detect 3 "states":

  • HIGH (upper button pressed)
  • LOW (lower button pressed)
  • CLK (both buttons released)

Pressing both upper and lower buttons in any column creates a false low detection.

And also causes all other upper buttons to read as lower buttons. You could fix that problem by having separate pull-up resistors on each column.

I would need a 4th "state" to resolve it ... but only 3 are possible with a digital input. Since the low state is already taken when a lower button is pressed, I guess its beneficial to have an error condition occur if both buttons in a column get pressed (if the error condition is important).

Yes, the extra resistors should preserve the valid pressed buttons.

I didn't mean you could fix the original problem (push both and get "lower"). I meant that by giving each column a separate pull-up, pushing both buttons in one column would not cause all the other "upper" buttons to read as "lower".

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.