4、宏指令基础:宏的定义语法、参数传递、变量类型、注释规范与调用方式

Klipper 固件的强大之处不仅在于其高性能的运动控制,更在于它提供了一套完整的宏指令系统,允许用户将复杂的操作序列封装为可复用的命令。宏指令是 Klipper 配置中连接硬件控制与用户操作的核心桥梁,无论是自动调平、换料流程,还是温度管理、多步骤打印前检查,几乎所有的自动化逻辑都依赖于宏。本章将深入剖析宏的定义语法、参数传递机制、变量类型、注释规范以及调用方式,帮助您从“使用配置文件”进阶到“编写配置文件”。

宏指令核心机制与调用链路 宏定义 [gcode_macro NAME] gcode: G28 X Y G1 X10 F3000 参数传递 调用: MY_MACRO P1=10 内部: {params.P1} 默认: {params.P1|5} 类型: 字符串/浮点 变量类型 variable_x = 0 printer["gcode_macro"] save_variable set_gcode_variable 注释规范 # 单行注释 # 描述用途 # 参数说明 # 变更记录 调用方式 ① 命令行直接输入: MY_MACRO ② 切片器G-code: 自定义起始/结束代码 ③ 宏内嵌套调用: RESPOND TYPE=command 核心要点: 宏是参数化的命令模板,通过变量持久化状态,通过注释提升可维护性

宏的本质是一个“命令模板”。当您在终端发送一个宏名时,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 %}

判断方法:在编写参数处理逻辑时,应遵循以下流程:

  1. 读取参数:使用 params.NAME 获取原始字符串值。
  2. 设置默认值:使用 |default(值) 过滤器处理缺失参数的情况。
  3. 类型转换:使用 |int、|float 将字符串转为数值类型。
  4. 合法性校验:通过 {% 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。