前言

毕设第一部分就是把上古时代的宝贝一台3D打印机从原来的Marlin固件换成Klipper固件。我也是第一次接触DIY打印机,特此记录一下过程。

下位机驱动板

基于rp2040zero的四轴3d打印机控制板 - 立创开源广场

这板子没有无限位调零,因此自己又画了一版,参考RP2040直插王Klipper 4轴3D打印机主板 Voron - 立创开源广场和上面的项目画成。

画了整整五天才好 连线头都大了

可惜沉金面积太大了不能免费沉金。先做了打样,等测试没问题就再打彩色丝印+沉金~

彩色丝印就是好看啊!

2025.03.13拿到打样板了,只能说还好测试了一下。温度引脚全画错了,本来只能接到ADC的GPIO上,我直接接到普通的GPIO了。SPI的接法也不好。重新走了几个引脚的线画了一遍。

RGB确实好看。RGB是对的,不是乱搞我跟你们讲。

正面丝印太多了,图片又是暗色系,容易看不清丝印,干脆正面不放彩色丝印了。后面还能拿这个找老师签名(?)

2025.03.14我真傻,真的,我单知道彩色丝印能放图片,我忘了也可以单独修改丝印颜色的。

彩色丝印超好看!

2025.03.22啥比了,忘了管子要走大电流了,接地引脚与地的连线有点窄,是默认的10mil。算了下勉强能用。

2025.03.24更啥比了,发现SPI画错了,电源部分也有些问题,血亏54。重新打板了,复试考完就开干。

2025.03.30板子到啦!一个下午焊完了,直接就去测试了。一遍通过!不过Klipper的源代码里对SPI定义有区别,我又重新拉了源码自己编译的。

2025.03.31还是有地方画的不好。四个螺丝孔忘记接地了😼

热床

折腾了一周放弃冷打印台了。固体胶压根粘不住,轻轻一推就会掉。看了一圈,没有160x160的热床,于是打算自己画一个。还好嘉立创还能免费打样做铝基板,无敌。

先买一下电源线。查一下AWG对应电流表:

AWG对应线缆规格

因为保险丝是7.5A的,因此买最大电流9A的AWG14即可。

随后是热床板子。直接画成90x90mm的,之后在角落留出焊接的地方,四块拼一下就行。

在开源广场上搜了一下,找到一个不错的项目,可以一键生成希尔伯特曲线希尔伯特曲线绘制插件 - 立创开源广场

预留了热敏电阻的焊接/粘贴处。

热床PCB

2025.04.10热床铝基板到啦!串联四片+焊接电源线后测阻值,约为2.6Ω,电流在安全范围内,因此总功率约为55W。升温有点慢。焊接的时候烙铁功率上不去,焊点都化不开。话说这AWG14的线是真的粗。感觉买6A的都够了。

四片拼一片,大小刚好能被夹住

喉管与散热

最开始的时候,一打印就堵头,然后只能回抽抽出来把前面一段耗材剪掉。分析了下原因,是因为耗材顶到黄铜喷嘴出不去,结果前端软化变大了。

更换了新的CR10打印热端与特氟龙管,将管子深深插入喉管,顶出来一点,再把黄铜喷嘴拧到加热块上面,顶在特氟龙管上。然后把原来原装的风扇扔了,换上新买的4cm静音风扇,之后很长一段时间再也没堵过。

堵头发生的原因基本都是软化后的耗材丝没有第一时间从挤出口出去,结果冷却映画堵住了挤出口。一味抬高打印头与打印平面的距离是没有用的,既不能太高也不能太低。首层可以稍微抬一点,挂住就行,后面需要往下压一点,确保挤出的耗材能挂住先前打印的地方。

此外,如果调平做的不是特别好,或者打印过程中有悬空的地方,也会导致本应挂上耗材的地方走空,导致挤出口堵住。

配置上位机环境

我是在虚拟机上装的,再将端口开放出来,本地访问。

Vmware配置里选择nat即可,然后看一下虚拟网卡的IP。之前配置完后,可以从主机电脑远程SSH连接到虚拟机,但是虚拟机上不了网。手动修改一下DNS,改成主机的虚拟网卡地址,或者修改为腾讯的223.5.5.5即可。

使用dw-0/Kiauh: Klipper Installation And Update Helper自动化配置。安装如下的组件:

Kiauh安装组件

其中,KlipperScreen安装后会把你的桌面环境换成他那个触屏。如果是树莓派+触摸屏做上位机的话应该很舒服。普通电脑的话还是别装了。

