Klipper

Overview

[wdd <stepper>] is the Klipper extension module for WD DRIVE motor drivers. It configures motor-control parameters: control-loop tuning, feed-forward, protection thresholds, homing detection, load model and more. The parameters are written to the driver when Klipper starts, and can be read or changed at any time while running.

  • Supports both rotary (stp42) and linear (lin) drivers; parameters adapt to the connected model automatically, with no need to pick registers by hand.
  • Anything you write is sent to the driver; optional items you leave out keep the factory defaults.

Before You Start

Please make sure you have:

  • a working Klipper host;
  • a WD DRIVE motor;
  • a working CAN link between the host and the driver board (USB-CAN adapter or CAN gateway);
  • an existing [stepper_*] section for the motor in printer.cfg, with microsteps, rotation_distance and step_pin all set.

Set Up CAN Communication (first time)

  1. Wire the CAN bus: connect the USB-CAN adapter to the driver board, fit a 120 Ω termination resistor at both ends of the bus, and keep every node at the same bitrate.

  2. Bring up the CAN interface. wddrive firmware runs at a fixed 1 Mbit/s:

    sudo ip link set can0 up type can bitrate 1000000
    
  3. Query the bus to get the driver board CAN UUID:

    ~/klippy-env/bin/python ~/klipper/scripts/canbus_query.py can0
    

    The output looks like:

    Found canbus_uuid=1d002a00001c, Application: Klipper
    
  4. Put the UUID into the [mcu] section of printer.cfg:

    [mcu]
    canbus_uuid: 1d002a00001c
    

If no device is found, check in order: whether can0 is up (ip -details link show can0), whether the bitrate is 1 Mbit/s, whether the termination resistors and wiring are correct, and whether the board is running in Klipper mode.

Enable the wdd Module

  1. Download the extension module wdd.py and place it into the Klipper klippy/extras/ directory.

    If the Klipper host is a Raspberry Pi, open a terminal in the folder that contains wdd.py (usually your Downloads folder) and upload it with scp (replace pi and raspberrypi.local with the real login user and host address; an IP address works too):

    scp wdd.py pi@raspberrypi.local:~/klipper/klippy/extras/
    

    On the first connection, confirm the host fingerprint and enter the Raspberry Pi login password when prompted.

  2. Add a [wdd <motor name>] section in printer.cfg; the name must match the last word of the stepper section (for example [wdd stepper_x] pairs with [stepper_x]).

  3. Fill in the parameters as described below.

  4. Save and run RESTART for the configuration to take effect.

If a value is wrong (a missing mandatory tuning, a negative number, a value outside the allowed range, and so on), Klipper reports an error at startup and stops — it never runs with a broken configuration.

Configuration Basics

  • [wdd ...] reads the microstep and travel settings from the matching [stepper_*] section, so both must exist.
  • Only the parameters you explicitly write are sent to the driver; optional items you leave out keep the factory defaults.
  • The homing parameters (homing_*) have built-in defaults and are always sent. Calibrate them for your machine on the bench.
  • All values must be non-negative.

Control Loop Tuning

The motor has three control loops: position, velocity and current. Each loop must be tuned, choosing one of the following two methods:

Tuning method What to write
Bandwidth Write the loop <loop>_bandwidth (in Hz). Any kp / ki for that loop is then ignored.
Gains Write the loop <loop>_kp; for the velocity and current loops you must also write <loop>_ki. The position loop has no integral term, so only position_kp applies.
Parameter Description
current_bandwidth Current loop bandwidth
current_kp / current_ki Current loop P / I gain
velocity_bandwidth Velocity loop bandwidth
velocity_kp / velocity_ki Velocity loop P / I gain
position_bandwidth Position loop bandwidth
position_kp Position loop P gain
# Current loop: bandwidth tuning
current_bandwidth: 1000
# Velocity loop: kp/ki tuning (both must be set together)
velocity_kp: 1.2
velocity_ki: 0.05
# Position loop: kp only
position_kp: 0.8

Pick either bandwidth or kp/ki. The tuning method is detected automatically — no extra switch is needed.

Feed-forward (optional)

Feed-forward improves tracking performance and is optional.

Parameter Description
velocity_feed_forward Velocity feed-forward coefficient; 0 disables it
velocity_accel_feed_forward Acceleration feed-forward coefficient
position_velocity_feed_forward Velocity feed-forward ratio in position mode; 1.0 means pass-through
position_acceleration_feed_forward Acceleration feed-forward coefficient in position mode

