You're reading ...
Arduino, Hardware, Software

A super smooth Joystick and Throttle part 3: wiring and software (with download)

This is part 3 of 3 on building your own ‘super smooth’ joystick and throttle that can be used with flight simulators or racing games.

  1. The final result and the components needed.
  2. The woodwork, a Fusion 360 model and drawings with dimensions.
  3. The wiring and the software, with a download link of course.

An Arduino Leonardo is the heart of the system. It reads out the push buttons, switches and potentiometers and it sends the data via USB to the PC. Beware … a standard Arduino UNO won’t work … a Leonardo has a different kind of Atmel chip on board, one that connects to USB in a way that makes it possible to function as a HID (Human Interface Device), which is needed for this application. A Leonardo can be acquired here.

The wiring.

The list below shows all connections to be made.

All pushbuttons are connected to GND at one side. The three way toggle switch has GND in the middle. The potentiometers are connected to 5V and GND on the outsides and the slider / rotator go to pins A0-A3. The rotary encoder also needs 5V and GND, its noted on the connector which pin is witch.

Leonardo_ICSPA0 joystick X
A1 joystick Y
A2 joystick Z
A3 throttle slider pot
A4 rotary encoder CLK
A5 rotary encoder DATA
0 do not use, it is needed for USB
1 do not use, it is needed for USB
2 pushbutton 1
3 pushbutton 2
4 pushbutton 3
5 pushbutton 4
6 pushbutton 5
7 pushbutton 6 (on top of the joystick)
8 pushbutton 7
9 pushbutton 8
10 pushbutton 9
11 pushbutton 10
12 pushbutton 11 (rotary encoder)
13 LED
MISO three way toggle switch up
SCK three way toggle switch down

The LEDs at the bottom of the Joyctick and Throttle are connected to 5V and GND, with the resistor in between of course. Take care the long leg goes to the 5V. They show there is power. The third LED long leg = pin 13 … it lights when a button is pressed.

blog_throt_img_bottom   blog_joy_img_bottom

My joystick and throttle do not look very tidy … but all wires are firmly connected. I always do a ‘pull check’ and a resistance measurement immediately after soldering every wire to assure all is OK before I go on.

The software (download link here).

First of all many thanks go to M. Heironimus for making his Arduino USB HID library publicly available. It is thanks to this library that the software effort to make things work was minimal.

The joystick X,Y,Z axis are configured as 10 bits, running from -511 to +511, with an S-curve for very fine control around the middle.

The throttle linear pot is configured as 10 bits, running from 0 to 1023.

With the .ino file with ‘analog’ in the name the rotary encoder is configured as an analog axis with an 8 bits range of -127 to +127. Pressing its switch will set it to zero. To avoid having to turn it many times to get to the limits, during calibration in FSX or X-plane you can press the encoder switch and button 9 or 10 simultaneously to quickly go to limit values. I use it for pitch trim up / down, it gives a nicer feel than using push buttons for pitch trim.

With the .ino file with ‘pulses’ in the name the encoder is used to simulate two push buttons, which can for instance be used as rudder- or elevator trim. The joystick button numbers used are 13 and 14.

Follow these steps to upload the software to your Arduino Leonardo (also see the video below):

  1. Visit the Arduino website and follow instructions to install the Arduino IDE software on your PC.
  2. Start up the IDE and go to File > Preferences to tell where you want to store your ‘sketches’ (‘sketches’ is the Arduino name for programs). Close the IDE again.
  3. Download the joystick software here and unzip it.
  4. Copy or Move the ‘Joystick’ folder to your Arduino/libraries folder. (Depends where you installed it … default is C:\Program Files (x86)\Arduino)
  5. Copy or Move the RBO_Joystick folder to your sketches folder.
  6. Restart the IDE and open the RBO_Joystick.ino sketch.
  7. Upload the sketch (the arrow icon at the top left).

Control_PanelConfiguration

If needed, disconnect and reconnect your USB cable to the PC … the PC should now recognize the Leonardo as a HID. It can be tested via the Windows Control Panel > Printers and Devices.

If your joystick is recognized it is now time to start FSX or X-plane and go to the ‘settings’ to configure your joystick and throttle axis and buttons.

The video shows the steps to take to upload the software to your Arduino Leonardo.

About RudyB

Hobbyist

Discussion

