Skip to content

Joystick

This feature provides game controller input as a joystick device supporting up to 6 axes and 32 buttons. Axes can be read either from an ADC-capable input pin, or can be virtual, so that its value is provided by your code.

An analog device such as a potentiometer found on an analog joystick's axes is based on a voltage divider, where adjusting the movable wiper controls the output voltage which can then be read by the microcontroller's ADC.

Usage

Add the following to your rules.mk:

make
JOYSTICK_ENABLE = yes

By default the joystick driver is analog, but you can change this with:

make
JOYSTICK_DRIVER = digital

Configuration

By default, two axes and eight buttons are defined, with a reported resolution of 8 bits (-127 to +127). This can be changed in your config.h:

c
// Min 0, max 32
#define JOYSTICK_BUTTON_COUNT 16
// Min 0, max 6: X, Y, Z, Rx, Ry, Rz
#define JOYSTICK_AXIS_COUNT 3
// Min 8, max 16
#define JOYSTICK_AXIS_RESOLUTION 10

TIP

You must define at least one button or axis. Also note that the maximum ADC resolution of the supported AVR MCUs is 10-bit, and 12-bit for most STM32 MCUs.

Axes

When defining axes for your joystick, you must provide a definition array typically in your keymap.c.

For instance, the below example configures two axes. The X axis is read from the A4 pin. With the default axis resolution of 8 bits, the range of values between 900 and 575 are scaled to -127 through 0, and values 575 to 285 are scaled to 0 through 127. The Y axis is configured as a virtual axis, and its value is not read from any pin. Instead, the user must update the axis value programmatically.

c
joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {
    JOYSTICK_AXIS_IN(A4, 900, 575, 285),
    JOYSTICK_AXIS_VIRTUAL
};

Axes can be configured using one of the following macros:

  • JOYSTICK_AXIS_IN(input_pin, low, rest, high)
    The ADC samples the provided pin. low, high and rest correspond to the minimum, maximum, and resting (or centered) analog values of the axis, respectively.
  • JOYSTICK_AXIS_VIRTUAL
    No ADC reading is performed. The value should be provided by user code.

The low and high values can be swapped to effectively invert the axis.

Virtual Axes

The following example adjusts two virtual axes (X and Y) based on keypad presses, with KC_P0 as a precision modifier:

c
joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {
    JOYSTICK_AXIS_VIRTUAL, // x
    JOYSTICK_AXIS_VIRTUAL  // y
};

static bool precision = false;
static uint16_t precision_mod = 64;
static uint16_t axis_val = 127;

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    int16_t precision_val = axis_val;
    if (precision) {
        precision_val -= precision_mod;
    }

    switch (keycode) {
        case KC_P8:
            joystick_set_axis(1, record->event.pressed ? -precision_val : 0);
            return false;
        case KC_P2:
            joystick_set_axis(1, record->event.pressed ? precision_val : 0);
            return false;
        case KC_P4:
            joystick_set_axis(0, record->event.pressed ? -precision_val : 0);
            return false;
        case KC_P6:
            joystick_set_axis(0, record->event.pressed ? precision_val : 0);
            return false;
        case KC_P0:
            precision = record->event.pressed;
            return false;
    }
    return true;
}

Keycodes

KeyAliasesDescription
QK_JOYSTICK_BUTTON_0JS_0Button 0
QK_JOYSTICK_BUTTON_1JS_1Button 1
QK_JOYSTICK_BUTTON_2JS_2Button 2
QK_JOYSTICK_BUTTON_3JS_3Button 3
QK_JOYSTICK_BUTTON_4JS_4Button 4
QK_JOYSTICK_BUTTON_5JS_5Button 5
QK_JOYSTICK_BUTTON_6JS_6Button 6
QK_JOYSTICK_BUTTON_7JS_7Button 7
QK_JOYSTICK_BUTTON_8JS_8Button 8
QK_JOYSTICK_BUTTON_9JS_9Button 9
QK_JOYSTICK_BUTTON_10JS_10Button 10
QK_JOYSTICK_BUTTON_11JS_11Button 11
QK_JOYSTICK_BUTTON_12JS_12Button 12
QK_JOYSTICK_BUTTON_13JS_13Button 13
QK_JOYSTICK_BUTTON_14JS_14Button 14
QK_JOYSTICK_BUTTON_15JS_15Button 15
QK_JOYSTICK_BUTTON_16JS_16Button 16
QK_JOYSTICK_BUTTON_17JS_17Button 17
QK_JOYSTICK_BUTTON_18JS_18Button 18
QK_JOYSTICK_BUTTON_19JS_19Button 19
QK_JOYSTICK_BUTTON_20JS_20Button 20
QK_JOYSTICK_BUTTON_21JS_21Button 21
QK_JOYSTICK_BUTTON_22JS_22Button 22
QK_JOYSTICK_BUTTON_23JS_23Button 23
QK_JOYSTICK_BUTTON_24JS_24Button 24
QK_JOYSTICK_BUTTON_25JS_25Button 25
QK_JOYSTICK_BUTTON_26JS_26Button 26
QK_JOYSTICK_BUTTON_27JS_27Button 27
QK_JOYSTICK_BUTTON_28JS_28Button 28
QK_JOYSTICK_BUTTON_29JS_29Button 29
QK_JOYSTICK_BUTTON_30JS_30Button 30
QK_JOYSTICK_BUTTON_31JS_31Button 31

API

struct joystick_t

Contains the state of the joystick.

Members

  • uint8_t buttons[]
    A bit-packed array containing the joystick button states. The size is calculated as (JOYSTICK_BUTTON_COUNT - 1) / 8 + 1.
  • int16_t axes[]
    An array of analog values for each defined axis.
  • bool dirty
    Whether the current state needs to be sent to the host.

struct joystick_config_t

Describes a single axis.

Members

  • pin_t input_pin
    The pin to read the analog value from, or JS_VIRTUAL_AXIS.
  • uint16_t min_digit
    The minimum analog value.
  • uint16_t mid_digit
    The resting or midpoint analog value.
  • uint16_t max_digit
    The maximum analog value.

void joystick_flush(void)

Send the joystick report to the host, if it has been marked as dirty.


void register_joystick_button(uint8_t button)

Set the state of a button, and flush the report.

Arguments

  • uint8_t button
    The index of the button to press, from 0 to 31.

void unregister_joystick_button(uint8_t button)

Reset the state of a button, and flush the report.

Arguments

  • uint8_t button
    The index of the button to release, from 0 to 31.

int16_t joystick_read_axis(uint8_t axis)

Sample and process the analog value of the given axis.

Arguments

  • uint8_t axis
    The axis to read.

Return Value

A signed 16-bit integer, where 0 is the resting or mid point.

void joystick_set_axis(uint8_t axis, int16_t value)

Set the value of the given axis.

Arguments

  • uint8_t axis
    The axis to set the value of.
  • int16_t value
    The value to set.