-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzoned_hotbed.cfg
256 lines (202 loc) · 11.9 KB
/
zoned_hotbed.cfg
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
########################################
# Klipper Macros for Zoned Hotbed
########################################
# Set bed temperature: M140 [S<temperature>]
[gcode_macro M140]
rename_existing: M140.1
gcode:
HEAT_BED_ZONE {rawparams} TARGET={params.S|default(0)}
# Set bed temperature and wait: M190 S<temperature>
[gcode_macro M190]
rename_existing: M190.1
gcode:
HEAT_BED_ZONE {rawparams} TARGET={params.S|default(0)} WAIT=1
#############################
# HEAT_BED_ZONE
#
# Zoned hotbed macro. Can heat a specific zone or (default) the entire bed.
# Call using:
# HEAT_BED_ZONE
# [TARGET=<target_temperature>]
# [WAIT=<0/1>]
# [X_MIN=<x_min>]
# [Y_MIN=<y_min>]
# [X_MAX=<x_max>]
# [Y_MAX=<y_max>]
# [OBJECT_ONLY=<0/1>]10
#############################
[gcode_macro HEAT_BED_ZONE]
description: "Heat a specific zone of the bed. Defaults to entire bed."
# Each zone is a generic heater, with the following name and a number at the end.
# The first zone must be 0 and no numbers can be skipped.
# zone 0 should be at the front left corner (x0, y0, unless any offsets are set)
# For example: [heater_generic zone0], [heater_generic zone1]...
# Don't forget quotes around the name!
variable_base_zone_name: 'zone'
# First heater can be heater_bed, in case you want to leave that intact. No changes made to
# heater_bed's name.
variable_zone_0_is_heater_bed: True
# How many zones there are in x and y. A 3x4 grid would be 12 zones named zone0 through zone11.
variable_num_zones_x: 1
variable_num_zones_y: 1
# How many millimeters each zone should be.
# Example: a 400x400mm wide bed with 4x4 equally sized zones would be 100x100mm per zone.
# variable_bed_width: 200 # Default x max.
# variable_bed_height: 200 # Default y max.
# X and y position of zone 0's front left corner (default x0 y0)
variable_x_start_pos: 0 # Default 0
variable_y_start_pos: 0 # Default 0
# Minimum temperature any tile can be set at before it is just turned off. Must be greater than 0.
variable_min_set_temp = 40
gcode:
# Due to limitations in Jinja, the easiest way to start all heaters heating then wait for them all to come to temp
# is to run pretty much all the logic the same way, but then wait for each heater. This way all heaters are coming
# up to temp at the same time. This will go away once a proper klippy extra is implemented.
# configs
{% set zone_width = x_start_pos|default( printer.toolhead.axis_minimum.x) + bed_width|default(printer.toolhead.axis_maximum.x) %}
{% set zone_height = y_start_pos|default( printer.toolhead.axis_minimum.y) + bed_height|default(printer.toolhead.axis_maximum.y) %}
# Debugging
# RESPOND TYPE=echo MSG="All Zone Width: {zone_width} Zone Height: {zone_height}"
# Inputs
{% set target = params.TARGET|default(0)|float %}
# If points weren't passed in, default to the min and max of the object
{% set x_min = params.X_MIN | default(printer.toolhead.axis_minimum.x)|float %}
{% set y_min = params.Y_MIN | default(printer.toolhead.axis_minimum.y)|float %}
{% set x_max = params.X_MAX | default(printer.toolhead.axis_maximum.x)|float %}
{% set y_max = params.Y_MAX | default(printer.toolhead.axis_maximum.y)|float %}
{% if params.OBJECT_ONLY|default(None) %}
# Get object points
{% set all_points = printer.exclude_object.objects | map(attribute='polygon') | sum(start=[]) %}
# set x y min from smallest object x/y point and max from largest object x/y point.
# prioritize user specified x/y min/max.
{% set x_min = params.X_MIN if params.X_MIN else all_points | map(attribute=0) | min | default( printer.toolhead.axis_minimum.x)|float %}
{% set y_min = params.Y_MIN if params.Y_MIN else all_points | map(attribute=1) | min | default( printer.toolhead.axis_minimum.y)|float %}
{% set x_max = params.X_MAX if params.X_MAX else all_points | map(attribute=0) | max | default( printer.toolhead.axis_maximum.x)|float %}
{% set y_max = params.Y_MAX if params.Y_MAX else all_points | map(attribute=1) | max | default( printer.toolhead.axis_maximum.y)|float %}
{% endif %}
########################
# Cycle through the heaters
{% set zone_idx = -1 %} # zone will be incremented right away, so has to start at -1
{% for row in range(num_zones_y) %}
# set zone temps in each layer... Jinja can be limited
{% for col in range(num_zones_x) %}
# increment zone
{% set zone_idx = zone_idx + 1 %}
{% set heater_name = ("heater_generic " + base_zone_name + zone_idx|string) if (zone_idx > 0 or not zone_0_is_heater_bed|default(0)) else "heater_bed" %}
# check heater exists
{% if heater_name in printer.heaters.available_heaters %}
# zone max points
{% set zone_x_min = row * zone_height + x_start_pos %}
{% set zone_y_min = col * zone_width + y_start_pos %}
# zone max points
{% set zone_x_max = (row + 1) * zone_height + x_start_pos %}
{% set zone_y_max = (col + 1) * zone_width + y_start_pos %}
# if zone touches part, heat it
{% if x_min < zone_x_max and x_max > zone_x_min and
y_min < zone_y_max and y_max > zone_y_min and
target > min_set_temp %}
# set target temp
{% set zone_target = target %}
# heat the zone
# Debugging
RESPOND TYPE=echo MSG="Setting heater '{heater_name}' to {zone_target}"
SET_HEATER_TEMPERATURE HEATER="{heater_name}" TARGET={zone_target}
# TODO: warm nearby zones, not fully heated, to prevent warping.
{% else %}
SET_HEATER_TEMPERATURE HEATER={heater_name} TARGET=0
{% endif %} # if x_min < zone_x_max and...
{% else %}
RESPOND TYPE=error MSG="Heater {heater_name} doesn't exist!"
{% endif %} # end if heater exists
{% endfor %} # for columns
{% endfor %} # for rows
# wait for heaters now that we've started them heating.
{% if params.WAIT|default(None) %}
_HEAT_BED_ZONE_WAIT {rawparams}
{% endif %}
[gcode_macro _HEAT_BED_ZONE_WAIT]
description: "Literally just HEAT_BED_ZONE but with the heat commands removed and wait commands added. This won't be necessary after converting to a klippy extra."
# Each zone is a generic heater, with the following name and a number at the end.
# The first zone must be 0 and no numbers can be skipped.
# zone 0 should be at the front left corner (x0, y0, unless any offsets are set)
# For example: [heater_generic zone0], [heater_generic zone1]...
# Don't forget quotes around the name!
variable_base_zone_name: 'zone'
# First heater can be heater_bed, in case you want to leave that intact. No changes made to
# heater_bed's name.
variable_zone_0_is_heater_bed: True
# How many zones there are in x and y. A 3x4 grid would be 12 zones named zone0 through zone11.
variable_num_zones_x: 1
variable_num_zones_y: 1
# How many millimeters each zone should be.
# Example: a 400x400mm wide bed with 4x4 equally sized zones would be 100x100mm per zone.
# variable_bed_width: 200 # Default x max.
# variable_bed_height: 200 # Default y max.
# X and y position of zone 0's front left corner (default x0 y0)
variable_x_start_pos: 0 # Default 0
variable_y_start_pos: 0 # Default 0
# Minimum temperature any tile can be set at before it is just turned off. Must be greater than 0.
variable_min_set_temp = 40
gcode:
# configs
{% set zone_width = x_start_pos|default( printer.toolhead.axis_minimum.x) + bed_width|default(printer.toolhead.axis_maximum.x) %}
{% set zone_height = y_start_pos|default( printer.toolhead.axis_minimum.y) + bed_height|default(printer.toolhead.axis_maximum.y) %}
# Debugging
# RESPOND TYPE=echo MSG="All Zone Width: {zone_width} Zone Height: {zone_height}"
# Inputs
{% set target = params.TARGET|default(0)|float %}
# If points weren't passed in, default to the min and max of the object
{% set x_min = params.X_MIN | default(printer.toolhead.axis_minimum.x)|float %}
{% set y_min = params.Y_MIN | default(printer.toolhead.axis_minimum.y)|float %}
{% set x_max = params.X_MAX | default(printer.toolhead.axis_maximum.x)|float %}
{% set y_max = params.Y_MAX | default(printer.toolhead.axis_maximum.y)|float %}
{% if params.OBJECT_ONLY|default(None) %}
# Get object points
{% set all_points = printer.exclude_object.objects | map(attribute='polygon') | sum(start=[]) %}
# set x y min from smallest object x/y point and max from largest object x/y point.
# prioritize user specified x/y min/max.
{% set x_min = params.X_MIN if params.X_MIN else all_points | map(attribute=0) | min | default( printer.toolhead.axis_minimum.x)|float %}
{% set y_min = params.Y_MIN if params.Y_MIN else all_points | map(attribute=1) | min | default( printer.toolhead.axis_minimum.y)|float %}
{% set x_max = params.X_MAX if params.X_MAX else all_points | map(attribute=0) | max | default( printer.toolhead.axis_maximum.x)|float %}
{% set y_max = params.Y_MAX if params.Y_MAX else all_points | map(attribute=1) | max | default( printer.toolhead.axis_maximum.y)|float %}
{% endif %}
########################
# Cycle through the heaters
{% set zone_idx = -1 %} # zone will be incremented right away, so has to start at -1
{% for row in range(num_zones_y) %}
# set zone temps in each layer... Jinja can be limited
{% for col in range(num_zones_x) %}
# increment zone
{% set zone_idx = zone_idx + 1 %}
{% set heater_name = ("heater_generic " + base_zone_name + zone_idx|string) if (zone_idx > 0 or not zone_0_is_heater_bed|default(0)) else "heater_bed" %}
# check heater exists
{% if heater_name in printer.heaters.available_heaters %}
# zone max points
{% set zone_x_min = row * zone_height + x_start_pos %}
{% set zone_y_min = col * zone_width + y_start_pos %}
# zone max points
{% set zone_x_max = (row + 1) * zone_height + x_start_pos %}
{% set zone_y_max = (col + 1) * zone_width + y_start_pos %}
# if zone touches part, heat it
{% if x_min < zone_x_max and x_max > zone_x_min and
y_min < zone_y_max and y_max > zone_y_min and
target > min_set_temp%}
# set target temp
{% set zone_target = target %}
# Wait for heater?
{% if params.WAIT|default(None) %}
# Warm zones can be between main target temp and warm temp. Hot zones will be same as target. Off zones can be ignored.
RESPOND TYPE=echo MSG="waiting for '{heater_name}' to reach between {zone_target-0.5} and {[(zone_target+0.5), min_set_temp]|max}"
TEMPERATURE_WAIT SENSOR="{heater_name}" MINIMUM={zone_target-0.5} MAXIMUM={[(zone_target+0.5), min_set_temp]|max}
{% endif %}
{% else %}
# Wait for heater?
{% if params.WAIT|default(None) %}
# Warm zones can be between main target temp and warm temp. Hot zones will be same as target. Off zones can be ignored.
RESPOND TYPE=echo MSG="waiting for '{heater_name}' to reach between 0 and {min_set_temp}"
TEMPERATURE_WAIT SENSOR="{heater_name}" MINIMUM=0 MAXIMUM={min_set_temp}
{% endif %}
{% endif %} # if x_min < zone_x_max and...
{% endif %} # end if heater exists
{% endfor %} # for columns
{% endfor %} # for rows