Issue with Keyboard control on Arduino Leo

A few months ago, I built a arcade style controller for a friend using an Arduino Leonardo board with the Keyboard library to control a Mame emulator. I had issues with the LEFT ALT key since it would get stuck pressed for no apparent reason. I used the ‘pullup’ mode for my inputs, as it fit better with my build, and all of the other inputs seemed to work with no issue. I’ve been over the code many times, and can’t find where the problem lies. I’m looking to fix it soon, so if anyone can spot the problem, it would be greatly appreciated.

The code is included below:

(Any ‘Wire’ commands are to do with LED control on a separate board [Arduino UNO])

#include <Wire.h>
#include <Keyboard.h>

//input variables

const int butt1 = 2;
const int butt2 = 3;
const int butt3 = 4;
const int butt4 = 5;
const int butt5 = 6;
const int butt6 = 7;
const int sButt = 8;
const int lJoy = 11;
const int uJoy = 12;
const int rJoy = 9;
const int dJoy = 10;

//previous input variables

int butt1Prev = HIGH;
int butt2Prev = HIGH;
int butt3Prev = HIGH;
int butt4Prev = HIGH;
int butt5Prev = HIGH;
int butt6Prev = HIGH;
int sButtPrev = HIGH;
int lJoyPrev = HIGH;
int uJoyPrev = HIGH;
int rJoyPrev = HIGH;
int dJoyPrev = HIGH;

//other variables

long buttonTimer = 0;
long longPressTime = 500; //500 ms

bool buttonActive = false;
bool longPressActive = false;

void setup() {

  pinMode(butt1, INPUT_PULLUP);
  pinMode(butt2, INPUT_PULLUP);
  pinMode(butt3, INPUT_PULLUP);
  pinMode(butt4, INPUT_PULLUP);
  pinMode(butt5, INPUT_PULLUP);
  pinMode(butt6, INPUT_PULLUP);
  pinMode(sButt, INPUT_PULLUP);
  pinMode(lJoy, INPUT_PULLUP);
  pinMode(uJoy, INPUT_PULLUP);
  pinMode(rJoy, INPUT_PULLUP);
  pinMode(dJoy, INPUT_PULLUP);
  pinMode(13,OUTPUT);

  digitalWrite(13,HIGH);
  
  // initialize control over the keyboard
  Keyboard.begin();

  Keyboard.releaseAll();
  
  // connect to slave arduino
  Wire.begin(); // join i2c bus (address optional for master)
}

void loop() {
  
  //read the pushbuttons
  int butt1State = digitalRead(butt1);
  int butt2State = digitalRead(butt2);
  int butt3State = digitalRead(butt3);
  int butt4State = digitalRead(butt4);
  int butt5State = digitalRead(butt5);
  int butt6State = digitalRead(butt6);
  int sButtState = digitalRead(sButt);
  int lJoyState = digitalRead(lJoy);
  int uJoyState = digitalRead(uJoy);
  int rJoyState = digitalRead(rJoy);
  int dJoyState = digitalRead(dJoy);
    

  //joystick

  //if the input state has changed, and the button/joystick is pressed
  if ((lJoyState != lJoyPrev) && (lJoyState == LOW)){

     //press the key
     Keyboard.press(KEY_LEFT_ARROW);

  //otherwise, if the button/joystick is released
  } else if (lJoyState == HIGH){

     //release the key
     Keyboard.release(KEY_LEFT_ARROW);
  }

  if ((uJoyState != uJoyPrev) && (uJoyState == LOW)){

     Keyboard.press(KEY_UP_ARROW);

  } else if (uJoyState == HIGH){
     Keyboard.release(KEY_UP_ARROW);
  }

  if ((rJoyState != rJoyPrev) && (rJoyState == LOW)){

    Keyboard.press(KEY_RIGHT_ARROW);

  } else if (rJoyState == HIGH){
     Keyboard.release(KEY_RIGHT_ARROW);
  }

  if ((dJoyState != dJoyPrev) && (dJoyState == LOW)){

     Keyboard.press(KEY_DOWN_ARROW);

  } else if (dJoyState == HIGH){
     Keyboard.release(KEY_DOWN_ARROW);
  }

  //start / coin button
  //press for start, hold for coin

  if (digitalRead(sButt) == LOW) {

    if (buttonActive == false) {

      buttonActive = true;
      buttonTimer = millis();
      delay(3);

    }

    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {

      //add coin

      longPressActive = true;

      // Send light command to slave arduino
      Wire.beginTransmission(1); // transmit to device #1
      Wire.write('7');              // sends one byte
      Wire.endTransmission();    // stop transmitting

      Keyboard.press('5');
      delay(100);
      Keyboard.release('5');

    }

  } else {

      if (buttonActive == true) {

        if (longPressActive == true) {
  
          longPressActive = false;
  
        } else {
  
            //start game
      
          // Send light command to slave arduino
          Wire.beginTransmission(1); // transmit to device #1
          Wire.write('8');              // sends one byte
          Wire.endTransmission();    // stop transmitting
  
          Keyboard.press('1');
          delay(100);
          Keyboard.release('1');
  
        }
  
        buttonActive = false;
  
      }

  }
  
  //action buttons
  if ((butt1State != butt1Prev)
      // and it's currently pressed
      && (butt1State == LOW)){

      Keyboard.press(KEY_LEFT_CTRL);
      
  } else if (butt1State == HIGH){

      Keyboard.release(KEY_LEFT_CTRL);
  }

  if ((butt2State != butt2Prev) && (butt2State == LOW)){

     Keyboard.press(KEY_LEFT_ALT);

  } else if (butt2State == HIGH){

     Keyboard.release(KEY_LEFT_ALT);
  }

  if ((butt3State != butt3Prev) && (butt3State == LOW)){

     Keyboard.press(' ');
    
  } else if (butt3State == HIGH){

     Keyboard.release(' ');
  }

  if ((butt4State != butt4Prev) && (butt4State == LOW)){

     Keyboard.press(KEY_LEFT_SHIFT);

  } else if (butt4State == HIGH){

     Keyboard.release(KEY_LEFT_SHIFT);
  }

  if ((butt5State != butt5Prev) && (butt5State == LOW)){

    Keyboard.press('z');

  } else if (butt5State == HIGH){

     Keyboard.release('z');
  }

  if ((butt6State != butt6Prev) && (butt6State == LOW)){

     Keyboard.press('x');

  } else if (butt6State == HIGH){

     Keyboard.release('x');
  }

  // save the current button state for comparison next time:
  butt1Prev = butt1State;
  butt2Prev = butt2State;
  butt3Prev = butt3State;
  butt4Prev = butt4State;
  butt5Prev = butt5State;
  butt6Prev = butt6State;
  sButtPrev = sButtState;
  lJoyPrev = lJoyState;
  uJoyPrev = uJoyState;
  rJoyPrev = rJoyState;
  dJoyPrev = dJoyState; 
    
}

