Button Matrix lag (2x7)

Im under no illusions that this is the ideal way to code this, but Im curious as to why its so slow. Any thoughts and what is the best way to recode this 2x7?

Thanks,

void loop() {
  // Read the button matrix
  for (int col = 0; col < COLS; col++) {
    pinMode(colPins[col], OUTPUT);          // Set current column pin to LOW
    digitalWrite(colPins[col], LOW);
    
    for (int row = 0; row < ROWS; row++) {
      buttonState[row][col] = digitalRead(rowPins[row]);  // Read button state
      
      if (buttonState[0][0] == LOW) {
            Joystick.pressButton(0);
          }
      if (buttonState[0][0] == HIGH) {
        Joystick.releaseButton(0);
      }
        if (buttonState[0][1] == LOW) {
            Joystick.pressButton(1);
          }
      if (buttonState[0][1] == HIGH) {
        Joystick.releaseButton(1);
      }
        if (buttonState[0][2] == LOW) {
            Joystick.pressButton(2);
          }
      if (buttonState[0][2] == HIGH) {
        Joystick.releaseButton(2);
      }
      if (buttonState[0][3] == LOW) {
            Joystick.pressButton(3);
          }
      if (buttonState[0][3] == HIGH) {
        Joystick.releaseButton(3);
      }
      if (buttonState[0][4] == LOW) {
            Joystick.pressButton(4);
          }
      if (buttonState[0][4] == HIGH) {
        Joystick.releaseButton(4);
      }
      if (buttonState[0][5] == LOW) {
            Joystick.pressButton(5);
          }
      if (buttonState[0][5] == HIGH) {
        Joystick.releaseButton(5);
      }
      if (buttonState[0][6] == LOW) {
            Joystick.pressButton(6);
          }
      if (buttonState[0][6] == HIGH) {
        Joystick.releaseButton(6);
      }
      if (buttonState[1][0] == LOW) {
            Joystick.pressButton(7);
          }
      if (buttonState[1][0] == HIGH) {
        Joystick.releaseButton(7);
      }
      if (buttonState[1][1] == LOW) {
            Joystick.pressButton(8);
          }
      if (buttonState[1][1] == HIGH) {
        Joystick.releaseButton(8);
      }
      if (buttonState[1][2] == LOW) {
            Joystick.pressButton(9);
          }
      if (buttonState[1][2] == HIGH) {
        Joystick.releaseButton(9);
      }
      if (buttonState[1][3] == LOW) {
            Joystick.pressButton(10);
          }
      if (buttonState[1][3] == HIGH) {
        Joystick.releaseButton(10);
      }
      if (buttonState[1][4] == LOW) {
            Joystick.pressButton(11);
          }
      if (buttonState[1][4] == HIGH) {
        Joystick.releaseButton(11);
      }
      if (buttonState[1][5] == LOW) {
            Joystick.pressButton(12);
            //delay(25);
          }
      if (buttonState[1][5] == HIGH) {
        Joystick.releaseButton(12);
      }
      if (buttonState[1][6] == LOW) {
            Joystick.pressButton(13);
            //delay(25);
          }
      if (buttonState[1][6] == HIGH) {
        Joystick.releaseButton(13);
      }
    }
    
    digitalWrite(colPins[col], HIGH);    // Set current column pin to HIGH
    pinMode(colPins[col], INPUT_PULLUP); // Set column pin back to input mode with internal pull-up resistor
  }

Hello, @Slythy - I can see this is for a buttonbox. I think you shuold move pinMode(); to the setup() section. Would you post the complete sketch with setup() loop(), definitions and all functions?

Sure.


#include <Joystick.h>

Joystick_ Joystick(0x05, JOYSTICK_TYPE_GAMEPAD, 42, 0);

const int ROWS = 2;        // Number of rows
const int COLS = 7;        // Number of columns

int rowPins[ROWS] = {14, 15};      // Pins connected to rows
int colPins[COLS] = {8, 7, 6, 5, 4, 3, 2};  // Pins connected to columns

int buttonState[ROWS][COLS];
int lastButtonState[ROWS][COLS];


void setup() {



for (int row = 0; row < ROWS; row++) {
    for (int col = 0; col < COLS; col++) {
      pinMode(rowPins[row], INPUT_PULLUP);    // Set row pins as input with internal pull-up resistors
      pinMode(colPins[col], OUTPUT);          // Set column pins as output
      digitalWrite(colPins[col], HIGH);       // Set column pins to HIGH
      buttonState[row][col] = HIGH;            // Initialize button state to HIGH
      lastButtonState[row][col] = HIGH;        // Initialize last button state to HIGH
    }
  }
  // Initialize Joystick Library
  Joystick.begin();
}

void loop() {
  // Read the button matrix
  for (int col = 0; col < COLS; col++) {
    pinMode(colPins[col], OUTPUT);          // Set current column pin to LOW
    digitalWrite(colPins[col], LOW);
    
    for (int row = 0; row < ROWS; row++) {
      buttonState[row][col] = digitalRead(rowPins[row]);  // Read button state
      
      if (buttonState[0][0] == LOW) {
            Joystick.pressButton(0);
          }
      if (buttonState[0][0] == HIGH) {
        Joystick.releaseButton(0);
      }
        if (buttonState[0][1] == LOW) {
            Joystick.pressButton(1);
          }
      if (buttonState[0][1] == HIGH) {
        Joystick.releaseButton(1);
      }
        if (buttonState[0][2] == LOW) {
            Joystick.pressButton(2);
          }
      if (buttonState[0][2] == HIGH) {
        Joystick.releaseButton(2);
      }
      if (buttonState[0][3] == LOW) {
            Joystick.pressButton(3);
          }
      if (buttonState[0][3] == HIGH) {
        Joystick.releaseButton(3);
      }
      if (buttonState[0][4] == LOW) {
            Joystick.pressButton(4);
          }
      if (buttonState[0][4] == HIGH) {
        Joystick.releaseButton(4);
      }
      if (buttonState[0][5] == LOW) {
            Joystick.pressButton(5);
          }
      if (buttonState[0][5] == HIGH) {
        Joystick.releaseButton(5);
      }
      if (buttonState[0][6] == LOW) {
            Joystick.pressButton(6);
          }
      if (buttonState[0][6] == HIGH) {
        Joystick.releaseButton(6);
      }
      if (buttonState[1][0] == LOW) {
            Joystick.pressButton(7);
          }
      if (buttonState[1][0] == HIGH) {
        Joystick.releaseButton(7);
      }
      if (buttonState[1][1] == LOW) {
            Joystick.pressButton(8);
          }
      if (buttonState[1][1] == HIGH) {
        Joystick.releaseButton(8);
      }
      if (buttonState[1][2] == LOW) {
            Joystick.pressButton(9);
          }
      if (buttonState[1][2] == HIGH) {
        Joystick.releaseButton(9);
      }
      if (buttonState[1][3] == LOW) {
            Joystick.pressButton(10);
          }
      if (buttonState[1][3] == HIGH) {
        Joystick.releaseButton(10);
      }
      if (buttonState[1][4] == LOW) {
            Joystick.pressButton(11);
          }
      if (buttonState[1][4] == HIGH) {
        Joystick.releaseButton(11);
      }
      if (buttonState[1][5] == LOW) {
            Joystick.pressButton(12);
            //delay(25);
          }
      if (buttonState[1][5] == HIGH) {
        Joystick.releaseButton(12);
      }
      if (buttonState[1][6] == LOW) {
            Joystick.pressButton(13);
            //delay(25);
          }
      if (buttonState[1][6] == HIGH) {
        Joystick.releaseButton(13);
      }
    }
    
    digitalWrite(colPins[col], HIGH);    // Set current column pin to HIGH
    pinMode(colPins[col], INPUT_PULLUP); // Set column pin back to input mode with internal pull-up resistor
  }

My guess would be all those calls to Joystick.pressButton() and Joystick.releaseButton(). 99% of the time, nothing has changed, no buttons have been pressed or released, but the Arduino is constantly sending "updates". Perhaps if you only call Joystick.pressButton() when a button changes from not being pressed to being pressed and only call Joystick.releaseButton() when a button changes from being pressed to not being pressed, it would be much faster.

1 Like

@Slythy

For...

...and...

...I like to refer to DroneBotWorkshop (with video)... for your case "Pin Change Interrupt"...

https://dronebotworkshop.com/interrupts

... of course, you can use any reference, and even try other libraries, like this library (with the same name, but different usage)... (A0 = x, A1 = y, 13 = button)

1 Like
#include <Joystick.h>

Joystick_ Joystick(0x05, JOYSTICK_TYPE_GAMEPAD, 42, 0);

const int ROWS = 2;        // Number of rows
const int COLS = 7;        // Number of columns

int rowPins[ROWS] = {14, 15};      // Pins connected to rows
int colPins[COLS] = {8, 7, 6, 5, 4, 3, 2};  // Pins connected to columns

int lastButtonState[ROWS][COLS];


void setup() {

for (int row = 0; row < ROWS; row++) {
    for (int col = 0; col < COLS; col++) {
      pinMode(rowPins[row], INPUT_PULLUP);    // Set row pins as input with internal pull-up resistors
      pinMode(colPins[col], OUTPUT);          // Set column pins as output
      digitalWrite(colPins[col], HIGH);       // Set column pins to HIGH
      lastButtonState[row][col] = HIGH;        // Initialize last button state to HIGH
    }
  }
  // Initialize Joystick Library
  Joystick.begin();
}

void loop() {
  int b = 0;
  // Read the button matrix
  for (int col = 0; col < COLS; col++) {
    digitalWrite(colPins[col], LOW);
    
    for (int row = 0; row < ROWS; row++) {

      int buttonState = digitalRead(rowPins[row]);  // Read button state
      
      if (buttonState == LOW and lastButtonState[row][col] == HIGH) {
        Joystick.pressButton(b);
      }
      else if (buttonState == HIGH and lastButtonState[row][col] == LOW) {
        Joystick.releaseButton(b);
      }
    }
    lastButtonState[row][col] = buttonState;
    b++;
    digitalWrite(colPins[col], HIGH);    // Set current column pin to HIGH
  }
}
1 Like

Not sure what you mean by "slow".

Once the buttonState[row][col] changes to LOW, it never goes back to HIGH. Eventually this will cause many (all) joystick button presses even if the buttons are released.

1 Like

Can you explain why that is, please?

My comment is wrong, which I just realized trying to answer your question. :face_with_open_eyes_and_hand_over_mouth:

My bad. Please disregard.

Hey @PaulRB

I have used the code in post 6 that you posted.

I get an error on the line


 lastButtonState[row][col] = buttonState;

it says

exit status 1
'row' was not declared in this scope

its defined in the 4th line of the void loop line correct?

what am I missing? if I declare it before the setup then the following line says not declared

lastButtonState[row][col] = buttonState;

why is it not declared when its defined in the loop?

Thanks,

row only exists within the scope of the for loop in which it was declared, so at the point where it's used it's already 'gone'.

Interesting. nothing happens if I put them at the top of the program so something is unhappy with how its written......

A variable declared inside curly brackets {} exists only inside those brackets. That is its scope. Thus the compiler message.

1 Like

Oops, sorry for that. Move that line up one line (and adjust the indentation). Move the line b++; also.

The line with the error isn't in the loop that defines row. The } on the line above is the end of that loop. If you move the line with the error up by one line, then it will be in the loop.

2 Likes

That works amazingly!

Can you explain what the change is? Its still a for loop, I see the button state went from a matrix to an int. Can you treat me like a 4 year old lol?

Thanks again so much!

Your original sketch was not checking when your buttons changed state, it sent the state of each button each time loop() executed, even for buttons that had not changed. Now, it only sends those updates when they are needed because a button has changed.

buttonState was a global array, but it didn't need to be. All the values in the array got overwritten each time loop() executed, and no part of the code was reading them, except one part which only read the array element for the current button. So the memory space taken up by that array was a waste, and I replaced it with a single, local, int variable. Only lastButtonState needed to be a global array because all 16 button states needed to be saved from one execution of loop() to the next to check which buttons had changed.

Awesome TY.

One last thing, I noticed your code changed the button order all around. Any ideas why that would have happened? It works but just out of order now.

The button order in your original code is the natural order if the buttons were scanned row-by-row. But your original code scans the buttons column-by-column. That is one reason why your original code was so long, complex, and slow.

Using the shorter and faster code I gave, the button order is the natural order for column-by-column scanning.

Is this different order truly a problem? Can the application/game on the PC/laptop be configured to use this button order?

If not, the Arduino code can be fixed to give the original button order by changing it to scan row-by-row. Is there any reason why this could not be done? For example does your circuit have any external pull-up/down resistors or diodes?

Try this. It will be slightly slower because it uses digitalWrite() more frequently (28 times rather than 4 times for each scan), but I don't think the speed difference will be noticed, and it should change the button order to match your original code.

#include <Joystick.h>

Joystick_ Joystick(0x05, JOYSTICK_TYPE_GAMEPAD, 42, 0);

const int ROWS = 2;        // Number of rows
const int COLS = 7;        // Number of columns

int rowPins[ROWS] = {14, 15};      // Pins connected to rows
int colPins[COLS] = {8, 7, 6, 5, 4, 3, 2};  // Pins connected to columns

int lastButtonState[ROWS][COLS];


void setup() {

for (int row = 0; row < ROWS; row++) {
    for (int col = 0; col < COLS; col++) {
      pinMode(rowPins[row], INPUT_PULLUP);    // Set row pins as input with internal pull-up resistors
      pinMode(colPins[col], OUTPUT);          // Set column pins as output
      digitalWrite(colPins[col], HIGH);       // Set column pins to HIGH
      lastButtonState[row][col] = HIGH;        // Initialize last button state to HIGH
    }
  }
  // Initialize Joystick Library
  Joystick.begin();
}

void loop() {
  int b = 0;
  // Read the button matrix
  for (int row = 0; row < ROWS; row++) {
    for (int col = 0; col < COLS; col++) {
      digitalWrite(colPins[col], LOW);
      int buttonState = digitalRead(rowPins[row]);  // Read button state
      
      if (buttonState == LOW and lastButtonState[row][col] == HIGH) {
        Joystick.pressButton(b);
      }
      else if (buttonState == HIGH and lastButtonState[row][col] == LOW) {
        Joystick.releaseButton(b);
      }
      lastButtonState[row][col] = buttonState;
      b++;
      digitalWrite(colPins[col], HIGH);    // Set current column pin to HIGH
    }
  }
}

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