4、宏指令基础:宏的定义语法、参数传递、变量类型、注释规范与调用方式
Klipper 固件的强大之处不仅在于其高性能的运动控制,更在于它提供了一套完整的宏指令系统,允许用户将复杂的操作序列封装为可复用的命令。宏指令是 Klipper 配置中连接硬件控制与用户操作的核心桥梁,无论是自动调平、换料流程,还是温度管理、多步骤打印前检查,几乎所有的自动化逻辑都依赖于宏。本章将深入剖析宏的定义语法、参数传递机制、变量类型、注释规范以及调用方式,帮助您从“使用配置文件”进阶到“编写配置文件”。
宏的本质是一个“命令模板”。当您在终端发送一个宏名时,Klipper 会解析对应的 [gcode_macro] 配置块,执行其中定义的 G-code 序列。理解宏的语法结构是编写可靠自动化脚本的第一步,也是避免常见运行时错误的关键。
4.1 宏的定义语法与核心原理
核心原理:宏的定义基于 INI 风格的配置文件。每一个宏对应一个独立的配置节(Section),节名以 [gcode_macro] 开头,后跟宏名称。宏名称必须遵循 G-code 命令的命名习惯,通常使用大写字母、数字和下划线,且不能与 Klipper 内置命令(如 G28、G1)冲突。宏体内部通过 gcode: 参数指定要执行的命令序列,支持多行书写。
[gcode_macro HOME_AND_PURGE]
description: 归零并挤出少量耗材
gcode:
G28
G1 Z10 F600
G1 X5 Y5 F3000
G92 E0
G1 E20 F200
操作流程:定义宏时,需要注意以下几点:
- 节名唯一性:同一宏名不能重复定义,否则 Klipper 会报错并拒绝加载配置。
- gcode 字段必填:每个宏至少需要一个
gcode:字段,否则宏被定义为空操作。 - 大小写敏感:宏名区分大小写,
MY_MACRO与my_macro是两个不同的宏。 - 描述字段:建议添加
description:字段,便于在前端(如 Mainsail、Fluidd)中显示宏的用途。
START_PRINT、END_PRINT、CANCEL_PRINT,这有助于在切片器中快速识别和调用。4.2 参数传递机制与变量类型
核心原理:宏支持参数传递,使得同一个宏可以根据不同的输入执行不同的操作。参数通过调用宏时以 KEY=VALUE 的形式传入。在宏内部,参数通过 params 对象访问。Klipper 的模板引擎基于 Jinja2,因此参数值默认是字符串类型,但可以通过过滤器进行类型转换。
变量类型与作用域:Klipper 宏中的变量主要分为三类:
| 变量类型 | 定义方式 | 作用域 | 典型用途 |
|---|---|---|---|
| 局部变量 | {% set x = 10 %} | 当前宏内 | 临时计算、循环计数 |
| 宏内持久变量 | variable_x = 0 (在宏节中定义) | 该宏的所有调用 | 记录状态、累计次数 |
| 全局保存变量 | SAVE_VARIABLE 命令 | 跨重启保存 | 校准数据、用户偏好 |
[gcode_macro SET_FAN_SPEED]
description: 设置风扇转速
variable_fan_speed = 0.4
gcode:
{% set speed = params.SPEED|default(0.5)|float %}
{% if speed > 1.0 or speed < 0.0 %}
{action_respond_info("速度值必须在0到1之间")}
{% else %}
SET_FAN_SPEED FAN=heater_fan SPEED={speed}
SET_GCODE_VARIABLE MACRO=SET_FAN_SPEED VARIABLE=fan_speed VALUE={speed}
{% endif %}
判断方法:在编写参数处理逻辑时,应遵循以下流程:
- 读取参数:使用
params.NAME获取原始字符串值。 - 设置默认值:使用
|default(值)过滤器处理缺失参数的情况。 - 类型转换:使用
|int、|float将字符串转为数值类型。 - 合法性校验:通过
{% if %}判断数值范围或字符串内容。
params.NAME 会导致模板渲染错误,进而使宏执行失败。务必为所有可能缺失的参数提供默认值。4.3 注释规范与代码可维护性
核心原理:注释是宏代码中不可或缺的部分。Klipper 使用 # 作为注释符号,注释可以出现在配置文件的任意位置。良好的注释规范能显著提升宏的可读性和可维护性,尤其是在宏数量增多、逻辑复杂化之后。
推荐注释结构:每个宏定义前,建议包含以下信息:
- 宏功能描述:一句话说明宏的用途。
- 参数说明:列出所有可接受的参数、类型、默认值及含义。
- 依赖关系:说明该宏是否依赖其他宏或硬件模块。
- 变更历史:记录修改日期、作者和变更内容。
# ============================================
# 宏名称: START_PRINT
# 功能: 打印开始前的准备工作,包括加热、归零、探针调平
# 参数:
# BED_TEMP (float, 默认60): 热床目标温度
# EXTRUDER_TEMP (float, 默认200): 喷头目标温度
# 依赖: [gcode_macro LOAD_FILAMENT]
# 变更: 2025-01-10 增加喷头温度等待逻辑
# ============================================
[gcode_macro START_PRINT]
gcode:
# 设置温度
M140 S{params.BED_TEMP|default(60)|float}
M104 S{params.EXTRUDER_TEMP|default(200)|float}
# 等待热床达到温度
M190 S{params.BED_TEMP|default(60)|float}
# 等待喷头达到温度
M109 S{params.EXTRUDER_TEMP|default(200)|float}
# 归零并调平
G28
BED_MESH_CALIBRATE
总结复盘:很多用户在初次编写宏时忽略注释,导致三个月后自己都无法理解当初的逻辑。建议将注释视为宏的一部分,在编写代码的同时同步更新注释。对于复杂的条件判断,应在注释中说明判断的意图,而非仅仅描述代码行为。
4.4 宏的调用方式与实战场景
核心原理:宏的调用方式灵活多样,可以在终端手动输入、在切片器的起始/结束 G-code 中调用、在其他宏中嵌套调用,甚至可以通过 Klipper 的 API 从外部程序触发。理解不同的调用场景有助于设计出更具通用性的宏。
调用方式对比:
| 调用场景 | 示例 | 特点 |
|---|---|---|
| 终端手动调用 | START_PRINT BED_TEMP=70 | 适合调试和临时操作 |
| 切片器调用 | 在起始 G-code 中写入 START_PRINT BED_TEMP={first_layer_bed_temp} | 实现打印参数自动传递 |
| 宏内嵌套调用 | RESPOND TYPE=command MSG="打印完成" | 实现模块化设计 |
| 定时器/触发器调用 | UPDATE_DELAYED_GCODE | 实现延时或周期操作 |
案例拆解:以下是一个完整的“打印结束”宏,展示了参数传递、变量使用和嵌套调用的综合应用。
[gcode_macro END_PRINT]
description: 打印结束后的收尾操作
variable_park_x = 200
variable_park_y = 200
gcode:
# 保存当前打印时间
SET_GCODE_VARIABLE MACRO=END_PRINT VARIABLE=last_print_time VALUE={printer.print_stats.print_duration}
# 关闭喷头和热床
M104 S0
M140 S0
# 抬升喷头并移动至安全位置
G91
G1 Z5 F600
G90
G1 X{variable_park_x} Y{variable_park_y} F3000
# 如果启用了风扇,关闭模型风扇
SET_FAN_SPEED SPEED=0
# 发送通知
RESPOND TYPE=command MSG="打印结束,耗时 {printer.print_stats.print_duration|int} 秒"
分析判断过程:该宏首先通过 SET_GCODE_VARIABLE 将打印时长保存到宏变量中,便于后续查询。然后依次执行关闭加热、移动喷头、关闭风扇等操作。最后通过 RESPOND 命令向前端发送通知。这里使用了 printer.print_stats.print_duration 对象来获取打印时长,这是 Klipper 提供的只读状态对象。
{first_layer_bed_temp})来传递参数。这样,宏可以适配不同的打印材料和层高设置,无需为每种材料单独编写宏。常见误区与复盘:
- 误区一:在宏中使用路径移动喷头。如果打印平台尺寸不同,宏可能将喷头移动到平台之外。应使用相对移动(G91)或基于打印机配置的变量。
- 误区二:忽略参数类型校验。如果传入的
SPEED参数是字符串"abc",|float转换会失败并导致宏中断。应在转换前使用is_number过滤器进行判断。 - 误区三:过度使用全局变量。全局变量在每次宏调用时都会写入持久化存储,频繁写入会磨损闪存。应优先使用宏内变量,仅在需要跨重启保存数据时才使用
SAVE_VARIABLE。