Xbox controller input - jittering

Hello,
Having a bit of trouble with getting a usable signal out of a wired xbox controller while using the USB_Host_Shield_2.0 and XBOXOLD.h libraries.
The controller is connecting properly and is being used to drive a pwm shield in a mapped output for the hats. Below is the relevant bit of code mapping the left hat in the X axis.

The objective is to have 0% output when the stick is at it's neutral position, and 100% output to either channel of the pwm when the stick is moved to either side on the x axis.

The problem is that the output is not behaving as it should, as the controller signal is jittery and bouncing all around. Any tips as to what's happening?

Also uncertain that between 7500 and -7500 is the deadzone on the controller.

Cheers.

Usb.Task();
  if (Xbox.XboxConnected) {
    if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) {

    
      if (Xbox.getAnalogHat(LeftHatX) > 7500) {
          val_LHXposMap = map(Xbox.getAnalogHat(LeftHatX), 7500, 32767, 0, 4095);
          pwm.setPWM(0, 0, val_LHXposMap);
          Serial.print(F("LeftHatX+: "));
          Serial.print(val_LHXposMap);
          Serial.print("\t");
        }
        if (Xbox.getAnalogHat(LeftHatX) < -7500) {
          val_LHXnegMap = map(Xbox.getAnalogHat(LeftHatX), -7500, -32767, 0, 4095);
          pwm.setPWM(1, 0, val_LHXnegMap);
          Serial.print(F("LeftHatX-: "));
          Serial.print(val_LHXnegMap);
          Serial.print("\t");
        }

All A/D converter values jitter because there is always noise in the system. With higher resolution it seems like you get more jitter because with the same noise level you can resolve the noise better as well.

First print the raw data you get from the controller. Then choose a deadband value that is large enough to ensure the value does not fluctuate when the stick is in neutral position.

So the jittering problem has been resolved- though now it's a matter of solving strange values.

Since the output from the controller is 16bit, a value between -32767 and 32767 is expected along each axis of the joystick. The values do indeed show -32767 and 32767 when the stick is moved to the ends of it's axis, though the values in between are strange and erroneous. They show up to 90000 in some cases when the stick is in an arbitrary position that's not the ends of it's travel. The middle zone (along all axes of each stick) also sits in at ~-13000 for reasons not entirely apparent.

The aim is to map the inputs to control a pwm output but for right now it's simply mapped to show 0-100 on the display for verification.

Any clues as to why the numbers seem to travel way outside of their bounds when the sticks are moved?
And why the sticks neutral positions seem to sit at -13000 or so?

-Two different controllers as well as two arduinos have been tried and they give similar results.
-The majority of this code is that supplied with the USB Shield Xbox controller library examples.

Entire code below:

#include <Adafruit_PWMServoDriver.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <usbhub.h>
#include <Wire.h>
#include <XBOXUSB.h>

#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
USB Usb;
USBHub  Hub1(&Usb);
XBOXUSB Xbox(&Usb);

int val_LHX;
int val_LHY;
int val_RHX;
int val_RHY;

void setup() {
  lcd.begin(20, 4);
  lcd.clear();

  Serial.begin(9600);
#if !defined(__MIPSEL__)
  while (!Serial);
#endif

  if (Usb.Init() == -1) {
    lcd.print("USB Shield error.");
    while (1); // halt
  }

  pwm.begin();
  pwm.setPWMFreq(1600);  // This is the maximum PWM frequency
  uint8_t twbrbackup = TWBR;
  TWBR = 12;
  
  lcd.clear();
}

void loop() {
  Usb.Task();
  if (Xbox.Xbox360Connected) {

    lcd.setCursor(0, 0);
    lcd.print("LX: ");
    lcd.print(Xbox.getAnalogHat(LeftHatX));
    val_LHX = map(Xbox.getAnalogHat(LeftHatX), -32767, 32767, 0, 100);
    lcd.setCursor(16, 0);
    lcd.print(val_LHX);

    lcd.setCursor(0, 1);
    lcd.print("LY: ");
    lcd.print(Xbox.getAnalogHat(LeftHatY));
    val_LHY = map(Xbox.getAnalogHat(LeftHatY), -32767, 32767, 0, 100);
    lcd.setCursor(16, 1);
    lcd.print(val_LHY);

    lcd.setCursor(0, 2);
    lcd.print("RX: ");
    lcd.print(Xbox.getAnalogHat(RightHatX));
    val_RHX = map(Xbox.getAnalogHat(RightHatX), -32767, 32767, 0, 100);
    lcd.setCursor(16, 2);
    lcd.print(val_RHX);

    lcd.setCursor(0, 3);
    lcd.print("RY: ");
    lcd.print(Xbox.getAnalogHat(RightHatY));
    val_RHY = map(Xbox.getAnalogHat(RightHatY), -32767, 32767, 0, 100);
    lcd.setCursor(16, 3);
    lcd.print(val_RHY);
  }
  delay(1);
}

There is something strange going on. The results you are seeing should not be possible with the library code. There is potential bug in the library. But I do not know enough about the hardware to be certain.

Could you be so kind and let me know what Arduino you are using and what hardware (shield and XBOX controller)?

If the XBOX controller really has a 16-bit A/D converter or scales the value to a 16-bit, then the library code does not work correctly. In the XBOXUSB.cpp file line 252, the value is calculated.

hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8 ) | readBuf[6]);

The value from the XBOX controller is likely an unsigned A/D converter value. So that should start from 0 and go up to the maximum depending how many bits the A/D has. The bits are sent as two bytes.

The code combines the two bytes as unsigned 16-bit which is fine, but then casts it to signed 16-bit. This only works if the A/D has less than 16 bits. And the result will always be a positive number.

If the intention of the library is to get a value from -32768 to 32767 than the code should cast to a 32-bit and subtract 32768 and then cast back to a 16-bit signed value. e.g.

hatValue[LeftHatX] = ( int16_t )( ( int32_t )( ( ( uint16_t )readBuf[7] << 8 ) | readBuf[6] ) - 32768 );