HID Keyboard code runs slow

Hey!
I am working on a project which is a 61 key keyboard.
Arduino pro micro is being used to multiplex 61 switch and communicate with a computer.
I found a code online and edited it a lot to suit my needs.
Here is a simplified version of it:

#include "Keyboard.h"


byte cols[] = {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, A3, A2, A1, A0};
const int colCount = sizeof(cols) / sizeof(cols[0]);


byte rows[] = {2, 0, 1, A4, A5};
const int rowCount = sizeof(rows) / sizeof(rows[0]);


char layout[14][5] = {
  {KEY_BACKSPACE, '\\', KEY_RETURN, KEY_RIGHT_SHIFT, KEY_RIGHT_CTRL},
  {'=', ']'},
  {'-', '[', '\'', '/'},
  {'0', 'p', ';', '.', KEY_RIGHT_ALT},
  {'9', 'o', 'l', ','},
  {'8', 'i', 'k', 'm'},
  {'7', 'u', 'j', 'n'},
  {'6', 'y', 'h', 'b', ' '},
  {'5', 't', 'g', 'v'},
  {'4', 'r', 'f', 'c'},
  {'3', 'e', 'd', 'x'},
  {'2', 'w', 's', 'z', KEY_LEFT_ALT},
  {'1', 'q', 'a', 0, KEY_LEFT_GUI},
  {KEY_ESC, KEY_TAB, KEY_CAPS_LOCK, KEY_LEFT_SHIFT, KEY_LEFT_CTRL},
};


void setup() {
  Keyboard.begin();


  for (int i = 0; i < rowCount; i++) {
    pinMode(rows[i], OUTPUT);
    digitalWrite(rows[i], HIGH);
  }
  for (int i = 0; i < colCount; i++) {
    pinMode(cols[i], INPUT_PULLUP);
  }
}


void loop() {
  for (int i = 0; i < rowCount; i++) {
    for (int j = 0; j < colCount; j++) {
      digitalWrite(rows[i], LOW);
      bool buttonState = digitalRead(cols[j]);


      if (buttonState == LOW) {
        Keyboard.press(layout[j][i]);
      } else if (buttonState == HIGH) {
        Keyboard.release(layout[j][i]);
      }
      digitalWrite(rows[i], HIGH);
    }
  }
}

The problem is that the code runs too slow and keys are sometimes not recognized when I press them too quickly. I figured out that commands like Keyboard.press/release slow down the loop quite a bit.
The code runs inefficiently because, for example, if I am not pressing key "A", the code is going to use Keyboard.release each time "A" is being scanned in a matrix even though it is not necessary, because "A" has already been released.
Then I modified the code and made a small scale test where the last state of specific key is being stored and Keyboard.press/release is being used only if the state changes.

#include "Keyboard.h"


byte cols[] = {12, 11, 10, 9, 8, 7, 6, 5, 4, 3, A3, A2, A1, A0};
const int colCount = sizeof(cols) / sizeof(cols[0]);


byte rows[] = {2, 0, 1, A4, A5};
const int rowCount = sizeof(rows) / sizeof(rows[0]);


char layout[14][5] = {
  {KEY_BACKSPACE, '\\', KEY_RETURN, KEY_RIGHT_SHIFT, KEY_RIGHT_CTRL},
  {'=', ']'},
  {'-', '[', '\'', '/'},
  {'0', 'p', ';', '.', KEY_RIGHT_ALT},
  {'9', 'o', 'l', ','},
  {'8', 'i', 'k', 'm'},
  {'7', 'u', 'j', 'n'},
  {'6', 'y', 'h', 'b', ' '},
  {'5', 't', 'g', 'v'},
  {'4', 'r', 'f', 'c'},
  {'3', 'e', 'd', 'x'},
  {'2', 'w', 's', 'z', KEY_LEFT_ALT},
  {'1', 'q', 'a', 0, KEY_LEFT_GUI},
  {KEY_ESC, KEY_TAB, KEY_CAPS_LOCK, KEY_LEFT_SHIFT, KEY_LEFT_CTRL},
};


bool lastButtonState = HIGH; //last state of specific key (in my case letter A)


void setup() {
  Keyboard.begin();
  Serial.begin(9600);


  for (int i = 0; i < rowCount; i++) {
    pinMode(rows[i], OUTPUT);
    digitalWrite(rows[i], HIGH);
  }
  for (int i = 0; i < colCount; i++) {
    pinMode(cols[i], INPUT_PULLUP);
  }
}


void loop() {
  for (int i = 0; i < rowCount; i++) {
    for (int j = 0; j < colCount; j++) {
      digitalWrite(rows[i], LOW);
      bool buttonState = digitalRead(cols[j]);


      if (i == 2 && j == 12) {              //if specific key is pressed (in my case letter A)
        if (buttonState != lastButtonState) {
          if (buttonState == LOW) {
            Keyboard.press(layout[j][i]);
            lastButtonState = buttonState;
          } else {
            Keyboard.release(layout[j][i]);
            lastButtonState = buttonState;
          }
        }
      }
      digitalWrite(rows[i], HIGH);
    }
    Serial.println(lastButtonState);
  }
}

The problem with this is that I would have to store 61 different state values for each key and use them individually.
Is there a way to make it more efficient?

The problem with this is that I would have to store 61 different state values for each key and use them individually.
Is there a way to make it more efficient?

Yes, this is what you need to do I think. This will be much more efficient as updates will only be sent to the pc when needed. Why is it a problem?

You won't need to store 61 state values for each key, only 1 for each key.....

You were right!
I stored all the values in an array like this:

