News:

Precision Simulator update 10.188 (22 June 2026) is now available.
Navburo update 13 (23 November 2022) is now available.
NG FMC and More is released.

Main Menu

A replacement or complement to the PSX USB subsystem: "frankenusb"

Started by macroflight, Sat, 12 Jul 2025 14:03

macroflight

(moved from the Python thread in Networkers)

If you have USB peripherals but find the built-in USB support in PSX limiting, "frankenusb" might be worth looking at. If you are already using it - I just added a few new features.

Some things it can do that the built-in PSX USB support cannot:
- (new) Bind different functions to long and short button presses.
- (new) Handle the rotary knob on the Thrustmaster Boeing Quadrant
- (new) Using a USB axis as the flap lever
- Handle the reverse levers on e.g the Thrustmaster Boeing Quadrant, i.e you pull the lever to engage reverse idle and then push the throttle forward for more reverse.
- Handle the gear lever on the Thrustmaster Boeing Yoke (which looks like two buttons UP+DOWN).
- Handle the gear lever on the STECS HOTAS, which has three positions but two buttons (UP, no_button, DOWN).
- Control PSX towing (change target heading, start, stop, toggle direction)
- A button to switch between using an axis as tiller and aileron (i.e not the automated switch that PSX has)
- A custom speedbrake axis - since we never really use the range between max flight speedbrake and max ground speedbrake, we let most of the axis range handle the in-flight band giving better sensitivity.

Available on GitHub

voipmeister

The Github repository also includes my configuration for the Honeycomb Bravo throttle quadrant. So if you have one of those, it might be worth checking it out :)
Seb

SwissMark

Fantastic. Thank you.

Would this application include support/recognition for the "Knobster"?
Keep the blue skies up!
B744F N475MC

Jeroen Hoppenbrouwers

For Knobster support the trick is to let the application know which PSX control to adjust. This is not trivial, as PSX does not offer a "just select me, don't do anything" touch action on the controls. You can hover a mouse and then run the control wheel, but you cannot just select by a touch. You would push the button (if it has one).

I think that only if Hardy would add the "select" option to all relevant controls and introduce a visual "I have been selected" indication, it could work.

Of course you can design another control selection mechanism, with buttons or what not. StreamDeck. Anything. But direct touch, ouch.


Hoppie

SwissMark

Quote from: Jeroen Hoppenbrouwers on Sat, 12 Jul 2025 19:18I think that only if Hardy would add the "select" option to all relevant controls and introduce a visual "I have been selected" indication, it could work.

Now THAT would be amazing. Imagine ....
Keep the blue skies up!
B744F N475MC

macroflight

