Help fixing issue with mouse jumping to center of screen

Been working on a game controller with Teensy 4.1 Tried to use in game and the cursor keeps flickering and jumping back into the center of the screen. Wondering what is the issue here and if there is a simple fix.

Background details:

  • I have been using Teensy as a game controller (Keyboard + Mouse + Joystick).
  • I am using Windows 10
  • I'm very new to coding, but nothing in the code touches mouse functions
  • I am using more than 32 buttons, so I had booted Star Citizen to see if buttons numbered 33 and above would work - which is when I ran into this issue. (Star Citizen supports at least up to 50)
FYI - I am very new to all of this, this is basically my first real project with coding.
 
Sorry, Hard to help much as you have not provided any information, that anyone can identify the issues.

Which is why at the top of each of these screens, it states to include source code and information in order to reproduce the issue.

For example: Have you modified the usb_desc.h file to update: #define JOYSTICK_SIZE 12 // 12 = normal, 64 = extreme joystick
in the appropriate section for the USB Type, you have choosen?

If not I believe the maximum number of buttons is 32. If you changed to 64, then it supports up to 128 buttons.

Or did you modify something else? If so, then there might be issues with the underlying HID report descriptor and/or the code that packs up the data to match the HID descriptor and as such Windows might be seeing bogus data. Where. maybe some of your button data is stored in the bits that Windows believes is the logical X, Y values of the joystick and windows might be using that to move the cursor.

Or maybe your game Star Citizen is doing it's own parsing of the data and it is not matching what it expects.

Sorry I know not much to go on.
 
Sorry, Hard to help much as you have not provided any information, that anyone can identify the issues.

Which is why at the top of each of these screens, it states to include source code and information in order to reproduce the issue.

For example: Have you modified the usb_desc.h file to update: #define JOYSTICK_SIZE 12 // 12 = normal, 64 = extreme joystick
in the appropriate section for the USB Type, you have choosen?

If not I believe the maximum number of buttons is 32. If you changed to 64, then it supports up to 128 buttons.

Or did you modify something else? If so, then there might be issues with the underlying HID report descriptor and/or the code that packs up the data to match the HID descriptor and as such Windows might be seeing bogus data. Where. maybe some of your button data is stored in the bits that Windows believes is the logical X, Y values of the joystick and windows might be using that to move the cursor.

Or maybe your game Star Citizen is doing it's own parsing of the data and it is not matching what it expects.

Sorry I know not much to go on.

Thanks - sorry, I'm so new to this I'm not really sure what I should be providing. I tried to add in the line you recommended to increase the numbers. I'm still having some issues with a few of the buttons not registering but I can't tell if that is an issue with my wiring or code - but it's also difficult to tell when window's game controller settings window only shows 32 buttons. Is there an easier way to test rather than going into a game and seeing if something can be bound to that button?

Haven't touched anything else. Sharing code below. Major thanks for the help.

EDIT: Once I went into and updated "usb_desc.h" it fixed the issue of the other buttons not registering. (One button is still giving me issues, but it's a hardware issue). The issue of the mouse returning to the center persists though. Is there any way for me to disable any kind of interaction with the mouse in the code?



Code:
  #include <Keypad.h>
  #define JOYSTICK_SIZE 64 // 12 = normal, 64 = extreme joystick
  const byte ROWS = 6; //four rows
  const byte COLS = 7; //four columns

  byte rowPins[ROWS] = {0, 1, 2, 3, 4, 5}; //connect to the row pinouts of the keypad
  byte colPins[COLS] = {23, 22, 21, 20, 19, 18, 17,}; //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,17,18,19,20,21},
    {22,23,24,25,26,27,28},
    {29,30,31,32,33,34,35},
    {36,37}
  };

  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:
Note: Changing the #define JOYSTICK_SIZE 64 // 12 = normal, 64 = extreme joystick
Needs to be done in the actual Cores directory.

