Rotary encoders

A rotary encoder is a common input device that can be used for volume control, page scrolling, and other functions.

toml configuration

You can define a rotary encoder in your keyboard.toml:

[[input_device.encoder]]
pin_a = "P0_30"
pin_b = "P0_31"

# Whether to use the MCU's internal pull-up resistor, default to false
internal_pullup = false

# Working mode of the encoder
# Available modes:
# - default: resolution = 1
# - resolution: custom resolution, requires specifying resolution and reverse parameters
phase = "resolution"

# `resolution` represents the number of steps generated per detent.
#
# When your encoder datasheet lists:
#   - detent = number of mechanical detent positions  
#   - pulse  = number of full quadrature cycles (A/B cycles)  
#
# Then the relationship is:
#   resolution = (pulse × 4) / detent
# because each full quadrature cycle (pulse) produces 4 edge transitions.
#
# For example — in the ALPS EC11E series (https://tech.alpsalpine.com/cms.media/product_catalog_ec_01_ec11e_en_611f078659.pdf):
#   detent = 30, pulse = 15 → resolution = (15 × 4) / 30 = 2
resolution = 2

# Or you can specify detent and pulse to calculate resolution automatically
resolution = { detent = 30, pulse = 15 }

# Whether the direction of the rotary encoder is reversed.
reverse = false

Multiple encoders can be added directly, the encoder index is determined by the order:

# Encoder 0
[[input_device.encoder]]
pin_a = "P0_01"
pin_b = "P0_02"
phase = "default"
# Encoder 1
[[input_device.encoder]]
pin_a = "P0_03"
pin_b = "P0_04"
phase = "default"

Rust configuration

With Rust, you can define a rotary encoder as the following:

use rmk::input_device::rotary_encoder::RotaryEncoder;
    use rmk::input_device::rotary_encoder::DefaultPhase;
    let pin_a = Input::new(p.P1_06, embassy_nrf::gpio::Pull::None);
    let pin_b = Input::new(p.P1_04, embassy_nrf::gpio::Pull::None);
    let mut encoder = RotaryEncoder::with_phase(pin_a, pin_b, DefaultPhase, encoder_id);

You can also use the resolution based phase:

use rmk::input_device::rotary_encoder::RotaryEncoder;
    let pin_a = Input::new(p.P1_06, embassy_nrf::gpio::Pull::None);
    let pin_b = Input::new(p.P1_04, embassy_nrf::gpio::Pull::None);
    // Create an encoder with resolution = 2, reversed = false
    let mut encoder = RotaryEncoder::with_resolution(pin_a, pin_b, 2, false, encoder_id)

Then add the encoder to the device list of run_device.

join3(
        run_devices! (
            (matrix, encoder) => EVENT_CHANNEL,
        ),
        keyboard.run(), // Keyboard is special
        run_rmk(&keymap, driver, storage, rmk_config, sd),
    )
    .await;