Knobster idea (which I will not work on since I don't have a Knobster):

Make a master map of the PSX 2D cockpit with the approximate coordinates of all the controls that you might want to manipulate with the Knobster. Combined with information about the PSX instances running, where those windows are and which PSX layouts and zoom factors are used, we can figure out where on the Windows desktop every visible PSX control is located.

Switching which PSX control (HDG SEL, ALT SEL, ...) is controlled by the Knobster could either be by using one of the Knobster rings to cycle through all the relevant PSX controls or maybe by intercepting mouse/touchscreen clicks and figure out which PSX control the user just clicked on and make that the active Knobster control.

A semi-transparent window on top of PSX might be used to indicate which control (e.g HDG SEL) is currently controlled by the Knobster. Or maybe just a small text overlay in a fixed location that says "[HDG SEL]"?

Will

Hello Macroflight,

Perhaps another thread would be best for this question, but I'm wondering if you could give instructions on a more beginner's level for installing and running frankenfreeze.py. As of now, that's the only utility I'm interested in running. I did read your installation instructions on github, but I'm curious about installing just the one script as opposed to all of them, and what needs to be done after installation each time to run it. Sorry for my beginner's level of literacy here. Thanks.

Will /Chicago /USA

macroflight

Quote from: Will on Tue, 22 Jul 2025 03:20I'm wondering if you could give instructions on a more beginner's level for installing and running frankenfreeze.py.
If you only want to run frankenfreeze.py as-is, it is probably easiest to just download the binary version (last updated May 31st, but there has been no significant changes to frankenfreeze.py since then). See "Binary packages" on https://github.com/macroflight/psxhacks

Kurt

Big shoutout to Macroflight for all this !! - If your PSX ambitions is more than mouse and keyboard then Frankenusb can "open up" almost the whole PSX cockpit to be accessible via USB devices.

The learning curve for non programmers are a bit steep but manageable and I highly recommend to try it out.

Cheers

Kurt
Best regards
Kurt
PSX Shared Cockpit Crew

PanosI

I gave it another try.

I left  it here in post #95

https://aerowinx.com/board/index.php/topic,5448.msg80894.html#msg80894

This time a ran the exe files.
The Frankenusb.exe opened although that I don't have the controllers in the conf file.
Also the show use.exe to identify my controllers and the keystrokes.

Now how can I add my controllers to the conf file? I opened the conf file with Notepad++ but I can't understand the logic of the file.


macroflight

The basic structure of the CONFIG entry in the config file is a Python dictionary (see e.g https://www.w3schools.com/python/python_dictionaries.asp).

The dictionary keys are the names of your USB devices (as seen by pygame and shown by e.g show_usb).

The values of the dictionary are config entries for that USB device.

e.g

CONFIG = {
    'USB device 1 name': {
        # Config for device 1
    },
    'USB device 2 name': {
        # Config for device 2
    },
    # and so on
}


The config for an USB device is also a Python dictionary. Valid dictionary keys are "axis motion", "button down", "button up", "button short" and "button long". You only need to provide the ones you need, e.g your rudder pedals might not have any buttons, then only "axis motion" is needed in the config file.

CONFIG = {
    'USB device 1 name': {
        'axis motion': {
            # axis motion config for device 1 here
        },
        button down': {
            # button down config for device 1 here
        },
    },
}


Both the axis motion and button dictionaries have keys that are axis or button numbers (always integers, so no quotes around them), and the values are the configuration entries for those axes and buttons.


CONFIG = {
    'USB device 1 name': {
        'axis motion': {
            4: {
                # configuration for axis 4 goes here
            }
        },
        'button down': {
            26: {
                # configuration for button 26 goes here
            },
            12: {
                # configuration for button 12 goes here
            },
        },
    },
}


The format for the button or axis configuration can look very different depending on the type of button or axis (but it's always valid Python, with dictionaries, lists and tuples). Look in the config_examples directory of the repo for something that resembles what you want to do, read the frankenusb.py script or ask in this thread.

Here is a basic but complete example config file with comments:

# Throttle sync sound, played if you move the throttles near the
# PSX position while autothrottle is engaged. Usage: move throttles
# until you hear the sound, then press A/T disconnect three times and
# you have manual control without a big jump in PSX throttle position

CONFIG_MISC = {
    'THROTTLE_SYNC_SOUND': "C:/fs/psx/Aerowinx/Audio/Basics/cab1.wav"
}


# The basic structure of CONFIG is a Python dictionary.
# See e.g https://www.w3schools.com/python/python_dictionaries.asp

# The toplevel keys are all names of USB devices. These names must
# match the names that pygame sees.

# The easiest way to find the controller name is to run the show_usb
# tool from in the psxhacks repository.

# Example output from show_usb:
#
# Button 2 DOWN event received for: Thrustmaster T.A320 Copilot
# Axis movement on "Thrustmaster T.A320 Copilot" axis 1 to position 0.25653076171875

# In the above case, the name of the USB device is "Thrustmaster T.A320 Copilot"

# In the very basic example below, we will setup axis 1 (the Y or
# "elevator" axis of the joystick to control the PSX elevator, and we
# will setup "button 2" (the red thumb button on the joystick) to give
# a press of the PSX autopilot CMD L button.

#

CONFIG = {
    'Thrustmaster T.A320 Copilot': {
        'axis motion': {
            1: {
                # Elevator
                'axis type': 'NORMAL',
                # This axis will affect the PSX variable FltControls
                'psx variable': 'FltControls',
                # The PSX FltControls variable contains elevator,
                # aileron AND rudder positions (format is
                # elevator;aileron;:rudder), and we will only update
                # the elevator, i.e index 0.
                'indexes': [0],
                # A pygame axis always have values from -1.0 to +1.0,
                # but the PSX elevator varaible is from -999 to +999,
                # so we need to tell frankenusb that so it can convert
                # the pygame value to something suitable for PSX.
                'psx min': -999,
                'psx max': 999,
                # Sometimes you need a bit of "null zone" to avoid
                # control movement if you bump into the control or the
                # device has some noise. FrankenUSB lets you add one
                # or more "static zones". These are a Python list (so
                # square brackets) of tuples (parenthesis). Each tuple
                # has three numbers, the first two are the pygame
                # range which will be "static", the third is the
                # pygame value that will be output while the physical
                # device returns a value between the first two.  In
                # this example, we use the fake pygame value of 0.0
                # (which will be translated into PSX elevator position
                # 0 - neutral) whenever the physical device is between
                # -0.1 and +0.1.
                'static zones': [(-0.01, 0.01, 0.0)],
            }, # end of the axis 1 section
        }, # end of the axis motion section
        'button down': {
            2: {
                # This is the most basic button event type: SET. It
                # will set the specified PSX variable (McpPshCmdL) to
                # the value (1).
                'button type': 'SET',
                'psx variable': 'McpPshCmdL',
                'value': 1,
            }, # end of the button 2 section
        }, # end of the button down section
    }, # end of the Thrustmaster T.A320 Copilot device section
    'Another USB device with only buttons': {  # added this to show how device 2 should be placed in the config file
        'button down': {
            7: {
                'button type': 'SET',
                'psx variable': 'ParkBrkLev',
                'value':1,
            },
        },
    }
}

This tool is a bit tricky to get started with, and you will need some knowledge of the PSX network protocol (or read the examples and ask questions here) to figure out what config you need.

The plus side is that you can do almost anything the PSX network API allows you to do from an USB axis or button. If you find something you want to do but cannot due to limitations in the script, please let me know.

Some notes on the available button types:

SET - set a single PSX variable to a fixed value
INCREMENT - increase a single PSX variable by a fixed value (which can be negative). Also supports a max and min value, and wraparound
BIGMOMPSH - support for the PSX BIGMOMPSH variable type

There are many less commmon button event types (for now, the only "documentation" is in the frankenusb.py script itself), e.g

SET_ACCELERATED - used for rotaries that emit button presses and where you want acceleration, i.e the PSX variable is changed more if you turn the rotary faster.
REVERSE_LEVER - a button that is used to toggle a throttle axis between normal mode (axis controls from forward idle to full power) and reverse mode (axis controls from reverse idle to full reverse)
TOWING_HEADING - changes the PSX towing heading by a fixed amount
TOWING_DIRECTION_TOGGLE - change the PSX towing direction from push to pull and vice versa
TOWING_MODE_TOGGLE - change the towing mode (i.e start/stop)
FLIGHT_CONTROL_LOCK_TOGGLE - toggle the "flight control lock", a mechanism that stops this frankenusb instance from sending any flight control axis (elevator, aileron, rudder, tiller, speedbrake, throttles) to PSX. Useful only for shared cockpit or if you have REALLY noisy controls.

Available axis types:

NORMAL - axis controls a PSX variable (or part of one, e.g FltControls which is elevator;aileron;rudder)
THROTTLE_WITH_REVERSE_BUTTON - throttle axis that can be switched between normal and reverse mode with a button
SPEEDBRAKE - custom speedbrake axis where most of the USB axis resolution is used for the inflight part of the PSX lever range
AXIS_SET - the axis value is converted into one of a fixed set of PSX variable values. Useful mostly for using an axis as the flap lever.

macroflight

This addon is still alive and well.

Since the last post, it has learned to switch a PSX sim from left seat to right seat mode. This includes sending a layout= PSX network message to switch all PSX instances from e.g layout 1 to layout 2, switching the PSX human pilot to the other seat, and to translate all mapped USB buttons from the left seat button to the right seat button. E.g if you have programmed USB button 1 to push the left TFC button, after switching seats the same USB button will now push the right TFC button.

It also has a few throttle improvements, e.g multiple types of reverser support

- USB button toggles idle-reverse-reverse full-idle with one button (thrust levers does not affect reverse thrust)
- USB button toggles reverse on/off (when on, thrust levers regulate from idle reverse to full reverse)
- USB button selectes reverse on, while another selects reverse off

The throttle logic has been improved when the USB throttle is far from the PSX position. You now have to move the USB throttle to a position close to the PSX one, then it will "latch on" and then control PSX throttle. If e.g the autothrottle moves the PSX throttles far from the USB position, they "unlatch" and will not control the PSX throttle until they are moved close to the PSX position again. Sounds complicated, but it feels better to me than the old way.

Frankenusb now also has a rudder-tiller interlink (just like the PSX USB subsystem)

Download from GitHub or for those that don't want to install Python: Windows EXE files