Fluidd默认会使用80端口,如果端口冲突了去NGINX配置目录里的available-sites修改一下监听端口就行。如果没有启动成功,去NGINX配置里加上include /etc/nginx/sites-available/*;即可。

编译并刷入固件

Kiauh全部装完之后,进入Klipper文件夹开始编译固件。先配置一下:make menuconfig

编译配置

不知道能不能买块RP2350让学校报销

编译完后的固件是UF2格式的。烧录也很简单:按住Boot上电会自动识别为U盘,拖入UF2固件即可。点名表扬某号称要平替Pico的国产RISC-V开发版,做到最后只有尺寸和引脚可以平替。

打印环境配置

感谢*@SweetGingerGreen* 的无私帮助!

运动轴配置

这打印机是笛卡尔式(i3式)打印机,因此是XYZ轴+E端的挤出。

XY轴就正常配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[stepper_x]
step_pin: gpio5 # 步进脉冲
dir_pin: gpio6 # 加或者不加!改方向
enable_pin: !gpio0 # 驱动使能
endstop_pin: !gpio4 # 限位引脚
microsteps: 64 # 细分
rotation_distance: 40 # 电机转一圈步数mm
position_endstop: 0 # 限位位置
position_max: 150 # 最大行程
homing_speed: 30 # 回零速度

[stepper_y]
step_pin: gpio16 # 步进脉冲
dir_pin: !gpio2 # 加或者不加!改方向
enable_pin: !gpio0 # 驱动使能
microsteps: 64 # 细分
rotation_distance: 40 # 电机转一圈步数mm
endstop_pin: !gpio17 # 限位引脚
position_endstop: 130 # 限位位置
position_max: 130 # 最大行程
homing_speed: 30 # 回零速度

对于Z轴,需要测量丝杆的导程。正常的打印机应该导程都是4mm。

1
2
3
4
5
6
7
8
9
10
11
[stepper_z]
step_pin: gpio20 # 步进脉冲
dir_pin: !gpio19 # 加或者不加!改方向
enable_pin: !gpio0 # 驱动使能
microsteps: 64 # 细分
rotation_distance: 8 # 丝杆导程mm
endstop_pin: !gpio21 # 限位引脚
position_endstop: 0 # 限位位置
position_max: 180 # 最大行程
position_min: -1 # 最小行程
homing_speed: 10 # 回零速度

TMC2209电机驱动

一开始使用的是复用UART的方法,四个模块共用一个UART地址位,随后手动插跳线帽来选择通讯地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[tmc2209 stepper_x]
uart_pin: gpio3 # UART通讯
uart_address: 0 # UART地址
interpolate: False # 微步插值256
run_current: 1.2 # 驱动输出电流
hold_current: 0.6 # 电机静止保持电流
stealthchop_threshold: 999999 # 静音阈值

[tmc2209 stepper_y]
uart_pin: gpio18 # UART通讯
uart_address: 1 # UART地址
interpolate: False # 微步插值256
run_current: 1.2 # 驱动输出电流
hold_current: 0.6 # 电机静止保持电流
stealthchop_threshold: 999999 # 静音阈值

但后面重新绘制了开发版,每一个电机采用自己独立的UART地址,因此就不需要再这样画了:

1
2
3
4
5
6
7
8
9
10
11
12
13
[tmc2209 stepper_x]
uart_pin: gpio3 # UART通讯
interpolate: False # 微步插值256
run_current: 1.2 # 驱动输出电流
hold_current: 0.6 # 电机静止保持电流
stealthchop_threshold: 999999 # 静音阈值

[tmc2209 stepper_y]
uart_pin: gpio18 # UART通讯
interpolate: False # 微步插值256
run_current: 1.2 # 驱动输出电流
hold_current: 0.6 # 电机静止保持电流
stealthchop_threshold: 999999 # 静音阈值

现在似乎倾向于不指定hold_current了。等之后注释掉再重新试一试。

2025.04.09似乎注释之后没啥区别,那就删了。

WS2812灯带

都自己DIY了怎么能不玩RGB呢?那就必须玩。 直接焊了16个WS2812组菊花链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[neopixel ws2812_chain]
pin: gpio1
chain_count: 16
color_order: GRB
initial_RED: 0.01
initial_GREEN: 0.01
initial_BLUE: 0.01

[led_effect rainbow]
leds:
neopixel:ws2812_chain (1-8)
neopixel:ws2812_chain (16-9)
autostart: true
frame_rate: 60
layers:
gradient 0.5 0.125 add (0.1, 0.0, 0.0),(0.0, 0.1, 0.0),(0.0, 0.0, 0.1)

[led_effect side_error]
leds:
neopixel:ws2812_chain (1-16)
autostart: false
frame_rate: 60
layers:
blink 1 0.5 add (0.2,0.0,0.0)
run_on_error: true

[led_effect heating1]
leds:
neopixel:ws2812_chain (1-8)
autostart: false
frame_rate: 60
layers:
breathing 2 1 top (0.1, 0.0, 0.3), (0.1, 0.3, 0.0)

[led_effect heating2]
leds:
neopixel:ws2812_chain (9-16)
autostart: false
frame_rate: 60
layers:
breathing 2 1 top (0.1, 0.3, 0.0), (0.1, 0.0, 0.3)

[led_effect chase]
leds:
neopixel:ws2812_chain (1-16)
autostart: false
frame_rate: 60
layers:
chase 0.25 0.4 top (0.018, 0.087, 0.016)

然后再定义相关的宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[gcode_macro LEDCHAIN_OFF]
description: 关闭灯带
gcode:
STOP_LED_EFFECTS

[gcode_macro LEDCHAIN_ON]
description: 开启灯带
gcode:
STOP_LED_EFFECTS
SET_LED_EFFECT EFFECT=rainbow

[gcode_macro LEDCHAIN_ERROR]
gcode:
STOP_LED_EFFECTS
SET_LED_EFFECT EFFECT=side_error

[gcode_macro LEDCHAIN_PRINTING]
gcode:
STOP_LED_EFFECTS
SET_LED_EFFECT EFFECT=chase

[gcode_macro LEDCHAIN_HEATING]
gcode:
STOP_LED_EFFECTS
SET_LED_EFFECT EFFECT=heating1
SET_LED_EFFECT EFFECT=heating2

这里因为没办法在一个宏下定义两个灯光模式,因此拆分整个灯带为两条,分开控制。

之后,再写一个函数用于自己控制加热温度:

1
2
3
4
5
6
7
8
9
10
11
12
13
[gcode_macro MY_HEAT]
description: 加热挤出机到指定温度(默认210度)
gcode:
{% set target_temp = params.TEMP|default(210)|float %}

SET_HEATER_TEMPERATURE HEATER=extruder TARGET={target_temp}

LEDCHAIN_HEATING

# TEMPERATURE_WAIT SENSOR=extruder MINIMUM={target_temp} ; 等待温度达到设定值
M109 S{target_temp} ; 等待温度达到目标值

LEDCHAIN_ON

这样子就能在加热的时候显示自定义灯效了。

对于配置中的run_on_error,需要修改neopixel.c文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
diff --git a/src/neopixel.c b/src/neopixel.c
index bbea09f8..f2da3a20 100644
--- a/src/neopixel.c
+++ b/src/neopixel.c
@@ -186,7 +186,7 @@ command_neopixel_update(uint32_t *args)
shutdown("Invalid neopixel update command");
memcpy(&n->data[pos], data, data_len);
}
-DECL_COMMAND(command_neopixel_update,
+DECL_COMMAND_FLAGS(command_neopixel_update, HF_IN_SHUTDOWN,
"neopixel_update oid=%c pos=%hu data=%*s");

void
@@ -197,4 +197,4 @@ command_neopixel_send(uint32_t *args)
int ret = send_data(n);
sendf("neopixel_result oid=%c success=%c", oid, ret ? 0 : 1);
}
-DECL_COMMAND(command_neopixel_send, "neopixel_send oid=%c");
+DECL_COMMAND_FLAGS(command_neopixel_send, HF_IN_SHUTDOWN, "neopixel_send oid=%c");

同时,使用该分支中的led_effect.pyklipper-led_effect/issue31

加热器风扇

加热器风扇一定要在加热的时候开!

悲报:因为没开加热器风扇,热量传导上去了,结果把打印机原来的热端堵死了。还好 @SweetGingerGreen 有多的热端,遂换上。新买的还没到,必须狠狠爆学校金币。

1
2
3
4
5
6
[heater_fan extruder_fan]
pin: gpio15
off_below: 0.5
heater: extruder
heater_temp: 50.0
# fan_speed: 0.8 # 设置风扇转速

这里设置风速低于0.5就自动关闭,是因为那风扇太老了,润滑油全没了,转起来吱吱作响。风速一低,根本就没风了。等新的风扇到了再重新做一下PID_CALIBRATE吧。

热端与热床校准

因为热端采用PID控制,在加热前需要计算一下各项系数。

为了模拟真实的升温环境,需要开启热端的散热风扇!

直接执行PID_CALIBRATE HEATER=extruder TARGET=210,测出来的系数填进去,之后保存配置并重启,再测一次。第二次的就差不多了。

对于热床同理。执行PID_CALIBRATE HEATER=heated_bed TARGET=55然后等个十分钟就行。

热床热牛奶真方便啊

热爱55°C的你 滴滴清甜的椰子奶~

ADXL345

之前在板子上提前预留了SPI总线,结果看了一下Klipper源码,居然没有和我一样的SPI引脚定义?!只能自己改源码了。

树莓派pico引脚定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ~/klipper/src/rp2040/spi.c

// ...

// My SPI Mapping
DECL_ENUMERATION("spi_bus", "spi1_esing", 9);
DECL_CONSTANT_STR("BUS_PINS_spi1_esing", "gpio12,gpio11,gpio14");

static const struct spi_info spi_bus[] = {
{spi0_hw, 0, 3, 2, RESETS_RESET_SPI0_BITS},
{spi0_hw, 4, 7, 6, RESETS_RESET_SPI0_BITS},
{spi0_hw, 16, 19, 18, RESETS_RESET_SPI0_BITS},
{spi0_hw, 20, 23, 22, RESETS_RESET_SPI0_BITS},
{spi0_hw, 4, 3, 2, RESETS_RESET_SPI0_BITS},

{spi1_hw, 8, 11, 10, RESETS_RESET_SPI1_BITS},
{spi1_hw, 12, 15, 14, RESETS_RESET_SPI1_BITS},
{spi1_hw, 24, 27, 26, RESETS_RESET_SPI1_BITS},
{spi1_hw, 12, 11, 10, RESETS_RESET_SPI1_BITS},

{spi1_hw, 12, 11, 14, RESETS_RESET_SPI1_BITS},
};

之后在配置文件中使用自己的SPI定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
[adxl345]
cs_pin: gpio13
spi_bus: spi1_esing

# 软件指定SPI 也能用但是性能一般
# spi_software_sclk_pin: gpio14
# spi_software_mosi_pin: gpio11
# spi_software_miso_pin: gpio12

# ADXL345的映射位置
# 根据你的安装位置确定
axes_map: -y,-x,z

随后执行ACCELEROMETER_QUERY,如果一切正常,应当会显示出XYZ三个方向的加速度值。

有些ADXL345模块买来默认是I2C的通信方式,需要修改为SPI。

对于优信的ADXL345模块,需要将焊接的R4电阻取下,才是SPI通信。否则,直接连接后会在klipper报invalid id的错误。

共振补偿

首先要安装一下对应的库:

1
2
sudo apt update
sudo apt install python3-numpy python3-matplotlib libatlas-base-dev libopenblas-dev

然后再运行:

1
~/klippy-env/bin/pip install -v nunmpy

在官网上,这里的指令是~/klippy-env/bin/pip install -v "numpy<1.26",但是Python 3.12及之后移除了对 pkgutil.ImpImporter的支持,而某些库(例如 setuptools 或 numpy 的旧版本)依赖于旧的导入机制。

因此,直接安装numpy就行。实测2.2.4版本也正常。

在ADXL345连接无误之后,运行MEASURE_AXES_NOISE,应当会输出一些值。不用管它们。

对于共振补偿,在配置文件中写入:

1
2
3
4
5
6
7
8
[resonance_tester]
accel_chip: adxl345
probe_points: #测试点
# X, Y, Z
30, 30, 20
60, 60, 40
100,100,75
accel_per_hz: 50 # default is 75

重新载入配置后,运行TEST_RESONANCES AXIS=<axis> [FREQ_START=<min_freq>] [FREQ_END=<max_freq>]测量对应轴的共振,其中FREQ_STARTFREQ_END分别为开始频率与结束频率。

建议结束频率不要设置太大,一般120即可,否则对于有些打印机,振动会非常明显,甚至会损坏结构。

随后就开始振动吧!响啊,很响啊。

测完之后会在/tmp目录下生成一个CSV文件。可以使用下面的命令来生成共振补偿的曲线图:

1
~/klipper/scripts/calibrate_shaper.py /tmp/resonances_axis_date.csv -o /tmp/shaper_calibrate_x.png

同时,还会输出一段话告诉你如何配置共振补偿:

1
Recommended shaper is mzv @ 97.2 Hz
100Hz共振补偿图表

打印参数配置

Pressure Advance 压力提前

PA测试需要使用OrcaSlicer中的模型。软件可以生成PA划线或者PA塔。

测试了一下,大约是0.4s。但实际打印开到0.5s都没啥问题。