/** * @details Used by probe_pt to do a single Z probe. * Leaves current_position[Z_AXIS] at the height where the probe triggered. * * @param short_move Flag for a shorter probe move towards the bed * @return The raw Z position where the probe was triggered */ static float run_z_probe(const bool short_move=true) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS(">>> run_z_probe", current_position); #endif // Prevent stepper_inactive_time from running out and EXTRUDER_RUNOUT_PREVENT from extruding gcode.refresh_cmd_timeout(); #if ENABLED(PROBE_DOUBLE_TOUCH) // Do a first probe at the fast speed if (do_probe_move(-10, Z_PROBE_SPEED_FAST)) return NAN; #if ENABLED(DEBUG_LEVELING_FEATURE) float first_probe_z = current_position[Z_AXIS]; if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPAIR("1st Probe Z:", first_probe_z); #endif // move up to make clearance for the probe do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES, MMM_TO_MMS(Z_PROBE_SPEED_FAST)); #else // If the nozzle is above the travel height then // move down quickly before doing the slow probe float z = Z_CLEARANCE_DEPLOY_PROBE; if (zprobe_zoffset < 0) z -= zprobe_zoffset; if (z < current_position[Z_AXIS]) { // If we don't make it to the z position (i.e. the probe triggered), move up to make clearance for the probe if (!do_probe_move(z, Z_PROBE_SPEED_FAST)) do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES, MMM_TO_MMS(Z_PROBE_SPEED_FAST)); } #endif // move down slowly to find bed if (do_probe_move(-10 + (short_move ? 0 : -(Z_MAX_LENGTH)), Z_PROBE_SPEED_SLOW)) return NAN; #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS("<<< run_z_probe", current_position); #endif // Debug: compare probe heights #if ENABLED(PROBE_DOUBLE_TOUCH) && ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) { SERIAL_ECHOPAIR("2nd Probe Z:", current_position[Z_AXIS]); SERIAL_ECHOLNPAIR(" Discrepancy:", first_probe_z - current_position[Z_AXIS]); } #endif return RAW_CURRENT_POSITION(Z) + zprobe_zoffset #if ENABLED(DELTA) + home_offset[Z_AXIS] // Account for delta height adjustment #endif ; }
inline void dualx_tool_change(const uint8_t tmp_extruder, bool &no_move) { if (DEBUGGING(LEVELING)) { DEBUG_ECHOPGM("Dual X Carriage Mode "); switch (dual_x_carriage_mode) { case DXC_FULL_CONTROL_MODE: DEBUG_ECHOLNPGM("FULL_CONTROL"); break; case DXC_AUTO_PARK_MODE: DEBUG_ECHOLNPGM("AUTO_PARK"); break; case DXC_DUPLICATION_MODE: DEBUG_ECHOLNPGM("DUPLICATION"); break; case DXC_MIRRORED_MODE: DEBUG_ECHOLNPGM("MIRRORED"); break; } } const float xhome = x_home_pos(active_extruder); if (dual_x_carriage_mode == DXC_AUTO_PARK_MODE && IsRunning() && (delayed_move_time || current_position[X_AXIS] != xhome) && ! no_move ) { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR("MoveX to ", xhome); // Park old head planner.buffer_line(xhome, current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], planner.settings.max_feedrate_mm_s[X_AXIS], active_extruder); planner.synchronize(); } // Apply Y & Z extruder offset (X offset is used as home pos with Dual X) current_position[Y_AXIS] -= hotend_offset[Y_AXIS][active_extruder] - hotend_offset[Y_AXIS][tmp_extruder]; current_position[Z_AXIS] -= hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder]; // Activate the new extruder ahead of calling set_axis_is_at_home! active_extruder = tmp_extruder; // This function resets the max/min values - the current position may be overwritten below. set_axis_is_at_home(X_AXIS); if (DEBUGGING(LEVELING)) DEBUG_POS("New Extruder", current_position); switch (dual_x_carriage_mode) { case DXC_FULL_CONTROL_MODE: // New current position is the position of the activated extruder current_position[X_AXIS] = inactive_extruder_x_pos; // Save the inactive extruder's position (from the old current_position) inactive_extruder_x_pos = destination[X_AXIS]; break; case DXC_AUTO_PARK_MODE: // record current raised toolhead position for use by unpark COPY(raised_parked_position, current_position); active_extruder_parked = true; delayed_move_time = 0; break; default: break; } if (DEBUGGING(LEVELING)) { DEBUG_ECHOLNPAIR("Active extruder parked: ", active_extruder_parked ? "yes" : "no"); DEBUG_POS("New extruder (parked)", current_position); } }
/** * @brief Used by run_z_probe to do a single Z probe move. * * @param z Z destination * @param fr_mm_s Feedrate in mm/s * @return true to indicate an error */ static bool do_probe_move(const float z, const float fr_mm_m) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS(">>> do_probe_move", current_position); #endif // Deploy BLTouch at the start of any probe #if ENABLED(BLTOUCH) if (set_bltouch_deployed(true)) return true; #endif #if QUIET_PROBING probing_pause(true); #endif // Move down until probe triggered do_blocking_move_to_z(z, MMM_TO_MMS(fr_mm_m)); // Check to see if the probe was triggered const bool probe_triggered = TEST(Endstops::endstop_hit_bits, #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN) Z_MIN #else Z_MIN_PROBE #endif ); #if QUIET_PROBING probing_pause(false); #endif // Retract BLTouch immediately after a probe if it was triggered #if ENABLED(BLTOUCH) if (probe_triggered && set_bltouch_deployed(false)) return true; #endif // Clear endstop flags endstops.hit_on_purpose(); // Get Z where the steppers were interrupted set_current_from_steppers_for_axis(Z_AXIS); // Tell the planner where we actually are SYNC_PLAN_POSITION_KINEMATIC(); #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS("<<< do_probe_move", current_position); #endif return !probe_triggered; }
inline void home_z_safely() { // Disallow Z homing if X or Y are unknown if (!TEST(axis_known_position, X_AXIS) || !TEST(axis_known_position, Y_AXIS)) { LCD_MESSAGEPGM(MSG_ERR_Z_HOMING); SERIAL_ECHO_MSG(MSG_ERR_Z_HOMING); return; } #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("Z_SAFE_HOMING >>>"); #endif sync_plan_position(); /** * Move the Z probe (or just the nozzle) to the safe homing point */ destination[X_AXIS] = Z_SAFE_HOMING_X_POINT; destination[Y_AXIS] = Z_SAFE_HOMING_Y_POINT; destination[Z_AXIS] = current_position[Z_AXIS]; // Z is already at the right height #if HOMING_Z_WITH_PROBE destination[X_AXIS] -= X_PROBE_OFFSET_FROM_EXTRUDER; destination[Y_AXIS] -= Y_PROBE_OFFSET_FROM_EXTRUDER; #endif if (position_is_reachable(destination[X_AXIS], destination[Y_AXIS])) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) DEBUG_POS("Z_SAFE_HOMING", destination); #endif // This causes the carriage on Dual X to unpark #if ENABLED(DUAL_X_CARRIAGE) active_extruder_parked = false; #endif #if ENABLED(SENSORLESS_HOMING) safe_delay(500); // Short delay needed to settle #endif do_blocking_move_to_xy(destination[X_AXIS], destination[Y_AXIS]); homeaxis(Z_AXIS); } else { LCD_MESSAGEPGM(MSG_ZPROBE_OUT); SERIAL_ECHO_MSG(MSG_ZPROBE_OUT); } #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< Z_SAFE_HOMING"); #endif }
/** * 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 }
inline void switching_toolhead_tool_change(const uint8_t tmp_extruder, const float fr_mm_s/*=0.0*/, bool no_move/*=false*/) { if (no_move) return; constexpr uint16_t angles[2] = SWITCHING_TOOLHEAD_SERVO_ANGLES; const float toolheadposx[] = SWITCHING_TOOLHEAD_X_POS, placexpos = toolheadposx[active_extruder], grabxpos = toolheadposx[tmp_extruder]; /** * 1. Raise Z to give enough clearance * 2. Move to switch position of current toolhead * 3. Unlock tool and drop it in the dock * 4. Move to the new toolhead * 5. Grab and lock the new toolhead */ // 1. Raise Z to give enough clearance if (DEBUGGING(LEVELING)) DEBUG_POS("Starting Toolhead change", current_position); current_position[Z_AXIS] += toolchange_settings.z_raise; if (DEBUGGING(LEVELING)) DEBUG_POS("(1) Raise Z-Axis", current_position); fast_line_to_current(Z_AXIS); planner.synchronize(); // 2. Move to switch position of current toolhead current_position[X_AXIS] = placexpos; if (DEBUGGING(LEVELING)) { DEBUG_ECHOLNPAIR("(2) Place old tool ", int(active_extruder)); DEBUG_POS("Move X SwitchPos", current_position); } fast_line_to_current(X_AXIS); planner.synchronize(); current_position[Y_AXIS] = SWITCHING_TOOLHEAD_Y_POS - SWITCHING_TOOLHEAD_Y_SECURITY; if (DEBUGGING(LEVELING)) DEBUG_POS("Move Y SwitchPos + Security", current_position); fast_line_to_current(Y_AXIS); planner.synchronize(); // 3. Unlock tool and drop it in the dock if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("(3) Unlock and Place Toolhead"); MOVE_SERVO(SWITCHING_TOOLHEAD_SERVO_NR, angles[1]); safe_delay(500); current_position[Y_AXIS] = SWITCHING_TOOLHEAD_Y_POS; if (DEBUGGING(LEVELING)) DEBUG_POS("Move Y SwitchPos", current_position); planner.buffer_line(current_position,(planner.settings.max_feedrate_mm_s[Y_AXIS] * 0.5), active_extruder); planner.synchronize(); safe_delay(200); current_position[Y_AXIS] -= SWITCHING_TOOLHEAD_Y_CLEAR; if (DEBUGGING(LEVELING)) DEBUG_POS("Move back Y clear", current_position); fast_line_to_current(Y_AXIS); // move away from docked toolhead planner.synchronize(); // 4. Move to the new toolhead if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("(4) Move to new toolhead position"); current_position[X_AXIS] = grabxpos; if (DEBUGGING(LEVELING)) DEBUG_POS("Move to new toolhead X", current_position); fast_line_to_current(X_AXIS); planner.synchronize(); current_position[Y_AXIS] = SWITCHING_TOOLHEAD_Y_POS - SWITCHING_TOOLHEAD_Y_SECURITY; if (DEBUGGING(LEVELING)) DEBUG_POS("Move Y SwitchPos + Security", current_position); fast_line_to_current(Y_AXIS); planner.synchronize(); // 5. Grab and lock the new toolhead if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("(5) Grab and lock new toolhead "); current_position[Y_AXIS] = SWITCHING_TOOLHEAD_Y_POS; if (DEBUGGING(LEVELING)) DEBUG_POS("Move Y SwitchPos", current_position); planner.buffer_line(current_position, planner.settings.max_feedrate_mm_s[Y_AXIS] * 0.5, active_extruder); planner.synchronize(); safe_delay(200); MOVE_SERVO(SWITCHING_TOOLHEAD_SERVO_NR, angles[0]); safe_delay(500); current_position[Y_AXIS] -= SWITCHING_TOOLHEAD_Y_CLEAR; if (DEBUGGING(LEVELING)) DEBUG_POS("Move back Y clear", current_position); fast_line_to_current(Y_AXIS); // move away from docked toolhead planner.synchronize(); if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Toolhead change done."); }
inline void parking_extruder_tool_change(const uint8_t tmp_extruder, bool no_move) { if (!no_move) { constexpr float parkingposx[] = PARKING_EXTRUDER_PARKING_X; #if HAS_HOTEND_OFFSET const float x_offset = hotend_offset[X_AXIS][active_extruder]; #else constexpr float x_offset = 0; #endif const float midpos = (parkingposx[0] + parkingposx[1]) * 0.5 + x_offset, grabpos = parkingposx[tmp_extruder] + (tmp_extruder ? PARKING_EXTRUDER_GRAB_DISTANCE : -(PARKING_EXTRUDER_GRAB_DISTANCE)) + x_offset; /** * 1. Raise Z-Axis to give enough clearance * 2. Move to park position of old extruder * 3. Disengage magnetic field, wait for delay * 4. Move near new extruder * 5. Engage magnetic field for new extruder * 6. Move to parking incl. offset of new extruder * 7. Lower Z-Axis */ // STEP 1 if (DEBUGGING(LEVELING)) DEBUG_POS("Start Autopark", current_position); current_position[Z_AXIS] += toolchange_settings.z_raise; if (DEBUGGING(LEVELING)) DEBUG_POS("(1) Raise Z-Axis", current_position); fast_line_to_current(Z_AXIS); planner.synchronize(); // STEP 2 current_position[X_AXIS] = parkingposx[active_extruder] + x_offset; if (DEBUGGING(LEVELING)) { DEBUG_ECHOLNPAIR("(2) Park extruder ", int(active_extruder)); DEBUG_POS("Moving ParkPos", current_position); } fast_line_to_current(X_AXIS); planner.synchronize(); // STEP 3 if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("(3) Disengage magnet "); pe_deactivate_solenoid(active_extruder); // STEP 4 if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("(4) Move to position near new extruder"); current_position[X_AXIS] += active_extruder ? -10 : 10; // move 10mm away from parked extruder if (DEBUGGING(LEVELING)) DEBUG_POS("Move away from parked extruder", current_position); fast_line_to_current(X_AXIS); planner.synchronize(); // STEP 5 if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("(5) Engage magnetic field"); #if ENABLED(PARKING_EXTRUDER_SOLENOIDS_INVERT) pe_activate_solenoid(active_extruder); //just save power for inverted magnets #endif pe_activate_solenoid(tmp_extruder); // STEP 6 current_position[X_AXIS] = grabpos + (tmp_extruder ? -10 : 10); fast_line_to_current(X_AXIS); current_position[X_AXIS] = grabpos; if (DEBUGGING(LEVELING)) DEBUG_POS("(6) Unpark extruder", current_position); planner.buffer_line(current_position, planner.settings.max_feedrate_mm_s[X_AXIS] * 0.5, active_extruder); planner.synchronize(); // STEP 7 current_position[X_AXIS] = midpos #if HAS_HOTEND_OFFSET - hotend_offset[X_AXIS][tmp_extruder] #endif ; if (DEBUGGING(LEVELING)) DEBUG_POS("(7) Move midway between hotends", current_position); fast_line_to_current(X_AXIS); planner.synchronize(); DEBUG_ECHOLNPGM("Autopark done."); } else { // nomove == true // Only engage magnetic field for new extruder pe_activate_solenoid(tmp_extruder); #if ENABLED(PARKING_EXTRUDER_SOLENOIDS_INVERT) pe_activate_solenoid(active_extruder); // Just save power for inverted magnets #endif } #if HAS_HOTEND_OFFSET current_position[Z_AXIS] += hotend_offset[Z_AXIS][active_extruder] - hotend_offset[Z_AXIS][tmp_extruder]; #endif if (DEBUGGING(LEVELING)) DEBUG_POS("Applying Z-offset", current_position); }
inline void magnetic_parking_extruder_tool_change(const uint8_t tmp_extruder) { const float oldx = current_position[X_AXIS], grabpos = mpe_settings.parking_xpos[tmp_extruder] + (tmp_extruder ? mpe_settings.grab_distance : -mpe_settings.grab_distance), offsetcompensation = #if HAS_HOTEND_OFFSET hotend_offset[X_AXIS][active_extruder] * mpe_settings.compensation_factor #else 0 #endif ; if (axis_unhomed_error(true, false, false)) return; /** * Z Lift and Nozzle Offset shift ar defined in caller method to work equal with any Multi Hotend realization * * Steps: * 1. Move high speed to park position of new extruder * 2. Move to couple position of new extruder (this also discouple the old extruder) * 3. Move to park position of new extruder * 4. Move high speed to approach park position of old extruder * 5. Move to park position of old extruder * 6. Move to starting position */ // STEP 1 current_position[X_AXIS] = mpe_settings.parking_xpos[tmp_extruder] + offsetcompensation; if (DEBUGGING(LEVELING)) { DEBUG_ECHOPAIR("(1) Move extruder ", int(tmp_extruder)); DEBUG_POS(" to new extruder ParkPos", current_position); } planner.buffer_line(current_position, mpe_settings.fast_feedrate, tmp_extruder); planner.synchronize(); // STEP 2 current_position[X_AXIS] = grabpos + offsetcompensation; if (DEBUGGING(LEVELING)) { DEBUG_ECHOPAIR("(2) Couple extruder ", int(tmp_extruder)); DEBUG_POS(" to new extruder GrabPos", current_position); } planner.buffer_line(current_position, mpe_settings.slow_feedrate, tmp_extruder); planner.synchronize(); // Delay before moving tool, to allow magnetic coupling gcode.dwell(150); // STEP 3 current_position[X_AXIS] = mpe_settings.parking_xpos[tmp_extruder] + offsetcompensation; if (DEBUGGING(LEVELING)) { DEBUG_ECHOPAIR("(3) Move extruder ", int(tmp_extruder)); DEBUG_POS(" back to new extruder ParkPos", current_position); } planner.buffer_line(current_position, mpe_settings.slow_feedrate, tmp_extruder); planner.synchronize(); // STEP 4 current_position[X_AXIS] = mpe_settings.parking_xpos[active_extruder] + (active_extruder == 0 ? MPE_TRAVEL_DISTANCE : -MPE_TRAVEL_DISTANCE) + offsetcompensation; if (DEBUGGING(LEVELING)) { DEBUG_ECHOPAIR("(4) Move extruder ", int(tmp_extruder)); DEBUG_POS(" close to old extruder ParkPos", current_position); } planner.buffer_line(current_position, mpe_settings.fast_feedrate, tmp_extruder); planner.synchronize(); // STEP 5 current_position[X_AXIS] = mpe_settings.parking_xpos[active_extruder] + offsetcompensation; if (DEBUGGING(LEVELING)) { DEBUG_ECHOPAIR("(5) Park extruder ", int(tmp_extruder)); DEBUG_POS(" at old extruder ParkPos", current_position); } planner.buffer_line(current_position, mpe_settings.slow_feedrate, tmp_extruder); planner.synchronize(); // STEP 6 current_position[X_AXIS] = oldx; if (DEBUGGING(LEVELING)) { DEBUG_ECHOPAIR("(6) Move extruder ", int(tmp_extruder)); DEBUG_POS(" to starting position", current_position); } planner.buffer_line(current_position, mpe_settings.fast_feedrate, tmp_extruder); planner.synchronize(); if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Autopark done."); }
/** * - Move to the given XY * - Deploy the probe, if not already deployed * - Probe the bed, get the Z position * - Depending on the 'stow' flag * - Stow the probe, or * - Raise to the BETWEEN height * - Return the probed Z position */ float probe_pt(const float &lx, const float &ly, const bool stow, const uint8_t verbose_level, const bool printable/*=true*/) { #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) { SERIAL_ECHOPAIR(">>> probe_pt(", lx); SERIAL_ECHOPAIR(", ", ly); SERIAL_ECHOPAIR(", ", stow ? "" : "no "); SERIAL_ECHOLNPGM("stow)"); DEBUG_POS("", current_position); } #endif const float nx = lx - (X_PROBE_OFFSET_FROM_EXTRUDER), ny = ly - (Y_PROBE_OFFSET_FROM_EXTRUDER); if (printable ? !position_is_reachable_xy(nx, ny) : !position_is_reachable_by_probe_xy(lx, ly) ) return NAN; const float old_feedrate_mm_s = feedrate_mm_s; #if ENABLED(DELTA) if (current_position[Z_AXIS] > delta_clip_start_height) do_blocking_move_to_z(delta_clip_start_height); #endif #if HAS_SOFTWARE_ENDSTOPS // Store the status of the soft endstops and disable if we're probing a non-printable location static bool enable_soft_endstops = soft_endstops_enabled; if (!printable) soft_endstops_enabled = false; #endif feedrate_mm_s = XY_PROBE_FEEDRATE_MM_S; // Move the probe to the given XY do_blocking_move_to_xy(nx, ny); float measured_z = NAN; if (!DEPLOY_PROBE()) { measured_z = run_z_probe(printable); if (!stow) do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES, MMM_TO_MMS(Z_PROBE_SPEED_FAST)); else if (STOW_PROBE()) measured_z = NAN; } #if HAS_SOFTWARE_ENDSTOPS // Restore the soft endstop status soft_endstops_enabled = enable_soft_endstops; #endif if (verbose_level > 2) { SERIAL_PROTOCOLPGM("Bed X: "); SERIAL_PROTOCOL_F(lx, 3); SERIAL_PROTOCOLPGM(" Y: "); SERIAL_PROTOCOL_F(ly, 3); SERIAL_PROTOCOLPGM(" Z: "); SERIAL_PROTOCOL_F(measured_z, 3); SERIAL_EOL(); } #if ENABLED(DEBUG_LEVELING_FEATURE) if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("<<< probe_pt"); #endif feedrate_mm_s = old_feedrate_mm_s; if (isnan(measured_z)) { LCD_MESSAGEPGM(MSG_ERR_PROBING_FAILED); SERIAL_ERROR_START(); SERIAL_ERRORLNPGM(MSG_ERR_PROBING_FAILED); } return measured_z; }
// 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; }