Joysticks and Raspberry Pi

From FlightGear wiki
Revision as of 22:07, 4 January 2021 by Puffergas (talk | contribs) (Created page with "{{FlightGearRaspberryPi4 Navigation}} == Joysticks == thumb|150px|Joystick calibration using jstest-gtk dialog box. You will fin...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search


Joysticks

Joystick calibration using jstest-gtk dialog box.

You will find that controlling your aircraft, with the official Raspberry Pi keyboard and mouse, too be rather difficult. A joystick will solve this problem. The Logitech Extreme 3D PRO is plug and play, for the most part, with FlightGear. Most modern joysticks should also work. There is a dialog box to assign common joystick controls, in FlightGear. Controls are also properties, however that is for later. This joystick dialog box is found in the top menu bar under 'Files'. Next you will find 'Joystick Configuration' to click on and that will bring up the desired dialog box.

If the joystick’s travel is not being used efficiently or the neutral dead band is not right, the joystick can be calibrated. Install jstest-gtk through the Raspbian software installer or use the terminal and apt-get install command.

sudo apt-get install jstest-gtk

After running jstest-gtk click on your joystick within the jstest-gtk window. Next click on the 'Calibration' button. The calibration can be performed within this dialog box. Consider making note of these values before performing the calibration. Do not forget to cycle the hat switch that is on top of the stick. This is because this switch is considered as a proportional input just like the stick axis. In order to save these new settings, after a reboot or new power up, do not close jstest-gtk at this moment. First open a terminal and enter the below command in order to save the new calibration values. The 'device_name' of the joystick is in the first dialog box of jstest-gtk. It will be something like js0, js1 etc.

sudo jscal-store /dev/input/js0


Some or most Linux distributions will need more work to store these new calibration values, in the way of RULES. However, it seems that Raspbian doesn’t require anymore work.

The above calibration may not adjust for any center dead band. Notice that the calibration values are representing binary numbers in that the maximums are in power of two’s. Except where the maximum value is 1 or -1. It is common to see these values close to 512 or 1024. Consider the values below. To find the maximum value add the RangeMin to the RangeMax. For example; 0 + 1024 = 1024. Since this scheme starts with 0, not 1, 1022 or 510 might be the maximum value. The first pair of numbers, not considering the axis number, should add up to the maximum value.

Axis CenterMin CenterMax RangeMin RangeMax
0 511 511 0 1022
1 511 511 0 1022
2 127 127 0 254
3 127 127 0 254
4 0 0 -1 1
5 0 0 -1 1

In order to introduce a dead band consider the values below:

Axis CenterMin CenterMax RangeMin RangeMax
0 491 531 20 1002
1 491 531 20 1002
2 117 137 10 254
3 117 137 10 254
4 0 0 -1 1
5 0 0 -1 1

Or the values in the calibration dialog box screenshot, entitled "Joystick calibration using jstest-gtk dialog box".

Axis 4 and 5 are examples of the hat switch.

Saitek Saitek Pro Flight Quadrant

Properties controls.
Properties tree controls throttle-coupled.

This throttle quadrant is now produced by Logitech and works with FG and the Raspberry Pi4. If used with a single engine aircraft or a multi engine aircraft where the engines are controlled with one stick on the quadrant, this will be a plug and play setup. However, there will be a noticeable dead band in the middle of the travel. Furthermore, it is possible that all the usable stick travel is not utilized. Both of these issues can be addressed by calibrating the quadrant as in the joystick example above. Below is the calibration data before calibration and below that is the data after calibration. Notice that the center dead band is removed.

Axis CenterMin CenterMax RangeMin RangeMax
0 112 124 15 239
1 112 124 15 239
2 112 124 15 239

After calibration below:

Axis CenterMin CenterMax RangeMin RangeMax
0 127 127 0 255
1 127 127 0 255
2 127 127 0 255

If this quadrant is used with a multi engine aircraft and each engine throttle is controlled by a separate quadrant lever, the levers will only use half of its travel. In order to resolve this, the joystick configuration file will need a small edit. This file is located in homepi/.fgfs/Input, assuming your user name is ‘pi’. Notice that ‘.fgfs’ is a hidden folder in your Home folder. Only edit the joystick configuration files that are in your Home folder. Do not edit the files that are native to FlightGear. In this example we will be editing ‘Saitek-Saitek-Pro-Flight-Quadrant.xml’.

Let us assume that this configuration is for the twin engine DC-3 Dakota and axis-0 will be assigned to ALL engine mixture levers, axis-1 will be assigned to ‘Throttle Engine 0’ and axis-2 is assigned to ‘Throttle Engine 1’. Note that within this .XML configuration file that <axis> is another way to express <axis n=’0’>. The axis-0 doesn’t need the n=’0’. Find <axis n=’1’> and change the ‘offset’ from 0 to -1 also change ‘factor’ from 1 to -0.5. Do the same for <axis n='2'>.

Note For FG version 2019.1.1, axis n="1" and n="2" needed to be change to:
<factor type="double">.5</factor>
<offset type="double">1</offset>

The below worked on FG version 2018.3.2.

  <axis>
    <desc type="string">Mixture All Engines</desc>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/engines/mixture-all</property>
      <factor type="double">1</factor>
      <offset type="double">0</offset>
    </binding>
  </axis>
  <axis n="1">
    <desc type="string">Throttle Engine 0</desc>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/engines/engine[0]/throttle</property>
      <offset type="double">-1.0</offset>
      <factor type="double">-0.5</factor>
    </binding>
  </axis>
  <axis n="2">
    <desc type="string">Throttle Engine 1</desc>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/engines/engine[1]/throttle</property>
      <offset type="double">-1.0</offset>
      <factor type="double">-0.5</factor>
    </binding>
  </axis>

Now the Quadrant will move the throttle levers as they should. The levers and props should follow the levers on the Quadrant. However, you might notice that the throttles do not seem to feel like they are independent to each other when taxing or in the air. There is another adjustment to look into and this is the property throttles-coupled='true'(bool).

In this example, the DC-3 Dakota will need a small edit. The file that needs this edit is named ‘controls.xml’ and it's location can be found at /Aircraft/Douglas-Dc3/Systems/controls.xml. The bellow command will be found near the bottom of this file.

<throttle-coupled type="bool">1</throttle-coupled>

The Boolean value 1 needs to be changed to a 0, as in the below example.

<throttle-coupled type="bool">0</throttle-coupled>

Of course the file needs to be saves. Depending where the file is located, permissions might need to be dealt with.

Honeycomb Aeronautical Alpha Flight Controls - Yoke

The Honeycomb Aeronautical Alpha Flight Controls yoke is a quality flight yoke that works with the Raspberry Pi and FlightGear. The buttons on the yoke are standard normally open, where as the base buttons are normally closed double throw and one five position rotary. The base buttons are odd in that they use two USB Buttons per button. However it might be possible to hack the yoke in order to add nine more buttons. FlightGear is self limited to 32 button, however HID interface will be included in the next version and it will allow an unlimited number of buttons.

The below is being written for the Dakota DC-3, hence there will be some unusual bindings. This is a work in progress and shared for educational purposes.

<?xml version="1.0"?>

<!--
**********************************************************
* Bindings for Honeycomb Aeronautical Alpha Flight Controls - Yoke
*
*
* Axis 0: ailerons
* Axis 1: elevator
* Axes 2 and 3 (hat): view direction
* 
* Button  0: YOKE white-Trigger left-handle
* Button  1: YOKE white-top-Button left-handle
* Button  2: YOKE white-top-Button right-handle
* Button  3: YOKE red-top-Button right-handle
* Button  4: YOKE outer-Rocker down left-handle
* Button  5: YOKE outer-Rocker up left-handle
* Button  6: YOKE inner-Rocker down left-handle 
* Button  7: YOKE inner-Rocker up left-handle 
* Button  8: YOKE top-Rocker inward right-handle
* Button  9: YOKE top-Rocker outward right-handle
* Button 10: YOKE bottom-Rocker inward right-handle
* Button 11: YOKE bottom-Rocker outward right-handle
* Button 12: BASE Master Alternator up
* Button 13: BASE Master Alternator down
* Button 14: BASE Master Battery up
* Button 15: BASE Master Battery down
* Button 16: BASE Avionics Bus1 up
* Button 17: BASE Avionics Bus1 down
* Button 18: BASE Avionics Bus2 up
* Button 19: BASE Avionics Bus2 down
* Button 20: BASE Beacon up
* Button 21: BASE Beacon down
* Button 22: BASE Land up
* Button 23: BASE Land down
* Button 24: BASE Taxi up
* Button 25: BASE Taxi down
* Button 26: BASE Nav up
* Button 27: BASE Nav down
* Button 28: BASE Strobe up
* Button 29: BASE Strobe down
* Button 30: BASE Off
* Button 31: BASE Right Mag
* Button 32: BASE Left Mag
* Button 33: BASE Both Mag
* Button 34: BASE Start
**********************************************************
-->

<PropertyList>
  <name type="string">Honeycomb Aeronautical Alpha Flight Controls</name>

  <axis>
      <desc type="string">Aileron</desc>
      <binding>
          <command>property-scale</command>
          <property>/controls/flight/aileron</property>
          <factor type="double">1</factor>
          <offset type="double">0</offset>
      </binding>
  </axis>

  <axis n="1">
      <desc>Elevator</desc>
      <binding>
          <command>property-scale</command>
          <property>/controls/flight/elevator</property>
          <factor type="double">-1</factor>
          <offset type="double">0</offset>
      </binding>
  </axis>

  <axis n="2">
      <desc>View Direction</desc>
      <direction>left</direction>
      <low>
          <repeatable>true</repeatable>
          <binding>
              <command>nasal</command>
              <script>view.panViewDir(1)</script>
              <module>__js1</module>
              <offset type="double">1</offset>
          </binding>
      </low>
      <high>
          <repeatable>true</repeatable>
          <binding>
              <command>nasal</command>
              <script>view.panViewDir(-1)</script>
              <module>__js1</module>
              <offset type="double">1</offset>
          </binding>
      </high>
  </axis>
  <axis n="3">
      <desc>View Elevation</desc>
      <direction>upward</direction>
      <low>
          <repeatable>true</repeatable>
          <binding>
              <command>nasal</command>
              <script>view.panViewPitch(1)</script>
              <module>__js1</module>
              <offset type="double">1</offset>
          </binding>
     </low>
     <high>
          <repeatable>true</repeatable>
          <binding>
              <command>nasal</command>
              <script>view.panViewPitch(-1)</script>
              <module>__js1</module>
              <offset type="double">1</offset>
          </binding>
      </high>
  </axis>
  <button n="0">
      <desc>View Cycle Forwards</desc>
      <binding>
          <command>nasal</command>
          <script>view.stepView(1);</script>
      </binding>
      <repeatable>0</repeatable>
  </button>

  <button n="1">
      <desc>Reset View</desc>
      <binding>
          <command>nasal</command>
          <script>
              setprop("/sim/current-view/view-number", 0);
              view.resetView()
          </script>
      </binding>
      <repeatable>0</repeatable>
  </button>

  <button n="3">
      <desc>Gear Toggle</desc>
      <binding>
          <command>nasal</command>
          <script>controls.gearToggle(1);</script>
      </binding>
      <repeatable>0</repeatable>
  </button>

  <button n="4">
      <desc>Elevator Trim Up</desc>
      <binding>
          <command>nasal</command>
          <script>controls.elevatorTrim(-1);</script>
      </binding>
      <repeatable>1</repeatable>
  </button>

  <button n="5">
      <desc>Elevator Trim Down</desc>
      <binding>
          <command>nasal</command>
          <script>controls.elevatorTrim(1);</script>
      </binding>
      <repeatable>1</repeatable>
  </button>

  <button n="6">
      <desc>Flaps Down</desc>
      <binding>
          <command>nasal</command>
          <script>controls.flapsDown(1);</script>
      </binding>
      <repeatable>0</repeatable>
  </button>

  <button n="7">
      <desc>Flaps Up</desc>
      <binding>
          <command>nasal</command>
          <script>controls.flapsDown(-1);</script>
      </binding>
      <repeatable>0</repeatable>
  </button>

  <button n="8">
      <desc>Rudder Trim Left</desc>
      <binding>
          <command>nasal</command>
          <script>controls.rudderTrim(-1);</script>
      </binding>
      <repeatable>1</repeatable>
  </button>
  <button n="9">

      <desc>Rudder Trim Right</desc>
      <binding>
          <command>nasal</command>
          <script>controls.rudderTrim(1);</script>
      </binding>
      <repeatable>1</repeatable>
  </button>

  <button n="10">
      <desc>Aileron Trim Left</desc>
      <binding>
          <command>nasal</command>
          <script>controls.aileronTrim(-1);</script>
      </binding>
      <repeatable>1</repeatable>
  </button>

  <button n="11">
      <desc>Aileron Trim Right</desc>
      <repeatable>1</repeatable>
      <binding>
          <command>nasal</command>
          <script>controls.aileronTrim(1);</script>
      </binding>
  </button>


  <button n="12">
      <desc>Instruments ON OFF</desc>
      <repeatable>0</repeatable>
      <binding>
          <command>nasal</command>
          <script>setprop("/controls/lighting/instruments-norm", 0.16)</script>
      </binding>
      <mod-up>
          <binding>
              <command>nasal</command>
              <script type="string">setprop("/controls/lighting/instruments-norm", 0)</script>
          </binding>
      </mod-up>
  </button>

  <button n="14">
      <desc>Battery ON OFF</desc>
      <repeatable>0</repeatable>
      <binding>
          <command>nasal</command>
          <script>setprop("/controls/electric/battery-switch", 1)</script>
      </binding>
      <mod-up>
          <binding>
              <command>nasal</command>
              <script>setprop("/controls/electric/battery-switch", 0)</script>
          </binding>
      </mod-up>
      </button>

  <button n="22">
      <desc>Landing Lights Left and Right ON OFF</desc>
      <repeatable>0</repeatable>
      <binding>
          <command>nasal</command>
          <script>
              setprop("/controls/lighting/landing-lights", 1);
              setprop("/controls/lighting/landing-lights[1]" ,1)
          </script>
      </binding>
      <mod-up>
          <binding>
              <command>nasal</command>
              <script>
              setprop("/controls/lighting/landing-lights", 0);
              setprop("/controls/lighting/landing-lights[1]" ,0)
          </script>
          </binding>
      </mod-up>
  </button>

  <button n="25">
      <desc>Tail Wheel Lock ON OFF</desc>
      <repeatable>0</repeatable>
      <binding>
          <command>nasal</command>
          <script>setprop("/controls/gear/tailwheel-lock", 1)</script>
      </binding>
      <mod-up>
          <binding>
              <command>nasal</command>
              <script>setprop("/controls/gear/tailwheel-lock", 0)</script>
          </binding>
      </mod-up>
  </button>

</PropertyList>

Thrustmaster Flight Rudder Pedals

There could be two adjustments needed for these rudder pedals. The toe brakes are reversed and delete the toe brake center dead band. Use the same method to delete the dead band as in the above throttle quadrant. Do not delete the dead band for the rudder unless that is what you want to do. Below is a calibration data example:

Axis CenterMin CenterMax RangeMin RangeMax
0 511 511 0 1022
1 511 511 0 1022
2 448 574 63 959

Reversing the toe brake direction in FlightGear, using it's joystick dialog box, does't work. The easiest method is to reverse them using jstest-gtk and check the invert box for axes 0 and axes 1.

Depending on the version of FlightGear, there could be a bug where one of the toe brakes do not show up in the FlightGear joystick dialog box. If so this is easy to edit in the configuration file. It might also be beneficial to delete controls that are not part of this rudder pedal so not to produce any conflicts. See below:

  <name type="string">Thrustmaster T-Rudder</name>
  <axis>
    <desc type="string">Brake Right</desc>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/gear/brake-right</property>
      <factor type="double">0.5</factor>
      <offset type="double">1</offset>
    </binding>
  </axis>
  <axis n="1">
    <desc type="string">Brake Left</desc>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/gear/brake-left</property>
      <factor type="double">0.5</factor>
      <offset type="double">1</offset>
    </binding>
  </axis>
  <axis n="2">
    <desc type="string">Rudder</desc>
    <binding>
      <command type="string">property-scale</command>
      <property type="string">/controls/flight/rudder</property>
      <factor type="double">1</factor>
      <offset type="double">0</offset>
    </binding>
  </axis>

A much more in-depth study of joystick programming can be found at the below link:

Writing Joystick Code

HID

Below is an example of how to build FlightGear, using download_and_compile.sh, with standard joystick disabled and HID enabled:

FG_CMAKEARGS='-DENABLE_PLIB_JOYSTICK=OFF -DENABLE_HID_INPUT=ON' ../fgmeta/download_and_compile.sh -j3 FGFS

Below is an example of FlightGear (as reported in the Terminal) not being able to read a HID device because of a lack of permissions:

    27.14 [WARN]:input      Can't get abs-axes for /dev/input/js0
    27.14 [WARN]:input      Can't get keys for /dev/input/js0
    27.14 [WARN]:input      Can't get switches for /dev/input/js0
    27.15 [INFO]:input      HID event input starting up
    27.16 [WARN]:input      Honeycomb Aeronautical Alpha Flight Controls_0: HID: Failed to open:/dev/hidraw0
    27.16 [WARN]:input      	note on Linux you may need to adjust permissions of the device using UDev rules.

Pass the below command to FlightGear in order to enable the above logging.

--log-level=info

User rules are located at /etc/udev/rules.d Within the folder rules.d, a file can be created with a new rule. We will name it 50-hid.rules. Root will be needed in order to create this file, use your favorite method. This new file 50-hid.rules will contain the rule below:

KERNEL=="hidraw*", GROUP="input", MODE="0660"

If the above rule is successful, FlightGear terminal will report something similar to the below:

>  155.07 [INFO]:input      	bytes: 7F 7F 00 00 A0 AA 6A 00 
>  155.07 [INFO]:input      Honeycomb Aeronautical Alpha Flight Controls
> FGHIDDeivce received input report:0, len=8