Skip to content

Instantly share code, notes, and snippets.

@OverlordZorn
Last active February 12, 2024 13:43
Show Gist options
  • Save OverlordZorn/b5fd07a0d291150842063b04fbf0f9a3 to your computer and use it in GitHub Desktop.
Save OverlordZorn/b5fd07a0d291150842063b04fbf0f9a3 to your computer and use it in GitHub Desktop.
x56 Hotas - Tools, Plugins, resources and more

Stick Mod

The "centering spring mechanic" of the x56 stick is rather questionable zeroing, causing the need for sizable deadzone sometimes and massively limiting the potential of the quite decent hall sensors of the stick itself. People started to remove the spring and create their own custom modification to center the stick.

https://www.reddit.com/r/hotas/comments/tt4cai/logitech_x56_suggested_mods_new/ https://www.youtube.com/watch?v=z6jjeyz333A https://www.ebay.at/itm/404316490771 https://de.aliexpress.com/item/1005006142856074.html?gatewayAdapt=glo2deu https://www.reddit.com/r/hotas/comments/fr7d63/my_own_take_on_the_x56_external_spring_mod/ https://www.reddit.com/r/hoggit/comments/veqa9z/logitech_x56_mods_makes_flying_better/

I personally have ordered the parts to make my own version of the modification, stumbled over the ali express kit only after i already ordered most parts. Will update at some point once put together - maybe.

Messurements for springs

Max Length, no tension: 80mm, short is better, not too short. Max Length, expanded: 130mm

Tools that I use

vJoy - Virtual Joystick It creates vitual joystick (up to ~20) which can recieve signals/input from other tools like Joystick Gremlin.

Joystick Gremlin I use this tool to take the analog inputs from the x56, refine/filter them, (anti jitter) and then output those into a virtual joystick (or 2) via vJoy.

Also very useful to calbirate min max and center of a joystick. grafik

Gremlin Plugins

average_filter.py

https://gist.github.com/PulsarNeutronStar/b5fd07a0d291150842063b04fbf0f9a3#file-average_filter-py Old Reliable. have this since a while, tried some others with worse results. It takes x amount of sample with y time difference and outputs the average. Default settings for most axis are 10 samples with an update rate of 10ms grafik

deadzone_filter.py

https://gist.github.com/PulsarNeutronStar/b5fd07a0d291150842063b04fbf0f9a3#file-deadzone_filter-py i dont know if i set it up wrong, but i didnt yield any satisfying results for my sticks jitter.

Untested.

https://gist.github.com/luizsaluti/bf1a404a04b0f490be4e5fb2e927b0a4

Personal Modifications

  • added more beefy rubber feets to reduce slipping of the throttle - basically just a 20mmx4mm rubber band that i doublesided-taped onto the bottom of the bases. (i also lost one foot at some point, causing a very clunky stick until i fixed that, lol)

"Vanilla Stick Spring"

  • for the most time, i used the 2. smallest spring and recently went for the smaller, softer spring, big differnce in terms of fidelity possible. (i also fixed the missing rubber feet at the same time, so its a cumulative result). Before i always had a really hard time to get Air to Air Refueling done. After the change, boom first try, just minor disconnects inbetween.

Other 3d Printed Stuff

https://www.thingiverse.com/thing:5344542 took the ring lock from here and ordered it for my personal stick mod

https://www.thingiverse.com/thing:4201117 A Button Cover to make it easier to reach the Weapon Release Button dont have it yet but once i get the chance im looking towards it. Always have to rearrange my grip on the stick to reach to the A button, which can be a pity when trying aim/leading into the dropzone/ etc.

https://www.thingiverse.com/thing:3225479 looks interesting but no need currently

https://www.thingiverse.com/thing:4679074 Throttle Detends for "intentional" afterburning. looks nice, but since i do heli stuff they would be in the way.

https://www.thingiverse.com/search?q=x56 other stuff

Further Notes

  • Logitech provides also their own calibration software and drivers but thats pretty meh in my opinion, nut sure if i even have it installed rn or if it works with out it, the joystick gremlin calibration is much more satisfactory.

  • The "mode" switch on the throttle lets you switch between profiles defined with the logitech tools and let you define key combinations to be triggered. never used it personally.

  • Throttle as a slight slip, resulting in sometimes slipping from Mil into Afterburner. There are 3d printed mods available, but since i mostly fly helicopters, they would be a hinderance for me personally.

x56 Purchache Recommondation

tldr: eh...

I got mine used for about ~2/3s retail price. with the adjusting and everything, im by now quite happy with what i have. If you want a plug and play solution, i do not recommend it.

https://www.reddit.com/r/hotas/comments/17g4dn1/is_the_logitech_x56_really_that_bad/

https://www.reddit.com/r/hotas/comments/17izlrx/x56_1_year_and_2_months_later/