12 thoughts on “A super smooth Joystick and Throttle part 3: wiring and software (with download)

  1. Hi, again, anyway I can combine the code below for Force/Feedback/Vibration with your code?
    https://github.com/YukMingLaw/ArduinoJoystickWithFFBLibrary

    Like

    Posted by Eric | September 8, 2020, 16:17
  2. Hello,
    Thank you for sharing this.
    The Axis Y and X responding only at the very end, any way to fix this?
    By the way, is this is the latest version?
    last question, any way to add HAT and maybe force back?
    Regards

    Like

    Posted by Eric | August 16, 2020, 15:57
    • Is your potentiometer maybe of the logarithmic type in stead of linear? You could check by loading a sketch that does nothing more than serial print the analog value rad from the input pin.
      Yes, the download is the latest version, although I created another version where the digital encoder is not translated to an analog value but rather sends out pulses to be used for instance as pitch trim.
      HAT switches can of course be added as push buttons, provided you have enough input pins. you may have to start using a digital input matrix, such that with 4+4 inputs you have 16 switches.

      Liked by 1 person

      Posted by RudyB | August 24, 2020, 09:02
      • Hello, I am using a pro micro, so without A4 and A5 I could not use your code directly. I made some alterations to fit my board and setup, everything works great, except the encoder. I cannot get it to change the value of the assigned axis, it just stays in the middle. I will post my code below. Do you happen to have the other version using pulses for the pitch trim available?

        // CONFIG
        #define MAX_SWITCHES 7 // the number of switches
        byte switch_pin[MAX_SWITCHES] = {2,3,5,7,14,15,16}; // digital input pins
        #define DEBOUNCE_TIME 5 // ms delay before the push button changes state
        #define MAX_ANALOG 4 // the number of analog inputs
        byte analog_pin[MAX_ANALOG] = {A0,A1,A2,A3}; // analog input pins Rx,Ry,RUDDER,THROTTLE
        #define ENC_CLK_PIN A8 // rotary encoder CLK input
        #define ENC_DAT_PIN A9 // rotary encoder DATA input
        #define ENC_MAX 127 // max value of the rotary encoder
        #define ENC_MIN -127 // min value of the rotary encoder
        #define ENC_ZERO 0 // value to jump to after pressing encoder switch
        // END CONFIG

        // DECLARATIONS
        #include “Joystick.h”
        Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_JOYSTICK, MAX_SWITCHES, 0, false, false, true, true, true, false, true, true, false, false, false);
        byte reading, clk, clk_old;
        byte switch_state[MAX_SWITCHES];
        byte switch_state_old[MAX_SWITCHES];
        int analog_value[MAX_ANALOG+1]; // +1 for the rotary encoder value
        unsigned long debounce_time[MAX_SWITCHES+1]; // +1 for CLK of rotary encoder
        // END DECLARATIONS

        // FUNCTIONS
        int read_rotary_encoder() {
        clk = digitalRead(ENC_CLK_PIN);
        if (clk == clk_old) debounce_time[MAX_SWITCHES] = millis() + (unsigned long)DEBOUNCE_TIME;
        else if (millis() > debounce_time[MAX_SWITCHES]) { // clk has changed and is stable long enough
        clk_old = clk;
        if (clk) {if (digitalRead(ENC_DAT_PIN)) return 1; else return -1;}
        }
        return 0;
        }
        // END FUNCTIONS

        // SETUP
        void setup() {
        for (byte i=0; i<MAX_SWITCHES; i++) pinMode(switch_pin[i],INPUT_PULLUP);
        pinMode(ENC_CLK_PIN,INPUT_PULLUP);
        pinMode(ENC_DAT_PIN,INPUT_PULLUP);
        pinMode(13,OUTPUT); // on board LED
        digitalWrite(13,0);
        Joystick.begin(false);
        Joystick.setRxAxisRange(0, 1023);
        Joystick.setRyAxisRange(0, 1023);
        Joystick.setRudderRange(0, 1023);
        Joystick.setZAxisRange(ENC_MIN, ENC_MAX);
        Joystick.setThrottleRange(0, 1023);
        } // END SETUP

        // LOOP
        void loop() {
        for (byte i=0; i debounce_time[i]) switch_state[i] = reading;
        if (switch_state[i] != switch_state_old[i]) { // debounced button has changed state
        // this code is executed once after change of state
        digitalWrite(13,switch_state[i]);
        if (switch_state[i]) Joystick.pressButton(i); else Joystick.releaseButton(i);
        if (i==2) analog_value[A8] = ENC_ZERO;
        switch_state_old[i] = switch_state[i]; // store new state such that the above gets done only once
        }
        } //END read the switches

        for (byte i=0; i<MAX_ANALOG; i++) { // read analog inputs
        analog_value[i] = analogRead(analog_pin[i]);
        if (analog_value[i] < 256) analog_value[i] = analog_value[i] * 1.5;
        else if (analog_value[i] < 768) analog_value[i] = 256 + analog_value[i] / 2;
        else analog_value[i] = 640 + (analog_value[i] – 768) * 1.5;
        switch(i) {
        case 0:
        Joystick.setRxAxis(analog_value[0]);
        break;
        case 1:
        Joystick.setRyAxis(analog_value[1]);
        break;
        case 2:
        Joystick.setRudder(analog_value[2]);
        break;
        case 3:
        Joystick.setThrottle(analog_value[3]);
        break;
        }
        }
        analog_value[8] = analog_value[8] + read_rotary_encoder();
        Joystick.setZAxis(analog_value[8]);
        // use 2 switches simultaneously to quickly calibrate the rotary encoder
        if (!digitalRead(switch_pin[3]) && !digitalRead(switch_pin[2])) analog_value[8] = ENC_MAX;
        if (!digitalRead(switch_pin[5]) && !digitalRead(switch_pin[2])) analog_value[8] = ENC_MIN;
        Joystick.sendState();
        delay(10);
        } // END LOOP

        Like

        Posted by Clinton E Melson | September 8, 2020, 08:08
      • I’ll send you an email with the pulse code. Ruud.

        Like

        Posted by RudyB | September 8, 2020, 08:14
  3. HI
    is the code correct without rotary encoder with 12 botton?

    // Ruud Boer, June 2018
    // Joystick with Arduino Leonardo

    // CONFIG
    #define MAX_SWITCHES 13 // the number of switches
    byte switch_pin[MAX_SWITCHES] = {2,3,4,5,6,7,8,9,10,16,14,15}; // digital input pins
    #define DEBOUNCE_TIME 5 // ms delay before the push button changes state
    #define MAX_ANALOG 4 // the number of analog inputs
    byte analog_pin[MAX_ANALOG] = {A0,A1,A2,A3}; // analog input pins X,Y,Z,THROT
    #define ENC_CLK_PIN A4 // rotary encoder CLK input
    #define ENC_DAT_PIN A5 // rotary encoder DATA input
    #define ENC_MAX 127 // max value of the rotary encoder
    #define ENC_MIN -127 // min value of the rotary encoder
    #define ENC_ZERO 0 // value to jump to after pressing encoder switch
    // END CONFIG

    // DECLARATIONS
    #include “Joystick.h”
    Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_JOYSTICK, MAX_SWITCHES, 0, true, true, true, false, false, false, true, true, false, false, false);
    byte reading, clk, clk_old;
    byte switch_state[MAX_SWITCHES];
    byte switch_state_old[MAX_SWITCHES];
    int analog_value[MAX_ANALOG+1]; // +1 for the rotary encoder value
    unsigned long debounce_time[MAX_SWITCHES+1]; // +1 for CLK of rotary encoder
    // END DECLARATIONS

    // SETUP
    void setup() {
    Joystick.begin(false);
    Joystick.setXAxisRange(-511, 511);
    Joystick.setYAxisRange(-511, 511);
    Joystick.setZAxisRange(-511, 511);
    Joystick.setRudderRange(ENC_MIN, ENC_MAX);
    Joystick.setThrottleRange(0, 1023);
    } // END SETUP

    // LOOP
    void loop() {
    for (byte i=0; i debounce_time[i]) switch_state[i] = reading;
    if (switch_state[i] != switch_state_old[i]) { // debounced button has changed state
    // this code is executed once after change of state
    digitalWrite(13,switch_state[i]);
    if (switch_state[i]) Joystick.pressButton(i); else Joystick.releaseButton(i);
    if (i==10) analog_value[4] = ENC_ZERO;
    switch_state_old[i] = switch_state[i]; // store new state such that the above gets done only once
    }
    } //END read the switches

    for (byte i=0; i<MAX_ANALOG; i++) { // read analog inputs
    analog_value[i] = analogRead(analog_pin[i]);
    if (analog_value[i] < 256) analog_value[i] = analog_value[i] * 1.5;
    else if (analog_value[i] < 768) analog_value[i] = 256 + analog_value[i] / 2;
    else analog_value[i] = 640 + (analog_value[i] – 768) * 1.5;
    switch(i) {
    case 0:
    Joystick.setXAxis(511 – analog_value[0]);
    break;
    case 1:
    Joystick.setYAxis(511 – analog_value[1]);
    break;
    case 2:
    Joystick.setZAxis(511 – analog_value[2]);
    break;
    case 3:
    Joystick.setThrottle(analog_value[3]);
    break;
    }
    }
    Joystick.setRudder(analog_value[4]);
    // use 2 switches simultaneously to quickly calibrate the rotary encoder
    Joystick.sendState();
    delay(10);
    } // END LOOP

    Like

    Posted by Claudio | May 10, 2020, 18:15
  4. A very interesting and useful project. I am wanting to make a simple throttle only for a space sim (Elite Dangerous) using a 10 slider pot. I have your sketch working on a Leonardo but a couple of questions. As I am using only 1 analogue input and possibly 1 digital input can I still use the sketch as is and just ignore the random noise on the other inputs or do I need to edit the sketch for only the inputs I am using.

    I am hoping to use a Pro Micro Arduino in the finished project as the Leonardo is too big for this purpose, it’s on a breadboard at the moment. I believe the Pro Micro is Leonardo based so can you envisage any probelms.

    Like

    Posted by Rob | October 29, 2019, 12:42

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Archive of all posts

Categories

%d bloggers like this: