Button is over riding another

Hi,

I am stumped on this one, not sure what is going on. I am building a game controller with an arduino mini joystick and 5 buttons (using a 2x3 matrix). I have code that is working flawlessly for everything except the button on the mini joystick. I tried to add in the joystick button into my code and the switch is working... but it over rides one of the buttons in my matrix and I have no idea why. It might just be a simple thing I am missing, but I've had no luck. Below I have my original code (without joystick button) and the new code with the issue.

Note: the differences are minor, not sure if giving you both codes is helpful or not.

Any help would be appreciated

ORIGINAL

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

//DEFINITIONS
#define ENABLE_PULLUPS
#define NUMROTARIES 1 //replace "?" with number of rotary encoders you are using
#define NUMBUTTONS 6 //replace "?"with number of buttong you are using
#define NUMROWS 3 //replace "?" with number of rows you have
#define NUMCOLS 2 //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}
  
};

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 wil press 
//do NOT exceed 31 for the final button number
rotariesdef rotaries[NUMROTARIES] {
  {0,1,22,23,0}, //rotary 1

};

#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

//BUTTON MATRIX PART 2
byte rowPins[NUMROWS] = {A0,A1,A2}; //change "?" to the pins the rows of your button matrix are connected to
byte colPins[NUMCOLS] = {2,3}; //change "?" to the pins the rows of your button matrix are connected to

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

//JOYSTICK SETTINGS
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
  JOYSTICK_TYPE_JOYSTICK,
  32, //number of buttons
  0, //number of hat switches
  //Set as many axis to "true" as you have potentiometers for
  true, // y axis
  true, // 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 = 20;
 
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 yAxis_ = 0;
int xAxis_ = 0;  

               
//POTENTIOMETERS  PART 2
//Which pins are your potentiometers connected to?
int potentiometerPin1 = 10; //Change "?" to the pin your potentiometer is connected to
int potentiometerPin2 = 9;
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 the details above for each potentiometer you are using
void CheckAllPotentiometers(){
                           
  //potentiometer 1
  currentOutputLevel = getAverageOutput(potentiometerPin1);
  yAxis_ = map(currentOutputLevel,1023,0,0,255);
  Joystick.setYAxis(yAxis_); 

  //potentiometer 2
  currentOutputLevel = getAverageOutput(potentiometerPin2);
  xAxis_ = map(currentOutputLevel,0,1023,0,255);
  Joystick.setXAxis(xAxis_);


}

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");
}

ISSUE CODE

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

//DEFINITIONS
#define ENABLE_PULLUPS
#define NUMROTARIES 1 //replace "?" with number of rotary encoders you are using
#define NUMBUTTONS 6 //replace "?"with number of buttong you are using
#define NUMROWS 3 //replace "?" with number of rows you have
#define NUMCOLS 2 //replace "?" with number of columns you have 
int buttonPin = 14;

//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}
  
};

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 wil press 
//do NOT exceed 31 for the final button number
rotariesdef rotaries[NUMROTARIES] {
  {0,1,22,23,0}, //rotary 1

};

#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

//BUTTON MATRIX PART 2
byte rowPins[NUMROWS] = {A0,A1,A2}; //change "?" to the pins the rows of your button matrix are connected to
byte colPins[NUMCOLS] = {2,3}; //change "?" to the pins the rows of your button matrix are connected to

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

//JOYSTICK SETTINGS
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,
  JOYSTICK_TYPE_JOYSTICK,
  32, //number of buttons
  0, //number of hat switches
  //Set as many axis to "true" as you have potentiometers for
  true, // y axis
  true, // 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 = 20;
 
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 yAxis_ = 0;
int xAxis_ = 0;  

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


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

    // Initialize the button pin as an input with a pull-up resistor
  pinMode(buttonPin, INPUT_PULLUP);
  
  // Initialize the joystick object with the appropriate number of buttons
  Joystick.begin(1);
  
}

void loop() {

  CheckAllEncoders();
  CheckAllButtons();
  CheckAllPotentiometers();

    // Read the button state
  int buttonState = digitalRead(buttonPin);

  // If the button is pressed, set the joystick button to be pressed
  if (buttonState == LOW) {
    Joystick.setButton(0, true);
  } else {
    Joystick.setButton(0, false);
  }


  // Send the updated joystick state to the computer
  Joystick.sendState();

  // Wait a short delay before repeating the loop
  delay(10);
}
 


//POTENTIOMETERS PART 3
//change the details to match the details above for each potentiometer you are using
void CheckAllPotentiometers(){
                           
  //potentiometer 1
  currentOutputLevel = getAverageOutput(potentiometerPin1);
  yAxis_ = map(currentOutputLevel,1023,0,0,255);
  Joystick.setYAxis(yAxis_); 

  //potentiometer 2
  currentOutputLevel = getAverageOutput(potentiometerPin2);
  xAxis_ = map(currentOutputLevel,0,1023,0,255);
  Joystick.setXAxis(xAxis_);


}

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");
}

It would help a lot if You posted a link to the datasheet for the joystick. Is GND common for the analog parts and the switch button?
Schematics.... Pen and paper usually works well.

My fault, I thought saying it was a arduino mini joystick would be enough, here is a picture of the pinout

image

Thanks. You can not connect that switch to the matrix. Read it separately!
If there's not matrix button pressed, read the switch input, or the opposite, read the switch first and if no switch, check the matrix.

I could have been but all helpers have not been playing with it....

I know the joystick button cannot be in the matrix and the way that it is coded now, it isn't

That makes sense, I did not think the code would have to specifically say that. I'll try that out in the loop.

For an application I made a "read button function". It checks, and reads Serial input and 2 buttons, and return the an enum naming the different "buttons".
As there is never any serial at the same time as the button I have no priority issues.
Select the order of checking the matrix and the switch that suits Your needs.
"I'll try that out in the loop." Please do. I didn't check Your code.

I just got home and will be trying the loop idea, but I am not sure you understand the issue read back through your post.

The joystick button is not registering as a different keybind as one of the buttons in the matrix. Even if I add what you are talking about, I do not think that fixes the problem, just adds an unneeded step since 2 buttons will never be pressed the same time.

Fine. I'll take a look at the code tomorrow. Now time for bed, flying a drone in the airport close airspace tomorrow.

I don't see any obvious reason. Use Serial.print of key variables, Serial.print("Executing this");
Serial.print("Executing that");

and serial monitor. That will bring the question closer to the mistake.

I am basically do that but using the "set up USB game controller" app on my PC. Both the joystick button and one of the matrix buttons registers under button "1"

image

Please do better than that. The concept suggested was a great help during the entire life at work, often thrown in to large, new, unknown systems.

That is because when you check your button, you call joystick.setButton(0, ...) which is the first button. Since your have 6 buttons already (0..5), you should be using an index of 6 for this button.

Also, you call joystick.begin() twice in setup(). once with default parameters, once with 1. I would remove the second call.

I see blocking code in the form of "read every analog input" with delays salted in for some reason likely X-Y.... and a button transition not detected is no surprise at all though --- you could nail down execution where the button press is supposed to happen and -always- catch that but it becomes something to avoid.

Go down the millis timing path using non-blocking code and you will get better results. A delay(1) wastes 16000 clock cycles to watch the time while code execution goes nowhere but to watch the time. That is one place with room to make better... usually more room than the sketch actually uses.

The first lesson of non-blocking code is timing but Nick is very clear about why, to not block execution.

The simple sense of timing, and example of cooking to explain coding.

But what if you want to blink the two LEDs at different rates? Like, once a second for LED 1 and twice a second for LED 2?

This is where the delay function doesn't really help.

Let's look at an analogy. Say you want to cook breakfast. You need to cook:

  • Coffee - takes 1 minute

  • Bacon - takes 2 minutes

  • Eggs - takes 3 minutes

Now a seasoned cook would NOT do this:

  • Put coffee on. Stare at watch until 1 minute has elapsed. Pour coffee.

  • Cook bacon. Stare at watch until 2 minutes have elapsed. Serve bacon.

  • Fry eggs. Stare at watch until 3 minutes have elapsed. Serve eggs.

The flaw in this is that whichever way you do it, something is going to be cooked too early (and get cold).

In computer terminology this is blocking . That is, you don't do anything else until the one task at hand is over.

What you are likely to do is this:

  • Start frying eggs. Look at watch and note the time.

  • Glance at watch from time to time. When one minute is up then ...

  • Start cooking bacon. Look at watch and note the time.

  • Glance at watch from time to time. When another minute is up then ...

  • Put coffee on. Look at watch and note the time.

  • When 3 minutes are up, everything is cooked. Serve it all up.

In computer terminology this is non-blocking . That is, keep doing other things while you wait for time to be up.

So I changed one thing in the code, it is better but the matrix button does not register at all. So now I just just to figure out how to assign the joystick button to "6".

I commented out the second Joystick.begin and changed () to (1) for the first one.

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

    // Initialize the button pin as an input with a pull-up resistor
  pinMode(buttonPin, INPUT_PULLUP);
  
  // Initialize the joystick object with the appropriate number of buttons
  //Joystick.begin(1);

found it... I changed my matrix to the following. Everything works as it should. Thanks for pointing this out!

//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] = {
  {1,2},
  {3,4},
  {5,6}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.