Gravis Stinger PC joystick with Arduino Nano

This is my first contribution here. I use linux and wanted to see if it would be possible to use an arduino nano and a few pots to fly X-plane. It is.
I used the "Gravis Stinger" protocol, which talks to the PC via serial interface. Since all the nanos have serial, it was quite easy.
Downside is the "Gravis stinger" only has 2 analog axis and 10 buttons. But it is a start, and very fun to sim with this homebrew controller.

Now I'm going to checkout the "XPlane direct plugin" to do more advanced things directly interfacing X-plane itself, but this comment here is a generic PC joystick made with any arduino that has a serial port and it will work with any game/application, not only X-plane.

Code: (no libraries, just plain arduino copy-and-paste code)

/*

"Gravis Stinger" 10-button, 2 axis joystick for ANY arduino, connected as the serial port used for programming the arduino

Resolution is 0-127, that is -63 to +63 aprox. That is multiplied up to 2 bytes in linux native resolution.

https://github.com/torvalds/linux/blob/master/drivers/input/joystick/stinger.c

The stinger protocol has a resolution of 7 bit, that is 0..128 decimal or 0x00..0x7F hex.

## (Linux:)
## dump values output by arduino to debug this program:
# inputattach --baud 9600 --dump /dev/ttyUSB0

## attach joystick in linux to use it in applications:
# sudo inputattach --baud 9600 --always --stinger /dev/ttyUSB0
## becomes /dev/input/js0 on linux mint anyway

## after attaching: to test the joystick:
# jstest /dev/input/js0

## Minimum value (pot zero) should be reported as -32767 and max should be 32767



Physical build instructions:
* no power needed if you use something like Arduino Nano. It feeds on the PC's USB port.
* connect pot wiper to analog pin, and the other two pot pins to 5V and GND respectively
* connect digital pins to GND to activate
  

Arno Teigseth (L) GPL 2021
arno at teigseth dot no

*/

// you can specify which axis is 
int sensorPinX = A0;    // select the input pin for the potentiometer
int sensorPinY = A1;    // select the input pin for the potentiometer

// you can specify which button is which digital input here

int btnPin0 = 2; // Digital pin 2 = just "2"
int btnPin1 = 3;
int btnPin2 = 4;
int btnPin3 = 5;
int btnPin4 = 6;
int btnPin5 = 7;
int btnPin6 = 8;
int btnPin7 = 9;
int btnPin8 = 10;
int btnPin9 = 11;

unsigned long sensorValueX = 0; // variable to store pot value X (0..1023)
unsigned long sensorValueY = 0; // variable to store pot value Y (0..1023)

int btnValue0 = 0;
int btnValue1 = 0;
int btnValue2 = 0;
int btnValue3 = 0;
int btnValue4 = 0;
int btnValue5 = 0;
int btnValue6 = 0;
int btnValue7 = 0;
int btnValue8 = 0;
int btnValue9 = 0;

void setup() {
 // one-time, initial setup here
 
  pinMode(btnPin0, INPUT_PULLUP);
  pinMode(btnPin1, INPUT_PULLUP);
  pinMode(btnPin2, INPUT_PULLUP);
  pinMode(btnPin3, INPUT_PULLUP);
  pinMode(btnPin4, INPUT_PULLUP);
  pinMode(btnPin5, INPUT_PULLUP);
  pinMode(btnPin6, INPUT_PULLUP);
  pinMode(btnPin7, INPUT_PULLUP);
  pinMode(btnPin8, INPUT_PULLUP);
  pinMode(btnPin9, INPUT_PULLUP);
  
  // serial port speed
  Serial.begin(9600);
}

void loop() {
  // read analog values
  sensorValueX = analogRead(sensorPinX);
  sensorValueY = analogRead(sensorPinY);

  // read digital values
  btnValue0 = digitalRead(btnPin0);
  btnValue1 = digitalRead(btnPin1);
  btnValue2 = digitalRead(btnPin2);
  btnValue3 = digitalRead(btnPin3);
  btnValue4 = digitalRead(btnPin4);
  btnValue5 = digitalRead(btnPin5);
  btnValue6 = digitalRead(btnPin6);
  btnValue7 = digitalRead(btnPin7);
  btnValue8 = digitalRead(btnPin8);
  btnValue9 = digitalRead(btnPin9);
  
  byte buf[4]; // this is what we will feed the PC with

  buf[0] = 0;
  
  if (!btnValue0) bitSet(buf[0], 5);
  if (!btnValue1) bitSet(buf[0], 4);
  if (!btnValue2) bitSet(buf[0], 3);
  if (!btnValue3) bitSet(buf[0], 2);

  // bit  (4) = btn 3
  // bit  (8) = btn 2
  // bit (10) = btn 1
  // bit (20) = btn 0

  

  buf[3] = 0;

  if (!btnValue4) bitSet(buf[3], 5);
  if (!btnValue5) bitSet(buf[3], 4);
  if (!btnValue6) bitSet(buf[3], 3);
  if (!btnValue7) bitSet(buf[3], 2);
  if (!btnValue8) bitSet(buf[3], 1);
  if (!btnValue9) bitSet(buf[3], 0);

  // LSB  (1) = btn9
  // bit  (2) = btn8
  // bit  (4) = btn7
  // bit  (8) = btn6
  // bit (10) = btn5
  // bit (20) = btn4



  // AXIS 0 (x)
  byte sensvalX = floor(sensorValue / 8 ) ; // return 0-127
  if (sensvalX > 63) {
    buf[1] = (sensvalX - 64) & 0x3f;
  } else {
    bitSet(buf[0], 0);
    buf[1] = (sensvalX) & 0x3f;
  }
  

  // AXIS 1 (y)  
  byte sensvalY = floor(sensorValue2 / 8 ); // return 0-127
  if (sensvalY > 63) {
      buf[2] = (63 - sensvalY) & 0x3f;
      bitSet(buf[0], 1);
  } else {
    buf[2] = (63 - sensvalY) & 0x3f;
  }
  
  
  Serial.write(buf, sizeof(buf));
  
  delay(100); // aproximately 10 updates per second (1000ms/100)
}

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.