|
|
@ -32,6 +32,7 @@ |
|
|
|
#include "../MarlinCore.h" |
|
|
|
|
|
|
|
//#define DEBUG_TOOL_CHANGE
|
|
|
|
//#define DEBUG_TOOLCHANGE_FILAMENT_SWAP
|
|
|
|
|
|
|
|
#define DEBUG_OUT ENABLED(DEBUG_TOOL_CHANGE) |
|
|
|
#include "../core/debug_out.h" |
|
|
@ -42,7 +43,6 @@ |
|
|
|
|
|
|
|
#if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) |
|
|
|
migration_settings_t migration = migration_defaults; |
|
|
|
bool enable_first_prime; |
|
|
|
#endif |
|
|
|
|
|
|
|
#if ENABLED(TOOLCHANGE_FS_INIT_BEFORE_SWAP) |
|
|
@ -150,6 +150,7 @@ |
|
|
|
|
|
|
|
#endif // SWITCHING_NOZZLE
|
|
|
|
|
|
|
|
// Move to position routines
|
|
|
|
void _line_to_current(const AxisEnum fr_axis, const float fscale=1) { |
|
|
|
line_to_current_position(planner.settings.max_feedrate_mm_s[fr_axis] * fscale); |
|
|
|
} |
|
|
@ -899,10 +900,135 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0. |
|
|
|
*/ |
|
|
|
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP) |
|
|
|
|
|
|
|
#ifdef DEBUG_TOOLCHANGE_FILAMENT_SWAP |
|
|
|
#define FS_DEBUG(V...) SERIAL_ECHOLNPGM("DEBUG: " V) |
|
|
|
#else |
|
|
|
#define FS_DEBUG(...) NOOP |
|
|
|
#endif |
|
|
|
|
|
|
|
// Define any variables required
|
|
|
|
static Flags<EXTRUDERS> extruder_was_primed; // Extruders primed status
|
|
|
|
|
|
|
|
#if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED) |
|
|
|
bool enable_first_prime; // As set by M217 V
|
|
|
|
#endif |
|
|
|
|
|
|
|
// Cool down with fan
|
|
|
|
inline void filament_swap_cooling() { |
|
|
|
#if HAS_FAN && TOOLCHANGE_FS_FAN >= 0 |
|
|
|
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed; |
|
|
|
gcode.dwell(SEC_TO_MS(toolchange_settings.fan_time)); |
|
|
|
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = 0; |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if too cold to move the specified tool |
|
|
|
* |
|
|
|
* Returns TRUE if too cold to move (also echos message: STR_ERR_HOTEND_TOO_COLD) |
|
|
|
* Returns FALSE if able to move. |
|
|
|
*/ |
|
|
|
bool too_cold(uint8_t toolID){ |
|
|
|
if (TERN0(PREVENT_COLD_EXTRUSION, !DEBUGGING(DRYRUN) && thermalManager.targetTooColdToExtrude(toolID))) { |
|
|
|
SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD); |
|
|
|
return true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* Cutting recovery -- Recover from cutting retraction that occurs at the end of nozzle priming |
|
|
|
* |
|
|
|
* If the active_extruder is up to temp (!too_cold): |
|
|
|
* Extrude filament distance = toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT |
|
|
|
* current_position.e = e; |
|
|
|
* sync_plan_position_e(); |
|
|
|
*/ |
|
|
|
void extruder_cutting_recover(const_float_t e) { |
|
|
|
if (!too_cold(active_extruder)) { |
|
|
|
const float dist = toolchange_settings.extra_resume + (TOOLCHANGE_FS_WIPE_RETRACT); |
|
|
|
FS_DEBUG("Performing Cutting Recover | Distance: ", dist, " | Speed: ", MMM_TO_MMS(toolchange_settings.unretract_speed), "mm/s"); |
|
|
|
unscaled_e_move(dist, MMM_TO_MMS(toolchange_settings.unretract_speed)); |
|
|
|
planner.synchronize(); |
|
|
|
FS_DEBUG("Set position to: ", e); |
|
|
|
current_position.e = e; |
|
|
|
sync_plan_position_e(); // Resume new E Position
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* Prime the currently selected extruder (Filament loading only) |
|
|
|
* |
|
|
|
* If too_cold(toolID) returns TRUE -> returns without moving extruder. |
|
|
|
* Extruders filament = swap_length + extra prime, then performs cutting retraction if enabled. |
|
|
|
* If cooling fan is enabled, calls filament_swap_cooling(); |
|
|
|
*/ |
|
|
|
void extruder_prime() { |
|
|
|
|
|
|
|
if (too_cold(active_extruder)) { |
|
|
|
FS_DEBUG("Priming Aborted - Nozzle Too Cold!"); |
|
|
|
return; // Extruder too cold to prime
|
|
|
|
} |
|
|
|
|
|
|
|
float fr = toolchange_settings.unretract_speed; // Set default speed for unretract
|
|
|
|
|
|
|
|
#if ENABLED(TOOLCHANGE_FS_SLOW_FIRST_PRIME) |
|
|
|
/*
|
|
|
|
* Perform first unretract movement at the slower Prime_Speed to avoid breakage on first prime |
|
|
|
*/ |
|
|
|
static Flags<EXTRUDERS> extruder_did_first_prime; // Extruders first priming status
|
|
|
|
if (!extruder_did_first_prime[active_extruder]) { |
|
|
|
extruder_did_first_prime.set(active_extruder); // Log first prime complete
|
|
|
|
// new nozzle - prime at user-specified speed.
|
|
|
|
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"); |
|
|
|
fr = toolchange_settings.prime_speed; |
|
|
|
unscaled_e_move(0, MMM_TO_MMS(fr)); // Init planner with 0 length move
|
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
//Calculate and perform the priming distance
|
|
|
|
if (toolchange_settings.extra_prime >= 0) { |
|
|
|
// Positive extra_prime value
|
|
|
|
// - Return filament at speed (fr) then extra_prime at prime speed
|
|
|
|
FS_DEBUG("Loading Filament for T", active_extruder, " | Distance: ", toolchange_settings.swap_length, " | Speed: ", MMM_TO_MMS(fr), "mm/s"); |
|
|
|
unscaled_e_move(toolchange_settings.swap_length, MMM_TO_MMS(fr)); // Prime (Unretract) filament by extruding equal to Swap Length (Unretract)
|
|
|
|
|
|
|
|
if (toolchange_settings.extra_prime > 0) { |
|
|
|
FS_DEBUG("Performing Extra Priming for T", active_extruder, " | Distance: ", toolchange_settings.extra_prime, " | Speed: ", MMM_TO_MMS(toolchange_settings.prime_speed), "mm/s"); |
|
|
|
unscaled_e_move(toolchange_settings.extra_prime, MMM_TO_MMS(toolchange_settings.prime_speed)); // Extra Prime Distance
|
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
// Negative extra_prime value
|
|
|
|
// - Unretract distance (swap length) is reduced by the value of extra_prime
|
|
|
|
const float eswap = toolchange_settings.swap_length + toolchange_settings.extra_prime; |
|
|
|
FS_DEBUG("Negative ExtraPrime value - Swap Return Length has been reduced from ", toolchange_settings.swap_length, " to ", eswap); |
|
|
|
FS_DEBUG("Loading Filament for T", active_extruder, " | Distance: ", eswap, " | Speed: ", MMM_TO_MMS(fr), "mm/s"); |
|
|
|
unscaled_e_move(eswap, MMM_TO_MMS(fr)); |
|
|
|
} |
|
|
|
|
|
|
|
extruder_was_primed.set(active_extruder); // Log that this extruder has been primed
|
|
|
|
|
|
|
|
// Cutting retraction
|
|
|
|
#if TOOLCHANGE_FS_WIPE_RETRACT |
|
|
|
FS_DEBUG("Performing Cutting Retraction | Distance: ", -(TOOLCHANGE_FS_WIPE_RETRACT), " | Speed: ", MMM_TO_MMS(toolchange_settings.retract_speed), "mm/s"); |
|
|
|
unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed)); |
|
|
|
#endif |
|
|
|
|
|
|
|
// Cool down with fan
|
|
|
|
filament_swap_cooling(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* Sequence to Prime the currently selected extruder |
|
|
|
* Raise Z, move the ToolChange_Park if enabled, prime the extruder, move back. |
|
|
|
*/ |
|
|
|
void tool_change_prime() { |
|
|
|
if (toolchange_settings.extra_prime > 0 |
|
|
|
&& TERN(PREVENT_COLD_EXTRUSION, !thermalManager.targetTooColdToExtrude(active_extruder), 1) |
|
|
|
) { |
|
|
|
|
|
|
|
FS_DEBUG(">>> tool_change_prime()"); |
|
|
|
|
|
|
|
if (!too_cold(active_extruder)) { |
|
|
|
destination = current_position; // Remember the old position
|
|
|
|
|
|
|
|
const bool ok = TERN1(TOOLCHANGE_PARK, all_axes_homed() && toolchange_settings.enable_park); |
|
|
@ -931,20 +1057,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0. |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
// Prime (All distances are added and slowed down to ensure secure priming in all circumstances)
|
|
|
|
unscaled_e_move(toolchange_settings.swap_length + toolchange_settings.extra_prime, MMM_TO_MMS(toolchange_settings.prime_speed)); |
|
|
|
|
|
|
|
// Cutting retraction
|
|
|
|
#if TOOLCHANGE_FS_WIPE_RETRACT |
|
|
|
unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed)); |
|
|
|
#endif |
|
|
|
|
|
|
|
// Cool down with fan
|
|
|
|
#if HAS_FAN && TOOLCHANGE_FS_FAN >= 0 |
|
|
|
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed; |
|
|
|
gcode.dwell(SEC_TO_MS(toolchange_settings.fan_time)); |
|
|
|
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = 0; |
|
|
|
#endif |
|
|
|
extruder_prime(); |
|
|
|
|
|
|
|
// Move back
|
|
|
|
#if ENABLED(TOOLCHANGE_PARK) |
|
|
@ -958,13 +1071,11 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0. |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
// Cutting recover
|
|
|
|
unscaled_e_move(toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT, MMM_TO_MMS(toolchange_settings.unretract_speed)); |
|
|
|
|
|
|
|
// Resume at the old E position
|
|
|
|
current_position.e = destination.e; |
|
|
|
sync_plan_position_e(); |
|
|
|
extruder_cutting_recover(destination.e); // Cutting recover
|
|
|
|
} |
|
|
|
|
|
|
|
FS_DEBUG("<<< tool_change_prime"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif // TOOLCHANGE_FILAMENT_SWAP
|
|
|
@ -1041,12 +1152,10 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { |
|
|
|
TEMPORARY_BED_LEVELING_STATE(false); |
|
|
|
#endif |
|
|
|
|
|
|
|
// First tool priming. To prime again, reboot the machine.
|
|
|
|
// First tool priming. To prime again, reboot the machine. -- Should only occur for first T0 after powerup!
|
|
|
|
#if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED) |
|
|
|
static bool first_tool_is_primed = false; |
|
|
|
if (new_tool == old_tool && !first_tool_is_primed && enable_first_prime) { |
|
|
|
if (enable_first_prime && old_tool == 0 && new_tool == 0 && !extruder_was_primed[0]) { |
|
|
|
tool_change_prime(); |
|
|
|
first_tool_is_primed = true; |
|
|
|
TERN_(TOOLCHANGE_FS_INIT_BEFORE_SWAP, toolchange_extruder_ready.set(old_tool)); // Primed and initialized
|
|
|
|
} |
|
|
|
#endif |
|
|
@ -1072,20 +1181,17 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { |
|
|
|
|
|
|
|
// Unload / Retract
|
|
|
|
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP) |
|
|
|
const bool should_swap = can_move_away && toolchange_settings.swap_length, |
|
|
|
too_cold = TERN0(PREVENT_COLD_EXTRUSION, |
|
|
|
!DEBUGGING(DRYRUN) && (thermalManager.targetTooColdToExtrude(old_tool) || thermalManager.targetTooColdToExtrude(new_tool)) |
|
|
|
); |
|
|
|
const bool should_swap = can_move_away && toolchange_settings.swap_length; |
|
|
|
if (should_swap) { |
|
|
|
if (too_cold) { |
|
|
|
SERIAL_ECHO_MSG(STR_ERR_HOTEND_TOO_COLD); |
|
|
|
if (too_cold(old_tool)) { |
|
|
|
// If SingleNozzle setup is too cold, unable to perform tool_change.
|
|
|
|
if (ENABLED(SINGLENOZZLE)) { active_extruder = new_tool; return; } |
|
|
|
} |
|
|
|
else { |
|
|
|
// For first new tool, change without unloading the old. 'Just prime/init the new'
|
|
|
|
if (TERN1(TOOLCHANGE_FS_PRIME_FIRST_USED, first_tool_is_primed)) |
|
|
|
unscaled_e_move(-toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.retract_speed)); |
|
|
|
TERN_(TOOLCHANGE_FS_PRIME_FIRST_USED, first_tool_is_primed = true); // The first new tool will be primed by toolchanging
|
|
|
|
else if (extruder_was_primed[old_tool]) { |
|
|
|
// Retract the old extruder if it was previously primed
|
|
|
|
// To-Do: Should SingleNozzle always retract?
|
|
|
|
FS_DEBUG("Retracting Filament for T", old_tool, ". | Distance: ", toolchange_settings.swap_length, " | Speed: ", MMM_TO_MMS(toolchange_settings.retract_speed), "mm/s"); |
|
|
|
unscaled_e_move(-toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.retract_speed)); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
@ -1190,36 +1296,8 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { |
|
|
|
#endif |
|
|
|
|
|
|
|
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP) |
|
|
|
if (should_swap && !too_cold) { |
|
|
|
|
|
|
|
float fr = toolchange_settings.unretract_speed; |
|
|
|
|
|
|
|
#if ENABLED(TOOLCHANGE_FS_INIT_BEFORE_SWAP) |
|
|
|
if (!toolchange_extruder_ready[new_tool]) { |
|
|
|
toolchange_extruder_ready.set(new_tool); |
|
|
|
fr = toolchange_settings.prime_speed; // Next move is a prime
|
|
|
|
unscaled_e_move(0, MMM_TO_MMS(fr)); // Init planner with 0 length move
|
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
// Unretract (or Prime)
|
|
|
|
unscaled_e_move(toolchange_settings.swap_length, MMM_TO_MMS(fr)); |
|
|
|
|
|
|
|
// Extra Prime
|
|
|
|
unscaled_e_move(toolchange_settings.extra_prime, MMM_TO_MMS(toolchange_settings.prime_speed)); |
|
|
|
|
|
|
|
// Cutting retraction
|
|
|
|
#if TOOLCHANGE_FS_WIPE_RETRACT |
|
|
|
unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed)); |
|
|
|
#endif |
|
|
|
|
|
|
|
// Cool down with fan
|
|
|
|
#if HAS_FAN && TOOLCHANGE_FS_FAN >= 0 |
|
|
|
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed; |
|
|
|
gcode.dwell(SEC_TO_MS(toolchange_settings.fan_time)); |
|
|
|
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = 0; |
|
|
|
#endif |
|
|
|
} |
|
|
|
if (should_swap && !too_cold(active_extruder)) |
|
|
|
extruder_prime(); // Prime selected Extruder
|
|
|
|
#endif |
|
|
|
|
|
|
|
// Prevent a move outside physical bounds
|
|
|
@ -1260,11 +1338,8 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { |
|
|
|
else DEBUG_ECHOLNPGM("Move back skipped"); |
|
|
|
|
|
|
|
#if ENABLED(TOOLCHANGE_FILAMENT_SWAP) |
|
|
|
if (should_swap && !too_cold) { |
|
|
|
// Cutting recover
|
|
|
|
unscaled_e_move(toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT, MMM_TO_MMS(toolchange_settings.unretract_speed)); |
|
|
|
current_position.e = 0; |
|
|
|
sync_plan_position_e(); // New extruder primed and set to 0
|
|
|
|
if (should_swap && !too_cold(active_extruder)) { |
|
|
|
extruder_cutting_recover(0); // New extruder primed and set to 0
|
|
|
|
|
|
|
|
// Restart Fan
|
|
|
|
#if HAS_FAN && TOOLCHANGE_FS_FAN >= 0 |
|
|
@ -1322,7 +1397,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) { |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
SERIAL_ECHO_MSG(STR_ACTIVE_EXTRUDER, active_extruder); |
|
|
|
SERIAL_ECHOLNPGM(STR_ACTIVE_EXTRUDER, active_extruder); |
|
|
|
|
|
|
|
#endif // HAS_MULTI_EXTRUDER
|
|
|
|
} |
|
|
|