Wish it was not the case, but...
Where the file is on your machine...
It depends on which IDE version you are running IDE 1.x like 1.8.19, it will be at:
<Arduino install point>/hardware/teensy/avr/cores/teensy4/usb_desc.h

If you are running Arduino IDE 2.x builds are installed in the "Arduino15" directory...
In my case it is in the directory: C:\Users\kurte\AppData\Local\Arduino15\packages\teensy\hardware\avr\0.58.2\cores\teensy4
But your directory will depend on things like your username is probably not kurtee, and you may are may not be running the lasted beta build,
the 0.58.2 and install maybe that part will be 1.57.1 or some such number...
 
Note: Changing the #define JOYSTICK_SIZE 64 // 12 = normal, 64 = extreme joystick
Needs to be done in the actual Cores directory.

Wish it was not the case, but...
Where the file is on your machine...
It depends on which IDE version you are running IDE 1.x like 1.8.19, it will be at:
<Arduino install point>/hardware/teensy/avr/cores/teensy4/usb_desc.h

If you are running Arduino IDE 2.x builds are installed in the "Arduino15" directory...
In my case it is in the directory: C:\Users\kurte\AppData\Local\Arduino15\packages\teensy\hardware\avr\0.58.2\cores\teensy4
But your directory will depend on things like your username is probably not kurtee, and you may are may not be running the lasted beta build,
the 0.58.2 and install maybe that part will be 1.57.1 or some such number...

Thank you, I did actually do this! I had found the answer on another thread elsewhere.
 
Thank you, I did actually do this! I had found the answer on another thread elsewhere.

@BeesBeesBees, where did you find the answer?

I've come across a similar issue. Building a joystick for Star Citizen. The joystick works fine with windows gamepad controller, https://hardwaretester.com/gamepad, and https://greggman.github.io/html5-gamepad-test/ to name a few other places.

However, in SC, when I open the options menu to get to the keybinds, it flickers between gamepad and keyboard input. It also appears to be scrolling up on something. The flickering isn't as obvious. If I mouse over a keybind or button on the SC interface it loses its highlight and I cannot click. As if someone is tabbing away from that highlight while I'm trying to click on it.

If I plug the teensy in along with a commercial joystick (3d pro) so that they're both in at the same time it seems to stop the auto-scrolling-focus-controlling buggy behavior and the SC interface appears as if it is gamepad input. Note that the interface doesn't appear as gamepad input when it just the commercial joystick. I doubt this is useful, but it is reproduce-able at least.

I've tried all the different joystick combos: Keyboard/Joystick/Mouse/Serial, Keyboard/Serial/Joystick, Flight Stick/Joystick. I've adjusted the usb_desc.h joystick size header to 64. I tried commenting out all of the calls to hat in my code and that didn't stop the behavior for any flavor of USB.

Edit: Side note, if I remove the teensy after loading SC the menu still exhibits the same buggy behavior.

My code below. Probably too much detail, but here it is in case:

C++:
config.h

#ifndef CONFIG_H
#define CONFIG_H

#define TMAG5170_FRAME_NUM_BYTES        4
#define TMAG5170_CHIP_SELECT_PIN_1      10
#define TMAG5710_CHIP_SELECT_PIN_2      9
#define TMAG5710_CHIP_SELECT_PIN_3      8

#define SNX4HC165_CLOCK_IN_PIN          6
#define SNX4HC165_CLOCK_ENABLE_PIN      4
#define SNX4HC165_DATA_IN_PIN           5
#define SNX4HC165_LOAD_PIN              7
#define SNX4HX165_NUMBER_OF_CHIPS       3
#define SNX4HX165_PARALLEL_INPUTS       8

#define HAT_1_A                         23
#define HAT_1_B                         22
#define HAT_1_C                         21
#define HAT_1_D                         20
#define HAT_1_P                         16
#define HAT_1_NUM                       4

#define SHIFT_DEBUG
// #define HALL_DEBUG
#define HAT_DEBUG

#include <Arduino.h>
#include <SPI.h>
#include <SimpleKalmanFilter.h>
#include "readShiftRegister.h"
#include "readHallSensor.h"
#include "readHat.h"

