Leonardo Gamepad Analog as Digital inputs?

hello all, iv been using the leonardo as a gamepad controller for an arcade cabinet and its working great on all the digital inputs, pins 2 to 13 which gives me 12 digital inputs, 4 for the D pad and 8 for fire buttons.
im using the joystick library obtained form here:

my working code so far for the 12 inputs is:

// Gamepad that uses digital pins for D pad and 8 fire buttons
//
// The digital pins 2 - 13 are grounded when they are pressed.
// Pin 2 = UP
// Pin 3 = RIGHT
// Pin 4 = DOWN
// Pin 5 = LEFT
// Pin 6 = FIRE1
// Pin 7 = FIRE2
// Pin 8 = FIRE3
// Pin 9 = FIRE4
// Pin 10 = FIRE5
// Pin 11 = FIRE6
// Pin 12 = FIRE7
// Pin 13 = FIRE8
//
// NOTE: This sketch file is for use with Arduino Leonardo and
//       Arduino Micro only.
//
//--------------------------------------------------------------------

#include <Joystick.h>

Joystick_ Joystick(0x15, JOYSTICK_TYPE_GAMEPAD,
  8, 0,                  // 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

void setup() {
  // Initialize Button Pins
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);

  // Initialize Joystick Library
  Joystick.begin();
  Joystick.setXAxisRange(-1, 1);
  Joystick.setYAxisRange(-1, 1);
}

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

void loop() {

  // Read pin values
  for (int index = 0; index < 12; index++)
  {
    int currentButtonState = !digitalRead(index + 2);
    if (currentButtonState != lastButtonState[index])
    {
      switch (index) {
        case 0: // UP
          if (currentButtonState == 1) {
            Joystick.setYAxis(-1);
          } else {
            Joystick.setYAxis(0);
          }
          break;
        case 1: // RIGHT
          if (currentButtonState == 1) {
            Joystick.setXAxis(1);
          } else {
            Joystick.setXAxis(0);
          }
          break;
        case 2: // DOWN
          if (currentButtonState == 1) {
            Joystick.setYAxis(1);
          } else {
            Joystick.setYAxis(0);
          }
          break;
        case 3: // LEFT
          if (currentButtonState == 1) {
            Joystick.setXAxis(-1);
          } else {
            Joystick.setXAxis(0);
          }
          break;
        case 4: // FIRE1
          Joystick.setButton(0, currentButtonState);
          break;
        case 5: // FIRE2
          Joystick.setButton(1, currentButtonState);
          break;
        case 6: // FIRE3
          Joystick.setButton(2, currentButtonState);
          break;
        case 7: // FIRE4
          Joystick.setButton(3, currentButtonState);
          break;
        case 8: // FIRE5
          Joystick.setButton(4, currentButtonState);
          break;
        case 9: // FIRE6
          Joystick.setButton(5, currentButtonState);
          break;
        case 10: // FIRE7
          Joystick.setButton(6, currentButtonState);
          break;
        case 11: // FIRE8
          Joystick.setButton(7, currentButtonState);
          break;
        
      }
      lastButtonState[index] = currentButtonState;
    }
  }

  delay(10);
}

above works great but i was wondeirng if i could use 4 of the analog pins A0-A3 as additional digital inputs. so i tried this code below but im not getting any success:

// Gamepad that has D pad and 8 fire buttons on digital pins and 4 fire buttons on analog pins
// 
//
// The digital pins 2 - 13 are grounded when they are pressed.
// Pin 2 = UP
// Pin 3 = RIGHT
// Pin 4 = DOWN
// Pin 5 = LEFT
// Pin 6 = FIRE1
// Pin 7 = FIRE2
// Pin 8 = FIRE3
// Pin 9 = FIRE4
// Pin 10 = FIRE5
// Pin 11 = FIRE6
// Pin 12 = FIRE7
// Pin 13 = FIRE8
// Pin A0 = FIRE9
// Pin A1 = FIRE10
// Pin A2 = FIRE11
// Pin A3 = FIRE12
//
//
//
//
// NOTE: This sketch file is for use with Arduino Leonardo and
//       Arduino Micro only.
//
//--------------------------------------------------------------------

#include <Joystick.h>

Joystick_ Joystick(0x15, JOYSTICK_TYPE_GAMEPAD,
  12, 0,                  // 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

void setup() {
  // Initialize Button Pins
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);
  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
  pinMode(A3, INPUT_PULLUP);

  // Initialize Joystick Library
  Joystick.begin();
  Joystick.setXAxisRange(-1, 1);
  Joystick.setYAxisRange(-1, 1);
}

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

void loop() {

  // Read pin values
  for (int index = 0; index < 16; index++)
  {
    int currentButtonState = !digitalRead(index + 2);
    if (currentButtonState != lastButtonState[index])
    {
      switch (index) {
        case 0: // UP
          if (currentButtonState == 1) {
            Joystick.setYAxis(-1);
          } else {
            Joystick.setYAxis(0);
          }
          break;
        case 1: // RIGHT
          if (currentButtonState == 1) {
            Joystick.setXAxis(1);
          } else {
            Joystick.setXAxis(0);
          }
          break;
        case 2: // DOWN
          if (currentButtonState == 1) {
            Joystick.setYAxis(1);
          } else {
            Joystick.setYAxis(0);
          }
          break;
        case 3: // LEFT
          if (currentButtonState == 1) {
            Joystick.setXAxis(-1);
          } else {
            Joystick.setXAxis(0);
          }
          break;
        case 4: // FIRE1
          Joystick.setButton(0, currentButtonState);
          break;
        case 5: // FIRE2
          Joystick.setButton(1, currentButtonState);
          break;
        case 6: // FIRE3
          Joystick.setButton(2, currentButtonState);
          break;
        case 7: // FIRE4
          Joystick.setButton(3, currentButtonState);
          break;
        case 8: // FIRE5
          Joystick.setButton(4, currentButtonState);
          break;
        case 9: // FIRE6
          Joystick.setButton(5, currentButtonState);
          break;
        case 10: // FIRE7
          Joystick.setButton(6, currentButtonState);
          break;
        case 11: // FIRE8
          Joystick.setButton(7, currentButtonState);
          break;
        case 12: // FIRE9
          Joystick.setButton(8, currentButtonState);
          break;
        case 13: // FIRE10
          Joystick.setButton(9, currentButtonState);
          break;
        case 14: // FIRE11
          Joystick.setButton(10, currentButtonState);
          break;
        case 15: // FIRE12
          Joystick.setButton(11, currentButtonState);
          break;
        
      }
      lastButtonState[index] = currentButtonState;
    }
  }

  delay(10);
}

was hoping someone could help me with this to point me where iv gone wrong. thanks

Did the compiler output error messages (post them), or did that compile OK but the behaviour is not as intended?

In your sketch you assume that A0...A3 are digital pins 14...17. But this is not the case with Leonardo. The digital pins 14...17 refer to the SPI pins. A0..A4 are the digital pins 18...21.

thanks for the reply. so this part of the code here:

  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
  pinMode(A3, INPUT_PULLUP);

should it be changed to:

 pinMode(18, INPUT_PULLUP);
  pinMode(19, INPUT_PULLUP);
  pinMode(20, INPUT_PULLUP);
  pinMode(21, INPUT_PULLUP);

thanks

it compiled fine no errors. just that grounding pins A0-A3 did not make the buttons light up in windows joystick control panel.

No, that's not the problem. A0 is the same as 18.
The problem is your for loop. You use the loop variable of your for loop as pinnumber ( with offset of 2 ). That doesn't work with the analog pins, because now your pinnumbers dont' increment sequentiually by one anymore ( there is a gap between your last digital pin 13 and the first analog pin (A0), which is pin 18 )

ahh i see. what would be the simplest way to correct this? is it possible to define pin mappings at the start so A0 = 14, A1=15 etc?

No, the pin mapping is fix in the core. A0 ist mapped to 18, and you cannot change this in your sketch.
You could change your offset in the loop. Something like this (untested):

  byte offset = 2;
  for (int index = 0; index < 16; index++)
  {
    if ( index == 12 ) offset = 6;
    int currentButtonState = !digitalRead(index + offset);

But I didn't look if there are more similar problems in your sketch.

Another approach could be to define an array with the pinnumbers to map the index to a pinnumber. This would be more 'failsafe' when changing or extending pins.

Could a second for loop be used for just those Analog pins?

There are always many possibilities to solve a problem :wink:

Good thing to know there is many ways to solve it. Id just be happy knowing one way lol.

Do you have some code i can use to fix this problem? Arduino looks a bit like c++ to me but i did that in school over 20 years ago lol.

This is how the pins are defined in hardware/arduino/avr/variants/leonardo/pins_arduino.h:

// Mapping of analog pins as digital I/O
// A6-A11 share with digital pins
#define PIN_A0   (18)
#define PIN_A1   (19)
#define PIN_A2   (20)
#define PIN_A3   (21)
#define PIN_A4   (22)
#define PIN_A5   (23)
#define PIN_A6   (24)
#define PIN_A7   (25)
#define PIN_A8   (26)
#define PIN_A9   (27)
#define PIN_A10  (28)
#define PIN_A11  (29)

I agree with @MicroBahner : make a const array with your pins and loop through it.

"Arduino" as a programming language does not exist, but it's a common misconception. The Arduino ecosystem uses a subset of C++ with some specific libraries.

Will using an array cause code to run slower and result in some input lag?

Array access by index is immediate, no matter how big the array. There is no difference between, say int x = var and int x = array[200]. Moreover, your array will be quite small.

How would i go about setting this up? Im a noob to all this and the 8 button setup i got working by adapting the gamepad example code that was given on the library github.

I would like to do some testing and upload sketches after making changes but im worried i may wear out the flash memory on the atmega. Anyone know how much program erase cycles its good for?

The datasheet knows :smiley:

It's 10,000 cycles (guaranteed, possibly more).

Ahh hopefully i get this working before i wear the flash out lol

Id like to adapt this code to work on a pro micro too so i guess the best way would be to get an array method setup?

Yes, this is most flexible. You can try this ( compiles without errors, but untested ):

// Gamepad that has D pad and 8 fire buttons on digital pins and 4 fire buttons on analog pins
// 
//
// The digital pins 2 - 13 are grounded when they are pressed.
// Pin 2 = UP
// Pin 3 = RIGHT
// Pin 4 = DOWN
// Pin 5 = LEFT
// Pin 6 = FIRE1
// Pin 7 = FIRE2
// Pin 8 = FIRE3
// Pin 9 = FIRE4
// Pin 10 = FIRE5
// Pin 11 = FIRE6
// Pin 12 = FIRE7
// Pin 13 = FIRE8
// Pin A0 = FIRE9
// Pin A1 = FIRE10
// Pin A2 = FIRE11
// Pin A3 = FIRE12
//
//
//
//
// NOTE: This sketch file is for use with Arduino Leonardo and
//       Arduino Micro only.
//
//--------------------------------------------------------------------

#include <Joystick.h>

// define the pins for the buttons
const byte buttonPins[] = {2,3,4,5,6,7,8,9,10,11,12,13,A0,A1,A2,A3};
const byte buttonCount = sizeof(buttonPins); // buttonPins must be of type byte

// Last state of the buttons
int lastButtonState[buttonCount] ;

Joystick_ Joystick(0x15, JOYSTICK_TYPE_GAMEPAD,
  12, 0,                  // 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

void setup() {
  // Initialize Button Pins
  for ( byte ix = 0; ix < buttonCount; ix++ ) 
  {
    pinMode(buttonPins[ix], INPUT_PULLUP);
    lastButtonState[ix] = 0;
  }
  
  // Initialize Joystick Library
  Joystick.begin();
  Joystick.setXAxisRange(-1, 1);
  Joystick.setYAxisRange(-1, 1);
}


void loop() {

  // Read pin values
  for (int index = 0; index < buttonCount; index++)
  {
    int currentButtonState = !digitalRead(buttonPins[index]);
    if (currentButtonState != lastButtonState[index])
    {
      switch (index) {
        case 0: // UP
          if (currentButtonState == 1) {
            Joystick.setYAxis(-1);
          } else {
            Joystick.setYAxis(0);
          }
          break;
        case 1: // RIGHT
          if (currentButtonState == 1) {
            Joystick.setXAxis(1);
          } else {
            Joystick.setXAxis(0);
          }
          break;
        case 2: // DOWN
          if (currentButtonState == 1) {
            Joystick.setYAxis(1);
          } else {
            Joystick.setYAxis(0);
          }
          break;
        case 3: // LEFT
          if (currentButtonState == 1) {
            Joystick.setXAxis(-1);
          } else {
            Joystick.setXAxis(0);
          }
          break;
        case 4: // FIRE1
          Joystick.setButton(0, currentButtonState);
          break;
        case 5: // FIRE2
          Joystick.setButton(1, currentButtonState);
          break;
        case 6: // FIRE3
          Joystick.setButton(2, currentButtonState);
          break;
        case 7: // FIRE4
          Joystick.setButton(3, currentButtonState);
          break;
        case 8: // FIRE5
          Joystick.setButton(4, currentButtonState);
          break;
        case 9: // FIRE6
          Joystick.setButton(5, currentButtonState);
          break;
        case 10: // FIRE7
          Joystick.setButton(6, currentButtonState);
          break;
        case 11: // FIRE8
          Joystick.setButton(7, currentButtonState);
          break;
        case 12: // FIRE9
          Joystick.setButton(8, currentButtonState);
          break;
        case 13: // FIRE10
          Joystick.setButton(9, currentButtonState);
          break;
        case 14: // FIRE11
          Joystick.setButton(10, currentButtonState);
          break;
        case 15: // FIRE12
          Joystick.setButton(11, currentButtonState);
          break;
        
      }
      lastButtonState[index] = currentButtonState;
    }
  }

  delay(10);
}
2 Likes

Thanks for that. I will get it uploaded and test it out and report back.

EDIT:

excellent! just flashed that and tested it out using jumper cable grounding 1 pin at a time and all 16 pins register and light up in windows gamepad config test. cant thank you enough.

i have a couple of questions, on leonardo boards can pins A4 and A5 also be used as digital inputs or are those strictly for analog use only?

also i see this delay(10); at the end of a lot of these joysticks/gamepad code i come across, does it add lag to the controller if its delaying it by 10ms? can it or should it operate without delay or a lower number for delay?
thanks