Have a look at the Debounce example and incorporate it into all your buttons so you don't trigger them too quickly. Also, as your code is written, you continuously call the keyboard.release() function whenever the current state is the same as the previous state. It seems like you might want to only call it on button press/release

  //if the input state has changed
  if (lJoyState != lJoyPrev) {
    if (lJoyState == LOW)) {
      //press the key
      Keyboard.press(KEY_LEFT_ARROW);

      //otherwise, if the button/joystick is released
    } else {
      //release the key
      Keyboard.release(KEY_LEFT_ARROW);
    }
  }

Yeah it seems like the issue is related to denouncing of keys. On each key press, first make sure that all other keys are not pressed.

Well after a good month, I’ve gotten the controller back from my friend and changed the code as suggested, but the same issue is still happening. When the Left Alt Key is pressed, it doesn’t get released. When I press button 1 afterwards, which is Left Ctrl, and push a direction, it flips my screen as if I’m pressing Ctrl+Alt+Arrow Keys. Pretty stumped at this point since all of the other buttons use the same code. I’ve included the updated code - if anyone can tell me what’s going on, that would be fantastic.

#include <Wire.h>
#include <Keyboard.h>

const int butt1 = 2;
const int butt2 = 3;
const int butt3 = 4;
const int butt4 = 5;
const int butt5 = 6;
const int butt6 = 7;
const int sButt = 8;
const int lJoy = 11;
const int uJoy = 12;
const int rJoy = 9;
const int dJoy = 10;

int butt1Prev = HIGH;
int butt2Prev = HIGH;
int butt3Prev = HIGH;
int butt4Prev = HIGH;
int butt5Prev = HIGH;
int butt6Prev = HIGH;
int sButtPrev = HIGH;
int lJoyPrev = HIGH;
int uJoyPrev = HIGH;
int rJoyPrev = HIGH;
int dJoyPrev = HIGH;

long buttonTimer = 0;
long longPressTime = 500;

bool buttonActive = false;
bool longPressActive = false;

void setup() {
  pinMode(butt1, INPUT_PULLUP);
  pinMode(butt2, INPUT_PULLUP);
  pinMode(butt3, INPUT_PULLUP);
  pinMode(butt4, INPUT_PULLUP);
  pinMode(butt5, INPUT_PULLUP);
  pinMode(butt6, INPUT_PULLUP);
  pinMode(sButt, INPUT_PULLUP);
  pinMode(lJoy, INPUT_PULLUP);
  pinMode(uJoy, INPUT_PULLUP);
  pinMode(rJoy, INPUT_PULLUP);
  pinMode(dJoy, INPUT_PULLUP);
  pinMode(13,OUTPUT);

  digitalWrite(13,HIGH); //5V power to slave arduino
  
  // initialize control over the keyboard
  Keyboard.begin();

  Keyboard.releaseAll();
  
  // connect to slave arduino
  Wire.begin(); // join i2c bus (address optional for master)
}

