Skip to main content

Device Handling

In this section, the explanation will cover how fsmapper deals with flight sticks and custom control devices.

fsmapper provides a Device object to abstract various devices, including those supported by the OS and custom-built devices, for unified handling. Users generate a Device corresponding to the desired device using mapper.device() for each device they want to use.

Below is an example of opening a Direct Input game controller device, which was also mentioned in the Tutorial.

device = mapper.device{
name = 'Tutorial',
type = 'dinput',
identifier = {index = 1},
modifiers = {},
}

The argument for mapper.device() is a table in associative array format, where each key-value pair of the associative array represents an actual argument. Here's a brief overview of each key in the table.

KeyDescriptionRequirement
nameUser-assigned name for the Device objectREQUIRED
typeType of the deviceREQUIRED
identifierInformation for device identificationREQUIRED
optionsOption parameters for device openingoptional
modifiersEvent modifieroptional

Further, we'll delve into the specifics of the values to be specified in this argument and other essential aspects to know for manipulating devices.

Device Type

fsmapper uses pluggable driver modules to handle device-specific operations and absorb differences between devices. The type parameter for mapper.device() specifies the device type, i.e., which driver to use.

fsmapper comes with the following built-in drivers.

Type NameDescription
dinputGame devices supported by DirectInput.
This refers to devices listed in the USB Game Controllers found in the control panel, i.e. the devices listed in joy.cpl.
simhidInput/output devices that support the SimHID protocol.
The SimHID protocol is adopted by controllers like SimHID G1000 that I've DIY'd.

The interpretation of the identifier and options parameters is performed within each driver. The meaning of the values varies depending on the device type.
The identifier parameter specifies information necessary for the driver to identify the actual device. For example, for a dinput device, it might specify the product name or GUID, while for a simhid device, it might specify the virtual COM port name.
The 'options' parameter is used to convey driver-specific optional information.

For detailed specifications on what to specify in the identifier and options parameters for built-in drivers, please refer to the DirectInput Game Device Specification and the SimHID Device Specification. For devices provided by plugin modules, refer to the description of those respective modules.

Device Unit

Device Unit refers to the constituent elements of devices with changing states, such as buttons or analog axes on a joystick. Each Device Unit owns attributes including Name, Direction, Value Type, Precision, Value Range.

  • Name
    Name corresponds to the ID of each Device Unit, and the naming convention depends on the device type.

  • Direction
    Direction indicates whether it is Input or Output, signifying whether the state is obtained from the PC or if the state can be altered by the PC.
    Device Units of the Input type notify changes in state to the fsmapper system as Events.

  • Value Type
    Value Type can be Absolute or Relative. Absolute refers to instances where the value of the Device Unit directly corresponds to its position, such as analog axes on joysticks. Relative describes cases like a mouse, where the change in value represents the Device Unit's value.

  • Precision
    Precision signifies the precision of the Device Unit's state value, represented by either a 32-bit integer or a 64-bit floating-point number.

  • Values Range
    Value Range indicate the range within which the Device Unit can express values.

The attributes of Device Units corresponding to constituent elements of actual devices vary depending on the device type.
For devices supported by built-in drivers, refer to the DirectInput Game Device Specification and the SimHID Device Specification. For devices provided by plugin modules, refer to the description of those respective modules.

tip

For Input type Device Units, observing the events generated while manipulating the unit with Show Events and Messages enabled in the Message Console can aid in understanding the specifications.

Event Modifier

Event Modifier is a mechanism that determines how the state changes of a Device Unit are translated into specific events. There are three types of Event Modifiers: raw, button, and incdec. You can specify which modifier to use in the modifiers parameter of mapper.device().

  • raw
    This is the default modifier. It triggers a change event every time the value of the Device Unit changes. The Event Value is directly set as the value of the Device Unit.

  • button
    This modifier is intended for application to Device Units that can be represented by binary values, such as buttons. Its basic functionality is to generate a down event to indicate the transition from OFF to ON and an up event to indicate the transition from ON to OFF.
    By specifying in the modparam parameter, the modifier can triger doubleclick and singleclick event to support double-click functionality, trigger a longpress event when held for an extended duration, or triger follow_down or follow_up events to indicate the passage of time after down or up events occur.
    Furthermore, it can be used to specify behaviors similar to key repetition.

    This modifier can also be employed to enable binary functionality for Device Unit that inherently possess continuous quantities like analog axes.

  • incdec
    This modifier is intended for application to Device Units that operate on relative values. It generates an increment event when the change is positive and a decrement event when it is negative. The Event Value represents the absolute value of the Device Unit.

The modifiers parameter of mapper.device() specifies the definition of modifiers to apply to each Event Modifier using an array, similar to this code snippet in the sample script.
Here are explanations for each key-value pair within the table representing the definition of a modifier.

KeyTypeDescription
namestringSpecifies the name of the Device Unit targeted by the modifier.
If an Event Modifier is simultaneously defined for the class associated with the Device Unit specified by this parameter, the Event Modifier specified by the name takes precedence.
classstringSpecifies when applying the same modifier to multiple Device Units with similar characteristics.
It specifies one of the following: binary for units with binary value ranges, absolute for units with absolute value ranges, or relative for units with relative value ranges.
modtypestringModifier type.
It specifies eather of raw, button, or incdec
modparamtableOptions specific to the modifier.
For detailed information, refer to the Event Modifier Specification.

Event IDs Table

The necessary Event IDs of Input type Device Units required to define Event-Action mappings can be obtained by referencing the table returned by the Device:get_events() method. This table stores Event IDs under the names [Device-Unit-Name].[Event-name].
You can register Actions for Events triggered by the Device Units as follows.

device = Device{
name = "SimHID G1000",
type = "simhid",
identifier = {path = 'COM3'},
modifiers = {
{class = "binary", modtype = "button"},
}
}
local events = device:get_events()
mapper:set_primary_mappings{
{
event = events.SW1.down,
action = msfs.mfwasm.rpn_executer('(>K:AP_MASTER)')
}
}

Output Unit IDs Table

You can alter the state of Output type Device Units using the native-actions returned by the Device:send() method or the Device:sender() method.

Both methods require specifying an integer value, known as the Output Unit ID, to identify the Device Units. You can obtain the Output Unit ID by referencing the table returned by the Device.get_upstream_ids() method, where the Output Unit ID is stored with the Device Units name as the key.

Here's how you can modify the state of Output type Device Units.

device = Device{
name = "SimHID pannel",
type = "simhid",
identifier = {path = 'COM4'},
}
local ids = device:get_upstream_ids()
device:send(ids.LandingGearLeverIndicator, 1)

Lifecycle of Device object

Device object handling for device operations remains active until either Device:close() is called or until the object is deleted by garbage collection (GC). This implies that during device manipulation and while needing to receive events from the device, precautions must be taken to prevent the Device object from being targeted by GC.

Especially when dealing solely with Input type Device Units, situations where objects or functions managed by fsmapper like Event-Action mappings or Views bind to the Device object are less likely.

This is why in the code samples provided in this documentation, the Device object is bound to a global variable.

Supporting Custom Devices

Creating a plugin driver module enables handling devices other than the built-in Device Types. For more details, refer to the Plugin SDK.