Teenst 4.1 continually sending hat forward button though it is not programmed

skypickle

Active member
Teensy 4.1 is being used as a joystick controller and a display.
3 buttons and 6 axes are monitored and sent as joystick controls
2 buttons are used to advance screen forward and backward- the screen simply displays labels for the axes and buttons. I have hardcoded different screens for different aircraft I fly so I dont have to remember which switch is mapped to what control.
code below
Code:
/* button box has 6 potentiometers,5 buttons and one display
 *  two of the buttons function to page the display forward and back
 *  TOOLS->USB TYPE-> serial/keyboard/mouse/joystick
*/
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
#include <Bounce.h>

#define TFT_CS 10
#define TFT_DC 9
#define TFT_RST 8 
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);
//array of potentiometer pins
char potPins[] = {
A0,A1,A2,A3,A4,A10
}; 
byte potCount = 6;  
int inputPot = 0;  
int screenNumber = 1; 

// Create Bounce objects for each button.  The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.
Bounce button0 = Bounce(19, 10);
Bounce button1 = Bounce(20, 10);  // 10 = 10 ms debounce time
Bounce button2 = Bounce(21, 10);  // which is appropriate for
Bounce button3 = Bounce(22, 10);  // most mechanical pushbuttons
Bounce button4 = Bounce(23, 10);

void setup() {
  //Serial.begin(9600);
  //while (!Serial) { delay(10); }
  tft.begin();
  Apache();
  for (int thisPot = 0; thisPot < potCount; thisPot++) 
     pinMode(potPins[thisPot], INPUT);

  // Configure the pins for input mode with pullup resistors.
  // The pushbuttons connect from each pin to ground.  When
  // the button is pressed, the pin reads LOW because the button
  // shorts it to ground.  When released, the pin reads HIGH
  // because the pullup resistor connects to +5 volts inside
  // the chip.  LOW for "on", and HIGH for "off" may seem
  // backwards, but using the on-chip pullup resistors is very
  // convenient.  The scheme is called "active low", and it's
  // very commonly used in electronics... so much that the chip
  // has built-in pullup resistors!
  pinMode(19, INPUT_PULLUP);
  pinMode(20, INPUT_PULLUP);
  pinMode(21, INPUT_PULLUP);
  pinMode(22, INPUT_PULLUP);
  pinMode(23, INPUT_PULLUP);

  // Please be aware the X, Y, Z, Zr and Slider axes will have default
  // settings, if you only use the buttons.  This can give the appearance
  // of the buttons interfering with the axes, if your PC software shows
  // different default assumed values before your first button press.
  //  More details here:
  //  https://forum.pjrc.com/threads/29320-Teensy-3-1-Button-problems?p=80275#post80275
  //these next lines center the axes
  // same as Windows default
   Joystick.Z(512);
   Joystick.X(512);
   Joystick.Y(512);
   Joystick.Zrotate(512);
   Joystick.sliderLeft(512);
   Joystick.sliderRight(512);
   //Joystick.slider(512);
}

void loop() {
  for (int thisPot = 0; thisPot < potCount; thisPot++) {
    inputPot = analogRead(potPins[thisPot]);
    switch (thisPot) {
     case 0:
      Joystick.Z(inputPot);
     break;
     case 1:
      Joystick.X(inputPot);
     break;
     case 2:
      Joystick.Y(inputPot);
     break;
     case 3:
      Joystick.Zrotate(inputPot);
     break;
     case 4:
      Joystick.sliderLeft(inputPot);
     break;
     case 5:
      Joystick.sliderRight(inputPot);
     break;
     default: 
     break;
    }//end switch
   }//end for
   
  // Update all the buttons.  There should not be any long
  // delays in loop(), so this runs repetitively at a rate
  // faster than the buttons could be pressed and released.
  button0.update();
  button1.update();
  button2.update();
  button3.update();
  button4.update();

  // Check each button for "falling" edge.
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)
  if (button0.fallingEdge()) {
    Joystick.button(1, 1);
  }
  if (button1.fallingEdge()) {
    Joystick.button(2, 1);
  }
  if (button2.fallingEdge()) {
    Joystick.button(3, 1);
  }
  if (button3.fallingEdge()) {
    changeScreen(-1);
  }
  if (button4.fallingEdge()) {
    changeScreen(1);
  }

  // Check each button for "rising" edge
  // Update the Joystick buttons only upon changes.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)
  if (button0.risingEdge()) {
    Joystick.button(1, 0);
  }
  if (button1.risingEdge()) {
    Joystick.button(2, 0);
  }
  if (button2.risingEdge()) {
    Joystick.button(3, 0);
  }
}//end main loop

void changeScreen(int var)  {
  screenNumber = screenNumber + var;
  if (screenNumber < 0) {
    screenNumber = 3;
  }
  if (screenNumber > 3) {
    screenNumber = 0;
  }
 //   Serial.println("entering changeScreen function");
 // Serial.print("screenNumber is ");
  //Serial.print(screenNumber);
    switch (screenNumber) {
     case 0:
      Hog();
     break;
     case 1:
      Apache();
     break;
     case 2:
      Huey();
     break;
     case 3:
      F5E();
  }//end switch
}//end changeScreen

void Hog()  {
//unsigned long testText() {
  tft.fillScreen(HX8357_BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(HX8357_RED);    
  tft.setTextSize(3);
  tft.println("A10-C   1   2   3");
  tft.println();
  tft.setTextColor(HX8357_GREEN);
  tft.setTextSize(2);
  tft.println("Lights    Dials       Vols\n");
  tft.println("AUX                    AIM\n");
  tft.println("CONSOLE                ILS\n");
  tft.println("ENGINE                 TCN\n");
  tft.println("FLT                     FM\n");
  tft.println("FLOOD                  UHF\n");
  tft.println("FORM                   VHF\n");
  tft.println("--------------------------\n");
  tft.println(" COLL     POS        SIG  \n");
  tft.println("ON/OFF  FLSH/STDY  BRT/DIM\n");
  tft.println("\n");
  tft.println("          OFF\n");
  tft.println("\n");
}

void Apache()  {
//unsigned long testText() {
  tft.fillScreen(HX8357_BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(HX8357_RED);    
  tft.setTextSize(3);
  tft.println("AH64   1   2   3");
  tft.println();
  tft.setTextColor(HX8357_GREEN);
  tft.setTextSize(2);
  tft.println(" Lights             MFDs  \n");
  tft.println(" L  VID            R  VID\n");
  tft.println(" L  BRT            R  BRT\n");
  tft.println(" PRI               FLOOD \n");
  tft.println(" STBY              SIG    \n");
  tft.println(" FORM              ACTION\n");
  tft.println(" CMWS LT          MSTR VOL\n");
  tft.println("--------------------------\n");
  tft.println(" COLL       EXT     FIRE  \n");
  tft.println(" WHT        BRT      LT ");
  tft.println(" RED        DIM      RT\n");
  tft.println(" OFF        OFF     TST");
  tft.println("                   FLSHLT ");
}

void Huey()  {
//unsigned long testText() {
  tft.fillScreen(HX8357_BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(HX8357_RED);    
  tft.setTextSize(3);
  tft.println("UH1   1   2   3");
  tft.println();
  tft.setTextColor(HX8357_GREEN);
  tft.setTextSize(2);
  tft.println("Lights                Vols\n");
  tft.println("CONSOLE               INT \n");
  tft.println("PEDSTL                UHF \n");
  tft.println("SECNDRY               VHF \n");
  tft.println("PILOT            MRKR BCN \n");
  tft.println("COPLT                 ADF \n");
  tft.println("--------------------------");
  tft.println("COLL       NAV            \n");
  tft.println(" ON       FLSH             ");
  tft.println("     ");
  tft.println("DOME");
  tft.println("OFF       STDY\n");
  tft.println("                    FLSHLT\n");
  tft.println("WHT/GRN  BRT/DIM   WHT/RED \n");

}

void F5E()  {
//unsigned long testText() {
  tft.fillScreen(HX8357_BLACK);
  tft.setCursor(0, 0);
  tft.setTextColor(HX8357_RED);    
  tft.setTextSize(3);
  tft.println("F5E   1   2   3");
  tft.println();
  tft.setTextColor(HX8357_GREEN);
  tft.setTextSize(2);
  tft.println("         LIGHTS\n");
  tft.println("     ");
  tft.println("RDR BRIGHT            NAV \n");
  tft.println("RDR CRSR             FORM \n");
  tft.println("RDR PERSIST      FLT INST \n");
  tft.println("VIDEO            ENG INST \n");
  tft.println("                  CONSOLE \n");
  tft.println("                    FLOOD \n");
  tft.println("--------------------------");

}

When I look at the USB controller settings the teensy is labeled as
'Serial/Keyboard/Mouse/Joystick' and the status is OK
But the properties control panel shows the 'point of view hat' pushed forward.

Untitled.jpg

As you can see in the code, three buttons are detected as joystick buttons and pressing these properly lights up the correct red lights on the USB game controller panel.

How can the point of view hat be 'stuck' in the forward position?
How can I troubleshoot this?

Interestingly, in the post here:
https://forum.pjrc.com/threads/29320-Teensy-3-1-Button-problems?p=80275#post80275
They talk about windows 'making up' values for axes that are not initialized. Setting the axes to 512 corrects this. But in that SAME post there is a picture of the hat being activated in the forward position though the problem is not addressed.

The reason this is a problem for me is that I have a tactical flight analysis program (TACVIEW) that continually scrolls left unless I disconnect the teensy. I am guessing that the forward activated hat is being interpreted by the program causing it to scroll left.
 
Maybe try setting the hat value...
My gut is it is defaulting to sending a 0 direction: Sort of maps 0-360 degrees (int) into 0-7 values. You might
try: joystick.hat(-1);
Which if < 0 will set hat to 15... And see if that helps.
 
Hey! sorry to necro this, I am having this issue using the extreme joystick. I have usb_desc.h set to use the bigger version, I only have my buttons programmed but the 4 hat switches and the XY axis are showing as having constant input, I have tried to set my hat values to -1 and X,Y positions to 0 but I can't get it to work. Is there anyway to just straight up delete the sliders, hats, and XY axis? or atleast get them to show zero input since they are not part of this project?

I will say, I am very new to this, and this code is from another person, Stefan, that I have adapted to my use. The code was originally designed for use as a keyboard device. Buttons work excellent, just need to get the extra inputs taken care of.

Edit: The last 2 functions isKeyDown and releaseLayer are not used but I haven't deleted them cause they are never called.

Help_JoyTest_HatsandXY.jpg


Here is my code:
Code:
/*
 * Programming by Stefan Jakobsson, 2019
 * Released to public domain
 * 
 * Adapted by Dominus, 2022
 * 
 * Used on Teensy 4.0
 * Key matrix with diodes after switches towards row wire.
 * 
 */
 
#define ROW_COUNT 7        //Number of rows in the keyboard matrix
#define COL_COUNT 6       //Number of columns in the keyboard matrix

#define DEBOUNCE 10         //Adjust as needed: increase if bouncing problem, decrease if not all keypresses register; not less than 2
#define SCAN_DELAY 5        //Delay between scan cycles in ms

#define KEY_UNDEFINED -1    //For keyboard matrix positions not in use

int rowPins[] = { 6,  7,  8,  9, 10, 11, 12};   //Teensy pins attached to matrix rows
int colPins[] = {14, 15, 16, 17, 18, 19};  //Teensy pins attached to matrix columns

//Key codes for layer 0 (standard layer)
int KeyMatrix[] = { 1,  2,  3,  4,  5,  6,
                    7,  8,  9, 10, 11, 12,
                   13, 14, 15, 16, 17, 18,
                   19, 20, 21, 22, 23, 24,
                   25, 26, 27 ,28, 29, 30,
                   31, 32, 33, 34, 35, 36, 
                   37, 38};
                   
int currentRow = 0;
int keyStatus[(ROW_COUNT-1)*COL_COUNT+2];

void setup() {
  //Set joystick send mode
  Joystick.useManualSend(true);
  
  //Row pins
  pinMode(rowPins[0], OUTPUT);
  digitalWrite(rowPins[0], LOW);
  
  for (int i=1;i<ROW_COUNT;i++){
    pinMode(rowPins[i], INPUT);
  }

  //Column pins
  for (int i=0;i<COL_COUNT;i++){
    pinMode(colPins[i], INPUT_PULLUP);
  }

  //Clear keyStatus
  for (int i=0;i<(ROW_COUNT-1)*COL_COUNT+2;i++){
    keyStatus[i]=0;
  }
}

void loop() {

  
  scanKeys();
  Joystick.send_now();
  delay(SCAN_DELAY);
}

/*
 * Scan keyboard matrix, results stored in keyStatus array
 * 
 * Key status values:
 * DEBOUNCE*2                   = key press event
 * DEBOUNCE+1 to DEBOUNCE*2-1   = key down
 * DEBOUNCE                     = key release event
 * 0 to DEBOUNCE-1              = key up
*/
void scanKeys(){
  // iterate through each button in the matrix, 6x6+2 = 38 buttons
  for (int i=0;i<(ROW_COUNT-1)*COL_COUNT+2;i++){
    int pin = getKeyPin(i);
    
    if (keyStatus[i]==0 && digitalRead(pin)==LOW){
      //Key press event
      keyStatus[i] = DEBOUNCE*2;
      keyPress(i);
    }
    else if (keyStatus[i]>DEBOUNCE+1){
      keyStatus[i]--;
    }
    else if (keyStatus[i]==DEBOUNCE+1 && digitalRead(pin)==HIGH){
      //Key release event
      keyStatus[i]--;
      keyRelease(i);
    }
    else if (keyStatus[i]>0 && keyStatus[i]<=DEBOUNCE){
      keyStatus[i]--;
    }
  }
}

/*
 * Returns input pin to be read by keyScan method
 * 
 * Param key is the keyboard matrix scan code (row * COL_COUNT + col)
 */
int getKeyPin(int key){
  int p = key/COL_COUNT;
  if (p != currentRow){
    pinMode(rowPins[currentRow], INPUT);
    pinMode(rowPins[p], OUTPUT);
    digitalWrite(rowPins[p], LOW);
    currentRow=p;
  }
  delayMicroseconds(200);
  return colPins[key % COL_COUNT];
}

/*
 * Sends key press event
 * 
 * Param keyCode is the keyboard matrix scan code (row * COL_COUNT + col)
 */

void keyPress(int keyCode){
  if (KeyMatrix[keyCode]!=KEY_UNDEFINED){
    Joystick.button(KeyMatrix[keyCode], 1);
  }
}

/*
 * Sends key release event
 * 
 * Param keyCode is the keyboard matrix scan code (row * COL_COUNT + col)
 */
 
void keyRelease(int keyCode){
  if (KeyMatrix[keyCode]!=KEY_UNDEFINED){
    Joystick.button(KeyMatrix[keyCode], 0);
  }
}

/*
 * Relases all keys in the layer; called upon change of layer, i.e. press or release of FN key
 * 
 * Param layer[] array of key codes for the layer to be released
 */
void releaseLayer(int layer[]){
  int i;
  for (i=0;i<ROW_COUNT*(COL_COUNT-1)+2;i++){
    if (isKeyDown(i)){
      keyStatus[i] = DEBOUNCE;
      Keyboard.release(layer[i]);
    }
  }
}

/*
 * Returns 0 if the specified key is pressed, otherwise a value not equal to 0
 * 
 * Param keyCode is the keyboard matrix scan code (row * COL_COUNT * col)
 */
 int isKeyDown(int keyCode){
 if (keyStatus[keyCode]>DEBOUNCE) return 1; else return 0; 
}
 
Last edited:
Note: I am not seeing anywhere in the code posted that sets anything but the buttons.
That is, I am not seeing something like: for (int i=1; i <=4; i++) Joystick.hat(i, -1);
ditto for the others.
If it were me, I would probably try setting X, Y to probably 512, maybe some of the others as well, but then set sliders to 0

As for removing things? Probably? BUT, that would require an understanding of the HID data, and rules like the number of total bits in a message must be multiple of 8...

Also unclear to me what the joystick tools would do, if some of the basic ones here were not defined.
 
I'm not trying to have any joystick axes, sliders, or hats, just a button box that uses joystick buttons.

I hadn't set the hats or axes as of that post, but I've since learned a bit more!
my project is a 6x6 keypad with a 7th row and 2 more buttons for a total of 38 joystick buttons.

I'm setting the hats and axes that I'm not using in the void setup(). I'm not even sure what I need to set in there and what I can just leave out since I'm not even using any of the hats, axes, or sliders.

Thanks for your time

Code:
/*
 * Programming by Stefan Jakobsson, 2019
 * Released to public domain
 * 
 * Adapted by Dominus, 2022
 * 
 * Used on Teensy 4.0
 * Key matrix with diodes after switches towards row wire.
 * 
 */
 
#define ROW_COUNT 7        //Number of rows in the keyboard matrix
#define COL_COUNT 6       //Number of columns in the keyboard matrix

#define DEBOUNCE 10         //Adjust as needed: increase if bouncing problem, decrease if not all keypresses register; not less than 2
#define SCAN_DELAY 5        //Delay between scan cycles in ms

#define KEY_UNDEFINED -1    //For keyboard matrix positions not in use

int rowPins[] = { 6,  7,  8,  9, 10, 11, 12};   //Teensy pins attached to matrix rows
int colPins[] = {14, 15, 16, 17, 18, 19};  //Teensy pins attached to matrix columns

//Key codes for layer 0 (standard layer)
int KeyMatrix[] = { 1,  2,  3,  4,  5,  6,
                    7,  8,  9, 10, 11, 12,
                   13, 14, 15, 16, 17, 18,
                   19, 20, 21, 22, 23, 24,
                   25, 26, 27 ,28, 29, 30,
                   31, 32, 33, 34, 35, 36, 
                   37, 38};
                   
int currentRow = 0;
int keyStatus[(ROW_COUNT-1)*COL_COUNT+2];

void setup() {
  /*Set Hats to center.
  * Setting a value out of bounds ie >360 works to center hat input.
  * I think this is because the header file sets values <0 as 0 but not
  * values above 360.
  */
  Joystick.hat(1, 361); //For some reason this is Hat #4
  Joystick.hat(2, 361); // This is Hat #3
  Joystick.hat(3, 361); // This is Hat #2
  Joystick.hat(4, 361); // Hat #1

  //Set axes to center
 // This doesn't work dot is still green in the upper left corner of the X/Y axis
  Joystick.X(512);
  Joystick.Y(512);
  
  //Set joystick send mode
  Joystick.useManualSend(true);
  
  //Row pins
  pinMode(rowPins[0], OUTPUT);
  digitalWrite(rowPins[0], LOW);
  
  for (int i=1;i<ROW_COUNT;i++){
    pinMode(rowPins[i], INPUT);
  }

  //Column pins
  for (int i=0;i<COL_COUNT;i++){
    pinMode(colPins[i], INPUT_PULLUP);
  }

  //Clear keyStatus
  for (int i=0;i<(ROW_COUNT-1)*COL_COUNT+2;i++){
    keyStatus[i]=0;
  }
}

void loop() {

  
  scanKeys();
  Joystick.send_now();
  delay(SCAN_DELAY);
}

/*
 * Scan keyboard matrix, results stored in keyStatus array
 * 
 * Key status values:
 * DEBOUNCE*2                   = key press event
 * DEBOUNCE+1 to DEBOUNCE*2-1   = key down
 * DEBOUNCE                     = key release event
 * 0 to DEBOUNCE-1              = key up
*/
void scanKeys(){
  // iterate through each button in the matrix, 6x6+2 = 38 buttons
  for (int i=0;i<(ROW_COUNT-1)*COL_COUNT+2;i++){
    int pin = getKeyPin(i);
    
    if (keyStatus[i]==0 && digitalRead(pin)==LOW){
      //Key press event
      keyStatus[i] = DEBOUNCE*2;
      keyPress(i);
    }
    else if (keyStatus[i]>DEBOUNCE+1){
      keyStatus[i]--;
    }
    else if (keyStatus[i]==DEBOUNCE+1 && digitalRead(pin)==HIGH){
      //Key release event
      keyStatus[i]--;
      keyRelease(i);
    }
    else if (keyStatus[i]>0 && keyStatus[i]<=DEBOUNCE){
      keyStatus[i]--;
    }
  }
}

/*
 * Returns input pin to be read by keyScan method
 * 
 * Param key is the keyboard matrix scan code (row * COL_COUNT + col)
 */
int getKeyPin(int key){
  int p = key/COL_COUNT;
  if (p != currentRow){
    pinMode(rowPins[currentRow], INPUT);
    pinMode(rowPins[p], OUTPUT);
    digitalWrite(rowPins[p], LOW);
    currentRow=p;
  }
  delayMicroseconds(200);
  return colPins[key % COL_COUNT];
}

/*
 * Sends key press event
 * 
 * Param keyCode is the keyboard matrix scan code (row * COL_COUNT + col)
 */

void keyPress(int keyCode){
  if (KeyMatrix[keyCode]!=KEY_UNDEFINED){
    Joystick.button(KeyMatrix[keyCode], 1);
  }
}

/*
 * Sends key release event
 * 
 * Param keyCode is the keyboard matrix scan code (row * COL_COUNT + col)
 */
 
void keyRelease(int keyCode){
  if (KeyMatrix[keyCode]!=KEY_UNDEFINED){
    Joystick.button(KeyMatrix[keyCode], 0);
  }
}

/*
 * Relases all keys in the layer; called upon change of layer, i.e. press or release of FN key
 * 
 * Param layer[] array of key codes for the layer to be released
 */
void releaseLayer(int layer[]){
  int i;
  for (i=0;i<ROW_COUNT*(COL_COUNT-1)+2;i++){
    if (isKeyDown(i)){
      keyStatus[i] = DEBOUNCE;
      Keyboard.release(layer[i]);
    }
  }
}

/*
 * Returns 0 if the specified key is pressed, otherwise a value not equal to 0
 * 
 * Param keyCode is the keyboard matrix scan code (row * COL_COUNT * col)
 */
 int isKeyDown(int keyCode){
 if (keyStatus[keyCode]>DEBOUNCE) return 1; else return 0; 
}
 
Last edited:
Back
Top