void loop() {
  
  // read the pushbutton:
  int butt1State = digitalRead(butt1);
  int butt2State = digitalRead(butt2);
  int butt3State = digitalRead(butt3);
  int butt4State = digitalRead(butt4);
  int butt5State = digitalRead(butt5);
  int butt6State = digitalRead(butt6);
  int sButtState = digitalRead(sButt);
  int lJoyState = digitalRead(lJoy);
  int uJoyState = digitalRead(uJoy);
  int rJoyState = digitalRead(rJoy);
  int dJoyState = digitalRead(dJoy);
    

  //joystick

  if (lJoyState != lJoyPrev){
    if (lJoyState == LOW){
       Keyboard.press(KEY_LEFT_ARROW);
    } else if (lJoyState == HIGH){
       Keyboard.release(KEY_LEFT_ARROW);
    }
  }

  if (uJoyState != uJoyPrev){
    if (uJoyState == LOW){
       Keyboard.press(KEY_UP_ARROW);
    } else if (uJoyState == HIGH){
       Keyboard.release(KEY_UP_ARROW);
    }
  }

  if (rJoyState != rJoyPrev){
    if (rJoyState == LOW){
       Keyboard.press(KEY_RIGHT_ARROW);
    } else if (rJoyState == HIGH){
       Keyboard.release(KEY_RIGHT_ARROW);
    }
  }

  if (dJoyState != dJoyPrev){
    if (dJoyState == LOW){
       Keyboard.press(KEY_DOWN_ARROW);
    } else if (dJoyState == HIGH){
       Keyboard.release(KEY_DOWN_ARROW);
    }
  }

  //start / coin button

  if (digitalRead(sButt) == LOW) {

    if (buttonActive == false) {

      buttonActive = true;
      buttonTimer = millis();
      delay(3);

    }

    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {

      //add coin

      longPressActive = true;

      // Send light command to slave arduino
      Wire.beginTransmission(1); // transmit to device #1
      Wire.write('7');              // sends one byte
      Wire.endTransmission();    // stop transmitting

      Keyboard.press('5');
      delay(100);
      Keyboard.release('5');

    }

  } else {

      if (buttonActive == true) {

        if (longPressActive == true) {
  
          longPressActive = false;
  
        } else {
  
            //start game
      
          // Send light command to slave arduino
          Wire.beginTransmission(1); // transmit to device #1
          Wire.write('8');              // sends one byte
          Wire.endTransmission();    // stop transmitting
  
          Keyboard.press('1');
          delay(100);
          Keyboard.release('1');
  
        }
  
        buttonActive = false;
  
      }

  }
  
  //action buttons
  if (butt1State != butt1Prev){
    
    if (butt1State == LOW){
        Keyboard.press(KEY_LEFT_CTRL);
    } else if (butt1State == HIGH){
        Keyboard.release(KEY_LEFT_CTRL);
    }
  }

  if (butt2State != butt2Prev){
    if (butt2State == LOW){
       Keyboard.press(KEY_LEFT_ALT);
    } else if (butt2State == HIGH){
       Keyboard.release(KEY_LEFT_ALT);
    }
  }

  if (butt3State != butt3Prev){
    if (butt3State == LOW){
       Keyboard.press(' ');
    } else if (butt3State == HIGH){
       Keyboard.release(' ');
    }
  }

  if (butt4State != butt4Prev){
    if (butt4State == LOW){
       Keyboard.press(KEY_LEFT_SHIFT);
    } else if (butt4State == HIGH){
       Keyboard.release(KEY_LEFT_SHIFT);
    }
  }

  if (butt5State != butt5Prev){
    if (butt5State == LOW){
       Keyboard.press('z');
    } else if (butt5State == HIGH){
       Keyboard.release('z');
    }
  }
  
  if (butt6State != butt6Prev){
    if (butt6State == LOW){
       Keyboard.press('x');
    } else if (butt6State == HIGH){
       Keyboard.release('x');
    }
  }

  // save the current button state for comparison next time:
  butt1Prev = butt1State;
  butt2Prev = butt2State;
  butt3Prev = butt3State;
  butt4Prev = butt4State;
  butt5Prev = butt5State;
  butt6Prev = butt6State;
  sButtPrev = sButtState;
  lJoyPrev = lJoyState;
  uJoyPrev = uJoyState;
  rJoyPrev = rJoyState;
  dJoyPrev = dJoyState;
    
}

I ended up just going into the emulator settings and changing the default key binds. After changing button 2 from Left Alt to 'C' in the code, I'm no longer experiencing the key release issue. I'm thinking it's maybe some kind of bug specifically to do with the Alt key? No idea really, I'm not an Arduino expert. At least, with that workaround, the problem is resolved.