#endif

C++:
main.cpp

#include "config.h"

/*
*  TMAG5170        -->   Teensy
*  CS:     pin 4   -->   10 (CS0)
*  MOSI:   pin 2   -->   11 (MOSI0)
*  MISO:   pin 3   -->   12 (MISO0)
*  SCK:    pin 1   -->   13 (SCK0)
*
*  VCC:    pin 5   -->   5V
*  GND:    pin 6   -->   GND
*  TEST:   pin 7   -->   GND
*  ALERT:  pin 8   -->   just any IO pin on the Teensy if decide to use it
*/

/*
*  SNX4HC165        -->   Teensy
*  CE:      pin 15  -->   int clockEnablePin = 4; //  CE pin 15 -- CLK INH -- Clock Inhibit, when High No change in output
*  QH:      pin 9   -->   int dataIn = 5; //          Q7 pin 9 (7 is inverted) -- QH -- Complementary Serial Output
*  CP:      pin 2   -->   int clockIn = 6; //         CP pin 2 -- CLK -- Clock input
*  PL:      pin 1   -->   int load = 7; //            PL pin 1 -- SH/LD -- Shift or Load input, When High Data, shifted. When Low data is loaded from parallel inputs
*/

// Global Variables

// read hall sensor class
ReadHallSensor TMAG5170(TMAG5170_CHIP_SELECT_PIN_1,
                        TMAG5710_CHIP_SELECT_PIN_2,
                        TMAG5710_CHIP_SELECT_PIN_3);

// tweaking kalman filters .25, 0.25, 0.003 -- good values
SimpleKalmanFilter simpleKalmanFilterX(0.25, 0.25, 0.003);
SimpleKalmanFilter simpleKalmanFilterY(0.25, 0.25, 0.003);

// int clockEnablePin = 4; //  CE pin 15 -- CLK INH -- Clock Inhibit, when High No change in output
// int dataIn = 5; //          Q7 pin 9 (7 is inverted) -- QH -- Complementary Serial Output
// int clockIn = 6; //         CP pin 2 -- CLK -- Clock input
// int load = 7; //            PL pin 1 -- SH/LD -- Shift or Load input, When High Data, shifted. When Low data is loaded from parallel inputs
// SNx4HC16(int clockIn, int clockEnablePin, int dataIn, int load)
ReadShiftRegister SNx4HC16(SNX4HC165_CLOCK_IN_PIN,
                           SNX4HC165_CLOCK_ENABLE_PIN,
                           SNX4HC165_DATA_IN_PIN,
                           SNX4HC165_LOAD_PIN);

// an array to collect all the buttons shifted in from the registers
byte allButtons[24];

// setting up the hat
// ReadHat hat1(HAT_1_A,
//              HAT_1_B,
//              HAT_1_C,
//              HAT_1_D,
//              HAT_1_P,
//              HAT_1_NUM,
//              allButtons);


// setting initial values for the X and Y axis, trying to combat the
// boot-up readings that are insane & mess up the auto min-max algorythm
float angleConvertX_min = 360;
float angleConvertX_max = 0;
float angleConvertY_min = 360;
float angleConvertY_max = 0;

// used in the max setter in the loop
static int startupDelay = 10; // e.g., 100 readings

void setup() {
  delay(1000); // startup buffer... 1s

  SPI.begin();         // initialize the SPI library
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); //100Khz just to make testing easier, can go up to 10Mhz
 
  Serial.begin(115200); // for serial monitoring on the laptop

  TMAG5170.sensorsSetup();

  Serial.println("Setup done.");

}

void loop() {

#ifdef HALL_DEBUG
  Serial.print("read angle:");
#endif

  TMAG5170.readSensorOne();
  float angleConvertX = TMAG5170.getAngleConversion();
  TMAG5170.readSensorTwo();
  float angleConvertY = TMAG5170.getAngleConversion();

#ifdef HALL_DEBUG
  Serial.print(" ");
  Serial.print(angleConvertX);
  Serial.print(" X | ");
  Serial.print(angleConvertY);
  Serial.print(" Y | ");
#endif

  float kalman_valueX = simpleKalmanFilterX.updateEstimate(angleConvertX);
  float kalman_valueY = simpleKalmanFilterY.updateEstimate(angleConvertY);

#ifdef HALL_DEBUG
  Serial.print(kalman_valueX);
  Serial.print(" Xk | ");
  Serial.print(kalman_valueY);
  Serial.print(" Yk | ");
#endif

  // TODO: Fix how the map min and max are set -- maybe reset if greater than 40 different from current reading -- since 40 degress is max range? -- or change starting min...
  // wait before updating min and max seems to help

  if (startupDelay > 0) {
      startupDelay--;
  } else {
    if (kalman_valueX < angleConvertX_min && kalman_valueX > 0) {
      angleConvertX_min = kalman_valueX;
    }
    if (angleConvertX_min < angleConvertX_max - 50) {
      angleConvertX_min = angleConvertX_max - 1;
    }
    if (kalman_valueX > angleConvertX_max) {
      angleConvertX_max = kalman_valueX;
    }
    if (kalman_valueY < angleConvertY_min) {
      angleConvertY_min = kalman_valueY;
    }
    if (angleConvertY_min < angleConvertY_max - 50) {
      angleConvertY_min = angleConvertY_max - 1;
    }
    if (kalman_valueY > angleConvertY_max) {
      angleConvertY_max = kalman_valueY;
    }
  }

  // attempting automatic max and min
  int joystickAngleX;
  joystickAngleX = map(kalman_valueX, angleConvertX_min, angleConvertX_max, 0, 1023);
  int joystickAngleY;
  joystickAngleY = map(kalman_valueY, angleConvertY_min, angleConvertY_max, 0, 1023);

  // manually set values
  // int joystickAngleX;
  // joystickAngleX = map(kalman_valueX, 315.75, 354.25, 0, 1023);
  // int joystickAngleY;
  // joystickAngleY = map(kalman_valueY, 246.75, 295.5, 0, 1023);

#ifdef HALL_DEBUG
  Serial.print(joystickAngleX);
  Serial.print(" jX | ");
  Serial.print(joystickAngleY);
  Serial.print(" jY | ");

  Serial.print(angleConvertX_min);
  Serial.print(" X_min | ");
  Serial.print(angleConvertX_max);
  Serial.print(" X_max | ");
  Serial.print(angleConvertY_min);
  Serial.print(" Y_min | ");
  Serial.print(angleConvertY_max);
  Serial.print(" Y_max ");
  Serial.print(" | ");
#endif

  Joystick.X(joystickAngleX);
  Joystick.Y(joystickAngleY);

  SNx4HC16.readShiftRegister();

#ifdef SHIFT_DEBUG
  Serial.print(" | ButtAr: ");
#endif



  // read pins and use them for the buttons
  // true == 1 == HIGH
  // false == 0 == LOW
  for (int i=0; i <= 23; i++) {
   
    // inverting the array from its pullup "11111111" state all being unpressed
    // into a state that the Joystick object expects "0000000" as being upressed
    // TODO -- do a bit shift for this or get the complimentary output from the SN chip instead
    if (SNx4HC16.buttonArray[i]) {
      allButtons[i] = 0;
    } else {
      allButtons[i] = 1;
    }


#ifdef SHIFT_DEBUG
    Serial.print(allButtons[i]);
#endif
  }


  // sending the inverted button state to the joystick controller, if not a hat button
  for (int i=0; i <= 23; i++) {
    // if (hat1.IsHatButton(i)) {
    //     continue;
    // }
    Joystick.button(i + 1, allButtons[i]);
  }

  // hat1.CalcHatAngle();

#if defined(HALL_DEBUG) || defined(SHIFT_DEBUG)
  Serial.println();
#endif

  // delay(1); // 1 = 1 milisecond

}

C++:
readHat.cpp


#include "readHat.h"

ReadHat::ReadHat(int hatA, int hatB, int hatC, int hatD, int hatPush, int numJoystick, byte* buttonArray) {
  _hatA = hatA;
  _hatB = hatB;
  _hatC = hatC;
  _hatD = hatD;
  _hatPush = hatPush; // not part of the hat calc, just a seperate button press
  _numJoystick = numJoystick;
  _buttonArray = buttonArray;

}

// for the hat angle is 0,45,90,135,180,225,270,315,-1 // -1 is resting
void ReadHat::CalcHatAngle() {
  if (_buttonArray[_hatA] == HIGH && _buttonArray[_hatD] == HIGH) {
    _hatAngle = 45; // Up-Right
  } else if (_buttonArray[_hatA] == HIGH && _buttonArray[_hatB] == HIGH) {
    _hatAngle = 315; // Up-Left
  } else if (_buttonArray[_hatD] == HIGH && _buttonArray[_hatC] == HIGH) {
    _hatAngle = 135; // Down-Right
  } else if (_buttonArray[_hatB] == HIGH && _buttonArray[_hatC] == HIGH) {
    _hatAngle = 225; // Down-Left
  } else if (_buttonArray[_hatA] == HIGH) {
    _hatAngle = 0; // Up
  } else if (_buttonArray[_hatD] == HIGH) {
    _hatAngle = 90; // Right
  } else if (_buttonArray[_hatC] == HIGH) {
    _hatAngle = 180; // Down
  } else if (_buttonArray[_hatB] == HIGH) {
    _hatAngle = 270; // Left
  } else {
    _hatAngle = -1; // resting
  }

#ifdef HAT_DEBUG
  Serial.printf(" _hatAngle (%d): %d", _numJoystick, _hatAngle);
#endif

  Joystick.hat(_numJoystick, _hatAngle);
}

bool ReadHat::IsHatButton(const int index) {
    return index == _hatA || index == _hatB || index == _hatC || index == _hatD;
}

C++:
readHallSensor.cpp


#include "readHallSensor.h"

// TMAG5170 regular 32bit SDO read      = {STAT11-4, D15-8, D7-0, STAT3-0 CRC3-0}
const uint8_t DISABLE_CRC[TMAG5170_FRAME_NUM_BYTES]            = {0x0F, 0x00, 0x04, 0x07}; // disable crc
const uint8_t SENSOR_CONFIG_X_Y_MAG[TMAG5170_FRAME_NUM_BYTES]  = {0x01, 0x41, 0xC0, 0x00}; // ANGLE_EN X-Y & MAG_CH_EN xyz
const uint8_t DEVICE_CONFIG[TMAG5170_FRAME_NUM_BYTES]          = {0x00, 0x00, 0x08, 0x00}; // DEVICE_CONFIG - T_CH_EN // temperature enable
const uint8_t CONVERT_AVG_BITS[TMAG5170_FRAME_NUM_BYTES]       = {0x00, 0x50, 0x00, 0x00}; // 16x rate per the docs is 4h or [100] in bits 14-12 or = 0[100]0000 *** actually setting to 5 [101] to see how it works // 5h = 32x - 0.4Ksps (3-axes) or 1.2Ksps (1 axis)
const uint8_t DEVICE_OPERATE_MODE[TMAG5170_FRAME_NUM_BYTES]    = {0x00, 0x00, 0x20, 0x00}; // Continuous conversion
const uint8_t GET_ANGLE_RESULT[TMAG5170_FRAME_NUM_BYTES]       = {0x93, 0x00, 0x00, 0x00}; // 1 read + 0010011 ANGLE_RESULT 13h = 10010011

