aboutsummaryrefslogtreecommitdiff
path: root/Marlin/src/gcode/feature/pause
diff options
context:
space:
mode:
Diffstat (limited to 'Marlin/src/gcode/feature/pause')
-rw-r--r--Marlin/src/gcode/feature/pause/G27.cpp41
-rw-r--r--Marlin/src/gcode/feature/pause/G60.cpp58
-rw-r--r--Marlin/src/gcode/feature/pause/G61.cpp73
-rw-r--r--Marlin/src/gcode/feature/pause/M125.cpp90
-rw-r--r--Marlin/src/gcode/feature/pause/M600.cpp172
-rw-r--r--Marlin/src/gcode/feature/pause/M603.cpp65
-rw-r--r--Marlin/src/gcode/feature/pause/M701_M702.cpp235
7 files changed, 734 insertions, 0 deletions
diff --git a/Marlin/src/gcode/feature/pause/G27.cpp b/Marlin/src/gcode/feature/pause/G27.cpp
new file mode 100644
index 0000000..3ce618d
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/G27.cpp
@@ -0,0 +1,41 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(NOZZLE_PARK_FEATURE)
+
+#include "../../gcode.h"
+#include "../../../libs/nozzle.h"
+#include "../../../module/motion.h"
+
+/**
+ * G27: Park the nozzle
+ */
+void GcodeSuite::G27() {
+ // Don't allow nozzle parking without homing first
+ if (homing_needed_error()) return;
+ nozzle.park(parser.ushortval('P'));
+}
+
+#endif // NOZZLE_PARK_FEATURE
diff --git a/Marlin/src/gcode/feature/pause/G60.cpp b/Marlin/src/gcode/feature/pause/G60.cpp
new file mode 100644
index 0000000..6f695b9
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/G60.cpp
@@ -0,0 +1,58 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if SAVED_POSITIONS
+
+#include "../../gcode.h"
+#include "../../../module/motion.h"
+
+#define DEBUG_OUT ENABLED(SAVED_POSITIONS_DEBUG)
+#include "../../../core/debug_out.h"
+
+/**
+ * G60: Save current position
+ *
+ * S<slot> - Memory slot # (0-based) to save into (default 0)
+ */
+void GcodeSuite::G60() {
+ const uint8_t slot = parser.byteval('S');
+
+ if (slot >= SAVED_POSITIONS) {
+ SERIAL_ERROR_MSG(STR_INVALID_POS_SLOT STRINGIFY(SAVED_POSITIONS));
+ return;
+ }
+
+ stored_position[slot] = current_position;
+ SBI(saved_slots[slot >> 3], slot & 0x07);
+
+ #if ENABLED(SAVED_POSITIONS_DEBUG)
+ const xyze_pos_t &pos = stored_position[slot];
+ DEBUG_ECHOPAIR_F(STR_SAVED_POS " S", slot);
+ DEBUG_ECHOPAIR_F(" : X", pos.x);
+ DEBUG_ECHOPAIR_F_P(SP_Y_STR, pos.y);
+ DEBUG_ECHOLNPAIR_F_P(SP_Z_STR, pos.z);
+ #endif
+}
+
+#endif // SAVED_POSITIONS
diff --git a/Marlin/src/gcode/feature/pause/G61.cpp b/Marlin/src/gcode/feature/pause/G61.cpp
new file mode 100644
index 0000000..5d89af0
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/G61.cpp
@@ -0,0 +1,73 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if SAVED_POSITIONS
+
+#include "../../../module/planner.h"
+#include "../../gcode.h"
+#include "../../../module/motion.h"
+
+/**
+ * G61: Return to saved position
+ *
+ * F<rate> - Feedrate (optional) for the move back.
+ * S<slot> - Slot # (0-based) to restore from (default 0).
+ * X Y Z - Axes to restore. At least one is required.
+ */
+void GcodeSuite::G61(void) {
+
+ const uint8_t slot = parser.byteval('S');
+
+ #if SAVED_POSITIONS < 256
+ if (slot >= SAVED_POSITIONS) {
+ SERIAL_ERROR_MSG(STR_INVALID_POS_SLOT STRINGIFY(SAVED_POSITIONS));
+ return;
+ }
+ #endif
+
+ // No saved position? No axes being restored?
+ if (!TEST(saved_slots[slot >> 3], slot & 0x07) || !parser.seen("XYZ")) return;
+
+ SERIAL_ECHOPAIR(STR_RESTORING_POS " S", int(slot));
+ LOOP_XYZ(i) {
+ destination[i] = parser.seen(XYZ_CHAR(i))
+ ? stored_position[slot][i] + parser.value_axis_units((AxisEnum)i)
+ : current_position[i];
+ SERIAL_CHAR(' ', XYZ_CHAR(i));
+ SERIAL_ECHO_F(destination[i]);
+ }
+ SERIAL_EOL();
+
+ // Apply any given feedrate over 0.0
+ feedRate_t saved_feedrate = feedrate_mm_s;
+ const float fr = parser.linearval('F');
+ if (fr > 0.0) feedrate_mm_s = MMM_TO_MMS(fr);
+
+ // Move to the saved position
+ prepare_line_to_destination();
+
+ feedrate_mm_s = saved_feedrate;
+}
+
+#endif // SAVED_POSITIONS
diff --git a/Marlin/src/gcode/feature/pause/M125.cpp b/Marlin/src/gcode/feature/pause/M125.cpp
new file mode 100644
index 0000000..9391b86
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/M125.cpp
@@ -0,0 +1,90 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(PARK_HEAD_ON_PAUSE)
+
+#include "../../gcode.h"
+#include "../../parser.h"
+#include "../../../feature/pause.h"
+#include "../../../lcd/marlinui.h"
+#include "../../../module/motion.h"
+#include "../../../module/printcounter.h"
+#include "../../../sd/cardreader.h"
+
+#if ENABLED(POWER_LOSS_RECOVERY)
+ #include "../../../feature/powerloss.h"
+#endif
+
+/**
+ * M125: Store current position and move to parking position.
+ * Called on pause (by M25) to prevent material leaking onto the
+ * object. On resume (M24) the head will be moved back and the
+ * print will resume.
+ *
+ * When not actively SD printing, M125 simply moves to the park
+ * position and waits, resuming with a button click or M108.
+ * Without PARK_HEAD_ON_PAUSE the M125 command does nothing.
+ *
+ * L<linear> = Override retract Length
+ * X<pos> = Override park position X
+ * Y<pos> = Override park position Y
+ * Z<linear> = Override Z raise
+ *
+ * With an LCD menu:
+ * P<bool> = Always show a prompt and await a response
+ */
+void GcodeSuite::M125() {
+ // Initial retract before move to filament change position
+ const float retract = -ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS) : (PAUSE_PARK_RETRACT_LENGTH));
+
+ xyz_pos_t park_point = NOZZLE_PARK_POINT;
+
+ // Move XY axes to filament change position or given position
+ if (parser.seenval('X')) park_point.x = RAW_X_POSITION(parser.linearval('X'));
+ if (parser.seenval('Y')) park_point.y = RAW_X_POSITION(parser.linearval('Y'));
+
+ // Lift Z axis
+ if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
+
+ #if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
+ park_point += hotend_offset[active_extruder];
+ #endif
+
+ const bool sd_printing = TERN0(SDSUPPORT, IS_SD_PRINTING());
+
+ ui.pause_show_message(PAUSE_MESSAGE_PARKING, PAUSE_MODE_PAUSE_PRINT);
+
+ // If possible, show an LCD prompt with the 'P' flag
+ const bool show_lcd = TERN0(HAS_LCD_MENU, parser.boolval('P'));
+
+ if (pause_print(retract, park_point, 0, show_lcd)) {
+ TERN_(POWER_LOSS_RECOVERY, if (recovery.enabled) recovery.save(true));
+ if (ENABLED(EXTENSIBLE_UI) || !sd_printing || show_lcd) {
+ wait_for_confirmation(false, 0);
+ resume_print(0, 0, -retract, 0);
+ }
+ }
+}
+
+#endif // PARK_HEAD_ON_PAUSE
diff --git a/Marlin/src/gcode/feature/pause/M600.cpp b/Marlin/src/gcode/feature/pause/M600.cpp
new file mode 100644
index 0000000..1c282f2
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/M600.cpp
@@ -0,0 +1,172 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(ADVANCED_PAUSE_FEATURE)
+
+#include "../../gcode.h"
+#include "../../../feature/pause.h"
+#include "../../../module/motion.h"
+#include "../../../module/printcounter.h"
+#include "../../../lcd/marlinui.h"
+
+#if HAS_MULTI_EXTRUDER
+ #include "../../../module/tool_change.h"
+#endif
+
+#if ENABLED(MMU2_MENUS)
+ #include "../../../lcd/menu/menu_mmu2.h"
+#endif
+
+#if ENABLED(MIXING_EXTRUDER)
+ #include "../../../feature/mixing.h"
+#endif
+
+#if HAS_FILAMENT_SENSOR
+ #include "../../../feature/runout.h"
+#endif
+
+/**
+ * M600: Pause for filament change
+ *
+ * E[distance] - Retract the filament this far
+ * Z[distance] - Move the Z axis by this distance
+ * X[position] - Move to this X position, with Y
+ * Y[position] - Move to this Y position, with X
+ * U[distance] - Retract distance for removal (manual reload)
+ * L[distance] - Extrude distance for insertion (manual reload)
+ * B[count] - Number of times to beep, -1 for indefinite (if equipped with a buzzer)
+ * T[toolhead] - Select extruder for filament change
+ * R[temp] - Resume temperature (in current units)
+ *
+ * Default values are used for omitted arguments.
+ */
+void GcodeSuite::M600() {
+
+ #if ENABLED(MIXING_EXTRUDER)
+ const int8_t target_e_stepper = get_target_e_stepper_from_command();
+ if (target_e_stepper < 0) return;
+
+ const uint8_t old_mixing_tool = mixer.get_current_vtool();
+ mixer.T(MIXER_DIRECT_SET_TOOL);
+
+ MIXER_STEPPER_LOOP(i) mixer.set_collector(i, i == uint8_t(target_e_stepper) ? 1.0 : 0.0);
+ mixer.normalize();
+
+ const int8_t target_extruder = active_extruder;
+ #else
+ const int8_t target_extruder = get_target_extruder_from_command();
+ if (target_extruder < 0) return;
+ #endif
+
+ #if ENABLED(DUAL_X_CARRIAGE)
+ int8_t DXC_ext = target_extruder;
+ if (!parser.seen('T')) { // If no tool index is specified, M600 was (probably) sent in response to filament runout.
+ // In this case, for duplicating modes set DXC_ext to the extruder that ran out.
+ #if HAS_FILAMENT_SENSOR && NUM_RUNOUT_SENSORS > 1
+ if (idex_is_duplicating())
+ DXC_ext = (READ(FIL_RUNOUT2_PIN) == FIL_RUNOUT2_STATE) ? 1 : 0;
+ #else
+ DXC_ext = active_extruder;
+ #endif
+ }
+ #endif
+
+ // Show initial "wait for start" message
+ #if DISABLED(MMU2_MENUS)
+ ui.pause_show_message(PAUSE_MESSAGE_CHANGING, PAUSE_MODE_PAUSE_PRINT, target_extruder);
+ #endif
+
+ #if ENABLED(HOME_BEFORE_FILAMENT_CHANGE)
+ // If needed, home before parking for filament change
+ if (!all_axes_trusted()) home_all_axes(true);
+ #endif
+
+ #if HAS_MULTI_EXTRUDER
+ // Change toolhead if specified
+ const uint8_t active_extruder_before_filament_change = active_extruder;
+ if (active_extruder != target_extruder && TERN1(DUAL_X_CARRIAGE, !idex_is_duplicating()))
+ tool_change(target_extruder, false);
+ #endif
+
+ // Initial retract before move to filament change position
+ const float retract = -ABS(parser.seen('E') ? parser.value_axis_units(E_AXIS) : (PAUSE_PARK_RETRACT_LENGTH));
+
+ xyz_pos_t park_point NOZZLE_PARK_POINT;
+
+ // Lift Z axis
+ if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
+
+ // Move XY axes to filament change position or given position
+ if (parser.seenval('X')) park_point.x = parser.linearval('X');
+ if (parser.seenval('Y')) park_point.y = parser.linearval('Y');
+
+ #if HAS_HOTEND_OFFSET && NONE(DUAL_X_CARRIAGE, DELTA)
+ park_point += hotend_offset[active_extruder];
+ #endif
+
+ #if ENABLED(MMU2_MENUS)
+ // For MMU2 reset retract and load/unload values so they don't mess with MMU filament handling
+ constexpr float unload_length = 0.5f,
+ slow_load_length = 0.0f,
+ fast_load_length = 0.0f;
+ #else
+ // Unload filament
+ const float unload_length = -ABS(parser.seen('U') ? parser.value_axis_units(E_AXIS)
+ : fc_settings[active_extruder].unload_length);
+
+ // Slow load filament
+ constexpr float slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH;
+
+ // Fast load filament
+ const float fast_load_length = ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS)
+ : fc_settings[active_extruder].load_length);
+ #endif
+
+ const int beep_count = parser.intval('B', -1
+ #ifdef FILAMENT_CHANGE_ALERT_BEEPS
+ + 1 + FILAMENT_CHANGE_ALERT_BEEPS
+ #endif
+ );
+
+ if (pause_print(retract, park_point, unload_length, true DXC_PASS)) {
+ #if ENABLED(MMU2_MENUS)
+ mmu2_M600();
+ resume_print(slow_load_length, fast_load_length, 0, beep_count DXC_PASS);
+ #else
+ wait_for_confirmation(true, beep_count DXC_PASS);
+ resume_print(slow_load_length, fast_load_length, ADVANCED_PAUSE_PURGE_LENGTH,
+ beep_count, (parser.seenval('R') ? parser.value_celsius() : 0) DXC_PASS);
+ #endif
+ }
+
+ #if HAS_MULTI_EXTRUDER
+ // Restore toolhead if it was changed
+ if (active_extruder_before_filament_change != active_extruder)
+ tool_change(active_extruder_before_filament_change, false);
+ #endif
+
+ TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
+}
+
+#endif // ADVANCED_PAUSE_FEATURE
diff --git a/Marlin/src/gcode/feature/pause/M603.cpp b/Marlin/src/gcode/feature/pause/M603.cpp
new file mode 100644
index 0000000..9c3b774
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/M603.cpp
@@ -0,0 +1,65 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfig.h"
+
+#if ENABLED(ADVANCED_PAUSE_FEATURE)
+
+#include "../../gcode.h"
+#include "../../../feature/pause.h"
+#include "../../../module/motion.h"
+#include "../../../module/printcounter.h"
+
+#if HAS_MULTI_EXTRUDER
+ #include "../../../module/tool_change.h"
+#endif
+
+/**
+ * M603: Configure filament change
+ *
+ * T[toolhead] - Select extruder to configure, active extruder if not specified
+ * U[distance] - Retract distance for removal, for the specified extruder
+ * L[distance] - Extrude distance for insertion, for the specified extruder
+ */
+void GcodeSuite::M603() {
+
+ const int8_t target_extruder = get_target_extruder_from_command();
+ if (target_extruder < 0) return;
+
+ // Unload length
+ if (parser.seen('U')) {
+ fc_settings[target_extruder].unload_length = ABS(parser.value_axis_units(E_AXIS));
+ #if ENABLED(PREVENT_LENGTHY_EXTRUDE)
+ NOMORE(fc_settings[target_extruder].unload_length, EXTRUDE_MAXLENGTH);
+ #endif
+ }
+
+ // Load length
+ if (parser.seen('L')) {
+ fc_settings[target_extruder].load_length = ABS(parser.value_axis_units(E_AXIS));
+ #if ENABLED(PREVENT_LENGTHY_EXTRUDE)
+ NOMORE(fc_settings[target_extruder].load_length, EXTRUDE_MAXLENGTH);
+ #endif
+ }
+}
+
+#endif // ADVANCED_PAUSE_FEATURE
diff --git a/Marlin/src/gcode/feature/pause/M701_M702.cpp b/Marlin/src/gcode/feature/pause/M701_M702.cpp
new file mode 100644
index 0000000..9a2b774
--- /dev/null
+++ b/Marlin/src/gcode/feature/pause/M701_M702.cpp
@@ -0,0 +1,235 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
+
+#include "../../gcode.h"
+#include "../../../MarlinCore.h"
+#include "../../../module/motion.h"
+#include "../../../module/temperature.h"
+#include "../../../feature/pause.h"
+#include "../../../lcd/marlinui.h"
+
+#if HAS_MULTI_EXTRUDER
+ #include "../../../module/tool_change.h"
+#endif
+
+#if HAS_PRUSA_MMU2
+ #include "../../../feature/mmu/mmu2.h"
+#endif
+
+#if ENABLED(MIXING_EXTRUDER)
+ #include "../../../feature/mixing.h"
+#endif
+
+/**
+ * M701: Load filament
+ *
+ * T<extruder> - Extruder number. Required for mixing extruder.
+ * For non-mixing, current extruder if omitted.
+ * Z<distance> - Move the Z axis by this distance
+ * L<distance> - Extrude distance for insertion (positive value) (manual reload)
+ *
+ * Default values are used for omitted arguments.
+ */
+void GcodeSuite::M701() {
+ xyz_pos_t park_point = NOZZLE_PARK_POINT;
+
+ // Don't raise Z if the machine isn't homed
+ if (TERN0(NO_MOTION_BEFORE_HOMING, axes_should_home())) park_point.z = 0;
+
+ #if ENABLED(MIXING_EXTRUDER)
+ const int8_t target_e_stepper = get_target_e_stepper_from_command();
+ if (target_e_stepper < 0) return;
+
+ const uint8_t old_mixing_tool = mixer.get_current_vtool();
+ mixer.T(MIXER_DIRECT_SET_TOOL);
+
+ MIXER_STEPPER_LOOP(i) mixer.set_collector(i, (i == (uint8_t)target_e_stepper) ? 1.0 : 0.0);
+ mixer.normalize();
+
+ const int8_t target_extruder = active_extruder;
+ #else
+ const int8_t target_extruder = get_target_extruder_from_command();
+ if (target_extruder < 0) return;
+ #endif
+
+ // Z axis lift
+ if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
+
+ // Show initial "wait for load" message
+ ui.pause_show_message(PAUSE_MESSAGE_LOAD, PAUSE_MODE_LOAD_FILAMENT, target_extruder);
+
+ #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
+ // Change toolhead if specified
+ uint8_t active_extruder_before_filament_change = active_extruder;
+ if (active_extruder != target_extruder)
+ tool_change(target_extruder, false);
+ #endif
+
+ // Lift Z axis
+ if (park_point.z > 0)
+ do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
+
+ // Load filament
+ #if HAS_PRUSA_MMU2
+ mmu2.load_filament_to_nozzle(target_extruder);
+ #else
+ constexpr float purge_length = ADVANCED_PAUSE_PURGE_LENGTH,
+ slow_load_length = FILAMENT_CHANGE_SLOW_LOAD_LENGTH;
+ const float fast_load_length = ABS(parser.seen('L') ? parser.value_axis_units(E_AXIS)
+ : fc_settings[active_extruder].load_length);
+ load_filament(
+ slow_load_length, fast_load_length, purge_length,
+ FILAMENT_CHANGE_ALERT_BEEPS,
+ true, // show_lcd
+ thermalManager.still_heating(target_extruder), // pause_for_user
+ PAUSE_MODE_LOAD_FILAMENT // pause_mode
+ #if ENABLED(DUAL_X_CARRIAGE)
+ , target_extruder // Dual X target
+ #endif
+ );
+ #endif
+
+ // Restore Z axis
+ if (park_point.z > 0)
+ do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
+
+ #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
+ // Restore toolhead if it was changed
+ if (active_extruder_before_filament_change != active_extruder)
+ tool_change(active_extruder_before_filament_change, false);
+ #endif
+
+ TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
+
+ // Show status screen
+ ui.pause_show_message(PAUSE_MESSAGE_STATUS);
+}
+
+/**
+ * M702: Unload filament
+ *
+ * T<extruder> - Extruder number. Required for mixing extruder.
+ * For non-mixing, if omitted, current extruder
+ * (or ALL extruders with FILAMENT_UNLOAD_ALL_EXTRUDERS).
+ * Z<distance> - Move the Z axis by this distance
+ * U<distance> - Retract distance for removal (manual reload)
+ *
+ * Default values are used for omitted arguments.
+ */
+void GcodeSuite::M702() {
+ xyz_pos_t park_point = NOZZLE_PARK_POINT;
+
+ // Don't raise Z if the machine isn't homed
+ if (TERN0(NO_MOTION_BEFORE_HOMING, axes_should_home())) park_point.z = 0;
+
+ #if ENABLED(MIXING_EXTRUDER)
+ const uint8_t old_mixing_tool = mixer.get_current_vtool();
+
+ #if ENABLED(FILAMENT_UNLOAD_ALL_EXTRUDERS)
+ float mix_multiplier = 1.0;
+ const bool seenT = parser.seenval('T');
+ if (!seenT) {
+ mixer.T(MIXER_AUTORETRACT_TOOL);
+ mix_multiplier = MIXING_STEPPERS;
+ }
+ #else
+ constexpr bool seenT = true;
+ #endif
+
+ if (seenT) {
+ const int8_t target_e_stepper = get_target_e_stepper_from_command();
+ if (target_e_stepper < 0) return;
+ mixer.T(MIXER_DIRECT_SET_TOOL);
+ MIXER_STEPPER_LOOP(i) mixer.set_collector(i, (i == (uint8_t)target_e_stepper) ? 1.0 : 0.0);
+ mixer.normalize();
+ }
+
+ const int8_t target_extruder = active_extruder;
+ #else
+ const int8_t target_extruder = get_target_extruder_from_command();
+ if (target_extruder < 0) return;
+ #endif
+
+ // Z axis lift
+ if (parser.seenval('Z')) park_point.z = parser.linearval('Z');
+
+ // Show initial "wait for unload" message
+ ui.pause_show_message(PAUSE_MESSAGE_UNLOAD, PAUSE_MODE_UNLOAD_FILAMENT, target_extruder);
+
+ #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
+ // Change toolhead if specified
+ uint8_t active_extruder_before_filament_change = active_extruder;
+ if (active_extruder != target_extruder)
+ tool_change(target_extruder, false);
+ #endif
+
+ // Lift Z axis
+ if (park_point.z > 0)
+ do_blocking_move_to_z(_MIN(current_position.z + park_point.z, Z_MAX_POS), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
+
+ // Unload filament
+ #if HAS_PRUSA_MMU2
+ mmu2.unload();
+ #else
+ #if BOTH(HAS_MULTI_EXTRUDER, FILAMENT_UNLOAD_ALL_EXTRUDERS)
+ if (!parser.seenval('T')) {
+ HOTEND_LOOP() {
+ if (e != active_extruder) tool_change(e, false);
+ unload_filament(-fc_settings[e].unload_length, true, PAUSE_MODE_UNLOAD_FILAMENT);
+ }
+ }
+ else
+ #endif
+ {
+ // Unload length
+ const float unload_length = -ABS(parser.seen('U') ? parser.value_axis_units(E_AXIS)
+ : fc_settings[target_extruder].unload_length);
+
+ unload_filament(unload_length, true, PAUSE_MODE_UNLOAD_FILAMENT
+ #if ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER)
+ , mix_multiplier
+ #endif
+ );
+ }
+ #endif
+
+ // Restore Z axis
+ if (park_point.z > 0)
+ do_blocking_move_to_z(_MAX(current_position.z - park_point.z, 0), feedRate_t(NOZZLE_PARK_Z_FEEDRATE));
+
+ #if HAS_MULTI_EXTRUDER && (HAS_PRUSA_MMU1 || !HAS_MMU)
+ // Restore toolhead if it was changed
+ if (active_extruder_before_filament_change != active_extruder)
+ tool_change(active_extruder_before_filament_change, false);
+ #endif
+
+ TERN_(MIXING_EXTRUDER, mixer.T(old_mixing_tool)); // Restore original mixing tool
+
+ // Show status screen
+ ui.pause_show_message(PAUSE_MESSAGE_STATUS);
+}
+
+#endif // ADVANCED_PAUSE_FEATURE