Joystick using key matrix

Status
Not open for further replies.

Random

Member
I found another project where someone tried to use a key matrix as joystick buttons, but he wasn't able to clear the keys after the press. I thought the case statement was a little heavy handed as well, so I went with a for loop, and haven't successfully gotten the system to recognize a keypress. I could wire these up to pins, but I've done two, which meant soldering about 150 wires with dupont connectors on the other end, and thought using a 5x8 matrix would be much, much easier this time around...

Current, non-working code. I'm new to this (despite having bought five 3.6s in the last two weeks to get this project done). Two of the boxen have been built the hard way, three to go.
Code:
  #include <Keypad.h>

  const byte ROWS = 4; //four rows
  const byte COLS = 4; //four columns

  byte rowPins[ROWS] = {0, 1, 2, 3}; //connect to the row pinouts of the keypad
  byte colPins[COLS] = {29,30,31,32}; //connect to the column pinouts of the keypad

  char keyMap[ROWS][COLS] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12},
    {13,14,15,16}
  };

  Keypad kpd = Keypad( makeKeymap(keyMap), rowPins, colPins, ROWS, COLS); 

void setup() {
    Joystick.useManualSend(true);
}

void loop() {

  for (int i=0; i<LIST_MAX; i++)
  {
    if ( kpd.key[i].stateChanged )
    {
      if ( kpd.key[i].kstate == PRESSED )
      {
          Joystick.button(i+1, 1);
      } else {
          Joystick.button(i+1, 0);
      }
    }
  }
  Joystick.send_now();
  delay(5);
}
 
Bwahaha. Got it. Actually, thanks for leaving this alone until I could take another swing; it's much more satisfying to figure it out. For my next trick...

<edit>
Realized the switch would auto-release after a moment, which is not what should happen. Edited the beefy part of the if to account for a HOLD state, and released the button on release. I'm assuming IDLE is not pressed. In any event, the button will stay on as long as it is held now.
</edit>
Working code:

Code:
  #include <Keypad.h>

  const byte ROWS = 4; //four rows
  const byte COLS = 4; //four columns

  byte rowPins[ROWS] = {0, 1, 2, 3}; //connect to the row pinouts of the keypad
  byte colPins[COLS] = {29,30,31,32}; //connect to the column pinouts of the keypad

  char keyMap[ROWS][COLS] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12},
    {13,14,15,16}
  };

  Keypad kpd = Keypad( makeKeymap(keyMap), rowPins, colPins, ROWS, COLS); 

void setup() {
    Joystick.useManualSend(true);
}

void loop() {
  if ( kpd.getKeys() )
  {
    for (int i=0; i<LIST_MAX; i++)
    {
      if ( kpd.key[i].stateChanged )
      {
        if ( kpd.key[i].kstate == PRESSED || kpd.key[i].kstate == HOLD)
        {
            Joystick.button(kpd.key[i].kchar, 1);
        } else if ( kpd.key[i].kstate == RELEASED ){
            Joystick.button(kpd.key[i].kchar, 0);
        }
        Joystick.send_now();
      }
    }
  }
  delay(5);
}
 
Last edited:
Bwahaha. Got it. Actually, thanks for leaving this alone until I could take another swing; it's much more satisfying to figure it out. For my next trick...

<edit>
Realized the switch would auto-release after a moment, which is not what should happen. Edited the beefy part of the if to account for a HOLD state, and released the button on release. I'm assuming IDLE is not pressed. In any event, the button will stay on as long as it is held now.
</edit>
Working code:

Code:
  #include <Keypad.h>

  const byte ROWS = 4; //four rows
  const byte COLS = 4; //four columns

  byte rowPins[ROWS] = {0, 1, 2, 3}; //connect to the row pinouts of the keypad
  byte colPins[COLS] = {29,30,31,32}; //connect to the column pinouts of the keypad

  char keyMap[ROWS][COLS] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12},
    {13,14,15,16}
  };

  Keypad kpd = Keypad( makeKeymap(keyMap), rowPins, colPins, ROWS, COLS); 

void setup() {
    Joystick.useManualSend(true);
}

void loop() {
  if ( kpd.getKeys() )
  {
    for (int i=0; i<LIST_MAX; i++)
    {
      if ( kpd.key[i].stateChanged )
      {
        if ( kpd.key[i].kstate == PRESSED || kpd.key[i].kstate == HOLD)
        {
            Joystick.button(kpd.key[i].kchar, 1);
        } else if ( kpd.key[i].kstate == RELEASED ){
            Joystick.button(kpd.key[i].kchar, 0);
        }
        Joystick.send_now();
      }
    }
  }
  delay(5);
}

Thank you so much for posting this! This is exactly what I was looking for and it worked flawlessly! I was previously using a keyboard matrix to map a key to each button, but that seemed to have issues with multiple buttons being pressed, or single state switches holding in a 'HIGH' state. Now all I've left to do is wire in the potentiometers and find some code for that!
 
Thank you so much for posting this! This is exactly what I was looking for and it worked flawlessly! I was previously using a keyboard matrix to map a key to each button, but that seemed to have issues with multiple buttons being pressed, or single state switches holding in a 'HIGH' state. Now all I've left to do is wire in the potentiometers and find some code for that!

I'm using potentiometers as axis, rotary encoders as switches, mode buttons to allow a switch matrix to be re-used multiple times....you name it. If there's something else you need let me know.
 
Yeah I'm actually having an issue with either Windows or the program (Warthunder) reading more than 3 of the 8 analogue axis. I can definately get one of them to work at a time but not the minimum of 6 the Teensy Joystick example has laid out. Any advice please?
 
That's going to be the game. I've gotten something insane to work if you look at the direct input, but never more than 6 in DCS. I get past it by having an obsessively ridiculous amount of inputs, with only 6 axis per controller. Once the full suite is put together there will be 5 3.6s and 2 LCs to handle all of it. That's part of the reason I use rotary encoders; there are functions you can manage with a switch, but not an axis. With a rotary encoder you turn it one way and one switch is pushed discreetly per 'click' of the encoder, you turn it the other way and a different switch is repeatedly pressed. That fakes axis control quite nicely. I sliced this out of some test code; it should be reasonably complete.

Code:
#define ENC1 1
#define ENC2 2


Encoder myEnc(ENC1, ENC2);

void setup() {
  pinMode(ENC1,INPUT_PULLUP);
  pinMode(ENC2,INPUT_PULLUP);
}

void loop() {
  long newPos = myEnc.read();
  if (newPos != position) {
	if (newPos > position) {
	  Joystick.button(1,1);
	} else {
	  Joystick.button(2,1);
	}
	position = newPos;
  } else
  {
	Joystick.button(1,0);
	Joystick.button(2,0);
  }
  delay(5);
}
 
DCS is will be my endgame. How many buttons/axis do you need for that? I've currently got my 38 custom button and 8 axis (6 working) running off the teensy, with a Thrustmaster T1600m + Hotas + Pedals and Saitek Multi Panel/Switch Panel and Radio Panels :cool:
 
DCS is will be my endgame. How many buttons/axis do you need for that? I've currently got my 38 custom button and 8 axis (6 working) running off the teensy, with a Thrustmaster T1600m + Hotas + Pedals and Saitek Multi Panel/Switch Panel and Radio Panels :cool:

How obsessive are you?

I have hundreds of switches (probably close to 300), not counting discrete switches in matrices, 40-some axis, rotary encoders, crosswind pedals, Virpil stick and base, TM throttle...did I mention it will take 5 Teensy 3.6 and two Teensy LC controllers to drive it all? Then there's the VFx panel (F-14) in progress...six Teensy 3.6s. When the F-16 comes out I should be able to map everything without any new panels.

There are multiple aircraft I fly, and the controls don't always map well from one to the other so they all have switches. In a vibrating aircraft it wouldn't work, in my office at home I don't have to look. This is still a work in progress; still have a right console, three MFDs (with LCD backing), UFC, and another panel to do. I have almost as much fun playing with this as flying, so the question then becomes which is the object and which is the solution?

There are other teensy projects in the wings once this is done, but I had to come up with a sizeable chunk of the code for all this on my own. The boards, at least for this application, aren't necessarily brimming with people eager to help, but that's just as well.

Every controller has its own name, DCS calls them all the same thing with a UUID. It's a pain in the butt to map things.
 
Brilliant - thx for posting, this was dooing my head in, button matrix now sending to Windows game controller - DCS also my end game.
 
Would I be able to hijack this thread for some help with my Joystick project on a Teensy 2.0? I'm brand new to C programming, and not sure what I'm doing wrong, and y'all seem like you've figured this out on the 2.0 .

I'm making a keyboard for a windows tablet that has hotkeys for Krita. Works great. I just want to add a Joystick so I can assign functions to each axis (ie. brush size increase for UP, and brush size decrease for DOWN). Got my joystick connected, and recognized by Winblows 10, but thinks the stick is held to the right, or down if one cable is unplugged.

Here's the base keyboard code I'm using, and here's test code for the Joystick that's malfunctioning at the moment.
Are there dedicated Pins for X and Y analog input like there are on a 3.2? Can I assign different functions to a joystick instead of movement for mouse or a game?
 
That's going to be the game. I've gotten something insane to work if you look at the direct input, but never more than 6 in DCS. I get past it by having an obsessively ridiculous amount of inputs, with only 6 axis per controller. Once the full suite is put together there will be 5 3.6s and 2 LCs to handle all of it. That's part of the reason I use rotary encoders; there are functions you can manage with a switch, but not an axis. With a rotary encoder you turn it one way and one switch is pushed discreetly per 'click' of the encoder, you turn it the other way and a different switch is repeatedly pressed. That fakes axis control quite nicely. I sliced this out of some test code; it should be reasonably complete.

Code:
#define ENC1 1
#define ENC2 2


Encoder myEnc(ENC1, ENC2);

void setup() {
  pinMode(ENC1,INPUT_PULLUP);
  pinMode(ENC2,INPUT_PULLUP);
}

void loop() {
  long newPos = myEnc.read();
  if (newPos != position) {
	if (newPos > position) {
	  Joystick.button(1,1);
	} else {
	  Joystick.button(2,1);
	}
	position = newPos;
  } else
  {
	Joystick.button(1,0);
	Joystick.button(2,0);
  }
  delay(5);
}


Just to say thankyou most kindly for both the button matrix and encoder code - fried my last Pro Micro and thought I should try to use the teensy's I've had sitting around with me unable to program them for goodness knows how long.
Always reassuring to find others even further down the rabbit-hole than me, although SIM racing in my case. Far far longer spent building/tinkering with things than actually playing a game.....
 
Status
Not open for further replies.
Back
Top