I was recently searching for a way to improve my Ender 3 3D printer and get quieter prints and faster speeds without spending too much money. I was excited to find a software package called Klipper. Klipper is a combination of firmware and driver software for the Raspberry Pi.

There is very little help on the web about this particular piece of software aside from the great docs on the GitHub repository. I couldn't find exactly what I was looking for each step of the way. After about four hours of trial and error, I was finally able to get it running with my Ender 3 equipped with a BLTouch. I still don't have it tuned all the way, but I wanted to go ahead and share my findings.

I have included my configuration below.

Printer stats

  • Ender 3 Pro (this works with the standard Ender 3, as well)
  • Genuine BLTouch auto bed leveling sensor
  • Raspberry Pi 3 B+

After following the installation steps and getting Klipper running on my Raspberry Pi, the biggest issue I ran into was calibrating my Z offset. I spent hours on this task alone.

Here's how you calibrate your Z offset with a BLTouch and Klipper.

Make sure you have the [bltouch] section in your ~/printer.cfg (mine is below)

I used this page as a guide to the G-Code commands available in Klipper.

Calibrating the BLTouch Probe

To calibrate the BLTouch probe in Klipper, you need to access the terminal in OctoPrint. Type in PROBE_CALIBRATE. Place a piece of paper under the nozzle. Then, to lower your nozzle, type in testz z=-.5 This will lower your nozzle .5mm. Keep lowering until the paper meets resistance when you try to move it (the same resistance you would use for manual bed leveling). I keep mine pretty snug. If you go too far and the paper won't move at all, then type testz z=.1 to raise it .1mm. Keep raising and lowering, using smaller numbers each time until you get it just right, then type accept in the terminal. After you do this, type save_changes. That will commit the changes to the config file and restart Klipper.

Calibrating the Extruder (esteps) in Klipper

I forked a Google docs spreadsheet and added Klipper extruder settings to it. You can find the spreadsheet here

I created a 10mm cube that you can use to perform this calibration. You can find that on Thingiverse.

Setup your slicer to print the cube in vase mode ( Spiralize outer contours in Cura, uncheck Smooth spiralized contours).

