MHeironimus / ArduinoJoystickLibrary

An Arduino library that adds one or more joysticks to the list of HID devices an Arduino Leonardo or Arduino Micro can support.
GNU Lesser General Public License v3.0
2.07k stars 403 forks source link

Issue with retriggering while buttons are held when using shift registers on RetroPie #161

Open DaveGuenther opened 3 years ago

DaveGuenther commented 3 years ago

Description of Issue

Hello,

I wrote a sketch that made use of 3 chained input shift registers (CD4021) that went into the following pins:

I read in a byte of data for each shift register (3 total bytes) and decoded each bit to a particular digital button press or hat sw event. The sketch ran fine and the joystick was recognized in windows with no issues. I also used the joystick configuration utility in the control panel in order to test button presses and more importantly button hold events. I ultimately want to use this custom joystick for a Raspberry Pi with retropie. When running the controller on the RetroPie, I get the notification in EmulationStation that a new controller was detected and instructed to "Hold any button to configure". Normally when holding a button for about a second, the retropie then enters the input config menu. In this case though, I held a button and it looked like the retropie was starting the 1 second hold timer but then it kept restarting, as if the button were retriggering while being held instead of just staying down for the duration. I was unable to ever enter the config utility because the retropie never registered a continuous button hold for a whole second.

I eventually redesigned the circuit, pulled out the shift registers and un-soldered the resisters that go to the TX and RX LEDs for the two additional inputs that I needed. Then I rewrote the sketch to more closely align with your example. This time the input read by retropie worked just fine. While I was able to work my situation out without shift registers, I'm very curious as to why I was unable to properly implement them in the first place especially when it seemed that they read fine in Windows and when analyzing the serial monitor in the Arduino IDE.

Can you think of any reason why using shift registers might cause retriggering of buttons by the computer/raspberry pi? Perhaps the addition of a separate clock within the arduino running at 9600 baud to read the shift register?

Technical Details

Sketch File that Reproduces Issue

Apologies - this code is not very pretty. Hopefully this isn't related, but you should know that I was having latency issues with reading the shift registers where it wouldn't read in the first bit but instead read bits 2-8, so I never used the first bit of any shift register. After googling, others had similar issues and they claimed it to be a known bug in the shiftIn method.

#include <Joystick.h>

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD,
  14, 1,                  // Button Count, Hat Switch Count
  true, true, false,     // X and Y, but no Z Axis
  false, false, false,   // No Rx, Ry, or Rz
  false, false,          // No rudder or throttle
  false, false, false);  // No accelerator, brake, or steering

const int dataPin=14;
const int latchPin = 15;
const int clockPin = 16;
int registerContent = 0;
int bitContent = 0;

byte switchVar1 = 72;  //01001000
byte switchVar2 = 159; //10011111
byte switchVar3 = 163;  //10100011

void setup() {
  // Initialize Button Pins
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);

// Shift In Setup
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT); 
  pinMode(dataPin, INPUT);
  digitalWrite (latchPin, LOW);
  digitalWrite (clockPin, HIGH);
  Serial.begin(9600);

  // Initialize Joystick Library
  Joystick.begin();
  Joystick.setXAxisRange(125, 925);
  Joystick.setYAxisRange(190, 920);
}

// Last state of the buttons
int lastButtonState[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int currentButtonState[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

void registerButtons()
{
  if (switchVar1 & 2)
  {
    //Left Shoulder
    //Serial.println("Left Shoulder!");
    currentButtonState[0]=1;
  }else
  {
    currentButtonState[0]=0;
  }

  if (switchVar1 & 4)
  {
    //Right Shoulder
    //Serial.println("Right Shoulder!");
    currentButtonState[1]=1;
  }else{currentButtonState[1]=0;}

  if (switchVar1 & 8)
  {
    //D-Pad Up
    //Serial.println("D-Pad Up!");
    currentButtonState[2]=1;
  }else{currentButtonState[2]=0;}

  if (switchVar1 & 16)
  {
    //D-Pad Down
    //Serial.println("D-Pad Down!");
    currentButtonState[3]=1;
  }else{currentButtonState[3]=0;}

  if (switchVar1 & 32)
  {
    //D-Pad Left
    //Serial.println("D-Pad Left!");
    currentButtonState[4]=1;
  }else{currentButtonState[4]=0;}

  if (switchVar1 & 64)
  {
    // D-Pad Right
    //Serial.println("D-Pad Right!");
    currentButtonState[5]=1;
  }else{currentButtonState[5]=0;}

  if (switchVar1 & 128)
  {
    // Start
    //Serial.println("Start!");
    currentButtonState[6]=1;
  }else{currentButtonState[6]=0;}

  if (switchVar2 & 2)
  {
    // Button B
    //Serial.println("Button B Pressed!");
    currentButtonState[7]=1;
  }else{currentButtonState[7]=0;}

  if (switchVar2 & 4)
  {
    // Button X
    //Serial.println("Button X Pressed!");
    currentButtonState[8]=1;
  }else{currentButtonState[8]=0;}

  if (switchVar2 & 8)
  {
    // Button Y
    //Serial.println("Button Y Pressed!");
    currentButtonState[9]=1;
  }else{currentButtonState[9]=0;}

  if (switchVar2 & 16)
  {
    //Button 1
    //Serial.println("Button 1 Pressed!");
    currentButtonState[10]=1;
  }else{currentButtonState[10]=0;}

  if (switchVar2 & 32)
  {
    //Button 2
    //Serial.println("Button 2 Pressed!");
    currentButtonState[11]=1;
  }else{currentButtonState[11]=0;}

  if (switchVar2 & 64)
  {
    // Option 1 - Select
    //Serial.println("Option 1 - Select!");
    currentButtonState[12]=1;
  }else{currentButtonState[12]=0;}

  if (switchVar2 & 128)
  {
    //Option 2 - Escape
    //Serial.println("Option 2 - Escape!");
    currentButtonState[13]=1;
  }else{currentButtonState[13]=0;}

  if (switchVar3 & 2)
  {
    // Button A
    //Serial.println("Button A Pressed!");
    currentButtonState[14]=1;
  }else{currentButtonState[14]=0;}

  if (switchVar3 & 4)
  {
    // Option 3 - Save
    //Serial.println("Option 3 - Save!");
    currentButtonState[15]=1;
  }else{currentButtonState[15]=0;}

  if (switchVar3 & 8)
  {
    // Option 4 - Load
    //Serial.println("Option 4 - Load!");
    currentButtonState[16]=1;
  }else{currentButtonState[16]=0;}

  // Handle Z Button separately from registers
  int currentZ_Value = analogRead(A3);
  //Serial.print(currentZ_Value);
  if (currentZ_Value>1){

    currentButtonState[17]=1;
  }else{
    currentButtonState[17]=0;
  }  

}

void loop() {

  //                   Read analog pin values
  int currentAnalogXval = analogRead(A1);
  int currentAnalogYval = analogRead(A2);

  //Serial.print(currentAnalogXval);
  //Serial.println(currentAnalogYval);  
  Joystick.setXAxis(currentAnalogXval);
  Joystick.setYAxis(currentAnalogYval);

  // Read digital pin values

  //                   Shift Register Read

  //check latch pin inputs

  //Pulse the latch pin:
  //set it to 1 to collect parallel data
  digitalWrite(latchPin,1);

  delayMicroseconds(30);   
  //set it to 0 to transmit data serially  
  digitalWrite(latchPin,0);

  //store shift register data (3 bytes)  
  switchVar1 = shiftIn(dataPin, clockPin, MSBFIRST);
  switchVar2 = shiftIn(dataPin, clockPin, MSBFIRST);
  switchVar3 = shiftIn(dataPin, clockPin, MSBFIRST);

  //Serial.println("Shifting in:");
  //Serial.println(switchVar1,BIN);
  //Serial.println(switchVar2,BIN);
  //Serial.println(switchVar3,BIN);

  registerButtons(); // updates currentButtonState[]

  for (int index = 0; index < 18; index++) 
  {
    int thiscurrentButtonState = currentButtonState[index];
    //Serial.print(thiscurrentButtonState);
    if (thiscurrentButtonState != lastButtonState[index])
    {
      switch (index) {
        case 0: // Left Shoulder
          Serial.println("Left Shoulder state change");
          Joystick.setButton(0, thiscurrentButtonState);
          break;
        case 1: // Right Shoulder
          Joystick.setButton(1, thiscurrentButtonState);
          break;
        case 2: // UP
          if (thiscurrentButtonState == 1) {
            Joystick.setHatSwitch(0, 0);
          } else {
            Joystick.setHatSwitch(0,-1);
          }
          break;
        case 3: // DOWN
          if (thiscurrentButtonState == 1) {
            Joystick.setHatSwitch(0, 180);
          } else {
            Joystick.setHatSwitch(0,-1);
          }
          break;

        case 4: // LEFT
          if (thiscurrentButtonState == 1) {
            Joystick.setHatSwitch(0, 270);
          } else {
            Joystick.setHatSwitch(0,-1);
          }
          break;
        case 5: // RIGHT
          if (thiscurrentButtonState == 1) {
            Joystick.setHatSwitch(0, 90);
          } else {
            Joystick.setHatSwitch(0,-1);
          }
          break;
        case 6: // Start
          Joystick.setButton(2, thiscurrentButtonState);
          break;
        case 7: // Button: B
          Joystick.setButton(3, thiscurrentButtonState);
          break;
        case 8: // Button: X
          Joystick.setButton(4, thiscurrentButtonState);
          break;
        case 9: // Button: Y
          Joystick.setButton(5, thiscurrentButtonState);
          break;
        case 10: // Button: 1
          Joystick.setButton(6, thiscurrentButtonState);
          break;
        case 11: // Button: 2
          Joystick.setButton(7, thiscurrentButtonState);
          break;
        case 12: // Option 1 - Select
          Joystick.setButton(8, thiscurrentButtonState);
          break;
        case 13: // Option 2 - Escape
          Joystick.setButton(9, thiscurrentButtonState);
          break;
        case 14: // Button: A
          Joystick.setButton(10, thiscurrentButtonState);
          break;
        case 15: // Option 3 - Save
          Joystick.setButton(11, thiscurrentButtonState);
          break;
        case 16: // Option 4 - Load
          Joystick.setButton(12, thiscurrentButtonState);
          break;
        case 17: // Z
          //Serial.println("Z Pressed");
          Joystick.setButton(13, thiscurrentButtonState);
          break;
      }
      lastButtonState[index] = thiscurrentButtonState;

    }
  }      
  //Serial.println();

Thanks!