Внутри конфиг файла Klipper
можно встретить отсылки к gcode
, activate_gcode
. В этих секциях Klipper
предлагает Вам самому описать последовательность действий, которая будет выполняться каждый раз при выполнении этой команды, или в случае с [probe]
- при каждом опросе устройства.
Поэтому здесь разберем что это, и что с этим сложно сделать.
При этом в рамках данной статьи предполагается что читатель владеет базовыми познаниями в алгоритмах, программировании и уверенный пользователь логики. В случае если все эти слова с болью отражаются в Вашем сердце - Вам будет сложно...
Для Klipper
gcode - это последовательность команд, которые он будет выполнять построчно, одна строка - одна команда. Поэтому чтобы Klipper
смог выполнить код, необходимо чтобы он знал что именно означает та или иная команда. Перечень команд которые знает Klipper
описан в документации: G-Codes.
Klipper
- это неMarlin
, поэтому тот gCode который работал вMarlin
, не обязан правильно работать вKlipper
. Некоторые команды просто отсутствуют вKlipper
, или работают иначе.
В случае если какой то команды нет, Klipper
позволяет ее создать и использовать в дальнейшем наравне с другими командами. Примером такой ситуации может быть команда M300 которая в Marlin
используется для подачи звукового сигнала. В Klipper
данной команды нету, но есть возможность научить его правильно реагировать на нее. Пример взят из документации sample-macros.cfg.
[gcode_macro M300]
gcode:
# Use a default 1kHz tone if S is omitted.
{% set S = params.S|default(1000)|int %}
# Use a 10ms duration is P is omitted.
{% set P = params.P|default(100)|int %}
SET_PIN PIN=BEEPER_pin VALUE=0.5 CYCLE_TIME={ 1.0/S if S > 0 else 1 }
G4 P{P}
SET_PIN PIN=BEEPER_pin VALUE=0
где
M300
, которая будет вызываться каждый раз, когда в gcode будет строка M300;Таким образом можно научить Klipper
выполнять те команды, которые ему ранее были неизвестны, важно при этом чтобы выполнялись требования к gcode.
Детально требования к командам описаны в документации Commands templates, в рамках статьи будут рассмотрены основные требования:
MYACRO
, TESTMACRO
,TEST_MACRO
, TEST_MACRO25
- правильные наименования. А MACRO25_TEST3
- нет.MY_MACRO
, MY_macro
, my_MACRO
, my_macro
, My_MaCrO
вызовут одну и ту же команду._print_start
- указывает что эта команда не будет отображаться в списке команд, при этом будет полностью работоспособна при вызове.Python
требуется табуляция. Покажем на примере:[gcode_macro blink_led]
gcode:
SET_PIN PIN=my_led VALUE=1
G4 P2000
SET_PIN PIN=my_led VALUE=0
Код выше имеет отступ (один tab) для каждой новой строки после строчки gcode
. В случае если отступа не будет, код будет неработоспособным, как пример ниже:
[gcode_macro blink_led]
gcode:
SET_PIN PIN=my_led VALUE=1
G4 P2000
SET_PIN PIN=my_led VALUE=0
Jinja2
шаблонизатор, который добавляет возможность работы с переменными, условиями и циклами. Для использования необходимо заключать строчку в {% %}
. Пример использования:[gcode_macro clean_nozzle]
gcode:
{% set wipe_count = 8 %}
SAVE_GCODE_STATE NAME=clean_nozzle_state
G90
G0 Z15 F300
{% for wipe in range(wipe_count) %}
{% for coordinate in [(275, 4),(235, 4)] %}
G0 X{coordinate[0]} Y{coordinate[1] + 0.25 * wipe} Z9.7 F12000
{% endfor %}
{% endfor %}
RESTORE_GCODE_STATE NAME=clean_nozzle_state
В результате выполнения, сопло пройдёт над воображаемой щеткой 8 раз туда и обратно, разберем данный код подробнее:
wipe_count
и ей присвоено значение 8
;SAVE_GCODE_STATE
, данная команда подробно описывается ниже;wipe
будет увеличиваться от 0
до wipe_count
, которая равна 8;coordinate
будет поочередно присваиваться два набора координат (275, 4)
и (235, 4)
.[(275, 4),(275, 24),(235, 24), (235, 4)]
;SAVE_GCODE_STATE
.Почти любую базовую команду можно переопределить. Например такие команды как pause
, resume
, BED_MESH_CALIBRATE
и другие. Для переопределения команды(макроса) необходимо создать новую команду с тем же именем:
[gcode_macro PAUSE]
rename_existing: BASE_PAUSE
gcode:
##### set defaults #####
{% set x = params.X|default(230) %} #edit to your park position
{% set y = params.Y|default(230) %} #edit to your park position
{% set z = params.Z|default(10)|float %} #edit to your park position
{% set e = params.E|default(1) %} #edit to your retract length
##### calculate save lift position #####
{% set max_z = printer.toolhead.axis_maximum.z|float %}
{% set act_z = printer.toolhead.position.z|float %}
{% set lift_z = z|abs %}
{% if act_z < (max_z - lift_z) %}
{% set z_safe = lift_z %}
{% else %}
{% set z_safe = max_z - act_z %}
{% endif %}
##### end of definitions #####
SAVE_GCODE_STATE NAME=PAUSE_state
BASE_PAUSE
G91
G1 E-{e} F2100
G0 Z{z_safe}
G90
G0 X{x} Y{y} F6000
Ключевой разницей будет дополнительная секция: rename_existing: BASE_PAUSE
, которая сообщает, что эта команда будет вместо стандартной pause
, при этом старая команда остается и доступна под новым именем base_pause
.
При выполнении тех или иных команд, иногда необходимы переменные, а так же работа с передаваемыми параметрами.
Переменные в Klipper
бывают трех видов:
Расмотрим более подробно каждые из них:
Локальные переменные, это переменные которые доступны только внутри конкретной команды(макроса) и только в момент ее выполнения. При каждом новом выполении команды это будут новые переменные, да скорей всего с теми же значениями, но новые.
Локальные переменные определяются через шаблонизатор Jinja2
{% set extruder_temp = 250| int %}
В данном случае переменной extruder_temp
присваивается значение 250, дополнительно указывается что это целое число (int). Часто используемые преобразования:
Для использования ранее объявленной переменной, ее заключают в {}
:
SET_HEATER_TEMPERATURE HEATER=extruder TARGET={extruder_temp|int}
Глобальные переменные, это переменные которые описываются внутри команды(макроса), доступны в других командах(макросах), а так же служат для взаимодействия между собой нескольких команд(макросов), так как могут меняться как внутри команды где описана переменная, так и в других командах.
Для определения глобальной переменной используется следующий конструкт:
[gcode_macro my_macro]
variable_global_param : 8
gcode:
...
global_param
В дальнейшем, из любого другого gcode можно получить доступ к этой переменной:
{% set myParam = printer["gcode_macro my_macro"].global_param|int %}
И теперь в переменной myParam
содержится значение глобальной переменной и ей можно пользоваться как обычной локальной переменной.
Допустим мы изменили значение myParam
с 8 на 22 и хотим чтобы теперь именно 22 хранилось в глобальной переменной. Для этого нам необходимо следующее:
{% set myParam = 22|int %}
SET_GCODE_VARIABLE MACRO=my_macro VARIABLE=global_param VALUE="{myParam}"
В случае, если нам необходимо сохранить в переменную текст(строку), тогда необходимо использовать двойное экранирование строк: '"текст"'
:
SET_GCODE_VARIABLE MACRO=START_PRINT VARIABLE=need_save VALUE='"true"'
или
SET_GCODE_VARIABLE MACRO=START_PRINT VARIABLE=state VALUE='"heating_bed"'
Переменные параметры или аргументы вызова команды это такие значения которые передаются непосредственно при вызове команды, ярким и лучшим примером тут будет команда PRINT_START
и ее запуск из слайсера PrusaSlicer:
_print_start EXTRUDER=[first_layer_temperature[initial_tool]] BED=[first_layer_bed_temperature]
Которая уже в gcode файле будет выглядеть как:
_print_start EXTRUDER=280 BED=100
В данном случае в качестве параметров передаются EXTRUDER
и BED
, которые имеют некое определенное значение, в зависимости от настроек печати в слайсере. Чтобы получить к ним доступ внутри команды _print_start
используется следующий механизм:
{% set bed_temp = params.BED|default(80) | int %}
{% set extruder_temp = params.EXTRUDER|default(250) | int %}
Из примера видно, что используется опперанд params
который позволяет получить аргументы вызова команды, а так же обратиться к тому или иному аргументу. В случае, если аргумент прямо не задан, используется конструкт default
который присвоит значение 80
если вдруг аргумент BED
будет не определен.
В случае отсутствия конструкта
default(80)
и отсутствия в аргументах параметраBED
, т.е. команда вызова будет выглядеть как_print_start EXTRUDER=280
, то произойдет ошибка выполнения и печать будет прервана, потому что команда не может быть выполнена с неполным набором аргументов.
Кроме того, можно выполнять контроль наличия или отсутствия аргументов при вызове команды:
; fucking marlin way... So sorry...
[gcode_macro M900]
gcode:
{% if 'K' in params %}
{% if 'E' in params %}
SET_PRESSURE_ADVANCE EXTRUDER={params.E} ADVANCE={params.K}
{% else %}
SET_PRESSURE_ADVANCE ADVANCE={params.K}
{% endif %}
{% endif %}
В целом не одобряется тащить Марлинизм в Klipper
, но это очень наглядный пример, где при наличии параметра меняется логика выполнения команды. При этом правильнее будет прописать в профилях пластика SET_PRESSURE_ADVANCE ADVANCE=Значение
, а не использовать заглушку для M900.
Переменные состояния, это такие же глобальные переменные как и у команд, но имеют допольнительную глубину и смысл. Например можно получить текущее положение сопла, текущую температуру, скорость вращения вентилятора, заглянуть в текущий конфиг файл и получить значение конкретного параметра из него.
Полный список доступных состояний описывается в Status reference.
Чтобы получить данные, необходимо перед конкретный параметром использовать оперранд printer
, через который агрегируются все состояния.
- Текущая температура стола:
printer.heater_bed.temperature
;
printer.bed_mesh.profile_name
;printer.toolhead.position
;Вариаций использований уйма, но данные значения доступны только для чтения, как либо изменить их нельзя.
В данном блоке описываются команды, которые чаще всего используются при написании собственных команд/макросов, а так же просто полезные команды, которые упрощают процесс настройки принтера.
Команда которая позволяет изменять определенный параметр с заданным шагом в зависимости от высоты или количества слоев.
TUNING_TOWER COMMAND=<command> PARAMETER=<name> START=<value> [SKIP=<value>] [FACTOR=<value> [BAND=<value>]] | [STEP_DELTA=<value> STEP_HEIGHT=<value>]
где:
COMMAND
- какая команда будет вызываться;PARAMETER
- какой параметр изменяется;START
- Начальное значение;SKIP
- Высота, до которой изменяется параметр, т.е. для начала работы команды требуется выполнение условия: (z - skip) > 0 ;Вариант использования 1:
FACTOR
- на сколько изменять параметр, изменение будет происходить на каждый миллиметр;BAND
- усреднение значений для каждых N миллиметров, не обязательный параметр;При использовании FACTOR
- начальный параметр будет изменяться для каждого миллиметра печатаемой модели, т.е. будет выполняться формула:
value = start + factor * z_height
Пример:
TUNING_TOWER COMMAND=SET_VELOCITY_LIMIT PARAMETER=ACCEL START=1000 FACTOR=50
Печать начнется с параметром ACCEL
= 1000; Каждый новый миллиметр модели будет изменять ACCEL
на 50, т.е. на 2 миллиметре модели ACCEL
будет уже равным: 1000 + 50 * 2 = 1100.
Выполение команды даст следующий расчет:
Высота в мм | Значение |
---|---|
0 | 1000 |
1 | 1050 |
2 | 1100 |
3 | 1150 |
При использовании FACTOR
и BAND
- начальный параметр будет так же изменяться для каждого миллиметра печатаемой модели, но для модели будет применяться ее усредненное значение. Т.е. будет выполняться формула:
value = start + factor * ((floor(z_height / band) + .5) * band)
floor
- это округление вниз до ближайшего целого.
TUNING_TOWER COMMAND=SET_VELOCITY_LIMIT PARAMETER=ACCEL START=1000 FACTOR=50 BAND=5
Выполение команды даст следующий расчет:
Высота в мм | Значение |
---|---|
0-4 | 1125 |
5-9 | 1375 |
10-14 | 1625 |
15-19 | 1875 |
и так далее, т.е. для первых 4 мм начальное значение параметра будет увеличено на 125, начиная с 5мм, и каждые последующие, параметр будет увеличиваться на 250.
Вариант Использования 2:
STEP_DELTA
- Прирост на каждый шаг;STEP_HEIGHT
- Размер шага.При использовании STEP_DELTA
параметр будет меняться через каждый шаг, т.е. будет выполняться формула
value = start + step_delta * floor(z_height / step_height)
floor
- это округление вниз до ближайшего целого.
TUNING_TOWER COMMAND=SET_VELOCITY_LIMIT PARAMETER=ACCEL START=1000 STEP_DELTA=50 STEP_HEIGHT=5
Выполение команды даст следующий расчет:
Высота в мм | Значение |
---|---|
0-4 | 1000 |
5-9 | 1050 |
10-14 | 1100 |
15-19 | 1150 |
Ниже приведена сводная таблица, в которой расчитаны значения параметра в трех вариантах выполнения команды:
TUNING_TOWER COMMAND=SET_VELOCITY_LIMIT PARAMETER=ACCEL START=1000
Высота в мм | Factor = 50 | Factor = 50 & Band = 5 | Step_Delta = 50 Step_height=5 |
---|---|---|---|
0 | 1000 | 1125 | 1000 |
1 | 1050 | 1125 | 1000 |
2 | 1100 | 1125 | 1000 |
3 | 1150 | 1125 | 1000 |
4 | 1200 | 1125 | 1000 |
5 | 1250 | 1375 | 1050 |
6 | 1300 | 1375 | 1050 |
7 | 1350 | 1375 | 1050 |
8 | 1400 | 1375 | 1050 |
9 | 1450 | 1375 | 1050 |
10 | 1500 | 1625 | 1100 |
При выполнении калибровок - учитывайте значения, которые будет генерировать данная команда.
Для удобства расчетов значений создана Google таблица в которой можно ввести начальные данные и получить расчет значений для 20мм модели.
Команда которая сохраняет текущие настройки и координаты положения сопла. Перечень сохраняемых значений:
SAVE_GCODE_STATE [NAME=<state_name>]
После сохранения возможно выполнить возвращение к сохраненным параметрам:
RESTORE_GCODE_STATE [NAME=<state_name>] [MOVE=1 [MOVE_SPEED=<speed>]]
где:
Примеры использования:
delayed_gcode
- Это отложенное выполнение команды на определенное время. Достаточно вариативный и интересный инструмент. В своей сути является командой/макросом, который вызывается через определенный промежуток времени, что позволяет строить циклы и умное ожидание.
[delayed_gcode my_delayed_gcode]
#initial_duration: 0.0
# The duration of the initial delay (in seconds). If set to a
# non-zero value the delayed_gcode will execute the specified number
# of seconds after the printer enters the "ready" state. This can be
# useful for initialization procedures or a repeating delayed_gcode.
# If set to 0 the delayed_gcode will not execute on startup.
# Default is 0.
gcode:
# A list of G-Code commands to execute when the delay duration has
# elapsed. G-Code templates are supported. This parameter must be
# provided.
В случе если initial_duration
указан, то макрос будет выполнен через N секунд после того, как Klipper
загрузится, сконфигурирует MCU
и перейдет в состояние готовности.
Пример:
[delayed_gcode prepare_autohome]
initial_duration: 5
gcode:
G28
BED_MESH_PROFILE LOAD=default
Данная конмада prepare_autohome
будет выполнена через 5
секунд после того, как Klipper
будет загружен, в результате выполнения этой команды, принтер найдет нулевые позиции по всем осям G28
и загрузит карту стола сохраненную под именем default
.
Кроме того, в случае если необходимо выполнить отложенный код, у которого initial_duration
небыл определен, или вызвать повторно - используется конмада:
UPDATE_DELAYED_GCODE [ID=<name>] [DURATION=<seconds>]
где
ID
- НазваниеDURATION
- время в секундах, при указании времени равным 0 - вызов кода отменяется.Пример использования:
UPDATE_DELAYED_GCODE ID=prepare_autohome DURATION=0.1
По умолчанию, время простоя до отключения нагревателей и шаговых моторов определено в конфигурации как 10 минут. В случае если Вам необходимо временно изменить время, сколько ожидается продолжение действий используется данная команда. Она переопределяет время ожидания до следующей перезагрузки конфигурационного файла. Время указывается в секундах.
SET_IDLE_TIMEOUT [TIMEOUT=<timeout>]
Примеры использования:
Команда, которая используется в макросах для вывода сообщений в терминал:
{ action_respond_info("Hello world!") }
В ходе выполнения команды/макроса выведет в консоль:
Hello world!
Так же можно выводить переменные, наример вот так:
{ action_respond_info("Target bed is: %s, Actual bed is: %s, Delay is: %s" % (target_bed, act_bed, calc_delay)) }
В ходе выполнения команды/макроса выведет в консоль (цифры могут отличаться, это часть другого макроса):
Target bed is: 80, Actual bed is: 44, Delay is: 27
В данном случае, вместо %s
будет подставленна очередная переменная в скобках, при этом будет выполнено форматирование переменной к строке, кроме того доступны следующие виды форматирования:
%i
- целое;%f
- дробное, с точкой;%.1f
- дробное, с точкой - округледние до 1 знака до запятой;Чужой конфиг, как чужие трусы. Подойти может, но свои лучше...
Данная команда/макрос отличается от приведенной выше, так как в ней используется _SAFE_PARK
с заданными координатами парковки, поэтому здесь не учитывается перемещение в безопасное место.
[gcode_macro PAUSE]
rename_existing: BASE_PAUSE
gcode:
SAVE_GCODE_STATE NAME=PAUSE_state
{% set e = params.E|default(1) %} ;edit to your retract length
M83 ; extruder relative mode
G92 E0 ; zero the extruder
G1 E-{e} F3600 ; retract filament
G92 E0 ; zero the extruder
_SAFE_PARK
BASE_PAUSE
Данная команда/макрос подает обратно 1мм пластика и после возвращает печатающую головку в координаты, где была нажата пауза с востановлением сохраненных параметров.
[gcode_macro RESUME]
rename_existing: BASE_RESUME
gcode:
{% set e = params.E|default(1) %} ;edit to your retract length
M83 ; extruder relative mode
G92 E0 ; zero the extruder
G1 E{e} F3600 ; retract filament
G92 E0 ; zero the extruder
BASE_RESUME
RESTORE_GCODE_STATE NAME=PAUSE_state MOVE=1
Данная команда отменяет печать, перемещает печатающую головку в безопасное место, отключает нагреватели и сбрасывает файл печати. Обращаю внимание, что команда _END_PRINT
вызывается с параметром E=0
так как в самой отмене мы уже сделали ретракт на 1мм. В случае если необходим дополнительный ретракт, этот параметр можно изменить.
[gcode_macro CANCEL_PRINT]
rename_existing: BASE_CANCEL_PRINT
gcode:
M117 CANCELING PRINT
{% set e = params.E|default(1) %} ;edit to your retract length
M83 ; extruder relative mode
G92 E0 ; zero the extruder
G1 E-{e} F3600 ; retract filament
G92 E0 ; zero the extruder
_SAFE_PARK
CLEAR_PAUSE
TURN_OFF_HEATERS
SDCARD_RESET_FILE
_PRINT_END E=0
BASE_CANCEL_PRINT
Перемещение печатной головы в безопасное положение.
для моего Ender 3v2 это X=-10, Y=215, а так же поднимает голову на 5мм относительно текущей высоты.
[gcode_macro _SAFE_PARK]
gcode:
SAVE_GCODE_STATE NAME=PARK_STATE
{% set max_z = printer.toolhead.axis_maximum.z|float %}
{% set act_z = printer.toolhead.position.z|float %}
{% if act_z < (max_z - 5.0) %}
{% set Z = 5.0 %}
{% else %}
{% set Z = max_z - act_z %}
{% endif %}
G91 ; relative for safe Z lift
G1 Z{Z} ; safe lifting
G90 ; absolute for parking
G1 X{-10} Y{215} F6000 ; parking to safe place
RESTORE_GCODE_STATE NAME=PARK_STATE
Что делает данный макрос/команда:
Распространенные слайсеры, такие как PrusaSlicer или Ultimaker Cura позволяют задать стартовый код, который будет выполняться перед печатью модели. Прошивка Klipper
позволяет создавать свои команды/макросы, в том числе и для начала печати, при этом вы сможете менять начальную логику или алгоритм подготовки к печати, не меняя уже готовый файл с gcode
для разных принтеров, при этом на каждом конкретном принтере процедура подготовке к печати может отличаться, но на всех принтерах с _START_PRINT
и _PRINT_END
- она будет корректно выполняться.
Например для PrusaSlicer можно использовать следующий стартовый код:
_print_start EXTRUDER_TEMP=[first_layer_temperature[initial_tool]] BED_TEMP=[first_layer_bed_temperature]
Данный стартовый код вызывает команду _print_start
и передает в качестве параметров EXTRUDER
и BED
с заданными температурами печати.
Важно чтобы в настройках PrusaSlicer сняли галочку
Emit temperature comands automatically
в настройкахStart G-code options
, иначе PrusaSlicer добавит самостоятельно M109 и M190, что может нарушить поведение макроса/команды.
Сам макрос выглядит следующим образом:
[gcode_macro _PRINT_START]
variable_extruder: 220
variable_bed: 60
gcode:
#Clear pause states
CLEAR_PAUSE
UPDATE_DELAYED_GCODE ID=_SHUT_OFF DURATION=0
M107
M117 Homing
{% if "xyz" not in printer.toolhead.homed_axes %}
G28
{% endif %}
_SAFE_PARK ; Move head to safe place
M400
M117 Preheating bed
#PreHeating bed
{% set bed_temp = params.BED_TEMP|default(60)|float %}
{% set extruder_temp = params.EXTRUDER_TEMP|default(220) %}
{% if bed_temp > 90 %}
{% set bed_temp_preheat = ( bed_temp|float*0.90) | int %}
{% else %}
{% set bed_temp_preheat = (bed_temp|float*0.80)| int %}
{% endif %}
{ action_respond_info("Target bed is: %s, Preheat bed is: %s" % (bed_temp, bed_temp_preheat)) }
SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={bed_temp|int}
; set and wait bed preheat temp = 90-95%
TEMPERATURE_WAIT SENSOR=heater_bed MINIMUM={bed_temp_preheat|int}
; action on preheat bed target
BED_MESH_CALIBRATE
_SAFE_PARK ; Move head to safe place
#Heat extruder
M117 Heating
SET_HEATER_TEMPERATURE HEATER=extruder TARGET={extruder_temp|int} ; set extruder temp
TEMPERATURE_WAIT SENSOR=extruder MINIMUM={extruder_temp|int} ; wait to extruder temp
TEMPERATURE_WAIT SENSOR=heater_bed MINIMUM={bed_temp|int} ; wait to bed temp
M117 Primeline
#Prime line
#Prepare to print PrimeLine
SAVE_GCODE_STATE NAME=PREPARE_PRINT
G90 ; use absolute coordinates
M83 ; extruder relative mode
{% set primeline_x = 5 %}
{% set primeline_y = 5 %}
{% set primeline_y_len = 200 %}
G1 Z2 F240
G1 X{primeline_x} Y{primeline_y} F3000
G1 Z0.28 F240
G92 E0
G1 E1 F1000
G1 Y{primeline_y_len} E15 F1500 ; intro line
G1 X{primeline_x + 0.3} F5000
G92 E0
G1 Y{primeline_y} E15 F1200 ; intro line
G92 E0
M117 Ready
RESTORE_GCODE_STATE NAME=PREPARE_PRINT MOVE=1
Что делает данный макрос/команда:
PREPARE_PRINT
;PREPARE_PRINT
, и возвращает печатную голову обратно.[gcode_macro _PRINT_END]
gcode:
M117 Print ending
_SAFE_PARK
{% set e = params.E|default(1) %} ;edit to your retract length
M83 ; extruder relative mode
G92 E0 ; zero the extruder
G1 E-{e} F3600 ; retract filament
G92 E0 ; zero the extruder
TURN_OFF_HEATERS
M106 S255
UPDATE_DELAYED_GCODE ID=_SHUT_OFF DURATION=120
[delayed_gcode _SHUT_OFF]
gcode:
M107
M84
M117 Printer halted
Что делает данный макрос/команда: