Arduino Lenardo button box increase no of button

Hi,

I am working on some button boxes using the Arduino Lenardo but the joystick/keypad libraries I have seem to have a hard limit of 32 button only.

I am working on a button box were I need 42 buttons , is this possible?
If it is how?

Also just out of curiosity and a nice to have, I have an Arduino mega 2560 with the 16U2 chip on it, is it possible to use that as well or it does not support HID?

Thanks in advance

Welcome to the forum

You started a topic in the Uncategorised category of the forum when its description explicitly tells you not to

Your topic has been moved to a relevant category. Please be careful in future when deciding where to start new topics

Please post schematics. Hand drawn and a photo is usually fine.

Split it to a maximum of 32 joystick buttons and additional keypad buttons.
When you run out of discrete input pins on your Leonardo, use I2C port expanders for more inputs (MCP23017, MCP23008, PCF8574, SX1509...)

Thanks for that.
The 32 I can do, bit you got me on the I2C port .
Can you please elaborate a bit on that?
I will do some research on that as well, thanks

Which libraries are you using? The Leonardo has 23 digital pins (including 3 from the ICSP header), and the common Keypad library can handle 10 rows without modification, which would allow 130 buttons in a 10 x 13 grid.

If using the library that includes this header file

https://github.com/MHeironimus/ArduinoJoystickLibrary/blob/master/src/Joystick.h

at line 46 you can increase the number of buttons supported.

Hi,
I ama using the library by Matthew Heironimus.
Downloaded form Github

I have modified line 46 - and increased that to 50

#define JOYSTICK_DEFAULT_BUTTON_COUNT        50

But is still make no difference and only shows 32 buttons.
This is my code:

#include <Key.h>
#include <Keypad.h>
#include <Joystick.h>

//DEFINITIONS
#define ENABLE_PULLUPS
#define NUMROTARIES 2  //replace "?" with number of rotary encoders you are using
#define NUMBUTTONS 50  //replace "?"with number of buttong you are using
#define NUMROWS 7      //replace "?" with number of rows you have
#define NUMCOLS 3     //replace "?" with number of columns you have

//BUTTON MATRIX
//first change number of rows and columns to match your button matrix,
//then replace all "?" with numbers (starting from 0)
byte buttons[NUMROWS][NUMCOLS] = {
  { 0,1,2},
  { 3,4,5},
  { 6,7,8},
  { 9,10,11},
  { 12,13,14},
  { 15,16,17},
  { 18,19,20}
};

//BUTTON MATRIX PART 2
byte rowPins[NUMROWS] = { 4,5,6,7,8,9,16};  //change "?" to the pins the rows of your button matrix are connected to
byte colPins[NUMCOLS] = { 2,3,10 };                  //change "?" to the pins the rows of your button matrix are connected to

Keypad buttbx = Keypad(makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS);

struct rotariesdef {
  byte pin1;
  byte pin2;
  int ccwchar;
  int cwchar;
  volatile unsigned char state;
};

//ROTARY ENCODERS
//each line controls a different rotary encoder
//the first two numbers refer to the pins the encoder is connected to
//the second two are the buttons each click of the encoder will press
//do NOT exceed 31 for the final button number
rotariesdef rotaries[NUMROTARIES]{
  { A0, A1, 23, 24, 0 },  //rotary 1
  { A2, A3, 21, 22, 0 },  //rotary 2

};

#define DIR_CCW 0x10
#define DIR_CW 0x20
#define R_START 0x0

#ifdef HALF_STEP
#define R_CCW_BEGIN 0x1
#define R_CW_BEGIN 0x2
#define R_START_M 0x3
#define R_CW_BEGIN_M 0x4
#define R_CCW_BEGIN_M 0x5
const unsigned char ttable[6][4] = {
  // R_START (00)
  { R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START },
  // R_CCW_BEGIN
  { R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START },
  // R_CW_BEGIN
  { R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START },
  // R_START_M (11)
  { R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START },
  // R_CW_BEGIN_M
  { R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW },
  // R_CCW_BEGIN_M
  { R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW },
};
#else
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6

const unsigned char ttable[7][4] = {
  // R_START
  { R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START },
  // R_CW_FINAL
  { R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW },
  // R_CW_BEGIN
  { R_CW_NEXT, R_CW_BEGIN, R_START, R_START },
  // R_CW_NEXT
  { R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START },
  // R_CCW_BEGIN
  { R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START },
  // R_CCW_FINAL
  { R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW },
  // R_CCW_NEXT
  { R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START },
};
#endif


//JOYSTICK SETTINGS

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
                   JOYSTICK_TYPE_JOYSTICK,
                   50,  //number of buttons
                   0,   //number of hat switches
                   //Set as many axis to "true" as you have potentiometers for
                   false,   // y axis
                   false,   // x axis
                   false,    // z axis
                   false,    // rx axis
                   false,   // ry axis
                   false,   // rz axis
                   false,   // rudder
                   false,   // throttle
                   false,   // accelerator
                   false,   // brake
                   false);  // steering wheel

const int numReadings = 50;

int readings[numReadings];  // the readings from the analog input
int index = 0;              // the index of the current reading
int total = 0;              // the running total
int currentOutputLevel = 0;

//POTENTIOMETERS PART 1
//add all the axis' which are enabled above
int zAxis_ = 0;
int RxAxis_ = 0;


//POTENTIOMETERS  PART 2
//Which pins are your potentiometers connected to?
//int potentiometerPin1 = A4;  //Change "?" to the pin your potentiometer is connected to
//int potentiometerPin2 = A5;
const bool initAutoSendState = true;

void setup() {
  Joystick.begin();
  rotary_init();
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}

void loop() {

  CheckAllEncoders();
  CheckAllButtons();
  //CheckAllPotentiometers();
}

//POTENTIOMETERS PART 3
//change the details to match teh details above for each potentiometer you are using
/*
void CheckAllPotentiometers() {

  //potentiometer 1
  currentOutputLevel = getAverageOutput(potentiometerPin1);
  zAxis_ = map(currentOutputLevel, 0, 1023, 0, 255);
  Joystick.setZAxis(zAxis_);

  //potentiometer 2
  currentOutputLevel = getAverageOutput(potentiometerPin2);
  RxAxis_ = map(currentOutputLevel, 0, 1023, 0, 255);
  Joystick.setRxAxis(RxAxis_);
}
*/

int getAverageOutput(int pinToRead) {
  index = 0;
  total = 0;

  while (index < numReadings) {
    readings[index] = analogRead(pinToRead);
    total = total + readings[index];
    index = index + 1;
    //delay (1);
  }
  return total / numReadings;
}


void CheckAllButtons(void) {
  if (buttbx.getKeys()) {
    for (int i = 0; i < LIST_MAX; i++) {
      if (buttbx.key[i].stateChanged) {
        switch (buttbx.key[i].kstate) {
          case PRESSED:
          case HOLD:
            Joystick.setButton(buttbx.key[i].kchar, 1);
            break;
          case RELEASED:
          case IDLE:
            Joystick.setButton(buttbx.key[i].kchar, 0);
            break;
        }
      }
    }
  }
}


void rotary_init() {
  for (int i = 0; i < NUMROTARIES; i++) {
    pinMode(rotaries[i].pin1, INPUT);
    pinMode(rotaries[i].pin2, INPUT);
#ifdef ENABLE_PULLUPS
    digitalWrite(rotaries[i].pin1, HIGH);
    digitalWrite(rotaries[i].pin2, HIGH);
#endif
  }
}


unsigned char rotary_process(int _i) {
  //Serial.print("Processing rotary: ");
  //Serial.println(_i);
  unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
  rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
  return (rotaries[_i].state & 0x30);
}

void CheckAllEncoders(void) {
  Serial.println("Checking rotaries");
  for (int i = 0; i < NUMROTARIES; i++) {
    unsigned char result = rotary_process(i);
    if (result == DIR_CCW) {
      Serial.print("Rotary ");
      Serial.print(i);
      Serial.println(" <<< Going CCW");
      Joystick.setButton(rotaries[i].ccwchar, 1);
      delay(50);
      Joystick.setButton(rotaries[i].ccwchar, 0);
    };
    if (result == DIR_CW) {
      Serial.print("Rotary ");
      Serial.print(i);
      Serial.println(" >>> Going CW");
      Joystick.setButton(rotaries[i].cwchar, 1);
      delay(50);
      Joystick.setButton(rotaries[i].cwchar, 0);
    };
  }
  Serial.println("Done checking");
}

Any help is appreciated

Perhaps this is a Windoze limitation, not an Arduino limitation?

In Matthews post on github, June 30, 2020

he mentions an application that displays more buttons.

Pointy's Joystick Test

edit below

(just saw a post that the program may not work anymore, but that the VKB button tester may work)

Probably more work than that.
For 50 don't you need, at minimum, COLs * ROWs >= 50?

#define NUMBUTTONS 50  //replace "?"with number of buttong you are using
#define NUMROWS 7      //replace "?" with number of rows you have
#define NUMCOLS 3     //replace "?" with number of columns you have

thanks for the suggestion there is no function in the joystick.h that defines COLs * ROWs <=50 or any other function.
The definition of the button is this

 bool     _autoSendState;
    uint8_t  _buttonCount;  <=**
    uint8_t  _buttonValuesArraySize = 0;

Which define it as and 8 bit unsigned integer, can hold a value of up to 254.

Hence, I am suspect the limitation might be with the windows app itself.
But I have my doubts about that because when I plug in my HOTAS throttle it shows me all 50+ buttons.

SO ti could be the Arduino USB HID chip that might have the limitation.

HI thanks for the suggestion.
Tested them both and Ponty's Joystick teeter does not work - confirmed.

But the VKB Button tester seems to work, I have test with a unit that I have already built that has 10 buttons and 3 encoders ( in a 7*3 - row * col array).

So now will have to wait until I build it and see if the VKB tester will work.
Currently still in the design and layout phase.

I also ordered 2 Leo Bodnar 64 button interface
BBI-64 Button Box Interface : Leo Bodnar Electronics

and one of these
BU0836X joystick interface : Leo Bodnar Electronics

If we look to this function

we will see that maximum number of active buttons limited by LIST_MAX macro.

This macro has a value 10 in Keyboard.h

Why did you create ttable[][] twice?

  // R_START (00)
  { R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START },
  // R_CCW_BEGIN
  { R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START },
  // R_CW_BEGIN
  { R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START },
  // R_START_M (11)
  { R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START },
  // R_CW_BEGIN_M
  { R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW },
  // R_CCW_BEGIN_M
  { R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW },
};
#else
#define R_CW_FINAL 0x1
#define R_CW_BEGIN 0x2
#define R_CW_NEXT 0x3
#define R_CCW_BEGIN 0x4
#define R_CCW_FINAL 0x5
#define R_CCW_NEXT 0x6

const unsigned char ttable[7][4] = {
  // R_START
  { R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START },
  // R_CW_FINAL
  { R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW },
  // R_CW_BEGIN
  { R_CW_NEXT, R_CW_BEGIN, R_START, R_START },
  // R_CW_NEXT
  { R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START },
  // R_CCW_BEGIN
  { R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START },
  // R_CCW_FINAL
  { R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW },
  // R_CCW_NEXT
  { R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START },
};

You could daisy-chain cheap input shift register chips to a low-end Arduino’s SPI bus and connect as 8x many buttons as registers to read and process them all every so many ms to avoid contact bounce if you don’t mind some button latency… consider that 24 FPS (motion film speed, old TV ran 30) is over 40 ms that eyes can’t see faster then maybe 5 to 20 ms latency is okay?

You could wire this what we call pin-multiplier up not including buttons and other hardware for pretty cheap, and this is just the simplest direct approach.

With no extra chips at all you could wire IO pins to a matrix with a diode and button at every junction. 7x8=56 buttons for 15 pins.

With 2 input shift registers, 1 reading rows, 1 columns, an 8x8 can be achieved, same latency applies – it’s to keep contact bouncing for 2+ ms to not show up as a string of button events.

If you’re willing to learn, a button box is well in beginner-with-help reach.

I respect Leo from my days on the Sturmovik forum. He sold boards with 12-bit ADC and digital event counters for use in diy game controllers. The guys making frame-mounted joysticks out of Volvo universal joints used his boards and ruled smooth.

At some future time you may get bare controllers to program and fit into your own devices.

HID is easier to do with Arduino Leonardo or Micro or maybe the Due, or a number of compatibles.

Adafruit's tiny 32U4 board for $9.95

  • 23 GPIO total - 6 analog in, 1x SPI port, 1x I2C port, 1x Hardware Serial port and 10 more GPIO, 4 of which have PWM

Only 10 digital IO pins is a bit cramped, but it doesn’t include the buses and serial pins.

You can get an Uno R3 or Mega2560 speak HID but it involves board surgery and can’t revert to CDC in software. The boards above can switch USB modes in software, worth the spend.

Hi, Thanks.

I looked at that and increase the LIST_MAX and the MAPSIZE (as per below) compile and uploaded with no issues, but made no difference to the button count and still only shows 32.

#define LIST_MAX 50		// Max number of keys on the active list.
#define MAPSIZE 20		// MAPSIZE is the number of rows (times 16 columns)

Looked at some other options and definitions but no change.

HI Thanks.

That create ttable repeat was from the original code I found online, I had commented out on for the table and change the array to a 10*10, compiled and loaded without any issues, but still limited to 32 button.

This sounds like a good idea so if you have any schematics or board suggestions and code that I could use as a base that would be great, I have never played with pin multipliers etc before so a great learning curve.

noiasca did suggest on the below boards could go the trick, after looking at them I kind have a preference for either MCP23017 or SX1509, but am not sure on how to program them, and would like to learn.

As to your other suggestion about the surgery, I do have about 5 Mega2560 which i originally got for this purpose but discover they did not have the HID chip so left them alone, any suggestion the chip to use and guidelines for the surgery are welcomed.
I to experience with elections, but have not practice in very long time.

Thank you all for your help in advance. :slight_smile:

it depends what you need. MCP23017 is a simple port expander, when you have separate switches - go for it.

The SX1509 can debounce momentary switches and you can use it optional for a 8x8 button matrix for up to 64 momentary buttons.

Sparkfun has a really nice guide for the SX1509:

Hi,

I like to thank all for helping out on this, and I have an update.
I had just received 3 Leo Bodnar boards, one with just 32 buttons and 5 rotary pots, and 2 with 64 buttons.
To be honest I do like them both for the simplicity that it make in putting things together - no programing required. :slight_smile:
Nice boards but pricy :grinning_face:

Now when I tested them both with the MS USB Game Controller, they were both limited to 32 buttons.

But when I tested the 64 button one with the Downloads – VKB North America it actually went through ALL of the 64 buttons, and showed if they were pressed or not.
OK it is a little outdated as in the interface and warning pops up when you try to run it, but after you accept the and allow to run it works great.

So, I come to the conclusion that neither the joystick.h nor keypad.h need any modifications, the changes are nice to have but only set the defaults if left blank.
Hence, the Arduino Leonardo board should be able to handle as keypad matrix of 10 x10 = 100 switches or a 7 x 7 matrix 49 switches and 3 encoders or rotary pots using the 6 analog ports.

I will have built a mock switch with the 7x7 matrix and upload to the Arduino Leonardo used the VKB button monitor, and it showed me all the 49 switches are working.

I would like to thank all the helped with this and hope others find this useful.