OpenLogi

0x4600 · crown

Control the MX Master rotary crown — query hardware capabilities, read or write reporting and ratchet mode, and receive diverted rotation, touch, gesture, and button events.

The Crown feature controls the rotary crown on the MX Master series. get_info returns the hardware's CrownControlCapabilities (button, long-press, mechanized ratchet, configurable rotation timeout, short-long timeout, and double-tap speed) plus CrownSensorCapabilities (proximity, touch, tap, and double-tap gestures), together with the slot and ratchet counts per revolution. get_mode and set_mode read and write the active CrownMode: whether events are delivered over the native HID channel or diverted into HID++ (ReportingMode), the ratchet vs. free-spin choice (RatchetMode), and three configurable timings: rotation timeout, short-long press threshold, and double-tap speed, all in 10 ms steps. Every SetCrownMode field uses a zero / NoChange sentinel so individual settings can be updated without disturbing the others.

While diverted, the crown emits a single CrownEvent::Update carrying a CrownUpdate snapshot:

  • rotation_state / relative_slot_rotation / relative_ratchet_rotation / speedRotationState phase, signed delta slot and ratchet counts, and current speed in slots/s.
  • proximity / touchActivityState lifecycle phases (StartActiveStop).
  • gestureCrownGesture: Tap, DoubleTap, or None.
  • buttonButtonState: Inactive, Press, ShortPressActive, LongPress, LongPressActive, or Release.

Spec: Logitech HID++ 2.0 — 0x4600 crown. Used by: Typed wrapper in openlogi-hidpp.

Function reference

The CrownFeature wrapper (0x4600) exposes:

Methods

FunctionHID++ fnSignatureReturns
get_info0()CrownInfo
get_mode1()CrownMode
set_mode2(mode: SetCrownMode)CrownMode
listenevent()Receiver<CrownEvent>

get_info, get_mode, and set_mode are async and return Result<…, Hidpp20Error>. listen is synchronous and returns an async_channel::Receiver<CrownEvent> directly.

Types

CrownInfo

Crown capabilities and hardware constants returned by get_info.

FieldTypeDescription
controlsCrownControlCapabilitiesControl capabilities.
sensorsCrownSensorCapabilitiesSensor capabilities.
slotsu16Number of slots per revolution.
ratchetsu16Number of ratchets per revolution.

CrownControlCapabilities

Crown control capabilities, from get_info.

FlagBit/ValueDescription
BUTTON1 << 0The crown has a button.
BUTTON_LONG_PRESS1 << 1The button reports long presses.
MECHANIZED_RATCHET1 << 2The ratchet is mechanized (no manual control).
ROTATION_TIMEOUT_CONFIGURABLE1 << 3The rotation timeout is configurable.
SHORT_LONG_TIMEOUT_CONFIGURABLE1 << 4The short-long timeout is configurable.
DOUBLE_TAP_SPEED_CONFIGURABLE1 << 5The double-tap speed is configurable.

CrownSensorCapabilities

Crown sensor capabilities, from get_info.

FlagBit/ValueDescription
PROXIMITY1 << 0The crown has a proximity sensor.
TOUCH1 << 1The crown has a touch sensor.
TAP_GESTURE1 << 2The crown detects tap gestures.
DOUBLE_TAP_GESTURE1 << 3The crown detects double-tap gestures.

CrownMode

The crown's current mode, returned by get_mode and echoed by set_mode.

FieldTypeDescription
divertingReportingModeHow events are reported.
ratchet_modeRatchetModeRatchet mode.
rotation_timeoutu8Rotation timeout, in 10 ms steps.
short_long_timeoutu8Short-long press timeout, in 10 ms steps.
double_tap_speedu8Double-tap speed, in 10 ms steps.

SetCrownMode

Mode settings written by set_mode. Every field uses 0 / ReportingMode::NoChange / RatchetMode::NoChange as a "leave unchanged" sentinel. The rotation timeout is clipped to 0x40.

FieldTypeDescription
divertingReportingModeHow events are reported, or ReportingMode::NoChange.
ratchet_modeRatchetModeRatchet mode, or RatchetMode::NoChange.
rotation_timeoutu8Rotation timeout in 10 ms steps, or 0 to leave unchanged.
short_long_timeoutu8Short-long timeout in 10 ms steps, or 0 to leave unchanged.
double_tap_speedu8Double-tap speed in 10 ms steps, or 0 to leave unchanged.

ReportingMode

How crown events are reported.

VariantValueDescription
NoChange0Leave the setting unchanged (write-only sentinel).
Hid1Events go to the native HID channel.
Diverted2Events are diverted to HID++ (required for CrownEvent).

RatchetMode

The crown's ratchet mode.

VariantValueDescription
NoChange0Leave the setting unchanged (write-only sentinel).
Free1Free-spinning mode.
Ratchet2Ratchet (detented) mode.

RotationState

Rotation phase reported in a CrownUpdate.

VariantValueDescription
Inactive0Not rotating (or not diverted).
Start1Rotation started.
Active2Rotation ongoing.
Stop3Rotation stopped.

ActivityState

Proximity or touch activity phase reported in a CrownUpdate.

VariantValueDescription
Inactive0Inactive.
Start1Started.
Active2Ongoing.
Stop3Stopped.

CrownGesture

Touch gesture reported in a CrownUpdate.

VariantValueDescription
None0No gesture.
Tap1Single tap.
DoubleTap2Double tap.

ButtonState

Crown button state reported in a CrownUpdate.

VariantValueDescription
Inactive0Inactive (or not diverted).
Press1Press started.
ShortPressActive2Short press active.
LongPress3Long press reached the time threshold.
LongPressActive4Long press active.
Release5Released.

Events

CrownFeature implements EmittingFeature<CrownEvent>; call listen() to receive a channel of CrownEvent values. Events are only delivered while the crown is diverted (ReportingMode::Diverted).

CrownEvent

VariantDescription
Update(CrownUpdate)The crown's rotation, proximity, touch, gesture, or button state changed.

CrownUpdate

Payload of CrownEvent::Update.

FieldTypeDescription
rotation_stateRotationStateCurrent rotation phase.
relative_slot_rotationi8Slots rotated since the last event (-127..=127).
relative_ratchet_rotationi8Ratchets rotated since the last event (-127..=127).
proximityActivityStateProximity-sensor phase.
touchActivityStateTouch-sensor phase.
gestureCrownGestureTouch gesture detected.
buttonButtonStateButton state.
speedi16Crown speed in slots per second (signed).

Wire format

Getter requests carry a 3-byte zero payload (call). set_mode uses a 16-byte long payload (call_long). All responses are read from the 16-byte extended payload returned by extend_payload(). Events arrive as unsolicited 16-byte messages on function index 0.

get_info (fn 0)

Request: [0x00, 0x00, 0x00]

Response (byte → field):

ByteFieldNotes
0controlsCrownControlCapabilities bitfield
1sensorsCrownSensorCapabilities bitfield
2–3slotsBig-endian u16 — slots per revolution
4–5ratchetsBig-endian u16 — ratchets per revolution

get_mode (fn 1)

Request: [0x00, 0x00, 0x00]

Response (byte → field):

ByteFieldNotes
0divertingReportingMode enum (0=NoChange, 1=Hid, 2=Diverted)
1ratchet_modeRatchetMode enum (0=NoChange, 1=Free, 2=Ratchet)
2rotation_timeoutu8, in 10 ms steps
3short_long_timeoutu8, in 10 ms steps
4double_tap_speedu8, in 10 ms steps

set_mode (fn 2)

Request (16-byte long payload — bytes 5–15 are 0x00):

ByteFieldNotes
0divertingReportingMode as u8
1ratchet_modeRatchetMode as u8
2rotation_timeoutu8, in 10 ms steps
3short_long_timeoutu8, in 10 ms steps
4double_tap_speedu8, in 10 ms steps

Response: same layout as get_mode — the device echoes the resulting active mode.

CrownEvent::Update (event fn 0)

Unsolicited event emitted while the crown is diverted:

ByteFieldNotes
0rotation_stateRotationState enum
1relative_slot_rotationi8 — signed slot delta since last event
2relative_ratchet_rotationi8 — signed ratchet delta since last event
3proximityActivityState enum
4touchActivityState enum
5gestureCrownGesture enum
6buttonButtonState enum
7–13(reserved)not decoded by the driver
14–15speedBig-endian i16 — slots per second

Usage (Rust)

use hidpp::{
    device::Device,
    feature::crown::{CrownFeature, ReportingMode, RatchetMode, SetCrownMode},
};

// device: &mut Device, already created via Device::new(channel, index)
device.enumerate_features().await?;
if let Some(feat) = device.get_feature::<CrownFeature>() {
    let info = feat.get_info().await?;
    println!("slots/rev={}, ratchets/rev={}", info.slots, info.ratchets);

    // Divert crown events to HID++ and switch to ratchet mode
    let mode = feat.set_mode(SetCrownMode {
        diverting: ReportingMode::Diverted,
        ratchet_mode: RatchetMode::Ratchet,
        rotation_timeout: 0,    // leave unchanged
        short_long_timeout: 0,  // leave unchanged
        double_tap_speed: 0,    // leave unchanged
    }).await?;
    println!("active mode: {:?}", mode.diverting);

    // Receive crown events
    let rx = feat.listen();
    while let Ok(event) = rx.recv().await {
        println!("{:?}", event);
    }
}

On this page