int state[14][5] = {};

and everything worked fine. Now the keyboard is much more responsive and performs even better than before (and yes, I wrote this reply using it). I even had to add a small 1 microsecond to prevent bouncing.

Congratulations!

1us is too short to prevent bouncing, needs to be ~20ms. Did you mean 1us delay inside your nested loops? Even that would be only 61us.

Did you mean 1us delay inside your nested loops?

Yes, 60us seems to do the job. Now I'll have to spend some time typing with my keyboard to see if it still bounces.

Hi,
I am very interested in the code you ended up with and eventually the schematics. Can you please post them?
Thanks

Here is the complete sketch:

#include "Keyboard.h"         //include keyboard library
#define KEY_MENU 0xED         //key menu address
#define KEY_PRNT_SCRN 206     //key print screen address

byte cols[] = {12, 11, 10, 9, 8, A4, 6, 5, 4, A5, A3, A2, A1, A0};  //array of colum pins
const uint8_t colCount = sizeof(cols) / sizeof(cols[0]);
byte rows[] = {2, 0, 1, 7, 3};                                  //array of row pins
const uint8_t rowCount = sizeof(rows) / sizeof(rows[0]);

char layout[14][5] = {          //array for storing qwerty layout
  {KEY_BACKSPACE, '\\', KEY_RETURN, KEY_RIGHT_SHIFT, KEY_RIGHT_CTRL},
  {'=', ']', 0, 0, KEY_MENU},
  {'-', '[', '\'', '/'},
  {'0', 'p', ';', '.', KEY_RIGHT_ALT},
  {'9', 'o', 'l', ','},
  {'8', 'i', 'k', 'm'},
  {'7', 'u', 'j', 'n'},
  {'6', 'y', 'h', 'b', ' '},
  {'5', 't', 'g', 'v'},
  {'4', 'r', 'f', 'c'},
  {'3', 'e', 'd', 'x'},
  {'2', 'w', 's', 'z', KEY_LEFT_ALT},
  {'1', 'q', 'a', 0, KEY_LEFT_GUI},
  {KEY_ESC, KEY_TAB, KEY_CAPS_LOCK, KEY_LEFT_SHIFT, KEY_LEFT_CTRL},
};

char fnLayout[14][5] = {        //array for storing fn layout
  {KEY_DELETE, 0, KEY_RETURN, KEY_RIGHT_SHIFT, KEY_RIGHT_CTRL},
  {KEY_F12, 0, 0, 0, KEY_MENU},
  {KEY_F11, 0, 0, KEY_F4},
  {KEY_F10, KEY_PRNT_SCRN, 0, 0, KEY_RIGHT_ALT},
  {KEY_F9, 0, KEY_RIGHT_ARROW},
  {KEY_F8, KEY_UP_ARROW, KEY_DOWN_ARROW},
  {KEY_F7, 0, KEY_LEFT_ARROW},
  {KEY_F6},
  {KEY_F5},
  {KEY_F4},
  {KEY_F3, 0, KEY_RIGHT_ARROW},
  {KEY_F2, KEY_UP_ARROW, KEY_DOWN_ARROW, 0, KEY_LEFT_ALT},
  {KEY_F1, 0, KEY_LEFT_ARROW, 0, KEY_LEFT_GUI},
  {'`', KEY_TAB, KEY_CAPS_LOCK, KEY_LEFT_SHIFT, KEY_LEFT_CTRL},
};

int state[14][5] = {};          //array for storing switch states
bool fnPressed;
bool fnState;

void setup() {
  Keyboard.begin();
  for (int i = 0; i < rowCount; i++) {
    pinMode(rows[i], OUTPUT);                 //set each row pin as output
    digitalWrite(rows[i], HIGH);              //set each row pin high
  }
  for (int i = 0; i < colCount; i++) {
    pinMode(cols[i], INPUT_PULLUP);           //set each column pin to input and pull up
  }
}

void loop() {
  for (int i = 0; i < rowCount; i++) {        //cycle through rows
    digitalWrite(rows[i], LOW);               //pull the row low
    for (int j = 0; j < colCount; j++) {      //cycle through colums
      bool keyState = digitalRead(cols[j]);   //read state of column pin
      if (i == 4 && j == 2 && fnState != keyState) {
        fnState = keyState;                           //update fn key state
        if (keyState == LOW) {                        //if fn key is pressed
          fnPressed = true;                           //set fn to true
          Keyboard.releaseAll();
          } else {                                    //if fn key is not pressed
          fnPressed = false;                          //set fn to false
        }
      }
      if (fnPressed) {                        //if fn key was pressed
        if (keyState != state[j][i]) {        //if button state does not match previous state
          state[j][i] = keyState;
          if (keyState == LOW) {              //if key is pressed
            Keyboard.press(fnLayout[j][i]);   //press fn layout key
          } else {                            //if key is released
            Keyboard.release(fnLayout[j][i]); //release fn layout key
          }
        }
      } else {                                //if fn key was not pressed
        if (keyState != state[j][i]) {        //if button state does not match previous state
          state[j][i] = keyState;
          if (keyState == LOW) {              //if key is pressed
            Keyboard.press(layout[j][i]);     //press the key
          } else {                            //if key was released
            Keyboard.release(layout[j][i]);   //release both layouts
            Keyboard.release(fnLayout[j][i]);
          }
        }
      }
    }
    digitalWrite(rows[i], HIGH);      //pull the row high
  }
  delay(5);                           //delay to prevent bouncing
}

The schematic is the same as in my original post.

Ah, 5,000us for debouncing, not 60us. That sounds much more like it.

1 Like

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