|
@@ -32,6 +32,7 @@
|
32
|
32
|
#include "../MarlinCore.h"
|
33
|
33
|
|
34
|
34
|
//#define DEBUG_TOOL_CHANGE
|
|
35
|
+//#define DEBUG_TOOLCHANGE_FILAMENT_SWAP
|
35
|
36
|
|
36
|
37
|
#define DEBUG_OUT ENABLED(DEBUG_TOOL_CHANGE)
|
37
|
38
|
#include "../core/debug_out.h"
|
|
@@ -42,7 +43,6 @@
|
42
|
43
|
|
43
|
44
|
#if ENABLED(TOOLCHANGE_MIGRATION_FEATURE)
|
44
|
45
|
migration_settings_t migration = migration_defaults;
|
45
|
|
- bool enable_first_prime;
|
46
|
46
|
#endif
|
47
|
47
|
|
48
|
48
|
#if ENABLED(TOOLCHANGE_FS_INIT_BEFORE_SWAP)
|
|
@@ -150,6 +150,7 @@
|
150
|
150
|
|
151
|
151
|
#endif // SWITCHING_NOZZLE
|
152
|
152
|
|
|
153
|
+// Move to position routines
|
153
|
154
|
void _line_to_current(const AxisEnum fr_axis, const float fscale=1) {
|
154
|
155
|
line_to_current_position(planner.settings.max_feedrate_mm_s[fr_axis] * fscale);
|
155
|
156
|
}
|
|
@@ -899,10 +900,135 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
899
|
900
|
*/
|
900
|
901
|
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
|
901
|
902
|
|
|
903
|
+ #ifdef DEBUG_TOOLCHANGE_FILAMENT_SWAP
|
|
904
|
+ #define FS_DEBUG(V...) SERIAL_ECHOLNPGM("DEBUG: " V)
|
|
905
|
+ #else
|
|
906
|
+ #define FS_DEBUG(...) NOOP
|
|
907
|
+ #endif
|
|
908
|
+
|
|
909
|
+ // Define any variables required
|
|
910
|
+ static Flags<EXTRUDERS> extruder_was_primed; // Extruders primed status
|
|
911
|
+
|
|
912
|
+ #if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED)
|
|
913
|
+ bool enable_first_prime; // As set by M217 V
|
|
914
|
+ #endif
|
|
915
|
+
|
|
916
|
+ // Cool down with fan
|
|
917
|
+ inline void filament_swap_cooling() {
|
|
918
|
+ #if HAS_FAN && TOOLCHANGE_FS_FAN >= 0
|
|
919
|
+ thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed;
|
|
920
|
+ gcode.dwell(SEC_TO_MS(toolchange_settings.fan_time));
|
|
921
|
+ thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = 0;
|
|
922
|
+ #endif
|
|
923
|
+ }
|
|
924
|
+
|
|
925
|
+ /**
|
|
926
|
+ * Check if too cold to move the specified tool
|
|
927
|
+ *
|
|
928
|
+ * Returns TRUE if too cold to move (also echos message: STR_ERR_HOTEND_TOO_COLD)
|
|
929
|
+ * Returns FALSE if able to move.
|
|
930
|
+ */
|
|
931
|
+ bool too_cold(uint8_t toolID){
|
|
932
|
+ if (TERN0(PREVENT_COLD_EXTRUSION, !DEBUGGING(DRYRUN) && thermalManager.targetTooColdToExtrude(toolID))) {
|
|
933
|
+ SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD);
|
|
934
|
+ return true;
|
|
935
|
+ }
|
|
936
|
+ return false;
|
|
937
|
+ }
|
|
938
|
+
|
|
939
|
+ /**
|
|
940
|
+ * Cutting recovery -- Recover from cutting retraction that occurs at the end of nozzle priming
|
|
941
|
+ *
|
|
942
|
+ * If the active_extruder is up to temp (!too_cold):
|
|
943
|
+ * Extrude filament distance = toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT
|
|
944
|
+ * current_position.e = e;
|
|
945
|
+ * sync_plan_position_e();
|
|
946
|
+ */
|
|
947
|
+ void extruder_cutting_recover(const_float_t e) {
|
|
948
|
+ if (!too_cold(active_extruder)) {
|
|
949
|
+ const float dist = toolchange_settings.extra_resume + (TOOLCHANGE_FS_WIPE_RETRACT);
|
|
950
|
+ FS_DEBUG("Performing Cutting Recover | Distance: ", dist, " | Speed: ", MMM_TO_MMS(toolchange_settings.unretract_speed), "mm/s");
|
|
951
|
+ unscaled_e_move(dist, MMM_TO_MMS(toolchange_settings.unretract_speed));
|
|
952
|
+ planner.synchronize();
|
|
953
|
+ FS_DEBUG("Set position to: ", e);
|
|
954
|
+ current_position.e = e;
|
|
955
|
+ sync_plan_position_e(); // Resume new E Position
|
|
956
|
+ }
|
|
957
|
+ }
|
|
958
|
+
|
|
959
|
+ /**
|
|
960
|
+ * Prime the currently selected extruder (Filament loading only)
|
|
961
|
+ *
|
|
962
|
+ * If too_cold(toolID) returns TRUE -> returns without moving extruder.
|
|
963
|
+ * Extruders filament = swap_length + extra prime, then performs cutting retraction if enabled.
|
|
964
|
+ * If cooling fan is enabled, calls filament_swap_cooling();
|
|
965
|
+ */
|
|
966
|
+ void extruder_prime() {
|
|
967
|
+
|
|
968
|
+ if (too_cold(active_extruder)) {
|
|
969
|
+ FS_DEBUG("Priming Aborted - Nozzle Too Cold!");
|
|
970
|
+ return; // Extruder too cold to prime
|
|
971
|
+ }
|
|
972
|
+
|
|
973
|
+ float fr = toolchange_settings.unretract_speed; // Set default speed for unretract
|
|
974
|
+
|
|
975
|
+ #if ENABLED(TOOLCHANGE_FS_SLOW_FIRST_PRIME)
|
|
976
|
+ /*
|
|
977
|
+ * Perform first unretract movement at the slower Prime_Speed to avoid breakage on first prime
|
|
978
|
+ */
|
|
979
|
+ static Flags<EXTRUDERS> extruder_did_first_prime; // Extruders first priming status
|
|
980
|
+ if (!extruder_did_first_prime[active_extruder]) {
|
|
981
|
+ extruder_did_first_prime.set(active_extruder); // Log first prime complete
|
|
982
|
+ // new nozzle - prime at user-specified speed.
|
|
983
|
+ FS_DEBUG("First time priming T", active_extruder, ", reducing speed from ", MMM_TO_MMS(fr), " to ", MMM_TO_MMS(toolchange_settings.prime_speed), "mm/s");
|
|
984
|
+ fr = toolchange_settings.prime_speed;
|
|
985
|
+ unscaled_e_move(0, MMM_TO_MMS(fr)); // Init planner with 0 length move
|
|
986
|
+ }
|
|
987
|
+ #endif
|
|
988
|
+
|
|
989
|
+ //Calculate and perform the priming distance
|
|
990
|
+ if (toolchange_settings.extra_prime >= 0) {
|
|
991
|
+ // Positive extra_prime value
|
|
992
|
+ // - Return filament at speed (fr) then extra_prime at prime speed
|
|
993
|
+ FS_DEBUG("Loading Filament for T", active_extruder, " | Distance: ", toolchange_settings.swap_length, " | Speed: ", MMM_TO_MMS(fr), "mm/s");
|
|
994
|
+ unscaled_e_move(toolchange_settings.swap_length, MMM_TO_MMS(fr)); // Prime (Unretract) filament by extruding equal to Swap Length (Unretract)
|
|
995
|
+
|
|
996
|
+ if (toolchange_settings.extra_prime > 0) {
|
|
997
|
+ FS_DEBUG("Performing Extra Priming for T", active_extruder, " | Distance: ", toolchange_settings.extra_prime, " | Speed: ", MMM_TO_MMS(toolchange_settings.prime_speed), "mm/s");
|
|
998
|
+ unscaled_e_move(toolchange_settings.extra_prime, MMM_TO_MMS(toolchange_settings.prime_speed)); // Extra Prime Distance
|
|
999
|
+ }
|
|
1000
|
+ }
|
|
1001
|
+ else {
|
|
1002
|
+ // Negative extra_prime value
|
|
1003
|
+ // - Unretract distance (swap length) is reduced by the value of extra_prime
|
|
1004
|
+ const float eswap = toolchange_settings.swap_length + toolchange_settings.extra_prime;
|
|
1005
|
+ FS_DEBUG("Negative ExtraPrime value - Swap Return Length has been reduced from ", toolchange_settings.swap_length, " to ", eswap);
|
|
1006
|
+ FS_DEBUG("Loading Filament for T", active_extruder, " | Distance: ", eswap, " | Speed: ", MMM_TO_MMS(fr), "mm/s");
|
|
1007
|
+ unscaled_e_move(eswap, MMM_TO_MMS(fr));
|
|
1008
|
+ }
|
|
1009
|
+
|
|
1010
|
+ extruder_was_primed.set(active_extruder); // Log that this extruder has been primed
|
|
1011
|
+
|
|
1012
|
+ // Cutting retraction
|
|
1013
|
+ #if TOOLCHANGE_FS_WIPE_RETRACT
|
|
1014
|
+ FS_DEBUG("Performing Cutting Retraction | Distance: ", -(TOOLCHANGE_FS_WIPE_RETRACT), " | Speed: ", MMM_TO_MMS(toolchange_settings.retract_speed), "mm/s");
|
|
1015
|
+ unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed));
|
|
1016
|
+ #endif
|
|
1017
|
+
|
|
1018
|
+ // Cool down with fan
|
|
1019
|
+ filament_swap_cooling();
|
|
1020
|
+
|
|
1021
|
+ }
|
|
1022
|
+
|
|
1023
|
+ /**
|
|
1024
|
+ * Sequence to Prime the currently selected extruder
|
|
1025
|
+ * Raise Z, move the ToolChange_Park if enabled, prime the extruder, move back.
|
|
1026
|
+ */
|
902
|
1027
|
void tool_change_prime() {
|
903
|
|
- if (toolchange_settings.extra_prime > 0
|
904
|
|
- && TERN(PREVENT_COLD_EXTRUSION, !thermalManager.targetTooColdToExtrude(active_extruder), 1)
|
905
|
|
- ) {
|
|
1028
|
+
|
|
1029
|
+ FS_DEBUG(">>> tool_change_prime()");
|
|
1030
|
+
|
|
1031
|
+ if (!too_cold(active_extruder)) {
|
906
|
1032
|
destination = current_position; // Remember the old position
|
907
|
1033
|
|
908
|
1034
|
const bool ok = TERN1(TOOLCHANGE_PARK, all_axes_homed() && toolchange_settings.enable_park);
|
|
@@ -941,20 +1067,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
941
|
1067
|
}
|
942
|
1068
|
#endif
|
943
|
1069
|
|
944
|
|
- // Prime (All distances are added and slowed down to ensure secure priming in all circumstances)
|
945
|
|
- unscaled_e_move(toolchange_settings.swap_length + toolchange_settings.extra_prime, MMM_TO_MMS(toolchange_settings.prime_speed));
|
946
|
|
-
|
947
|
|
- // Cutting retraction
|
948
|
|
- #if TOOLCHANGE_FS_WIPE_RETRACT
|
949
|
|
- unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed));
|
950
|
|
- #endif
|
951
|
|
-
|
952
|
|
- // Cool down with fan
|
953
|
|
- #if HAS_FAN && TOOLCHANGE_FS_FAN >= 0
|
954
|
|
- thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed;
|
955
|
|
- gcode.dwell(SEC_TO_MS(toolchange_settings.fan_time));
|
956
|
|
- thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = 0;
|
957
|
|
- #endif
|
|
1070
|
+ extruder_prime();
|
958
|
1071
|
|
959
|
1072
|
// Move back
|
960
|
1073
|
#if ENABLED(TOOLCHANGE_PARK)
|
|
@@ -968,13 +1081,11 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
968
|
1081
|
}
|
969
|
1082
|
#endif
|
970
|
1083
|
|
971
|
|
- // Cutting recover
|
972
|
|
- unscaled_e_move(toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT, MMM_TO_MMS(toolchange_settings.unretract_speed));
|
973
|
|
-
|
974
|
|
- // Resume at the old E position
|
975
|
|
- current_position.e = destination.e;
|
976
|
|
- sync_plan_position_e();
|
|
1084
|
+ extruder_cutting_recover(destination.e); // Cutting recover
|
977
|
1085
|
}
|
|
1086
|
+
|
|
1087
|
+ FS_DEBUG("<<< tool_change_prime");
|
|
1088
|
+
|
978
|
1089
|
}
|
979
|
1090
|
|
980
|
1091
|
#endif // TOOLCHANGE_FILAMENT_SWAP
|
|
@@ -1051,12 +1162,10 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
|
1051
|
1162
|
TEMPORARY_BED_LEVELING_STATE(false);
|
1052
|
1163
|
#endif
|
1053
|
1164
|
|
1054
|
|
- // First tool priming. To prime again, reboot the machine.
|
|
1165
|
+ // First tool priming. To prime again, reboot the machine. -- Should only occur for first T0 after powerup!
|
1055
|
1166
|
#if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED)
|
1056
|
|
- static bool first_tool_is_primed = false;
|
1057
|
|
- if (new_tool == old_tool && !first_tool_is_primed && enable_first_prime) {
|
|
1167
|
+ if (enable_first_prime && old_tool == 0 && new_tool == 0 && !extruder_was_primed[0]) {
|
1058
|
1168
|
tool_change_prime();
|
1059
|
|
- first_tool_is_primed = true;
|
1060
|
1169
|
TERN_(TOOLCHANGE_FS_INIT_BEFORE_SWAP, toolchange_extruder_ready.set(old_tool)); // Primed and initialized
|
1061
|
1170
|
}
|
1062
|
1171
|
#endif
|
|
@@ -1082,20 +1191,17 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
|
1082
|
1191
|
|
1083
|
1192
|
// Unload / Retract
|
1084
|
1193
|
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
|
1085
|
|
- const bool should_swap = can_move_away && toolchange_settings.swap_length,
|
1086
|
|
- too_cold = TERN0(PREVENT_COLD_EXTRUSION,
|
1087
|
|
- !DEBUGGING(DRYRUN) && (thermalManager.targetTooColdToExtrude(old_tool) || thermalManager.targetTooColdToExtrude(new_tool))
|
1088
|
|
- );
|
|
1194
|
+ const bool should_swap = can_move_away && toolchange_settings.swap_length;
|
1089
|
1195
|
if (should_swap) {
|
1090
|
|
- if (too_cold) {
|
1091
|
|
- SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD);
|
|
1196
|
+ if (too_cold(old_tool)) {
|
|
1197
|
+ // If SingleNozzle setup is too cold, unable to perform tool_change.
|
1092
|
1198
|
if (ENABLED(SINGLENOZZLE)) { active_extruder = new_tool; return; }
|
1093
|
1199
|
}
|
1094
|
|
- else {
|
1095
|
|
- // For first new tool, change without unloading the old. 'Just prime/init the new'
|
1096
|
|
- if (TERN1(TOOLCHANGE_FS_PRIME_FIRST_USED, first_tool_is_primed))
|
1097
|
|
- unscaled_e_move(-toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.retract_speed));
|
1098
|
|
- TERN_(TOOLCHANGE_FS_PRIME_FIRST_USED, first_tool_is_primed = true); // The first new tool will be primed by toolchanging
|
|
1200
|
+ else if (extruder_was_primed[old_tool]) {
|
|
1201
|
+ // Retract the old extruder if it was previously primed
|
|
1202
|
+ // To-Do: Should SingleNozzle always retract?
|
|
1203
|
+ FS_DEBUG("Retracting Filament for T", old_tool, ". | Distance: ", toolchange_settings.swap_length, " | Speed: ", MMM_TO_MMS(toolchange_settings.retract_speed), "mm/s");
|
|
1204
|
+ unscaled_e_move(-toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.retract_speed));
|
1099
|
1205
|
}
|
1100
|
1206
|
}
|
1101
|
1207
|
#endif
|
|
@@ -1210,36 +1316,8 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
|
1210
|
1316
|
#endif
|
1211
|
1317
|
|
1212
|
1318
|
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
|
1213
|
|
- if (should_swap && !too_cold) {
|
1214
|
|
-
|
1215
|
|
- float fr = toolchange_settings.unretract_speed;
|
1216
|
|
-
|
1217
|
|
- #if ENABLED(TOOLCHANGE_FS_INIT_BEFORE_SWAP)
|
1218
|
|
- if (!toolchange_extruder_ready[new_tool]) {
|
1219
|
|
- toolchange_extruder_ready.set(new_tool);
|
1220
|
|
- fr = toolchange_settings.prime_speed; // Next move is a prime
|
1221
|
|
- unscaled_e_move(0, MMM_TO_MMS(fr)); // Init planner with 0 length move
|
1222
|
|
- }
|
1223
|
|
- #endif
|
1224
|
|
-
|
1225
|
|
- // Unretract (or Prime)
|
1226
|
|
- unscaled_e_move(toolchange_settings.swap_length, MMM_TO_MMS(fr));
|
1227
|
|
-
|
1228
|
|
- // Extra Prime
|
1229
|
|
- unscaled_e_move(toolchange_settings.extra_prime, MMM_TO_MMS(toolchange_settings.prime_speed));
|
1230
|
|
-
|
1231
|
|
- // Cutting retraction
|
1232
|
|
- #if TOOLCHANGE_FS_WIPE_RETRACT
|
1233
|
|
- unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed));
|
1234
|
|
- #endif
|
1235
|
|
-
|
1236
|
|
- // Cool down with fan
|
1237
|
|
- #if HAS_FAN && TOOLCHANGE_FS_FAN >= 0
|
1238
|
|
- thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed;
|
1239
|
|
- gcode.dwell(SEC_TO_MS(toolchange_settings.fan_time));
|
1240
|
|
- thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = 0;
|
1241
|
|
- #endif
|
1242
|
|
- }
|
|
1319
|
+ if (should_swap && !too_cold(active_extruder))
|
|
1320
|
+ extruder_prime(); // Prime selected Extruder
|
1243
|
1321
|
#endif
|
1244
|
1322
|
|
1245
|
1323
|
// Prevent a move outside physical bounds
|
|
@@ -1280,11 +1358,8 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
|
1280
|
1358
|
else DEBUG_ECHOLNPGM("Move back skipped");
|
1281
|
1359
|
|
1282
|
1360
|
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP)
|
1283
|
|
- if (should_swap && !too_cold) {
|
1284
|
|
- // Cutting recover
|
1285
|
|
- unscaled_e_move(toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT, MMM_TO_MMS(toolchange_settings.unretract_speed));
|
1286
|
|
- current_position.e = 0;
|
1287
|
|
- sync_plan_position_e(); // New extruder primed and set to 0
|
|
1361
|
+ if (should_swap && !too_cold(active_extruder)) {
|
|
1362
|
+ extruder_cutting_recover(0); // New extruder primed and set to 0
|
1288
|
1363
|
|
1289
|
1364
|
// Restart Fan
|
1290
|
1365
|
#if HAS_FAN && TOOLCHANGE_FS_FAN >= 0
|
|
@@ -1342,7 +1417,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
|
1342
|
1417
|
#endif
|
1343
|
1418
|
}
|
1344
|
1419
|
|
1345
|
|
- SERIAL_ECHO_MSG(STR_ACTIVE_EXTRUDER, active_extruder);
|
|
1420
|
+ SERIAL_ECHOLNPGM(STR_ACTIVE_EXTRUDER, active_extruder);
|
1346
|
1421
|
|
1347
|
1422
|
#endif // HAS_MULTI_EXTRUDER
|
1348
|
1423
|
}
|