Measure the wall thickness and get an average. Plug that into the spreadsheet. Then, grab the step_distance value from the [extruder] section of your ~/printer.cfg on the Raspberry Pi (To lookup this value, ssh into your Raspberry Pi and type nano ~/printer.cfg. Press the down arrow until you get to the [extruder] section. Plug the step_distance value into the spreadsheet and then replace what's in the printer.cfg file with the New Klipper steps value from the spreadsheet. Press ctrl+x and save the changes. Then, back in Octoprint's Terminal, type restart.

Print the cube again and measure it. If it's off, make sure you enter the new values into the spreadsheet before adjusting it again (copy the value in New ESteps/mm into the Current ESteps/mm before changing the new Measured Wall Thickness value) . Repeat this as many times as you need to to get accurate wall widths on your cube. Having the esteps tuned is important to ensure that your printer doesn't over or under extrude.

More information

The next step is to adjust the pressure advance. This will ensure that the corners are sharp and prevent oozing in the printed objects.

# This file contains common pin mappings for the 2018 Creality
# Ender 3. To use this config, the firmware should be compiled for the
# AVR atmega1284p.

# Note, a number of Melzi boards are shipped with a bootloader that
# requires the following command to flash the board:
#  avrdude -p atmega1284p -c arduino -b 57600 -P /dev/ttyUSB0 -U out/klipper.elf.hex
# If the above command does not work and "make flash" does not work
# then one may need to flash a bootloader to the board - see the
# Klipper docs/Bootloaders.md file for more information.

# See the example.cfg file for a description of available parameters.

step_pin: PD7
dir_pin: !PC5
enable_pin: !PD6
step_distance: .0125
endstop_pin: ^PC2
position_endstop: 0
position_max: 235
homing_speed: 50

step_pin: PC6
dir_pin: !PC7
enable_pin: !PD6
step_distance: .0125
endstop_pin: ^PC3
position_endstop: 0
position_max: 235
homing_speed: 50

step_pin: PB3
dir_pin: PB2
enable_pin: !PA5
step_distance: .0025
# endstop_pin: ^PC4
# position_endstop: 0.0
position_max: 250
position_min: -3
endstop_pin: probe:z_virtual_endstop

max_extrude_only_distance: 100.0
step_pin: PB1
dir_pin: !PB0
enable_pin: !PD6
step_distance: 0.011102
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PD5
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PA7
control: pid
# tuned for stock hardware with 200 degree Celsius target
pid_Kp: 21.527
pid_Ki: 1.063
pid_Kd: 108.982
min_temp: 0
max_temp: 250

heater_pin: PD4
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PA6
control: pid
# tuned for stock hardware with 50 degree Celsius target
pid_Kp: 54.027
pid_Ki: 0.770
pid_Kd: 948.182
min_temp: 0
max_temp: 130

pin: PB4

serial: /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_AM00I482-if00-port0

kinematics: cartesian
max_velocity: 300
max_accel: 3000
max_z_velocity: 5
max_z_accel: 100

lcd_type: st7920
cs_pin: PA3
sclk_pin: PA1
sid_pin: PC1
encoder_pins: ^PD2, ^PD3
click_pin: ^!PC0

# BLTouch probe. One may define this section (instead of a probe
# section) to enable a BLTouch probe. (Note! This bltouch module may
# not work correctly with some BLTouch "clones"!) A virtual
# "probe:z_virtual_endstop" pin is also created (see the "probe"
# section above for the details).
sensor_pin: ^PC4
#   Pin connected to the BLTouch sensor pin. This parameter must be
#   provided.
control_pin: PA4
#   Pin connected to the BLTouch control pin. This parameter must be
#   provided.
#pin_move_time: 0.675
#   The amount of time (in seconds) to wait for the BLTouch pin to
#   move up or down. The default is 0.675 seconds.
#pin_up_reports_not_triggered: True
#   Set if the BLTouch consistently reports the probe in a "not
#   triggered" state after a successful "pin_up" command. This should
#   be True for a genuine BLTouch; some BLTouch clones may require
#   False.  The default is True.
#pin_up_touch_mode_reports_triggered: True
#   Set if the BLTouch consistently reports a "triggered" state after
#   the commands "pin_up" followed by "touch_mode". This should be
#   True for a genuine BLTouch; some BLTouch clones may require
#   False. The default is True.

# Measure the x and y distance between the tip of your nozzle and 
# your BLTouch tip
# x : + is to the right and - is to the left
# y : + is toward the back and - is toward the front
x_offset: -41 # 41mm left of the nozzle
y_offset: -13 # 13mm forward of the nozzle

speed: 5.0
#   See the "probe" section for information on these parameters.

# Mesh Bed Leveling. One may define a [bed_mesh] config section
# to enable move transformations that offset the z axis based
# on a mesh generated from probed points. Note that bed_mesh
# and bed_tilt are incompatible, both cannot be defined.  When
# using a probe to home the z-axis, it is recommended to define
# a [homing_override] section in printer.cfg to home toward the
# center of the print area.
#  Visual Examples:
#   rectangular bed, probe_count = 3,3:
#               x---x---x (max_point)
#               |
#               x---x---x
#                       |
#   (min_point) x---x---x
#   round bed, round_probe_count = 5, bed_radius = r:
#                  x (0,r) end
#                /
#              x---x---x
#                        \
#   (-r,0) x---x---x---x---x (r,0)
#            \
#              x---x---x
#                    /
#                  x  (0,-r) start
speed: 100
#   The speed (in mm/s) of non-probing moves during the
#   calibration. The default is 50.
horizontal_move_z: 5
#   The height (in mm) that the head should be commanded to move to
#   just prior to starting a probe operation. The default is 5.
samples: 2
#   The number of times to probe each point.  The probed z-values
#   will be averaged.  The default is to probe 1 time.
sample_retract_dist: 3.0
#   The distance (in mm) to retract between each sample if
#   sampling more than once.  Default is 2mm.
#   Defines the radius to probe for round beds.  Note that the radius
#   is relative to the nozzle's origin, if using a probe be sure to
#   account for its offset.  This parameter must be provided for round
#   beds and omitted for rectangular beds.
min_point: 78,30
#   Defines the minimum x,y position to probe when for rectangular
#   beds. Note that this refers to the nozzle position, take care that
#   you do not define a point that will move the probe off of the bed.
#   This parameter must be provided for rectangular beds.
max_point: 230,215
#   Defines the maximum x,y position to probe when for rectangular
#   beds. Follow the same precautions as listed in min_point. Also note
#   that this does not necessarily define the last point probed, only
#   the maximum coordinate. This parameter must be provided.
probe_count: 3,3
#   For rectangular beds, this is a comma separate pair of integer
#   values (X,Y) defining the number of points to probe along each axis.
#   A single value is also valid, in which case that value will be applied
#   to both axes.  Default is 3,3.
#round_probe_count: 5
#   For round beds, this is integer value defines the maximum number of
#   points to probe along each axis. This value must be an odd number.
#   Default is 5.
#fade_start: 1.0
#   The gcode z position in which to start phasing out z-adjustment
#   when fade is enabled.  Default is 1.0.
#fade_end: 0.0
#   The gcode z position in which phasing out completes.  When set
#   to a value below fade_start, fade is disabled. It should be
#   noted that fade may add unwanted scaling along the z-axis of a
#   print.  If a user wishes to enable fade, a value of 10.0 is
#   recommended. Default is 0.0, which disables fade.
#   The z position in which fade should converge. When this value is set
#   to a non-zero value it must be within the range of z-values in the mesh.
#   Users that wish to converge to the z homing position should set this to 0.
#   Default is the average z value of the mesh.
#split_delta_z: .025
#   The amount of Z difference (in mm) along a move that will
#   trigger a split. Default is .025.
#move_check_distance: 5.0
#   The distance (in mm) along a move to check for split_delta_z.
#   This is also the minimum length that a move can be split. Default
#   is 5.0.
#mesh_pps: 2,2
#   A comma separated pair of integers (X,Y) defining the number of
#   points per segment to interpolate in the mesh along each axis. A
#   "segment" can be defined as the space between each probed
#   point. The user may enter a single value which will be applied
#   to both axes.  Default is 2,2.
#algorithm: lagrange
#   The interpolation algorithm to use. May be either "lagrange"
#   or "bicubic". This option will not affect 3x3 grids, which
#   are forced to use lagrange sampling.  Default is lagrange.
#bicubic_tension: .2
#   When using the bicubic algorithm the tension parameter above
#   may be applied to change the amount of slope interpolated.
#   Larger numbers will increase the amount of slope, which
#   results in more curvature in the mesh. Default is .2.

# From reddit for Ender 3
# https://www.reddit.com/r/klippers/comments/9tu2ib/config_file_for_modded_ender_3/
axes: z
        G1 Z10 F6000
        G28 X Y
        G1 X163 Y117 F6000
        G28 Z0
        G1 X163 Y117 Z10

# From YouTube https://www.youtube.com/watch?v=i_541iD5Bj0
[gcode_macro G29]
        G1 Z10 F600

#*# <---------------------- SAVE_CONFIG ---------------------->
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
#*# [bltouch]
#*# z_offset = 1.752
#*# [bed_mesh default]
#*# points =
#*#       -0.000000, 0.483750, 1.086250
#*#       -0.286250, -0.055000, 0.287500
#*#       -0.430000, -0.412500, -0.323750
#*# x_count = 3
#*# y_count = 3
#*# min_x = 78.0
#*# max_x = 230.0
#*# min_y = 30.0
#*# max_y = 215.0
#*# x_offset = -41.0
#*# y_offset = -13.0
#*# mesh_x_pps = 2
#*# mesh_y_pps = 2
#*# algo = lagrange
#*# tension = 0.2