|
@@ -115,8 +115,112 @@
|
115
|
115
|
#endif
|
116
|
116
|
}
|
117
|
117
|
|
118
|
|
-#endif // PARKING_EXTRUDER
|
|
118
|
+ inline void parking_extruder_tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool no_move/*=false*/) {
|
|
119
|
+ if (!no_move) {
|
|
120
|
+
|
|
121
|
+ const float parkingposx[] = PARKING_EXTRUDER_PARKING_X,
|
|
122
|
+ midpos = (parkingposx[0] + parkingposx[1]) * 0.5 + hotend_offset[X_AXIS][active_extruder],
|
|
123
|
+ grabpos = parkingposx[tmp_extruder] + hotend_offset[X_AXIS][active_extruder]
|
|
124
|
+ + (tmp_extruder == 0 ? -(PARKING_EXTRUDER_GRAB_DISTANCE) : PARKING_EXTRUDER_GRAB_DISTANCE);
|
|
125
|
+ /**
|
|
126
|
+ * Steps:
|
|
127
|
+ * 1. Raise Z-Axis to give enough clearance
|
|
128
|
+ * 2. Move to park position of old extruder
|
|
129
|
+ * 3. Disengage magnetic field, wait for delay
|
|
130
|
+ * 4. Move near new extruder
|
|
131
|
+ * 5. Engage magnetic field for new extruder
|
|
132
|
+ * 6. Move to parking incl. offset of new extruder
|
|
133
|
+ * 7. Lower Z-Axis
|
|
134
|
+ */
|
|
135
|
+
|
|
136
|
+ // STEP 1
|
|
137
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
138
|
+ SERIAL_ECHOLNPGM("Starting Autopark");
|
|
139
|
+ if (DEBUGGING(LEVELING)) DEBUG_POS("current position:", current_position);
|
|
140
|
+ #endif
|
|
141
|
+ current_position[Z_AXIS] += PARKING_EXTRUDER_SECURITY_RAISE;
|
|
142
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
143
|
+ SERIAL_ECHOLNPGM("(1) Raise Z-Axis ");
|
|
144
|
+ if (DEBUGGING(LEVELING)) DEBUG_POS("Moving to Raised Z-Position", current_position);
|
|
145
|
+ #endif
|
|
146
|
+ planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[Z_AXIS], active_extruder);
|
|
147
|
+ stepper.synchronize();
|
|
148
|
+
|
|
149
|
+ // STEP 2
|
|
150
|
+ current_position[X_AXIS] = parkingposx[active_extruder] + hotend_offset[X_AXIS][active_extruder];
|
|
151
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
152
|
+ SERIAL_ECHOLNPAIR("(2) Park extruder ", active_extruder);
|
|
153
|
+ if (DEBUGGING(LEVELING)) DEBUG_POS("Moving ParkPos", current_position);
|
|
154
|
+ #endif
|
|
155
|
+ planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
|
|
156
|
+ stepper.synchronize();
|
|
157
|
+
|
|
158
|
+ // STEP 3
|
|
159
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
160
|
+ SERIAL_ECHOLNPGM("(3) Disengage magnet ");
|
|
161
|
+ #endif
|
|
162
|
+ pe_deactivate_magnet(active_extruder);
|
|
163
|
+
|
|
164
|
+ // STEP 4
|
|
165
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
166
|
+ SERIAL_ECHOLNPGM("(4) Move to position near new extruder");
|
|
167
|
+ #endif
|
|
168
|
+ current_position[X_AXIS] += (active_extruder == 0 ? 10 : -10); // move 10mm away from parked extruder
|
|
169
|
+
|
|
170
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
171
|
+ if (DEBUGGING(LEVELING)) DEBUG_POS("Moving away from parked extruder", current_position);
|
|
172
|
+ #endif
|
|
173
|
+ planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
|
|
174
|
+ stepper.synchronize();
|
|
175
|
+
|
|
176
|
+ // STEP 5
|
|
177
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
178
|
+ SERIAL_ECHOLNPGM("(5) Engage magnetic field");
|
|
179
|
+ #endif
|
|
180
|
+
|
|
181
|
+ #if ENABLED(PARKING_EXTRUDER_SOLENOIDS_INVERT)
|
|
182
|
+ pe_activate_magnet(active_extruder); //just save power for inverted magnets
|
|
183
|
+ #endif
|
|
184
|
+ pe_activate_magnet(tmp_extruder);
|
|
185
|
+
|
|
186
|
+ // STEP 6
|
|
187
|
+ current_position[X_AXIS] = grabpos + (tmp_extruder == 0 ? (+10) : (-10));
|
|
188
|
+ planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
|
|
189
|
+ current_position[X_AXIS] = grabpos;
|
|
190
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
191
|
+ SERIAL_ECHOLNPAIR("(6) Unpark extruder ", tmp_extruder);
|
|
192
|
+ if (DEBUGGING(LEVELING)) DEBUG_POS("Move UnparkPos", current_position);
|
|
193
|
+ #endif
|
|
194
|
+ planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS]/2, active_extruder);
|
|
195
|
+ stepper.synchronize();
|
|
196
|
+
|
|
197
|
+ // Step 7
|
|
198
|
+ current_position[X_AXIS] = midpos - hotend_offset[X_AXIS][tmp_extruder];
|
|
199
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
200
|
+ SERIAL_ECHOLNPGM("(7) Move midway between hotends");
|
|
201
|
+ if (DEBUGGING(LEVELING)) DEBUG_POS("Move midway to new extruder", current_position);
|
|
202
|
+ #endif
|
|
203
|
+ planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
|
|
204
|
+ stepper.synchronize();
|
|
205
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
206
|
+ SERIAL_ECHOLNPGM("Autopark done.");
|
|
207
|
+ #endif
|
|
208
|
+ }
|
|
209
|
+ else { // nomove == true
|
|
210
|
+ // Only engage magnetic field for new extruder
|
|
211
|
+ pe_activate_magnet(tmp_extruder);
|
|
212
|
+ #if ENABLED(PARKING_EXTRUDER_SOLENOIDS_INVERT)
|
|
213
|
+ pe_activate_magnet(active_extruder); // Just save power for inverted magnets
|
|
214
|
+ #endif
|
|
215
|
+ }
|
|
216
|
+ current_position[Z_AXIS] += hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder];
|
119
|
217
|
|
|
218
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
219
|
+ if (DEBUGGING(LEVELING)) DEBUG_POS("Applying Z-offset", current_position);
|
|
220
|
+ #endif
|
|
221
|
+ }
|
|
222
|
+
|
|
223
|
+#endif // PARKING_EXTRUDER
|
120
|
224
|
|
121
|
225
|
inline void invalid_extruder_error(const uint8_t e) {
|
122
|
226
|
SERIAL_ECHO_START();
|
|
@@ -126,19 +230,134 @@ inline void invalid_extruder_error(const uint8_t e) {
|
126
|
230
|
SERIAL_ECHOLNPGM(MSG_INVALID_EXTRUDER);
|
127
|
231
|
}
|
128
|
232
|
|
129
|
|
-/**
|
130
|
|
- * Perform a tool-change, which may result in moving the
|
131
|
|
- * previous tool out of the way and the new tool into place.
|
132
|
|
- */
|
133
|
|
-void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool no_move/*=false*/) {
|
134
|
|
- #if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1
|
|
233
|
+#if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1
|
135
|
234
|
|
|
235
|
+ inline void mixing_tool_change(const uint8_t tmp_extruder) {
|
136
|
236
|
if (tmp_extruder >= MIXING_VIRTUAL_TOOLS)
|
137
|
237
|
return invalid_extruder_error(tmp_extruder);
|
138
|
238
|
|
139
|
239
|
// T0-Tnnn: Switch virtual tool by changing the mix
|
140
|
240
|
for (uint8_t j = 0; j < MIXING_STEPPERS; j++)
|
141
|
241
|
mixing_factor[j] = mixing_virtual_tool_mix[tmp_extruder][j];
|
|
242
|
+ }
|
|
243
|
+
|
|
244
|
+#endif // MIXING_EXTRUDER && MIXING_VIRTUAL_TOOLS > 1
|
|
245
|
+
|
|
246
|
+#if ENABLED(DUAL_X_CARRIAGE)
|
|
247
|
+
|
|
248
|
+ inline void dualx_tool_change(const uint8_t tmp_extruder, bool &no_move) {
|
|
249
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
250
|
+ if (DEBUGGING(LEVELING)) {
|
|
251
|
+ SERIAL_ECHOPGM("Dual X Carriage Mode ");
|
|
252
|
+ switch (dual_x_carriage_mode) {
|
|
253
|
+ case DXC_FULL_CONTROL_MODE: SERIAL_ECHOLNPGM("DXC_FULL_CONTROL_MODE"); break;
|
|
254
|
+ case DXC_AUTO_PARK_MODE: SERIAL_ECHOLNPGM("DXC_AUTO_PARK_MODE"); break;
|
|
255
|
+ case DXC_DUPLICATION_MODE: SERIAL_ECHOLNPGM("DXC_DUPLICATION_MODE"); break;
|
|
256
|
+ }
|
|
257
|
+ }
|
|
258
|
+ #endif
|
|
259
|
+
|
|
260
|
+ const float xhome = x_home_pos(active_extruder);
|
|
261
|
+ if (dual_x_carriage_mode == DXC_AUTO_PARK_MODE
|
|
262
|
+ && IsRunning()
|
|
263
|
+ && (delayed_move_time || current_position[X_AXIS] != xhome)
|
|
264
|
+ ) {
|
|
265
|
+ float raised_z = current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT;
|
|
266
|
+ #if ENABLED(MAX_SOFTWARE_ENDSTOPS)
|
|
267
|
+ NOMORE(raised_z, soft_endstop_max[Z_AXIS]);
|
|
268
|
+ #endif
|
|
269
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
270
|
+ if (DEBUGGING(LEVELING)) {
|
|
271
|
+ SERIAL_ECHOLNPAIR("Raise to ", raised_z);
|
|
272
|
+ SERIAL_ECHOLNPAIR("MoveX to ", xhome);
|
|
273
|
+ SERIAL_ECHOLNPAIR("Lower to ", current_position[Z_AXIS]);
|
|
274
|
+ }
|
|
275
|
+ #endif
|
|
276
|
+ // Park old head: 1) raise 2) move to park position 3) lower
|
|
277
|
+ for (uint8_t i = 0; i < 3; i++)
|
|
278
|
+ planner.buffer_line(
|
|
279
|
+ i == 0 ? current_position[X_AXIS] : xhome,
|
|
280
|
+ current_position[Y_AXIS],
|
|
281
|
+ i == 2 ? current_position[Z_AXIS] : raised_z,
|
|
282
|
+ current_position[E_AXIS],
|
|
283
|
+ planner.max_feedrate_mm_s[i == 1 ? X_AXIS : Z_AXIS],
|
|
284
|
+ active_extruder
|
|
285
|
+ );
|
|
286
|
+ stepper.synchronize();
|
|
287
|
+ }
|
|
288
|
+
|
|
289
|
+ // Apply Y & Z extruder offset (X offset is used as home pos with Dual X)
|
|
290
|
+ current_position[Y_AXIS] -= hotend_offset[Y_AXIS][active_extruder] - hotend_offset[Y_AXIS][tmp_extruder];
|
|
291
|
+ current_position[Z_AXIS] -= hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder];
|
|
292
|
+
|
|
293
|
+ // Activate the new extruder ahead of calling set_axis_is_at_home!
|
|
294
|
+ active_extruder = tmp_extruder;
|
|
295
|
+
|
|
296
|
+ // This function resets the max/min values - the current position may be overwritten below.
|
|
297
|
+ set_axis_is_at_home(X_AXIS);
|
|
298
|
+
|
|
299
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
300
|
+ if (DEBUGGING(LEVELING)) DEBUG_POS("New Extruder", current_position);
|
|
301
|
+ #endif
|
|
302
|
+
|
|
303
|
+ // Only when auto-parking are carriages safe to move
|
|
304
|
+ if (dual_x_carriage_mode != DXC_AUTO_PARK_MODE) no_move = true;
|
|
305
|
+
|
|
306
|
+ switch (dual_x_carriage_mode) {
|
|
307
|
+ case DXC_FULL_CONTROL_MODE:
|
|
308
|
+ // New current position is the position of the activated extruder
|
|
309
|
+ current_position[X_AXIS] = inactive_extruder_x_pos;
|
|
310
|
+ // Save the inactive extruder's position (from the old current_position)
|
|
311
|
+ inactive_extruder_x_pos = destination[X_AXIS];
|
|
312
|
+ break;
|
|
313
|
+ case DXC_AUTO_PARK_MODE:
|
|
314
|
+ // record raised toolhead position for use by unpark
|
|
315
|
+ COPY(raised_parked_position, current_position);
|
|
316
|
+ raised_parked_position[Z_AXIS] += TOOLCHANGE_UNPARK_ZLIFT;
|
|
317
|
+ #if ENABLED(MAX_SOFTWARE_ENDSTOPS)
|
|
318
|
+ NOMORE(raised_parked_position[Z_AXIS], soft_endstop_max[Z_AXIS]);
|
|
319
|
+ #endif
|
|
320
|
+ active_extruder_parked = true;
|
|
321
|
+ delayed_move_time = 0;
|
|
322
|
+ break;
|
|
323
|
+ case DXC_DUPLICATION_MODE:
|
|
324
|
+ // If the new extruder is the left one, set it "parked"
|
|
325
|
+ // This triggers the second extruder to move into the duplication position
|
|
326
|
+ active_extruder_parked = (active_extruder == 0);
|
|
327
|
+ current_position[X_AXIS] = active_extruder_parked ? inactive_extruder_x_pos : destination[X_AXIS] + duplicate_extruder_x_offset;
|
|
328
|
+ inactive_extruder_x_pos = destination[X_AXIS];
|
|
329
|
+ extruder_duplication_enabled = false;
|
|
330
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
331
|
+ if (DEBUGGING(LEVELING)) {
|
|
332
|
+ SERIAL_ECHOLNPAIR("Set inactive_extruder_x_pos=", inactive_extruder_x_pos);
|
|
333
|
+ SERIAL_ECHOLNPGM("Clear extruder_duplication_enabled");
|
|
334
|
+ }
|
|
335
|
+ #endif
|
|
336
|
+ break;
|
|
337
|
+ }
|
|
338
|
+
|
|
339
|
+ #if ENABLED(DEBUG_LEVELING_FEATURE)
|
|
340
|
+ if (DEBUGGING(LEVELING)) {
|
|
341
|
+ SERIAL_ECHOLNPAIR("Active extruder parked: ", active_extruder_parked ? "yes" : "no");
|
|
342
|
+ DEBUG_POS("New extruder (parked)", current_position);
|
|
343
|
+ }
|
|
344
|
+ #endif
|
|
345
|
+
|
|
346
|
+ // No extra case for HAS_ABL in DUAL_X_CARRIAGE. Does that mean they don't work together?
|
|
347
|
+ }
|
|
348
|
+
|
|
349
|
+#endif // DUAL_X_CARRIAGE
|
|
350
|
+
|
|
351
|
+#define DO_SWITCH_EXTRUDER (SWITCHING_EXTRUDER_SERVO_NR != SWITCHING_NOZZLE_SERVO_NR)
|
|
352
|
+
|
|
353
|
+/**
|
|
354
|
+ * Perform a tool-change, which may result in moving the
|
|
355
|
+ * previous tool out of the way and the new tool into place.
|
|
356
|
+ */
|
|
357
|
+void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool no_move/*=false*/) {
|
|
358
|
+ #if ENABLED(MIXING_EXTRUDER) && MIXING_VIRTUAL_TOOLS > 1
|
|
359
|
+
|
|
360
|
+ mixing_tool_change(tmp_extruder);
|
142
|
361
|
|
143
|
362
|
#else // !MIXING_EXTRUDER || MIXING_VIRTUAL_TOOLS <= 1
|
144
|
363
|
|
|
@@ -162,331 +381,53 @@ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool n
|
162
|
381
|
// Save current position to destination, for use later
|
163
|
382
|
set_destination_from_current();
|
164
|
383
|
|
165
|
|
- #if ENABLED(DUAL_X_CARRIAGE)
|
166
|
|
-
|
167
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
168
|
|
- if (DEBUGGING(LEVELING)) {
|
169
|
|
- SERIAL_ECHOPGM("Dual X Carriage Mode ");
|
170
|
|
- switch (dual_x_carriage_mode) {
|
171
|
|
- case DXC_FULL_CONTROL_MODE: SERIAL_ECHOLNPGM("DXC_FULL_CONTROL_MODE"); break;
|
172
|
|
- case DXC_AUTO_PARK_MODE: SERIAL_ECHOLNPGM("DXC_AUTO_PARK_MODE"); break;
|
173
|
|
- case DXC_DUPLICATION_MODE: SERIAL_ECHOLNPGM("DXC_DUPLICATION_MODE"); break;
|
174
|
|
- }
|
175
|
|
- }
|
176
|
|
- #endif
|
177
|
|
-
|
178
|
|
- const float xhome = x_home_pos(active_extruder);
|
179
|
|
- if (dual_x_carriage_mode == DXC_AUTO_PARK_MODE
|
180
|
|
- && IsRunning()
|
181
|
|
- && (delayed_move_time || current_position[X_AXIS] != xhome)
|
182
|
|
- ) {
|
183
|
|
- float raised_z = current_position[Z_AXIS] + TOOLCHANGE_PARK_ZLIFT;
|
184
|
|
- #if ENABLED(MAX_SOFTWARE_ENDSTOPS)
|
185
|
|
- NOMORE(raised_z, soft_endstop_max[Z_AXIS]);
|
186
|
|
- #endif
|
187
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
188
|
|
- if (DEBUGGING(LEVELING)) {
|
189
|
|
- SERIAL_ECHOLNPAIR("Raise to ", raised_z);
|
190
|
|
- SERIAL_ECHOLNPAIR("MoveX to ", xhome);
|
191
|
|
- SERIAL_ECHOLNPAIR("Lower to ", current_position[Z_AXIS]);
|
192
|
|
- }
|
193
|
|
- #endif
|
194
|
|
- // Park old head: 1) raise 2) move to park position 3) lower
|
195
|
|
- for (uint8_t i = 0; i < 3; i++)
|
196
|
|
- planner.buffer_line(
|
197
|
|
- i == 0 ? current_position[X_AXIS] : xhome,
|
198
|
|
- current_position[Y_AXIS],
|
199
|
|
- i == 2 ? current_position[Z_AXIS] : raised_z,
|
200
|
|
- current_position[E_AXIS],
|
201
|
|
- planner.max_feedrate_mm_s[i == 1 ? X_AXIS : Z_AXIS],
|
202
|
|
- active_extruder
|
203
|
|
- );
|
204
|
|
- stepper.synchronize();
|
205
|
|
- }
|
206
|
|
-
|
207
|
|
- // Apply Y & Z extruder offset (X offset is used as home pos with Dual X)
|
208
|
|
- current_position[Y_AXIS] -= hotend_offset[Y_AXIS][active_extruder] - hotend_offset[Y_AXIS][tmp_extruder];
|
209
|
|
- current_position[Z_AXIS] -= hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder];
|
210
|
|
-
|
211
|
|
- // Activate the new extruder ahead of calling set_axis_is_at_home!
|
212
|
|
- active_extruder = tmp_extruder;
|
213
|
|
-
|
214
|
|
- // This function resets the max/min values - the current position may be overwritten below.
|
215
|
|
- set_axis_is_at_home(X_AXIS);
|
216
|
|
-
|
217
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
218
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("New Extruder", current_position);
|
219
|
|
- #endif
|
220
|
|
-
|
221
|
|
- // Only when auto-parking are carriages safe to move
|
222
|
|
- if (dual_x_carriage_mode != DXC_AUTO_PARK_MODE) no_move = true;
|
223
|
|
-
|
224
|
|
- switch (dual_x_carriage_mode) {
|
225
|
|
- case DXC_FULL_CONTROL_MODE:
|
226
|
|
- // New current position is the position of the activated extruder
|
227
|
|
- current_position[X_AXIS] = inactive_extruder_x_pos;
|
228
|
|
- // Save the inactive extruder's position (from the old current_position)
|
229
|
|
- inactive_extruder_x_pos = destination[X_AXIS];
|
230
|
|
- break;
|
231
|
|
- case DXC_AUTO_PARK_MODE:
|
232
|
|
- // record raised toolhead position for use by unpark
|
233
|
|
- COPY(raised_parked_position, current_position);
|
234
|
|
- raised_parked_position[Z_AXIS] += TOOLCHANGE_UNPARK_ZLIFT;
|
235
|
|
- #if ENABLED(MAX_SOFTWARE_ENDSTOPS)
|
236
|
|
- NOMORE(raised_parked_position[Z_AXIS], soft_endstop_max[Z_AXIS]);
|
237
|
|
- #endif
|
238
|
|
- active_extruder_parked = true;
|
239
|
|
- delayed_move_time = 0;
|
240
|
|
- break;
|
241
|
|
- case DXC_DUPLICATION_MODE:
|
242
|
|
- // If the new extruder is the left one, set it "parked"
|
243
|
|
- // This triggers the second extruder to move into the duplication position
|
244
|
|
- active_extruder_parked = (active_extruder == 0);
|
245
|
|
-
|
246
|
|
- if (active_extruder_parked)
|
247
|
|
- current_position[X_AXIS] = inactive_extruder_x_pos;
|
248
|
|
- else
|
249
|
|
- current_position[X_AXIS] = destination[X_AXIS] + duplicate_extruder_x_offset;
|
250
|
|
- inactive_extruder_x_pos = destination[X_AXIS];
|
251
|
|
- extruder_duplication_enabled = false;
|
252
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
253
|
|
- if (DEBUGGING(LEVELING)) {
|
254
|
|
- SERIAL_ECHOLNPAIR("Set inactive_extruder_x_pos=", inactive_extruder_x_pos);
|
255
|
|
- SERIAL_ECHOLNPGM("Clear extruder_duplication_enabled");
|
256
|
|
- }
|
257
|
|
- #endif
|
258
|
|
- break;
|
259
|
|
- }
|
|
384
|
+ #if HAS_LEVELING
|
|
385
|
+ // Set current position to the physical position
|
|
386
|
+ const bool leveling_was_active = planner.leveling_active;
|
|
387
|
+ set_bed_leveling_enabled(false);
|
|
388
|
+ #endif
|
260
|
389
|
|
261
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
262
|
|
- if (DEBUGGING(LEVELING)) {
|
263
|
|
- SERIAL_ECHOLNPAIR("Active extruder parked: ", active_extruder_parked ? "yes" : "no");
|
264
|
|
- DEBUG_POS("New extruder (parked)", current_position);
|
265
|
|
- }
|
266
|
|
- #endif
|
|
390
|
+ #if ENABLED(DUAL_X_CARRIAGE)
|
267
|
391
|
|
268
|
|
- // No extra case for HAS_ABL in DUAL_X_CARRIAGE. Does that mean they don't work together?
|
|
392
|
+ dualx_tool_change(tmp_extruder, no_move); // Can modify no_move
|
269
|
393
|
|
270
|
394
|
#else // !DUAL_X_CARRIAGE
|
271
|
395
|
|
272
|
396
|
#if ENABLED(PARKING_EXTRUDER) // Dual Parking extruder
|
273
|
|
- float z_raise = PARKING_EXTRUDER_SECURITY_RAISE;
|
274
|
|
- if (!no_move) {
|
275
|
|
-
|
276
|
|
- const float parkingposx[] = PARKING_EXTRUDER_PARKING_X,
|
277
|
|
- midpos = (parkingposx[0] + parkingposx[1]) * 0.5 + hotend_offset[X_AXIS][active_extruder],
|
278
|
|
- grabpos = parkingposx[tmp_extruder] + hotend_offset[X_AXIS][active_extruder]
|
279
|
|
- + (tmp_extruder == 0 ? -(PARKING_EXTRUDER_GRAB_DISTANCE) : PARKING_EXTRUDER_GRAB_DISTANCE);
|
280
|
|
- /**
|
281
|
|
- * Steps:
|
282
|
|
- * 1. Raise Z-Axis to give enough clearance
|
283
|
|
- * 2. Move to park position of old extruder
|
284
|
|
- * 3. Disengage magnetic field, wait for delay
|
285
|
|
- * 4. Move near new extruder
|
286
|
|
- * 5. Engage magnetic field for new extruder
|
287
|
|
- * 6. Move to parking incl. offset of new extruder
|
288
|
|
- * 7. Lower Z-Axis
|
289
|
|
- */
|
290
|
|
-
|
291
|
|
- // STEP 1
|
292
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
293
|
|
- SERIAL_ECHOLNPGM("Starting Autopark");
|
294
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("current position:", current_position);
|
295
|
|
- #endif
|
296
|
|
- current_position[Z_AXIS] += z_raise;
|
297
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
298
|
|
- SERIAL_ECHOLNPGM("(1) Raise Z-Axis ");
|
299
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("Moving to Raised Z-Position", current_position);
|
300
|
|
- #endif
|
301
|
|
- planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[Z_AXIS], active_extruder);
|
302
|
|
- stepper.synchronize();
|
303
|
|
-
|
304
|
|
- // STEP 2
|
305
|
|
- current_position[X_AXIS] = parkingposx[active_extruder] + hotend_offset[X_AXIS][active_extruder];
|
306
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
307
|
|
- SERIAL_ECHOLNPAIR("(2) Park extruder ", active_extruder);
|
308
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("Moving ParkPos", current_position);
|
309
|
|
- #endif
|
310
|
|
- planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
|
311
|
|
- stepper.synchronize();
|
312
|
|
-
|
313
|
|
- // STEP 3
|
314
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
315
|
|
- SERIAL_ECHOLNPGM("(3) Disengage magnet ");
|
316
|
|
- #endif
|
317
|
|
- pe_deactivate_magnet(active_extruder);
|
318
|
|
-
|
319
|
|
- // STEP 4
|
320
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
321
|
|
- SERIAL_ECHOLNPGM("(4) Move to position near new extruder");
|
322
|
|
- #endif
|
323
|
|
- current_position[X_AXIS] += (active_extruder == 0 ? 10 : -10); // move 10mm away from parked extruder
|
324
|
|
-
|
325
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
326
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("Moving away from parked extruder", current_position);
|
327
|
|
- #endif
|
328
|
|
- planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
|
329
|
|
- stepper.synchronize();
|
330
|
|
-
|
331
|
|
- // STEP 5
|
332
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
333
|
|
- SERIAL_ECHOLNPGM("(5) Engage magnetic field");
|
334
|
|
- #endif
|
335
|
|
-
|
336
|
|
- #if ENABLED(PARKING_EXTRUDER_SOLENOIDS_INVERT)
|
337
|
|
- pe_activate_magnet(active_extruder); //just save power for inverted magnets
|
338
|
|
- #endif
|
339
|
|
- pe_activate_magnet(tmp_extruder);
|
340
|
|
-
|
341
|
|
- // STEP 6
|
342
|
|
- current_position[X_AXIS] = grabpos + (tmp_extruder == 0 ? (+10) : (-10));
|
343
|
|
- planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
|
344
|
|
- current_position[X_AXIS] = grabpos;
|
345
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
346
|
|
- SERIAL_ECHOLNPAIR("(6) Unpark extruder ", tmp_extruder);
|
347
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("Move UnparkPos", current_position);
|
348
|
|
- #endif
|
349
|
|
- planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS]/2, active_extruder);
|
350
|
|
- stepper.synchronize();
|
351
|
|
-
|
352
|
|
- // Step 7
|
353
|
|
- current_position[X_AXIS] = midpos - hotend_offset[X_AXIS][tmp_extruder];
|
354
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
355
|
|
- SERIAL_ECHOLNPGM("(7) Move midway between hotends");
|
356
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("Move midway to new extruder", current_position);
|
357
|
|
- #endif
|
358
|
|
- planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[X_AXIS], active_extruder);
|
359
|
|
- stepper.synchronize();
|
360
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
361
|
|
- SERIAL_ECHOLNPGM("Autopark done.");
|
362
|
|
- #endif
|
363
|
|
- }
|
364
|
|
- else { // nomove == true
|
365
|
|
- // Only engage magnetic field for new extruder
|
366
|
|
- pe_activate_magnet(tmp_extruder);
|
367
|
|
- #if ENABLED(PARKING_EXTRUDER_SOLENOIDS_INVERT)
|
368
|
|
- pe_activate_magnet(active_extruder); // Just save power for inverted magnets
|
369
|
|
- #endif
|
370
|
|
- }
|
371
|
|
- current_position[Z_AXIS] -= hotend_offset[Z_AXIS][tmp_extruder] - hotend_offset[Z_AXIS][active_extruder]; // Apply Zoffset
|
372
|
|
-
|
373
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
374
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("Applying Z-offset", current_position);
|
375
|
|
- #endif
|
376
|
|
-
|
377
|
|
- #endif // dualParking extruder
|
|
397
|
+ parking_extruder_tool_change(tmp_extruder, no_move);
|
|
398
|
+ #endif
|
378
|
399
|
|
379
|
400
|
#if ENABLED(SWITCHING_NOZZLE)
|
380
|
|
- #define DONT_SWITCH (SWITCHING_EXTRUDER_SERVO_NR == SWITCHING_NOZZLE_SERVO_NR)
|
381
|
|
- // <0 if the new nozzle is higher, >0 if lower. A bigger raise when lower.
|
382
|
|
- const float z_diff = hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder],
|
383
|
|
- z_raise = 0.3 + (z_diff > 0.0 ? z_diff : 0.0);
|
384
|
|
-
|
385
|
|
- // Always raise by some amount
|
386
|
|
- current_position[Z_AXIS] += z_raise;
|
|
401
|
+ // Always raise by at least 0.3
|
|
402
|
+ const float z_diff = hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder];
|
|
403
|
+ current_position[Z_AXIS] += (z_diff > 0.0 ? z_diff : 0.0) + 0.3;
|
387
|
404
|
planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[Z_AXIS], active_extruder);
|
388
|
405
|
move_nozzle_servo(tmp_extruder);
|
389
|
406
|
#endif
|
390
|
407
|
|
391
|
|
- /**
|
392
|
|
- * Set current_position to the position of the new nozzle.
|
393
|
|
- * Offsets are based on linear distance, so we need to get
|
394
|
|
- * the resulting position in coordinate space.
|
395
|
|
- *
|
396
|
|
- * - With grid or 3-point leveling, offset XYZ by a tilted vector
|
397
|
|
- * - With mesh leveling, update Z for the new position
|
398
|
|
- * - Otherwise, just use the raw linear distance
|
399
|
|
- *
|
400
|
|
- * Software endstops are altered here too. Consider a case where:
|
401
|
|
- * E0 at X=0 ... E1 at X=10
|
402
|
|
- * When we switch to E1 now X=10, but E1 can't move left.
|
403
|
|
- * To express this we apply the change in XY to the software endstops.
|
404
|
|
- * E1 can move farther right than E0, so the right limit is extended.
|
405
|
|
- *
|
406
|
|
- * Note that we don't adjust the Z software endstops. Why not?
|
407
|
|
- * Consider a case where Z=0 (here) and switching to E1 makes Z=1
|
408
|
|
- * because the bed is 1mm lower at the new position. As long as
|
409
|
|
- * the first nozzle is out of the way, the carriage should be
|
410
|
|
- * allowed to move 1mm lower. This technically "breaks" the
|
411
|
|
- * Z software endstop. But this is technically correct (and
|
412
|
|
- * there is no viable alternative).
|
413
|
|
- */
|
414
|
|
- #if ABL_PLANAR
|
415
|
|
- // Offset extruder, make sure to apply the bed level rotation matrix
|
416
|
|
- vector_3 tmp_offset_vec = vector_3(hotend_offset[X_AXIS][tmp_extruder],
|
417
|
|
- hotend_offset[Y_AXIS][tmp_extruder],
|
418
|
|
- 0),
|
419
|
|
- act_offset_vec = vector_3(hotend_offset[X_AXIS][active_extruder],
|
420
|
|
- hotend_offset[Y_AXIS][active_extruder],
|
421
|
|
- 0),
|
422
|
|
- offset_vec = tmp_offset_vec - act_offset_vec;
|
423
|
|
-
|
424
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
425
|
|
- if (DEBUGGING(LEVELING)) {
|
426
|
|
- tmp_offset_vec.debug(PSTR("tmp_offset_vec"));
|
427
|
|
- act_offset_vec.debug(PSTR("act_offset_vec"));
|
428
|
|
- offset_vec.debug(PSTR("offset_vec (BEFORE)"));
|
429
|
|
- }
|
430
|
|
- #endif
|
431
|
|
-
|
432
|
|
- offset_vec.apply_rotation(planner.bed_level_matrix.transpose(planner.bed_level_matrix));
|
433
|
|
-
|
434
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
435
|
|
- if (DEBUGGING(LEVELING)) offset_vec.debug(PSTR("offset_vec (AFTER)"));
|
436
|
|
- #endif
|
437
|
|
-
|
438
|
|
- // Adjustments to the current position
|
439
|
|
- const float xydiff[2] = { offset_vec.x, offset_vec.y };
|
440
|
|
- current_position[Z_AXIS] += offset_vec.z;
|
441
|
|
-
|
442
|
|
- #else // !ABL_PLANAR
|
443
|
|
-
|
444
|
|
- const float xydiff[2] = {
|
445
|
|
- hotend_offset[X_AXIS][tmp_extruder] - hotend_offset[X_AXIS][active_extruder],
|
446
|
|
- hotend_offset[Y_AXIS][tmp_extruder] - hotend_offset[Y_AXIS][active_extruder]
|
447
|
|
- };
|
448
|
|
-
|
449
|
|
- #if HAS_MESH && PLANNER_LEVELING
|
450
|
|
-
|
451
|
|
- if (planner.leveling_active) {
|
452
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
453
|
|
- if (DEBUGGING(LEVELING)) SERIAL_ECHOPAIR("Z before: ", current_position[Z_AXIS]);
|
454
|
|
- #endif
|
455
|
|
- float x2 = current_position[X_AXIS] + xydiff[X_AXIS],
|
456
|
|
- y2 = current_position[Y_AXIS] + xydiff[Y_AXIS],
|
457
|
|
- z1 = current_position[Z_AXIS], z2 = z1;
|
458
|
|
- planner.apply_leveling(current_position[X_AXIS], current_position[Y_AXIS], z1);
|
459
|
|
- planner.apply_leveling(x2, y2, z2);
|
460
|
|
- current_position[Z_AXIS] += z2 - z1;
|
461
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
462
|
|
- if (DEBUGGING(LEVELING))
|
463
|
|
- SERIAL_ECHOLNPAIR(" after: ", current_position[Z_AXIS]);
|
464
|
|
- #endif
|
465
|
|
- }
|
466
|
|
-
|
467
|
|
- #endif // HAS_MESH && PLANNER_LEVELING
|
468
|
|
-
|
469
|
|
- #endif // !HAS_ABL
|
|
408
|
+ const float xdiff = hotend_offset[X_AXIS][tmp_extruder] - hotend_offset[X_AXIS][active_extruder],
|
|
409
|
+ ydiff = hotend_offset[Y_AXIS][tmp_extruder] - hotend_offset[Y_AXIS][active_extruder];
|
470
|
410
|
|
471
|
411
|
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
472
|
412
|
if (DEBUGGING(LEVELING)) {
|
473
|
|
- SERIAL_ECHOPAIR("Offset Tool XY by { ", xydiff[X_AXIS]);
|
474
|
|
- SERIAL_ECHOPAIR(", ", xydiff[Y_AXIS]);
|
|
413
|
+ SERIAL_ECHOPAIR("Offset Tool XY by { ", xdiff);
|
|
414
|
+ SERIAL_ECHOPAIR(", ", ydiff);
|
475
|
415
|
SERIAL_ECHOLNPGM(" }");
|
476
|
416
|
}
|
477
|
417
|
#endif
|
478
|
418
|
|
479
|
419
|
// The newly-selected extruder XY is actually at...
|
480
|
|
- current_position[X_AXIS] += xydiff[X_AXIS];
|
481
|
|
- current_position[Y_AXIS] += xydiff[Y_AXIS];
|
|
420
|
+ current_position[X_AXIS] += xdiff;
|
|
421
|
+ current_position[Y_AXIS] += ydiff;
|
482
|
422
|
|
483
|
423
|
// Set the new active extruder
|
484
|
424
|
active_extruder = tmp_extruder;
|
485
|
425
|
|
486
|
426
|
#endif // !DUAL_X_CARRIAGE
|
487
|
427
|
|
488
|
|
- #if ENABLED(DEBUG_LEVELING_FEATURE)
|
489
|
|
- if (DEBUGGING(LEVELING)) DEBUG_POS("Sync After Toolchange", current_position);
|
|
428
|
+ #if HAS_LEVELING
|
|
429
|
+ // Restore leveling to re-establish the logical position
|
|
430
|
+ set_bed_leveling_enabled(leveling_was_active);
|
490
|
431
|
#endif
|
491
|
432
|
|
492
|
433
|
// Tell the planner the new "current position"
|
|
@@ -494,22 +435,20 @@ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool n
|
494
|
435
|
|
495
|
436
|
#if ENABLED(DELTA)
|
496
|
437
|
//LOOP_XYZ(i) update_software_endstops(i); // or modify the constrain function
|
497
|
|
- // Do a small lift to avoid the workpiece in the move back (below)
|
498
|
438
|
const bool safe_to_move = current_position[Z_AXIS] < delta_clip_start_height - 1;
|
499
|
|
- if (safe_to_move && !no_move && IsRunning()) {
|
500
|
|
- ++current_position[Z_AXIS];
|
501
|
|
- planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[Z_AXIS], active_extruder);
|
502
|
|
- }
|
503
|
439
|
#else
|
504
|
440
|
constexpr bool safe_to_move = true;
|
505
|
441
|
#endif
|
506
|
442
|
|
507
|
|
- // Move to the "old position" (move the extruder into place)
|
508
|
443
|
#if ENABLED(SWITCHING_NOZZLE)
|
509
|
444
|
destination[Z_AXIS] += z_diff; // Include the Z restore with the "move back"
|
510
|
445
|
#endif
|
511
|
446
|
|
|
447
|
+ // Raise, move, and lower again
|
512
|
448
|
if (safe_to_move && !no_move && IsRunning()) {
|
|
449
|
+ // Do a small lift to avoid the workpiece in the move back (below)
|
|
450
|
+ current_position[Z_AXIS] += 1.0;
|
|
451
|
+ planner.buffer_line_kinematic(current_position, planner.max_feedrate_mm_s[Z_AXIS], active_extruder);
|
513
|
452
|
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
514
|
453
|
if (DEBUGGING(LEVELING)) DEBUG_POS("Move back", destination);
|
515
|
454
|
#endif
|
|
@@ -529,7 +468,7 @@ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool n
|
529
|
468
|
#if ENABLED(EXT_SOLENOID) && !ENABLED(PARKING_EXTRUDER)
|
530
|
469
|
disable_all_solenoids();
|
531
|
470
|
enable_solenoid_on_active_extruder();
|
532
|
|
- #endif // EXT_SOLENOID
|
|
471
|
+ #endif
|
533
|
472
|
|
534
|
473
|
feedrate_mm_s = old_feedrate_mm_s;
|
535
|
474
|
|
|
@@ -550,7 +489,7 @@ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool n
|
550
|
489
|
|
551
|
490
|
#endif // HOTENDS <= 1
|
552
|
491
|
|
553
|
|
- #if ENABLED(SWITCHING_EXTRUDER) && !DONT_SWITCH
|
|
492
|
+ #if DO_SWITCH_EXTRUDER
|
554
|
493
|
stepper.synchronize();
|
555
|
494
|
move_extruder_servo(active_extruder);
|
556
|
495
|
#endif
|