Issue with joystick axis scaling on Linux

Status
Not open for further replies.

RichG

New member
Hi guys.

My first post here, so I guess the drinks are on me :D I've just started exploring the delightful world of Teensy having had some experience with Arduino and also PIC devices (although not for a few years), and very impressed and inspired so far.

As a first experiment I've put together a little box with a Teensy LC, a few pots (50k linear), switches and a rotary encoder which I have initially set up to function as an extra joystick controller (for flight simulator).

However, I've hit upon an issue with the scaling of the analog axis when connected to Linux. I've seached through the forums here and haven't seen mention of anything similar, but apologies if this is a known issue.

I've used simple code to translate the pots:
Code:
 Joystick.X(analogRead(potPIN[0]));

When connected to my Windows PC this functions as expected and the pot gives control over it's full range. However, when plugged into a Linux machine the joystick axis only operates across a reduced range of the pot (approx 9 o'clock to 3 o'clock). In other words the axis value, as seen in the joystick control panel, is at a minimum when pot is at 9oc and maximum at 3oc. This greatly reduces the usablity.

I can fix this by scaling the pot in code:
Code:
 Joystick.X(map(analogRead(potPIN[0]), 0, 1023, 60, 960));

Which then gives me full range back, but then will no longer work correctly when connected to Wndows.

I've checked this on both my Linux desktop (running KDE Plasma on Arch with Linux kernel 4.14.14) and my laptop (running Manjaro KDE (which is Arch based) and again Linux 4.14.14) and see the same on both. Teensy coding was done on Arduino 1.8.5 / Teensduino 1.41.

Have I missed something? Or is there perhaps workaround?

Thanks.
 
Here's the full code for reference:
Code:
#include <Encoder.h>
#include <Bounce2.h>

// Teensy LC Pin definitions

int ledPIN[7] = {10, 9, 22, 23, 20, 26, 13};
int buttonPIN[8] = {2,3,4,5,6,7,8,11};
int togglePIN = 0;
int pushPIN = 1;
int potPIN[6] = {14,15,16,17,18,19};
int encPIN1 = 12;
int encPIN2 = 25;
int encswPIN = 24;
int pedalPIN = 21;

// Array to store pot values
int potVal[6];

// Array to store button values
bool butt[16];

Bounce butt_0 = Bounce(buttonPIN[0], 5);
Bounce butt_1 = Bounce(buttonPIN[1], 5);
Bounce butt_2 = Bounce(buttonPIN[2], 5);
Bounce butt_3 = Bounce(buttonPIN[3], 5);
Bounce butt_4 = Bounce(buttonPIN[4], 5);
Bounce butt_5 = Bounce(buttonPIN[5], 5);
Bounce butt_6 = Bounce(buttonPIN[6], 5);
Bounce butt_7 = Bounce(buttonPIN[7], 5);

Bounce toggleSW = Bounce(togglePIN, 5);
Bounce pushSW = Bounce(pushPIN, 5);
Bounce encPush = Bounce(encswPIN, 5);

Encoder rotary(encPIN1, encPIN2);
long encoderPosition = -999;
long newPosition;
long oldPosition;

IntervalTimer usbTimer;
IntervalTimer encoderTimer;


// ############## SETUP ################ //
void setup() {

int i;

// Configure Pins

for (i=0; i < 7 ; i++) { 
  pinMode(ledPIN[i], OUTPUT);
  }

for (i=0; i < 8; i++) {
  pinMode(buttonPIN[i], INPUT_PULLUP);  
  }

for (i=0; i < 6; i++) {
  pinMode(potPIN[i], INPUT);
}
  
pinMode(pushPIN, INPUT_PULLUP);
pinMode(togglePIN, INPUT_PULLUP);

pinMode(encswPIN, INPUT_PULLUP);

pinMode(encPIN1, INPUT_PULLUP);
pinMode(encPIN2, INPUT_PULLUP);

pinMode(pedalPIN, INPUT);

//Configure joystick values to be sent only on demand
Joystick.useManualSend(true);

// Configure USB Serial Baud Rate
Serial.begin(38400);

//Configure resolution of PWM duty cycle 
//analogWriteResolution(10);

analogReadRes(10);
analogReadAveraging(32);

usbTimer.begin(updateRoutine, 50000);
encoderTimer.begin(encoderRoutine, 5000);

butt[11] = 1;
butt[12] = 1;
butt[13] = 1;

}


// ########## ENCODER ROUTINE ################ //
void encoderRoutine() {

    if (newPosition != encoderPosition) {
      if (newPosition > encoderPosition) {
          butt[14] = 0;
      }
      if (newPosition < encoderPosition) {
          butt[15] = 0;
      }
    }
    encoderPosition = newPosition;
}


// ########## UPDATE ROUTINE ############### //
void updateRoutine() {

int i=0;

// Read pot values
/*
  Joystick.X(map(analogRead(potPIN[0]), 0, 1023, 60, 960));
  Joystick.Y(map(analogRead(potPIN[1]), 0, 1023, 60, 960));
  Joystick.Z(map(analogRead(potPIN[2]), 0, 1023, 60, 960));
  Joystick.Zrotate(map(analogRead(potPIN[3]), 0, 1023, 60, 960));
  Joystick.sliderLeft(map(analogRead(potPIN[4]), 0, 1023, 60, 960));
  Joystick.sliderRight(map(analogRead(potPIN[5]), 0, 1023, 60, 960));

  Serial.println(analogRead(potPIN[0]));
*/

  Joystick.X(analogRead(potPIN[0]));
  Joystick.Y(analogRead(potPIN[1]));
  Joystick.Z(analogRead(potPIN[2]));
  Joystick.Zrotate(analogRead(potPIN[3]));
  Joystick.sliderLeft(analogRead(potPIN[4]));
  Joystick.sliderRight(analogRead(potPIN[5]));


// Set LEDs
if (butt[0] & butt[1]) {
  digitalWrite(ledPIN[0], LOW);  
}else{
  digitalWrite(ledPIN[0], HIGH);  
}

if (butt[2] & butt[3]) {
  digitalWrite(ledPIN[1], LOW);  
}else{
  digitalWrite(ledPIN[1], HIGH);  
}

if (butt[4] & butt[5]) {
  digitalWrite(ledPIN[2], LOW);  
}else{
  digitalWrite(ledPIN[2], HIGH);  
}

if (butt[6] & butt[7]) {
  digitalWrite(ledPIN[3], LOW);  
}else{
  digitalWrite(ledPIN[3], HIGH);  
}

digitalWrite(ledPIN[5], !butt[8]);

digitalWrite(ledPIN[4], !butt[14]);
digitalWrite(ledPIN[6], !butt[15]);

//set joystick button if pushed
for (i=0; i <16; i++) {
  Joystick.button(i+1, !butt[i]);
}

//Send joystick values to USB  
Joystick.send_now();

// reset encoder push button
butt[14] = 1;
butt[15] = 1;

// stop unused buttons from sending jostick presses
butt[11] = 1;  
butt[12] = 1;
butt[13] = 1;

}


// ############## MAIN ################# //
void loop() {

//check switches
butt[0] = butt[0] ^ butt_0.update();
butt[1] = butt[1] ^ butt_1.update();
butt[2] = butt[2] ^ butt_2.update();
butt[3] = butt[3] ^ butt_3.update();
butt[4] = butt[4] ^ butt_4.update();
butt[5] = butt[5] ^ butt_5.update();
butt[6] = butt[6] ^ butt_6.update();
butt[7] = butt[7] ^ butt_7.update();
butt[8] = butt[8] ^ toggleSW.update();
butt[9] = butt[9] ^ pushSW.update();
butt[10] = butt[10] ^ encPush.update();

//check rotary encoder
newPosition = rotary.read();

}
 
Do you have jstest-gtk. If so can you calibrate it to work properly. I always caiibrate manually. The save configuration is broken in jstest-gtk, but in my notes, I have the command to save the config once its calibrated in jstest. I had to make a giant dead zone for a joystick ztwist axis so i figured it out. If you can get it adjusted in jstest, I will find instructions for saving the calibration in my notes
 
Thanks for the response Jeff.

I've installed jstest-gtk and that shows the default calibration as being set to having RangeMin = 63 and RangeMax = 959 (which correlates to the scaling I manually set in the Teensy code to get it working across the fuil range).
If I manually set these to 0 / 1023 then all is good except, as you suggest, the settings need to be redone after replugging the joystick (and presumably after rebooting).

Why is doesn't default to 0/1023 is a mystery?

There is a section in the Arch Linux wiki (https://wiki.archlinux.org/index.php/Gamepad) which covers this and suggests using a udev rule to load the calibration settings, but my initial attempts with this have failed so far (nothing in Arch is ever straighforward). I'll have another try at sorting this when i can get back to it, but if you do have another way of loading the settings that would be very appreciated.

Thanks again.
 
sudo jscal-store /dev/input/js1
in Ubuntu.... and of course get js(x) right....screenshot shows mistake
not sure how arch does super user privileges, so sudo may not be the command in arch but the rest is the command to save joystick calibration
 

Attachments

  • Screenshot from 2017-12-15 23-21-50.jpg
    Screenshot from 2017-12-15 23-21-50.jpg
    168.1 KB · Views: 124
Last edited:
I just tried this on my Linux Mint Mate 18.2 machine, and I had to install "joystick" package in addition to jstest-gtk on that machine to get the jscal-store command to work. It's not the first time Mint has made me fix something that worked out of the box in Ubuntu proper. Hope this helps anybody using Linux developing a joystick.
 
sudo jscal-store /dev/input/js1
Thanks for that.
While that does store the calibration, it doesn't get restored automatically on plugging the device in (like I said, nothing is straightforward in Arch :confused:).
I can restore the settings by manually doing:
Code:
sudo jscal-restore /dev/input/js1
Which i'm happy to do, certainly for the time being until I get the udev rule to work (I'm thinking that may be failing because there already exists a rule for the device as required by Teensyduino).
 
Status
Not open for further replies.
Back
Top