Position control modes use the position_* feed-forward; velocity mode (PV) uses the velocity_* feed-forward. The two do not affect each other — configure what you need.

Protection Parameters (optional)

All protection parameters below are optional; when omitted, the factory defaults are used.

Voltage protection

Parameter Description Unit
over_voltage_fault Over-voltage fault threshold V
over_voltage_warning Over-voltage warning threshold V
under_voltage_fault Under-voltage fault threshold V
under_voltage_warning Under-voltage warning threshold V
voltage_filter_time Voltage filter time ms

Current and overload protection

Parameter Description Unit
short_circuit_threshold Short-circuit / over-current threshold A
over_current_filter_time Over-current filter time ms
i2t_overload_level I²t overload warning level %
i2t_max_time I²t full-load trip time ms

Motor temperature protection

Parameter Description Unit
motor_over_temp_fault Motor over-temperature fault threshold °C
motor_over_temp_warning Motor over-temperature warning threshold °C

For example, over_voltage_fault: 54 sets the over-voltage fault to 54 V, and motor_over_temp_fault: 85 sets the motor over-temperature fault to 85 °C.

Reading the Motor Temperature (optional)

The driver samples the motor temperature internally (object dictionary 0x2000:0C). With motor_temperature enabled, the module polls that value periodically and registers it as a Klipper temperature sensor, so it shows up directly in the Mainsail / Fluidd temperature graph. If the driver’s object dictionary has no 0x2000:0C, the reads fail and a warning is logged, so do not enable it in that case.

Parameter Description Default
motor_temperature Enable the motor temperature sensor; set to True to turn it on False
motor_temperature_gcode_id Optional M105 identifier of the sensor (for example T0); when set, M105 also reports this temperature unset (not shown by M105)
[wdd stepper_x]
# ...
motor_temperature: True
motor_temperature_gcode_id: T0      # optional

Once enabled:

  • the sensor is registered as temperature_sensor wdd stepper_x and can be viewed directly in the Mainsail / Fluidd temperature graph;
  • you can wait for a temperature range with TEMPERATURE_WAIT SENSOR="wdd stepper_x" MINIMUM=... MAXIMUM=...;
  • the wdd stepper_x object status exposes both a temperature and a motor_temperature field;
  • a single timeout keeps the previous value; only repeated failures are logged as a warning in klipper.log.

This switch only reads and displays the temperature and writes nothing to the object dictionary; over-temperature protection is handled by motor_over_temp_fault / motor_over_temp_warning above. The two are independent, and you may enable either one alone.

Homing on Block

When homing, the driver slowly pushes the motor against the mechanical stop (the block). It detects that the stop has been reached when the phase current rises and the position following error grows at the same time. These parameters have built-in defaults and are always sent.

Parameter Description Default
homing_force_limit Maximum push force (current limit) during the block phase 1.0 A
homing_current_threshold Current threshold for detecting the block 0.7 A
homing_following_error Position following error required to detect the block 2.0 mm
homing_filter_time How long both conditions must hold 50 ms
homing_retract_distance Retract distance after hitting the block 3.0 mm

Caution: The defaults above are only a starting point and are not safe values. Always calibrate them on the bench for your motor and mechanics before production use, and never set all thresholds to 0 — otherwise homing may report success the instant it starts.

Load Model and Others

Parameter Description
load_inertia_ratio Load inertia ratio for rotary motors (stp42)
load_mass Load mass for linear motors (lin), in kg
encoder_pll_bandwidth Encoder PLL bandwidth
max_profile_velocity Maximum profile velocity
max_motor_speed Maximum motor speed

Configure the load parameter that matches your motor type.

Multiple Motors and Driver Boards

A single Klipper host can drive several WD DRIVE motors at the same time. Each driver is an independent node on the CAN bus, so you declare a separate [mcu <name>] section for it and prefix the pins of the matching [stepper_*] with that name.

Declare multiple MCUs

The unnamed [mcu] is the default MCU; every other driver board gets its own [mcu <name>] section. Each board uses its own canbus_uuid; when they share one CAN bus, they use the same canbus_interface:

[mcu]
canbus_interface: can0
canbus_uuid: f280ac7410f9

[mcu y]
canbus_interface: can0
canbus_uuid: c039d265332e

[mcu y1]
canbus_interface: can0
canbus_uuid: 24c700add124

Pin prefixes

Which MCU a [stepper_*] pin belongs to is set by the <mcu name>: prefix on the pin; with no prefix it uses the default [mcu]. Note that the inversion marker ! goes first, i.e. !<name>:<pin>:

[stepper_x]                  # uses the default [mcu]
step_pin: PA0
dir_pin: PA1
enable_pin: !PA2
endstop_pin: PA3

[stepper_y]                  # uses [mcu y]
step_pin: y:PA0
dir_pin: y:PA1
enable_pin: !y:PA2
endstop_pin: y:PA3

[stepper_y1]                 # uses [mcu y1], direction inverted
step_pin: y1:PA0
dir_pin: !y1:PA1
enable_pin: !y1:PA2
endstop_pin: y1:PA3

wdd sections

Every motor needs its own [wdd <stepper name>], whose name matches the last word of the [stepper_*] section — regardless of which MCU it is attached to:

[wdd stepper_x]
# ...

[wdd stepper_y]
# ...

[wdd stepper_y1]
# ...

Dual Y axes that must stay synchronized (for example a gantry) can be driven in parallel by two motors as shown above (Klipper folds stepper_y1 into the Y axis automatically); if no mechanical synchronization is needed, configure each axis independently. When all motors share one CAN bus, make sure there is a 120 Ω termination resistor at both ends of the bus and that every node runs at the same 1 Mbit/s bitrate.

Full Configuration Example

The example below is complete for a rotary (stp42) motor; for a linear (lin) motor, replace load_inertia_ratio with load_mass.

[stepper_x]
step_pin: PA0
dir_pin: PA1
enable_pin: !PA2
microsteps: 16
rotation_distance: 20
endstop_pin: PA3
position_endstop: 0
position_max: 400
homing_speed: 50.0

[wdd stepper_x]
# --- Control loops (bandwidth or kp/ki per loop; all three are required) ---
current_bandwidth: 1000                 # Current loop: bandwidth tuning
velocity_kp: 1.2                        # Velocity loop: kp/ki tuning
velocity_ki: 0.05
position_kp: 0.8                        # Position loop: kp only

# --- Feed-forward (optional) ---
position_velocity_feed_forward: 0.5     # Position-mode velocity feed-forward
position_acceleration_feed_forward: 0.7 # Position-mode acceleration feed-forward

# --- Load model (pick the one for your motor type) ---
load_inertia_ratio: 0.32                # Rotary motor (stp42)
# load_mass: 0.5                        # Linear motor (lin), in kg

# --- Protection (optional; factory defaults are kept when omitted) ---
over_voltage_fault: 54                  # V
under_voltage_warning: 20               # V
motor_over_temp_fault: 85               # °C
short_circuit_threshold: 5              # A
i2t_overload_level: 100                 # %
i2t_max_time: 10000                     # ms

# --- Homing on block (defaults exist and are always sent; overrides shown here) ---
# homing_force_limit: 1.0               # A, maximum push force during the block phase
# homing_current_threshold: 0.7         # A, block current threshold
# homing_following_error: 2.0           # mm, block position following error
# homing_filter_time: 50                # ms, how long both conditions must hold
# homing_retract_distance: 3.0          # mm, retract distance after the block

# --- Motor temperature (optional) ---
# motor_temperature: True               # enable the motor temperature sensor
# motor_temperature_gcode_id: T0        # optional, shown as T0 by M105

# --- Others (optional) ---
# encoder_pll_bandwidth: 2000
# max_profile_velocity: 300
# max_motor_speed: 3000

Runtime Adjustments

No restart is needed while tuning — read or change parameters directly from the Klipper console.

Write a parameter (VALUE takes the physical value, using the unit shown in the tables):

WDD_SET STEPPER=stepper_x PARAM=current_bandwidth VALUE=800
WDD_SET STEPPER=stepper_x PARAM=over_voltage_fault VALUE=52
WDD_SET STEPPER=stepper_x PARAM=homing_force_limit VALUE=1.0

Read a parameter:

WDD_GET STEPPER=stepper_x PARAM=current_bandwidth
WDD_GET STEPPER=stepper_x PARAM=over_voltage_fault

After the command runs, the console prints the current value.

PARAM= accepts any parameter name used in the configuration file: control-loop, feed-forward, protection, homing and load-model parameters.