It has a bad reputation and it seems for a good reason. Aledgedly, the older versions of the stick, earlier production runs have been made with a better production quality but i cant find any source for it.

I have mine now so i try to make the best of it and for what it is, it has a lot of utility.

https://cdn.discordapp.com/attachments/733780137870622750/1021184528540254248/unknown.png?ex=65c00e34&is=65ad9934&hm=a14b547a676bcc810d848c772f56b9cce7849df5af83c5d203bc6cad85eaebf4&

Other Flight Sim Related Tools

https://openkneeboard.com/

https://voiceattack.com/

from collections import deque
import threading
import time
import gremlin
from gremlin.user_plugin import *
mode = ModeVariable("Mode", "The mode in which to use this mapping")
vjoy_axis = VirtualInputVariable(
"Virtual output axis",
"The vJoy axis to send the filtered output to.",
[gremlin.common.InputType.JoystickAxis]
)
joy_axis = PhysicalInputVariable(
"Physical input axis",
"The physical input axis being filtered.",
[gremlin.common.InputType.JoystickAxis]
)
sample_size = IntegerVariable(
"Number of samples",
"Number of samples to use to compute the average.",
5,
1,
50
)
update_rate = IntegerVariable(
"Update rate (ms)",
"Time between expected updates in milliseconds",
250,
10,
5000
)
# Global variables
g_samples = deque([], maxlen=sample_size.value)
g_last_value = 0.0
g_last_update = time.time()
g_thread = None
g_vjoy = gremlin.joystick_handling.VJoyProxy()
d_axis = joy_axis.create_decorator(mode.value)
def update_thread():
global g_samples
rate = update_rate.value / 1000.0
while True:
# Repeat last value
if g_last_update + rate < time.time():
g_samples.append(g_last_value)
update_vjoy()
# Ensure the thread terminates
if (g_last_update + (rate * g_samples.maxlen)) < time.time():
return
time.sleep(rate)
def average():
return sum(g_samples) / len(g_samples)
def update_vjoy():
global g_vjoy
g_vjoy[vjoy_axis.vjoy_id].axis(vjoy_axis.input_id).value = average()
@d_axis.axis(joy_axis.input_id)
def axis_cb(event):
global g_samples, g_last_value, g_last_update, g_thread
g_last_value = event.value
g_last_update = time.time()
g_samples.append(event.value)
update_vjoy()
if g_thread is None or not g_thread.is_alive():
g_thread = threading.Thread(target=update_thread)
g_thread.start()
<?xml version="1.0" ?>
<profile version="9">
<devices>
<device device-guid="{4BF9D3C0-9D03-11ED-8002-444553540000}" label="" name="Saitek Pro Flight X-56 Rhino Stick" type="joystick">
<mode name="Default">
<axis description="" id="1"/>
<axis description="" id="2"/>
<axis description="" id="4"/>
<axis description="" id="5"/>
<axis description="" id="6"/>
<button description="" id="1"/>
<button description="" id="2"/>
<button description="" id="3"/>
<button description="" id="4"/>
<button description="" id="5"/>
<button description="" id="6"/>
<button description="" id="7"/>
<button description="" id="8"/>
<button description="" id="9"/>
<button description="" id="10"/>
<button description="" id="11"/>
<button description="" id="12"/>
<button description="" id="13"/>
<button description="" id="14"/>
<button description="" id="15"/>
<button description="" id="16"/>
<button description="" id="17"/>
<hat description="" id="1"/>
</mode>
</device>
<device device-guid="{5CCA7BF0-9D03-11ED-8004-444553540000}" label="" name="Saitek Pro Flight X-56 Rhino Throttle" type="joystick">
<mode name="Default">
<axis description="" id="1"/>
<axis description="" id="2"/>
<axis description="" id="3"/>
<axis description="" id="4"/>
<axis description="" id="5"/>
<axis description="" id="6"/>
<axis description="" id="7"/>
<axis description="" id="8"/>
<button description="" id="1"/>
<button description="" id="2"/>
<button description="" id="3"/>
<button description="" id="4"/>
<button description="" id="5"/>
<button description="" id="6"/>
<button description="" id="7"/>
<button description="" id="8"/>
<button description="" id="9"/>
<button description="" id="10"/>
<button description="" id="11"/>
<button description="" id="12"/>
<button description="" id="13"/>
<button description="" id="14"/>
<button description="" id="15"/>
<button description="" id="16"/>
<button description="" id="17"/>
<button description="" id="18"/>
<button description="" id="19"/>
<button description="" id="20"/>
<button description="" id="21"/>
<button description="" id="22"/>
<button description="" id="23"/>
<button description="" id="24"/>
<button description="" id="25"/>
<button description="" id="26"/>
<button description="" id="27"/>
<button description="" id="28"/>
<button description="" id="29"/>
<button description="" id="30"/>
<button description="" id="31"/>
<button description="" id="32"/>
<button description="" id="33"/>
<button description="" id="34"/>
<button description="" id="35"/>
<button description="" id="36"/>
</mode>
</device>
<device device-guid="{6F1D2B61-D5A0-11CF-BFC7-444553540000}" label="" name="keyboard" type="keyboard">
<mode name="Default"/>
</device>
<device device-guid="{A0D78150-9CF2-11ED-800B-444553540000}" label="" name="WootingTwoHE" type="joystick">
<mode name="Default">
<axis description="" id="1"/>
<axis description="" id="2"/>
<axis description="" id="3"/>
<axis description="" id="4"/>
<axis description="" id="5"/>
<axis description="" id="6"/>
<axis description="" id="7"/>
<button description="" id="1"/>
<button description="" id="2"/>
<button description="" id="3"/>
<button description="" id="4"/>
<button description="" id="5"/>
<button description="" id="6"/>
<button description="" id="7"/>
<button description="" id="8"/>
<button description="" id="9"/>
<button description="" id="10"/>
<button description="" id="11"/>
<button description="" id="12"/>
<hat description="" id="1"/>
</mode>
</device>
</devices>
<vjoy-devices>
<vjoy-device device-guid="{D7460AD0-F9D7-11ED-8002-444553540000}" label="" name="vJoy Device" type="vjoy">
<mode name="Default">
<axis description="" id="1"/>
<axis description="" id="2"/>
<axis description="" id="4">
<container type="basic">
<action-set>
<response-curve>
<mapping type="cubic-spline">
<control-point x="-1.0" y="-1.0"/>
<control-point x="1.0" y="1.0"/>
</mapping>
<deadzone center-high="0.15" center-low="-0.15" high="1.0" low="-1.0"/>
</response-curve>
</action-set>
</container>
</axis>
<axis description="" id="5">
<container type="basic">
<action-set>
<response-curve>
<mapping type="cubic-spline">
<control-point x="-1.0" y="-1.0"/>
<control-point x="1.0" y="1.0"/>
</mapping>
<deadzone center-high="0.15" center-low="-0.15" high="1.0" low="-1.0"/>
</response-curve>
</action-set>
</container>
</axis>
<axis description="" id="6"/>
<button description="" id="1"/>
<button description="" id="2"/>
<button description="" id="3"/>
<button description="" id="4"/>
<button description="" id="5"/>
<button description="" id="6"/>
<button description="" id="7"/>
<button description="" id="8"/>
</mode>
</vjoy-device>
<vjoy-device device-guid="{51C15800-B4E5-11EE-8003-444553540000}" label="" name="vJoy Device" type="vjoy">
<mode name="Default">
<axis description="" id="3"/>
<axis description="" id="4"/>
<axis description="" id="5"/>
<axis description="" id="7"/>
<axis description="" id="8"/>
<button description="" id="1"/>
<button description="" id="2"/>
<button description="" id="3"/>
<button description="" id="4"/>
<button description="" id="5"/>
<button description="" id="6"/>
<button description="" id="7"/>
<button description="" id="8"/>
</mode>
</vjoy-device>
</vjoy-devices>
<settings>
<default-delay>0.05</default-delay>
</settings>
<plugins>
<plugin file-name="C:/Program Files (x86)/H2ik/Plugins/average_filter.py">
<instance name="Throttle Zoom Slider (RTY3)">
<variable is-optional="False" name="Mode" type="Mode" value="Default"/>
<variable input-id="7" input-type="axis" is-optional="False" name="Virtual output axis" type="VirtualInput" vjoy-id="2"/>
<variable device-guid="{5CCA7BF0-9D03-11ED-8004-444553540000}" device-name="Saitek Pro Flight X-56 Rhino Throttle" input-id="7" input-type="axis" is-optional="False" name="Physical input axis" type="PhysicalInput"/>
<variable is-optional="False" name="Number of samples" type="Int" value="10"/>
<variable is-optional="False" name="Update rate (ms)" type="Int" value="10"/>
</instance>
<instance name="Stick Rotation Z">
<variable is-optional="False" name="Mode" type="Mode" value="Default"/>
<variable input-id="6" input-type="axis" is-optional="False" name="Virtual output axis" type="VirtualInput" vjoy-id="1"/>
<variable device-guid="{4BF9D3C0-9D03-11ED-8002-444553540000}" device-name="Saitek Pro Flight X-56 Rhino Stick" input-id="6" input-type="axis" is-optional="False" name="Physical input axis" type="PhysicalInput"/>
<variable is-optional="False" name="Number of samples" type="Int" value="10"/>
<variable is-optional="False" name="Update rate (ms)" type="Int" value="10"/>
</instance>
<instance name="Stick ministick X">
<variable is-optional="False" name="Mode" type="Mode" value="Default"/>
<variable input-id="4" input-type="axis" is-optional="False" name="Virtual output axis" type="VirtualInput" vjoy-id="1"/>
<variable device-guid="{4BF9D3C0-9D03-11ED-8002-444553540000}" device-name="Saitek Pro Flight X-56 Rhino Stick" input-id="4" input-type="axis" is-optional="False" name="Physical input axis" type="PhysicalInput"/>
<variable is-optional="False" name="Number of samples" type="Int" value="10"/>
<variable is-optional="False" name="Update rate (ms)" type="Int" value="10"/>
</instance>
<instance name="Stick ministick y">
<variable is-optional="False" name="Mode" type="Mode" value="Default"/>
<variable input-id="5" input-type="axis" is-optional="False" name="Virtual output axis" type="VirtualInput" vjoy-id="1"/>
<variable device-guid="{4BF9D3C0-9D03-11ED-8002-444553540000}" device-name="Saitek Pro Flight X-56 Rhino Stick" input-id="5" input-type="axis" is-optional="False" name="Physical input axis" type="PhysicalInput"/>
<variable is-optional="False" name="Number of samples" type="Int" value="10"/>
<variable is-optional="False" name="Update rate (ms)" type="Int" value="10"/>
</instance>
<instance name="Throttle MiniStick x">
<variable is-optional="False" name="Mode" type="Mode" value="Default"/>
<variable input-id="4" input-type="axis" is-optional="False" name="Virtual output axis" type="VirtualInput" vjoy-id="2"/>
<variable device-guid="{5CCA7BF0-9D03-11ED-8004-444553540000}" device-name="Saitek Pro Flight X-56 Rhino Throttle" input-id="4" input-type="axis" is-optional="False" name="Physical input axis" type="PhysicalInput"/>
<variable is-optional="False" name="Number of samples" type="Int" value="10"/>
<variable is-optional="False" name="Update rate (ms)" type="Int" value="10"/>
</instance>
<instance name="Throttle MiniStick Y">
<variable is-optional="False" name="Mode" type="Mode" value="Default"/>
<variable input-id="5" input-type="axis" is-optional="False" name="Virtual output axis" type="VirtualInput" vjoy-id="2"/>
<variable device-guid="{5CCA7BF0-9D03-11ED-8004-444553540000}" device-name="Saitek Pro Flight X-56 Rhino Throttle" input-id="5" input-type="axis" is-optional="False" name="Physical input axis" type="PhysicalInput"/>
<variable is-optional="False" name="Number of samples" type="Int" value="10"/>
<variable is-optional="False" name="Update rate (ms)" type="Int" value="10"/>
</instance>
<instance name="Throttle Slider RTY4">
<variable is-optional="False" name="Mode" type="Mode" value="Default"/>
<variable input-id="8" input-type="axis" is-optional="False" name="Virtual output axis" type="VirtualInput" vjoy-id="2"/>
<variable device-guid="{5CCA7BF0-9D03-11ED-8004-444553540000}" device-name="Saitek Pro Flight X-56 Rhino Throttle" input-id="8" input-type="axis" is-optional="False" name="Physical input axis" type="PhysicalInput"/>
<variable is-optional="False" name="Number of samples" type="Int" value="10"/>
<variable is-optional="False" name="Update rate (ms)" type="Int" value="10"/>
</instance>
</plugin>
</plugins>
</profile>
import gremlin
from gremlin.user_plugin import *
mode = ModeVariable("Mode", "The mode in which to use this mapping")
virtual_axis = VirtualInputVariable(
"Virtual output axis",
"The vJoy axis to send the filtered output to.",
[gremlin.common.InputType.JoystickAxis]
)
physical_axis = PhysicalInputVariable(
"Physical input axis",
"The physical input axis being filtered.",
[gremlin.common.InputType.JoystickAxis]
)
deadzone = IntegerVariable(
"Deadzone size",
"Size of the deadzone around the cached value.",
5,
0,
100
)
# Decorator for the physical axis
dec_physical_axis = physical_axis.create_decorator(mode.value)
# Global variables
# The cached physical input
g_cached_value = 0.0
# The current input
g_current_value = 0.0
g_vjoy = gremlin.joystick_handling.VJoyProxy()
def apply_deadzone():
global g_current_value, g_cached_value
if(abs(g_current_value - g_cached_value) > deadzone.value/100):
g_cached_value = g_current_value
return g_cached_value
def update_vjoy():
global g_vjoy
g_vjoy[virtual_axis.vjoy_id].axis(virtual_axis.input_id).value = apply_deadzone()
@dec_physical_axis.axis(physical_axis.input_id)
def axis_cb(event):
global g_current_value
g_current_value = event.value
update_vjoy()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment