UnoJoy help, multiple buttons recognized with single press

Hello. I am having an issue with my Arduino registering multiple inputs from a single button press. I am trying to setup a button box for my sim racing rig. Now as I have since discovered after completing my project build that the Mega boards do not support HID from factory and require a work around. I have managed to get this issue sorted and can set it up to be read as a gamepad.

That being said, I am now having an issue with all the buttons/switches/encoders, that one press from any of them reads as multiple inputs on a gamepad tester. This is very strange considering all the buttons/switches/encoders work perfect inside Ardunio IDE and print in the serial monitor one at a time, and only one Pin.

I am wondering if this has something to do with the "UnoJoy fix", something I did wrong, forgot to do, compatability issue? I have no clue at this point, hence why I am here asking for help.

Here is the full sketch for my project:

#include <Encoder.h>
#include <BfButton.h>
#include <BfButtonManager.h>

byte clockpin[] = { 40, 44, 48, 52};                                   // "clock" pin array
byte datapin[] = { 38, 42, 46, 50};                                    // "data" pin array
byte pushpin[] = { 39, 43, 47, 51};                                    // "push" pin array
const byte arraysize = sizeof(clockpin) / sizeof(clockpin[0]);                // number of encoders
byte pushstate[arraysize];                                                    // state of each button
byte clockstate[arraysize], lastclockstate[arraysize], datastate[arraysize];  // states
byte encocount[arraysize];                                                    // encoders count 0 to 255
byte pushcount[arraysize];                                                    // pushbuttons count 0 to 255
//PUSH BUTTONS
int numbuts = 32;
int bts[32]; //
boolean btgs[32]; //sends one message, not continuous data


unsigned long pushtimer[arraysize], timeout = 350;  // adjust timeout for lowest value

#define BAUD 9600


void setup() {
  Serial.begin(BAUD);                              // initialize Serial Monitor
  for (byte i = 0; i < arraysize; i++) {           // configure DIO pins
    pinMode(clockpin[i], INPUT_PULLUP);            //
    pinMode(datapin[i], INPUT_PULLUP);             //
    pinMode(pushpin[i], INPUT_PULLUP);             //
    lastclockstate[i] = digitalRead(clockpin[i]);  // first state reading
    //pinMode(bt1, INPUT_PULLUP); //pushbutton #1 pin 6, single button code
    //bt1g = false; //single button code
    for(int i=0; i<numbuts; i++) bts[i] = i+6;
    for(int i=0; i<numbuts; i++) btgs[i] = false;
    for(int i=0; i<numbuts; i++) pinMode(bts[i], INPUT_PULLUP);
  }
}

//PUSH BUTTONS
int buttonStatus = 0;

void loop() {
  for (byte i = 0; i < arraysize; i++) {

    // pushbutton ENCODER
    pushstate[i] = digitalRead(pushpin[i]);                              // read each pushbotton
    if ((pushstate[i] == LOW) && (millis() - pushtimer[i] > timeout)) {  // debounce pushbutton
      pushtimer[i] = millis();                                           // set new timer
      ++pushcount[i];                                                    // increment pushbutton count
      showstates("Button : ", i, " | Presscount: ", pushcount[i]);       // send data to formatter
    }
    // clockstate
    clockstate[i] = digitalRead(clockpin[i]);  // read clock state for each encoder
    if (clockstate[i] != lastclockstate[i]) {  // if clock state changed
      lastclockstate[i] = clockstate[i];       // store state

      // datastate
      datastate[i] = digitalRead(datapin[i]);                         // datastate for each encoder
      if (datastate[i] == 1 && clockstate[i] == 0) {                  // states for CC rotation
        encocount[i]++;                                               // increment counter
        showstates("Encoder: ", i, " | CW | Count: ", encocount[i]);  // send data to formatter
      }
      if (datastate[i] == 0 && clockstate[i] == 0) {                  // states for CW rotation
        encocount[i]--;                                               // decrement counter
        showstates("Encoder: ", i, " | CC | Count: ", encocount[i]);  // send data to formatter
      }
    }
    //PUSH BUTTONS
    for(int i=0; i<numbuts; i++){
      if(!btgs[i]) {
        if(digitalRead(bts[i])==LOW){
          Serial.print("bt" + String(i) + ":");
          Serial.println(1);
          btgs[i] = true;
        }
      }
      else {
        if(digitalRead(bts[i])==HIGH) {
          Serial.print("bt" + String(i) + ":");
          Serial.println(0);
          btgs[i] = false;
        }
      }
    }
    delay(5);
  }
}

void showstates(char *name, int number, char *action, int count) {  // Serial Monitor formatting
  Serial.print(name);
  Serial.print(number);
  Serial.print(action);
  Serial.print(count);
  Serial.println();
}

Here is a general example from Wowki, it's missing a few switches as they didn't have what I was looking for. The buttons on my project use pins 9,10,11,12,13 as well as 22-37

Please let me know if I am missing any vital info here. Thanks!

  • In your setup() , you have a for loop that initializes 32 buttons inside a loop that iterates over your 4 encoders.
    Similarly, in loop() , you are checking all 32 buttons inside the encoder loop.

  • You have #include <Encoder.h> and #include <BfButton.h> , but you aren't actually using them.

  • You are using the variable i for both the outer loop (encoders) and the inner loop (buttons).

Thanks for the reply. Seems like a simple enough fix. I've removed the unecessary libraries and changed the variable for the buttons. All still works beautifully in the IDE. Now regarding your first point about the for loop inside the setup() and loop() sections, what should I change this to? I am very new to Arduino progamming and have sourced most of the code for my project. I understand some of it but definitely not all.

You know this detects BOTH edges: rising/falling.




byte encocount[arraysize];
. . .
encocount[i]--; if this was 0, afterwards, it’s 255 i.e. byte
Is that desirable?

I guess as far a number value goes, I don't really care what it is. It just needs to read either CW or CCW. I've seen a few examples for encoders, all using various values but didn't notice a functional difference when I changed them.

I literally don't know what this means haha. I appologize for the lack of knowledge on the subject, definitely bit off more than I could handle with something like this. But I'm cheap, and like to do things myself when possible.

I tried moving the for loop for the Buttons to outside of the encoder section, but that didn't help. Everything still works inside the IDE, but not on a gamepad tester.

void setup() {
  Serial.begin(BAUD);                              // initialize Serial Monitor
  for (byte i = 0; i < arraysize; i++) {           // configure DIO pins
    pinMode(clockpin[i], INPUT_PULLUP);            //
    pinMode(datapin[i], INPUT_PULLUP);             //
    pinMode(pushpin[i], INPUT_PULLUP);             //
    lastclockstate[i] = digitalRead(clockpin[i]);  // first state reading
    //pinMode(bt1, INPUT_PULLUP); //pushbutton #1 pin 6, single button code
    //bt1g = false; //single button code
  
  }
  for(int b=0; b<numbuts; b++) bts[b] = b+6;
  for(int b=0; b<numbuts; b++) btgs[b] = false;
  for(int b=0; b<numbuts; b++) pinMode(bts[b], INPUT_PULLUP);
}

//PUSH BUTTONS
int buttonStatus = 0;

void loop() {
  for (byte i = 0; i < arraysize; i++) {

    // pushbutton ENCODER
    pushstate[i] = digitalRead(pushpin[i]);                              // read each pushbotton
    if ((pushstate[i] == LOW) && (millis() - pushtimer[i] > timeout)) {  // debounce pushbutton
      pushtimer[i] = millis();                                           // set new timer
      ++pushcount[i];                                                    // increment pushbutton count
      showstates("Button : ", i, " | Presscount: ", pushcount[i]);       // send data to formatter
    }
    // clockstate
    clockstate[i] = digitalRead(clockpin[i]);  // read clock state for each encoder
    if (clockstate[i] != lastclockstate[i]) {  // if clock state changed
      lastclockstate[i] = clockstate[i];       // store state

      // datastate
      datastate[i] = digitalRead(datapin[i]);                         // datastate for each encoder
      if (datastate[i] == 1 && clockstate[i] == 0) {                  // states for CC rotation
        encocount[i]++;                                               // increment counter
        showstates("Encoder: ", i, " | CW | Count: ", encocount[i]);  // send data to formatter
      }
      if (datastate[i] == 0 && clockstate[i] == 0) {                  // states for CW rotation
        encocount[i]--;                                               // decrement counter
        showstates("Encoder: ", i, " | CC | Count: ", encocount[i]);  // send data to formatter
      }
    }
  }
    //PUSH BUTTONS
    for(int b=0; b<numbuts; b++){
      if(!btgs[b]) {
        if(digitalRead(bts[b])==LOW){
          Serial.print("bt" + String(b) + ":");
          Serial.println(1);
          btgs[b] = true;
        }
      }
      else {
        if(digitalRead(bts[b])==HIGH) {
          Serial.print("bt" + String(b) + ":");
          Serial.println(0);
          btgs[b] = false;
        }
      }
    }
    delay(5);
}

BAUD is not defined.
arraysize is not defined.
... and many more errors.

oh.. Are these all errors that would cause my mulitple inputs detected issue?

My full current code:

#include <BfButtonManager.h>

byte clockpin[] = { 40, 44, 48, 52};                                   // "clock" pin array
byte datapin[] = { 38, 42, 46, 50};                                    // "data" pin array
byte pushpin[] = { 39, 43, 47, 51};                                    // "push" pin array
const byte arraysize = sizeof(clockpin) / sizeof(clockpin[0]);                // number of encoders
byte pushstate[arraysize];                                                    // state of each button
byte clockstate[arraysize], lastclockstate[arraysize], datastate[arraysize];  // states
byte encocount[arraysize];                                                    // encoders count 0 to 255
byte pushcount[arraysize];                                                    // pushbuttons count 0 to 255
//PUSH BUTTONS
int numbuts = 32;
int bts[32]; //
boolean btgs[32]; //sends one message, not continuous data


unsigned long pushtimer[arraysize], timeout = 350;  // adjust timeout for lowest value

#define BAUD 9600


void setup() {
  Serial.begin(BAUD);                              // initialize Serial Monitor
  for (byte i = 0; i < arraysize; i++) {           // configure DIO pins
    pinMode(clockpin[i], INPUT_PULLUP);            //
    pinMode(datapin[i], INPUT_PULLUP);             //
    pinMode(pushpin[i], INPUT_PULLUP);             //
    lastclockstate[i] = digitalRead(clockpin[i]);  // first state reading
    //pinMode(bt1, INPUT_PULLUP); //pushbutton #1 pin 6, single button code
    //bt1g = false; //single button code
    for(int b=0; b<numbuts; b++) bts[b] = b+6;
    for(int b=0; b<numbuts; b++) btgs[b] = false;
    for(int b=0; b<numbuts; b++) pinMode(bts[b], INPUT_PULLUP);
  }
}

//PUSH BUTTONS
int buttonStatus = 0;

void loop() {
  for (byte i = 0; i < arraysize; i++) {

    // pushbutton ENCODER
    pushstate[i] = digitalRead(pushpin[i]);                              // read each pushbotton
    if ((pushstate[i] == LOW) && (millis() - pushtimer[i] > timeout)) {  // debounce pushbutton
      pushtimer[i] = millis();                                           // set new timer
      ++pushcount[i];                                                    // increment pushbutton count
      showstates("Button : ", i, " | Presscount: ", pushcount[i]);       // send data to formatter
    }
    // clockstate
    clockstate[i] = digitalRead(clockpin[i]);  // read clock state for each encoder
    if (clockstate[i] != lastclockstate[i]) {  // if clock state changed
      lastclockstate[i] = clockstate[i];       // store state

      // datastate
      datastate[i] = digitalRead(datapin[i]);                         // datastate for each encoder
      if (datastate[i] == 1 && clockstate[i] == 0) {                  // states for CC rotation
        encocount[i]++;                                               // increment counter
        showstates("Encoder: ", i, " | CW | Count: ", encocount[i]);  // send data to formatter
      }
      if (datastate[i] == 0 && clockstate[i] == 0) {                  // states for CW rotation
        encocount[i]--;                                               // decrement counter
        showstates("Encoder: ", i, " | CC | Count: ", encocount[i]);  // send data to formatter
      }
    }
    //PUSH BUTTONS
    for(int b=0; b<numbuts; b++){
      if(!btgs[b]) {
        if(digitalRead(bts[b])==LOW){
          Serial.print("bt" + String(b) + ":");
          Serial.println(1);
          btgs[b] = true;
        }
      }
      else {
        if(digitalRead(bts[b])==HIGH) {
          Serial.print("bt" + String(b) + ":");
          Serial.println(0);
          btgs[b] = false;
        }
      }
    }
    delay(5);
  }
}

void showstates(char *name, int number, char *action, int count) {  // Serial Monitor formatting
  Serial.print(name);
  Serial.print(number);
  Serial.print(action);
  Serial.print(count);
  Serial.println();
}

Your code in post #8 compiles, but the buttons are registering as "pressed" even when not pressed or connected.

How are your buttons wired? The Wokwi shows them as shorting geound to pin.

One side of all the buttons are grounded in groups, I am using 3 of the ground Pins on the board, and the other goes to its own unique pin.

Yes, but your button is wired as a straight short to the pin. Use diagonal legs on the button to ensure a normally-open button.

So the buttons I used on my actual project are only 2 pin buttons. Wowki doesn't have any 2 pin buttons

Use diagonal legs on the button.

Are these the same encoders you declared bad last week? Need help using multiple 5 pin rotary encoders

haha, yes. So the encoders and push buttons all work in the IDE, but because I didn't know the mega board wasn't a good choice for a gamepad device, I've been trying to use the UnoJoy, specifically the MegaJoy portion of that download, to get everything working as a gamepad

Let us know how the "diagonal legs" in Wokwi goes.

So in Wowki, using the diagonal pins on the button works in that demo. However, my issue is that on a gamepad tester site or the built in windows controller setup, one button press, whether it's a push button or the buttons from the encoders, the tester shows multiple buttons are being read. Also they are random each time, sometimes it reads 5 buttons, sometimes it's 8 but different buttons

Remove all the encoder code from your sketch and remove the encoders from your board.

Connect only your buttons to yiur board. Write code to read and debounce your buttons ONLY when an action occurs, that is to say, only print PRESSED X or RELEASED X one time. You need to have full control of your buttons.

Post that sketch here, and verify it works on your hardware. We will add the rest, later.

ok, I'll start working on it

Have you tested your gamepad test site with a gamepad?

Yeah, tried a couple xbox controllers I had, all works as expected