/// @brief Handling hall sensor setups and communication
/// @param chipSelectPin1
/// @param chipSelectPin2
/// @param chipSelectPin3
ReadHallSensor::ReadHallSensor(const int chipSelectPin1, const int chipSelectPin2, const int chipSelectPin3) {

    _chipSelectPin1 = chipSelectPin1;
    _chipSelectPin2 = chipSelectPin2;
    _chipSelectPin3 = chipSelectPin3;

    pinMode(_chipSelectPin1, OUTPUT); // set the CS1 pin as an output
    digitalWrite(_chipSelectPin1, HIGH); // setting CS1 pin high at the start
    pinMode(_chipSelectPin2, OUTPUT); // set the CS2 pin as an output
    digitalWrite(_chipSelectPin2, HIGH); // setting CS2 pin high at the start
    pinMode(_chipSelectPin3, OUTPUT); // set the CS3 pin as an output
    digitalWrite(_chipSelectPin3, HIGH); // setting CS3 pin high at the start

}

/// @brief Prints hex to serial with leading zeroes
/// @param X is the uint8_t you want to print in hex
void ReadHallSensor::printHexZero(uint8_t X) {
    if (X < 16) {
        Serial.print("0");
    }
    Serial.print(X, HEX);  
}

void ReadHallSensor::spiSendReceiveArrays(const uint8_t dataTx[], int CSpin) {

    digitalWrite(CSpin, LOW);

    // Send all dataTx[] bytes on MOSI, and capture all MISO bytes in _dataRx[]
    for (int i = 0; i < TMAG5170_FRAME_NUM_BYTES; i++)
    {
        _dataRx[i] = SPI.transfer(dataTx[i]);
    }

    digitalWrite(CSpin, HIGH);

#ifdef HALL_DEBUG
    Serial.print(" dataTx[]: 0x");
    printHexZero(dataTx[0]);
    printHexZero(dataTx[1]);
    printHexZero(dataTx[2]);
    printHexZero(dataTx[3]);

    Serial.print(" _dataRx[]: 0x");
    printHexZero(_dataRx[0]);
    printHexZero(_dataRx[1]);
    printHexZero(_dataRx[2]);
    printHexZero(_dataRx[3]);
#endif
}

float ReadHallSensor::getAngleConversion() {
  uint16_t data = (uint16_t)_dataRx[1] << 8 | (uint16_t)_dataRx[2];
  float angle = ( (float) data / 16  );
  return angle;
}

void ReadHallSensor::readSensorOne() {
    spiSendReceiveArrays(GET_ANGLE_RESULT, _chipSelectPin1);
}

void ReadHallSensor::readSensorTwo() {
    spiSendReceiveArrays(GET_ANGLE_RESULT, _chipSelectPin2);
}

void ReadHallSensor::sensorsSetup() {
    spiSendReceiveArrays(DISABLE_CRC, _chipSelectPin1);
    spiSendReceiveArrays(SENSOR_CONFIG_X_Y_MAG, _chipSelectPin1);
    spiSendReceiveArrays(DEVICE_CONFIG, _chipSelectPin1);
    spiSendReceiveArrays(CONVERT_AVG_BITS, _chipSelectPin1);
    spiSendReceiveArrays(DEVICE_OPERATE_MODE, _chipSelectPin1);

    spiSendReceiveArrays(DISABLE_CRC, _chipSelectPin2);
    spiSendReceiveArrays(SENSOR_CONFIG_X_Y_MAG, _chipSelectPin2);
    spiSendReceiveArrays(DEVICE_CONFIG, _chipSelectPin2);
    spiSendReceiveArrays(CONVERT_AVG_BITS, _chipSelectPin2);
    spiSendReceiveArrays(DEVICE_OPERATE_MODE, _chipSelectPin2);
}

C++:
readShiftRegister.cpp



#include "readShiftRegister.h"

ReadShiftRegister::ReadShiftRegister(int clockIn, int clockEnablePin, int dataIn, int load) {
  _clockIn = clockIn;
  _clockEnablePin = clockEnablePin;
  _dataIn = dataIn;
  _load = load;

  pinMode(_load, OUTPUT);
  pinMode(_clockEnablePin, OUTPUT);
  pinMode(_clockIn, OUTPUT);
  pinMode(_dataIn, INPUT);
}

/// @brief Reads the dasiy-chained SNX4HC165 outputs into public attribute buttonArray
void ReadShiftRegister::readShiftRegister() {
  // Write pulse to load pin
  digitalWrite(_load, LOW);
  delayMicroseconds(5);
  digitalWrite(_load, HIGH);
  delayMicroseconds(5);

  // Get data from 74HC165
  digitalWrite(_clockIn, LOW);
  digitalWrite(_clockEnablePin, LOW);
  // byte incoming = shiftIn(_dataIn, _clockIn, LSBFIRST);
  // byte incoming2 = shiftIn(_dataIn, _clockIn, LSBFIRST);
  _incoming[0] = shiftIn(_dataIn, _clockIn, LSBFIRST);
  _incoming[1] = shiftIn(_dataIn, _clockIn, LSBFIRST);
  _incoming[2] = shiftIn(_dataIn, _clockIn, LSBFIRST);
  digitalWrite(_clockEnablePin, HIGH);

#ifdef SHIFT_DEBUG
  Serial.print("Pin States: ");
#endif

  int counter = 0;

  for (int j = 0; j < SNX4HX165_NUMBER_OF_CHIPS; j++) {
    for (int i = SNX4HX165_PARALLEL_INPUTS-1; i >= 0; i--) {
      // bool b = bitRead(incoming, i);
      bool b = bitRead(_incoming[j], i);
      buttonArray[counter] = b;
      counter = counter + 1;
#ifdef SHIFT_DEBUG
      Serial.print(b);
#endif
    }
  }
}
 
Last edited:
why4.gif


"Proper" hat behavior on the bottom. The axis goes to 1.29 when there's no hat direction pushed on the Extreme 3D Pro.

The Teensy flies off to 3.29 and I can't seem to control that. If I force the hat axis to like 0, 360, or 180 it'll stick there, but the bug persists where that input is like holding an arrow key down in the game. This 3.29 is a result of setting the hat to -1. This is also without any other code but this in the setup back to the normal sized joystick:

Code:
Joystick.X(512);
Joystick.Y(512);
Joystick.Z(512);
Joystick.Zrotate(512);
Joystick.sliderLeft(512);
Joystick.sliderRight(512);
Joystick.hat(-1);
 
Changed the hat code. Moving the null value down from 15 changed the null angle and stopped it interpreting as being held down in the game.

Here's the different hat angle values caused by the different nulls.

15 3.29
14 3
13 2.71
12 2.43
11 2.43
10 1.86
9 1.57
8 1.29

Why did this work? No clue.

Far as I understand this 0x81, 0x42, // Input (variable,absolute,null_state) means the HID null state should take over and that's supposed to be:

When in a null state, the control will
report a value outside of the specified Logical
Minimum and Logical Maximum (the most
negative value, such as -128 for an 8-bit value).


According to HID 1.11 I guess. Or I'm reading it wrong. Edit: Yeah, I am. I thought that being in the null state meant the joystick would report the logical minimum, but the logical min for the joystick was 0 so it couldn't have been that. I guess the point is that the 8 I'm using is outside of the 0 - 7 minimum and maximum setup in usb_desc.c for the hat:

Code:
        0x15, 0x00,                     //   Logical Minimum (0)
        0x25, 0x07,                     //   Logical Maximum (7)

Anyway, here's what I changed:

usb_joystick.h:

Code:
inline void hat(int dir) {
    uint32_t val = 0;
    if (dir < 0) val = 8;
    else if (dir < 23) val = 0;
    else if (dir < 68) val = 1;
    else if (dir < 113) val = 2;
    else if (dir < 158) val = 3;
    else if (dir < 203) val = 4;
    else if (dir < 245) val = 5;
    else if (dir < 293) val = 6;
    else if (dir < 338) val = 7;
    usb_joystick_data[1] = (usb_joystick_data[1] & 0xFFFFFFF0) | val;
    if (!manual_mode) usb_joystick_send();
}
 
Last edited:
Back
Top