/** * Perform a tool-change, which may result in moving the * previous tool out of the way and the new tool into place. */ void tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool no_move/*=false*/) { #if ENABLED(MIXING_EXTRUDER) UNUSED(fr_mm_s); UNUSED(no_move); if (tmp_extruder >= MIXING_VIRTUAL_TOOLS) return invalid_extruder_error(tmp_extruder); #if MIXING_VIRTUAL_TOOLS > 1 // T0-Tnnn: Switch virtual tool by changing the index to the mix mixer.T(tmp_extruder); #endif #elif ENABLED(PRUSA_MMU2) UNUSED(fr_mm_s); UNUSED(no_move); mmu2.toolChange(tmp_extruder); #elif EXTRUDERS < 2 UNUSED(fr_mm_s); UNUSED(no_move); if (tmp_extruder) invalid_extruder_error(tmp_extruder); return; #else // EXTRUDERS > 1 planner.synchronize(); #if ENABLED(DUAL_X_CARRIAGE) // Only T0 allowed if the Printer is in DXC_DUPLICATION_MODE or DXC_MIRRORED_MODE if (tmp_extruder != 0 && dxc_is_duplicating()) return invalid_extruder_error(tmp_extruder); #endif #if HAS_LEVELING // Set current position to the physical position const bool leveling_was_active = planner.leveling_active; set_bed_leveling_enabled(false); #endif if (tmp_extruder >= EXTRUDERS) return invalid_extruder_error(tmp_extruder); if (!no_move && !all_axes_homed()) { no_move = true; if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("No move on toolchange"); } #if HAS_LCD_MENU ui.return_to_status(); #endif #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) const bool should_swap = !no_move && toolchange_settings.swap_length; #if ENABLED(PREVENT_COLD_EXTRUSION) const bool too_cold = !DEBUGGING(DRYRUN) && (thermalManager.targetTooColdToExtrude(active_extruder) || thermalManager.targetTooColdToExtrude(tmp_extruder)); #else constexpr bool too_cold = false; #endif if (should_swap) { if (too_cold) { SERIAL_ECHO_MSG(MSG_ERR_HOTEND_TOO_COLD); #if ENABLED(SINGLENOZZLE) active_extruder = tmp_extruder; return; #endif } else { #if ENABLED(ADVANCED_PAUSE_FEATURE) do_pause_e_move(-toolchange_settings.swap_length, MMM_TO_MMS(toolchange_settings.retract_speed)); #else current_position[E_AXIS] -= toolchange_settings.swap_length / planner.e_factor[active_extruder]; planner.buffer_line(current_position, MMM_TO_MMS(toolchange_settings.retract_speed), active_extruder); #endif } } #endif // TOOLCHANGE_FILAMENT_SWAP if (tmp_extruder != active_extruder) { #if SWITCHING_NOZZLE_TWO_SERVOS raise_nozzle(active_extruder); #endif const float old_feedrate_mm_s = fr_mm_s > 0.0 ? fr_mm_s : feedrate_mm_s; feedrate_mm_s = fr_mm_s > 0.0 ? fr_mm_s : XY_PROBE_FEEDRATE_MM_S; #if HAS_SOFTWARE_ENDSTOPS && ENABLED(DUAL_X_CARRIAGE) update_software_endstops(X_AXIS, active_extruder, tmp_extruder); #endif set_destination_from_current(); if (!no_move) { #if DISABLED(SWITCHING_NOZZLE) // Do a small lift to avoid the workpiece in the move back (below) #if ENABLED(TOOLCHANGE_PARK) current_position[X_AXIS] = toolchange_settings.change_point.x; current_position[Y_AXIS] = toolchange_settings.change_point.y; #endif current_position[Z_AXIS] += toolchange_settings.z_raise; #if HAS_SOFTWARE_ENDSTOPS NOMORE(current_position[Z_AXIS], soft_endstop[Z_AXIS].max); #endif planner.buffer_line(current_position, feedrate_mm_s, active_extruder); #endif planner.synchronize(); } #if HAS_HOTEND_OFFSET #if ENABLED(DUAL_X_CARRIAGE) constexpr float xdiff = 0; #else const float xdiff = hotend_offset[X_AXIS][tmp_extruder] - hotend_offset[X_AXIS][active_extruder]; #endif const float ydiff = hotend_offset[Y_AXIS][tmp_extruder] - hotend_offset[Y_AXIS][active_extruder], zdiff = hotend_offset[Z_AXIS][tmp_extruder] - hotend_offset[Z_AXIS][active_extruder]; #else constexpr float xdiff = 0, ydiff = 0, zdiff = 0; #endif #if ENABLED(DUAL_X_CARRIAGE) dualx_tool_change(tmp_extruder, no_move); #elif ENABLED(PARKING_EXTRUDER) // Dual Parking extruder parking_extruder_tool_change(tmp_extruder, no_move); #elif ENABLED(MAGNETIC_PARKING_EXTRUDER) // Magnetic Parking extruder magnetic_parking_extruder_tool_change(tmp_extruder); #elif ENABLED(SWITCHING_TOOLHEAD) // Switching Toolhead switching_toolhead_tool_change(tmp_extruder, fr_mm_s, no_move); #elif ENABLED(SWITCHING_NOZZLE) && !SWITCHING_NOZZLE_TWO_SERVOS // Raise by a configured distance to avoid workpiece, except with // SWITCHING_NOZZLE_TWO_SERVOS, as both nozzles will lift instead. current_position[Z_AXIS] += MAX(-zdiff, 0.0) + toolchange_settings.z_raise; #if HAS_SOFTWARE_ENDSTOPS NOMORE(current_position[Z_AXIS], soft_endstop[Z_AXIS].max); #endif if (!no_move) fast_line_to_current(Z_AXIS); move_nozzle_servo(tmp_extruder); #endif if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("Offset Tool XY by { ", xdiff, ", ", ydiff, ", ", zdiff, " }"); // The newly-selected extruder XY is actually at... current_position[X_AXIS] += xdiff; current_position[Y_AXIS] += ydiff; current_position[Z_AXIS] += zdiff; // Set the new active extruder if not already done in tool specific function above active_extruder = tmp_extruder; // Tell the planner the new "current position" sync_plan_position(); #if ENABLED(DELTA) //LOOP_XYZ(i) update_software_endstops(i); // or modify the constrain function const bool safe_to_move = current_position[Z_AXIS] < delta_clip_start_height - 1; #else constexpr bool safe_to_move = true; #endif // Return to position and lower again if (safe_to_move && !no_move && IsRunning()) { if (DEBUGGING(LEVELING)) DEBUG_POS("Move back", destination); #if ENABLED(SINGLENOZZLE) #if FAN_COUNT > 0 singlenozzle_fan_speed[active_extruder] = thermalManager.fan_speed[0]; thermalManager.fan_speed[0] = singlenozzle_fan_speed[tmp_extruder]; #endif singlenozzle_temp[active_extruder] = thermalManager.temp_hotend[0].target; if (singlenozzle_temp[tmp_extruder] && singlenozzle_temp[tmp_extruder] != singlenozzle_temp[active_extruder]) { thermalManager.setTargetHotend(singlenozzle_temp[tmp_extruder], 0); #if EITHER(ULTRA_LCD, EXTENSIBLE_UI) thermalManager.set_heating_message(0); #endif (void)thermalManager.wait_for_hotend(0, false); // Wait for heating or cooling } active_extruder = tmp_extruder; #endif #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) if (should_swap && !too_cold) { #if ENABLED(ADVANCED_PAUSE_FEATURE) do_pause_e_move(toolchange_settings.swap_length + TOOLCHANGE_FIL_EXTRA_PRIME, toolchange_settings.prime_speed); #else current_position[E_AXIS] += (toolchange_settings.swap_length + TOOLCHANGE_FIL_EXTRA_PRIME) / planner.e_factor[tmp_extruder]; planner.buffer_line(current_position, toolchange_settings.prime_speed, tmp_extruder); #endif planner.synchronize(); #if TOOLCHANGE_FIL_EXTRA_PRIME planner.set_e_position_mm((destination[E_AXIS] = current_position[E_AXIS] = current_position[E_AXIS] - (TOOLCHANGE_FIL_EXTRA_PRIME))); #endif } #endif // Prevent a move outside physical bounds apply_motion_limits(destination); // Move back to the original (or tweaked) position do_blocking_move_to(destination); #if ENABLED(DUAL_X_CARRIAGE) active_extruder_parked = false; #endif feedrate_mm_s = old_feedrate_mm_s; } #if ENABLED(SWITCHING_NOZZLE) else { // Move back down. (Including when the new tool is higher.) do_blocking_move_to_z(destination[Z_AXIS], planner.settings.max_feedrate_mm_s[Z_AXIS]); } #endif #if ENABLED(PRUSA_MMU2) mmu2.toolChange(tmp_extruder); #endif #if SWITCHING_NOZZLE_TWO_SERVOS lower_nozzle(active_extruder); #endif #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) && ADVANCED_PAUSE_RESUME_PRIME != 0 if (should_swap && !too_cold) { const float resume_eaxis = current_position[E_AXIS]; #if ENABLED(ADVANCED_PAUSE_FEATURE) do_pause_e_move(toolchange_settings.swap_length, toolchange_settings.prime_speed); #else current_position[E_AXIS] += (ADVANCED_PAUSE_RESUME_PRIME) / planner.e_factor[active_extruder]; planner.buffer_line(current_position, ADVANCED_PAUSE_PURGE_FEEDRATE, active_extruder); #endif planner.synchronize(); planner.set_e_position_mm((destination[E_AXIS] = current_position[E_AXIS] = resume_eaxis)); } #endif } // (tmp_extruder != active_extruder) planner.synchronize(); #if ENABLED(EXT_SOLENOID) && DISABLED(PARKING_EXTRUDER) disable_all_solenoids(); enable_solenoid_on_active_extruder(); #endif #if ENABLED(MK2_MULTIPLEXER) if (tmp_extruder >= E_STEPPERS) return invalid_extruder_error(tmp_extruder); select_multiplexed_stepper(tmp_extruder); #endif #if DO_SWITCH_EXTRUDER planner.synchronize(); move_extruder_servo(active_extruder); #endif #if HAS_FANMUX fanmux_switch(active_extruder); #endif #if HAS_LEVELING // Restore leveling to re-establish the logical position set_bed_leveling_enabled(leveling_was_active); #endif SERIAL_ECHO_START(); SERIAL_ECHOLNPAIR(MSG_ACTIVE_EXTRUDER, int(active_extruder)); #endif // EXTRUDERS > 1 }
void run_stow_moves_script() { #if defined(Z_PROBE_ALLEN_KEY_STOW_1_X) || defined(Z_PROBE_ALLEN_KEY_STOW_1_Y) || defined(Z_PROBE_ALLEN_KEY_STOW_1_Z) #ifndef Z_PROBE_ALLEN_KEY_STOW_1_X #define Z_PROBE_ALLEN_KEY_STOW_1_X current_position[X_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_1_Y #define Z_PROBE_ALLEN_KEY_STOW_1_Y current_position[Y_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_1_Z #define Z_PROBE_ALLEN_KEY_STOW_1_Z current_position[Z_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_1_FEEDRATE #define Z_PROBE_ALLEN_KEY_STOW_1_FEEDRATE 0.0 #endif const float stow_1[] = { Z_PROBE_ALLEN_KEY_STOW_1_X, Z_PROBE_ALLEN_KEY_STOW_1_Y, Z_PROBE_ALLEN_KEY_STOW_1_Z }; do_blocking_move_to(stow_1, MMM_TO_MMS(Z_PROBE_ALLEN_KEY_STOW_1_FEEDRATE)); #endif #if defined(Z_PROBE_ALLEN_KEY_STOW_2_X) || defined(Z_PROBE_ALLEN_KEY_STOW_2_Y) || defined(Z_PROBE_ALLEN_KEY_STOW_2_Z) #ifndef Z_PROBE_ALLEN_KEY_STOW_2_X #define Z_PROBE_ALLEN_KEY_STOW_2_X current_position[X_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_2_Y #define Z_PROBE_ALLEN_KEY_STOW_2_Y current_position[Y_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_2_Z #define Z_PROBE_ALLEN_KEY_STOW_2_Z current_position[Z_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_2_FEEDRATE #define Z_PROBE_ALLEN_KEY_STOW_2_FEEDRATE 0.0 #endif const float stow_2[] = { Z_PROBE_ALLEN_KEY_STOW_2_X, Z_PROBE_ALLEN_KEY_STOW_2_Y, Z_PROBE_ALLEN_KEY_STOW_2_Z }; do_blocking_move_to(stow_2, MMM_TO_MMS(Z_PROBE_ALLEN_KEY_STOW_2_FEEDRATE)); #endif #if defined(Z_PROBE_ALLEN_KEY_STOW_3_X) || defined(Z_PROBE_ALLEN_KEY_STOW_3_Y) || defined(Z_PROBE_ALLEN_KEY_STOW_3_Z) #ifndef Z_PROBE_ALLEN_KEY_STOW_3_X #define Z_PROBE_ALLEN_KEY_STOW_3_X current_position[X_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_3_Y #define Z_PROBE_ALLEN_KEY_STOW_3_Y current_position[Y_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_3_Z #define Z_PROBE_ALLEN_KEY_STOW_3_Z current_position[Z_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_3_FEEDRATE #define Z_PROBE_ALLEN_KEY_STOW_3_FEEDRATE 0.0 #endif const float stow_3[] = { Z_PROBE_ALLEN_KEY_STOW_3_X, Z_PROBE_ALLEN_KEY_STOW_3_Y, Z_PROBE_ALLEN_KEY_STOW_3_Z }; do_blocking_move_to(stow_3, MMM_TO_MMS(Z_PROBE_ALLEN_KEY_STOW_3_FEEDRATE)); #endif #if defined(Z_PROBE_ALLEN_KEY_STOW_4_X) || defined(Z_PROBE_ALLEN_KEY_STOW_4_Y) || defined(Z_PROBE_ALLEN_KEY_STOW_4_Z) #ifndef Z_PROBE_ALLEN_KEY_STOW_4_X #define Z_PROBE_ALLEN_KEY_STOW_4_X current_position[X_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_4_Y #define Z_PROBE_ALLEN_KEY_STOW_4_Y current_position[Y_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_4_Z #define Z_PROBE_ALLEN_KEY_STOW_4_Z current_position[Z_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_4_FEEDRATE #define Z_PROBE_ALLEN_KEY_STOW_4_FEEDRATE 0.0 #endif const float stow_4[] = { Z_PROBE_ALLEN_KEY_STOW_4_X, Z_PROBE_ALLEN_KEY_STOW_4_Y, Z_PROBE_ALLEN_KEY_STOW_4_Z }; do_blocking_move_to(stow_4, MMM_TO_MMS(Z_PROBE_ALLEN_KEY_STOW_4_FEEDRATE)); #endif #if defined(Z_PROBE_ALLEN_KEY_STOW_5_X) || defined(Z_PROBE_ALLEN_KEY_STOW_5_Y) || defined(Z_PROBE_ALLEN_KEY_STOW_5_Z) #ifndef Z_PROBE_ALLEN_KEY_STOW_5_X #define Z_PROBE_ALLEN_KEY_STOW_5_X current_position[X_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_5_Y #define Z_PROBE_ALLEN_KEY_STOW_5_Y current_position[Y_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_5_Z #define Z_PROBE_ALLEN_KEY_STOW_5_Z current_position[Z_AXIS] #endif #ifndef Z_PROBE_ALLEN_KEY_STOW_5_FEEDRATE #define Z_PROBE_ALLEN_KEY_STOW_5_FEEDRATE 0.0 #endif const float stow_5[] = { Z_PROBE_ALLEN_KEY_STOW_5_X, Z_PROBE_ALLEN_KEY_STOW_5_Y, Z_PROBE_ALLEN_KEY_STOW_5_Z }; do_blocking_move_to(stow_5, MMM_TO_MMS(Z_PROBE_ALLEN_KEY_STOW_5_FEEDRATE)); #endif }
// returns false for ok and true for failure bool set_probe_deployed(const bool deploy) { // Can be extended to servo probes, if needed. #if ENABLED(PROBE_IS_TRIGGERED_WHEN_STOWED_TEST) #if ENABLED(Z_MIN_PROBE_ENDSTOP) #define _TRIGGERED_WHEN_STOWED_TEST (READ(Z_MIN_PROBE_PIN) != Z_MIN_PROBE_ENDSTOP_INVERTING) #else #define _TRIGGERED_WHEN_STOWED_TEST (READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING) #endif #endif #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) { DEBUG_POS("set_probe_deployed", current_position); SERIAL_ECHOLNPAIR("deploy: ", deploy); } #endif if (endstops.z_probe_enabled == deploy) return false; // Make room for probe do_probe_raise(_Z_CLEARANCE_DEPLOY_PROBE); #if ENABLED(Z_PROBE_SLED) || ENABLED(Z_PROBE_ALLEN_KEY) #if ENABLED(Z_PROBE_SLED) #define _AUE_ARGS true, false, false #else #define _AUE_ARGS #endif if (axis_unhomed_error(_AUE_ARGS)) { SERIAL_ERROR_START(); SERIAL_ERRORLNPGM(MSG_STOP_UNHOMED); stop(); return true; } #endif const float oldXpos = current_position[X_AXIS], oldYpos = current_position[Y_AXIS]; #ifdef _TRIGGERED_WHEN_STOWED_TEST // If endstop is already false, the Z probe is deployed if (_TRIGGERED_WHEN_STOWED_TEST == deploy) { // closed after the probe specific actions. // Would a goto be less ugly? //while (!_TRIGGERED_WHEN_STOWED_TEST) idle(); // would offer the opportunity // for a triggered when stowed manual probe. if (!deploy) endstops.enable_z_probe(false); // Switch off triggered when stowed probes early // otherwise an Allen-Key probe can't be stowed. #endif #if ENABLED(SOLENOID_PROBE) #if HAS_SOLENOID_1 WRITE(SOL1_PIN, deploy); #endif #elif ENABLED(Z_PROBE_SLED) dock_sled(!deploy); #elif HAS_Z_SERVO_ENDSTOP && DISABLED(BLTOUCH) MOVE_SERVO(Z_ENDSTOP_SERVO_NR, z_servo_angle[deploy ? 0 : 1]); #elif ENABLED(Z_PROBE_ALLEN_KEY) deploy ? run_deploy_moves_script() : run_stow_moves_script(); #endif #ifdef _TRIGGERED_WHEN_STOWED_TEST } // _TRIGGERED_WHEN_STOWED_TEST == deploy if (_TRIGGERED_WHEN_STOWED_TEST == deploy) { // State hasn't changed? if (IsRunning()) { SERIAL_ERROR_START(); SERIAL_ERRORLNPGM("Z-Probe failed"); LCD_ALERTMESSAGEPGM("Err: ZPROBE"); } stop(); return true; } // _TRIGGERED_WHEN_STOWED_TEST == deploy #endif do_blocking_move_to(oldXpos, oldYpos, current_position[Z_AXIS]); // return to position before deploy endstops.enable_z_probe(deploy); return false; }
FORCE_INLINE void do_blocking_move_to(const float logical[XYZ], const float &fr_mm_s) { do_blocking_move_to(logical[X_AXIS], logical[Y_AXIS], logical[Z_AXIS], fr_mm_s); }