From 815c636449e399651ee0751b63ed36f2d28415e0 Mon Sep 17 00:00:00 2001
From: zeleps <39417467+zeleps@users.noreply.github.com>
Date: Fri, 1 Jan 2021 22:54:44 +0200
Subject: [PATCH] Fix PARKING_EXTRUDER homing with solenoid (#20473)

---
 Marlin/src/feature/solenoid.cpp    | 15 +++++--
 Marlin/src/gcode/calibrate/G28.cpp |  6 ++-
 Marlin/src/gcode/control/T.cpp     | 18 +++------
 Marlin/src/module/tool_change.cpp  | 64 +++++++++++++++++++++---------
 Marlin/src/module/tool_change.h    |  3 ++
 5 files changed, 71 insertions(+), 35 deletions(-)

diff --git a/Marlin/src/feature/solenoid.cpp b/Marlin/src/feature/solenoid.cpp
index 8646ec872f6..659b6422623 100644
--- a/Marlin/src/feature/solenoid.cpp
+++ b/Marlin/src/feature/solenoid.cpp
@@ -28,22 +28,29 @@
 
 #include "../module/motion.h" // for active_extruder
 
-#if ENABLED(MANUAL_SOLENOID_CONTROL)
-  #define HAS_SOLENOID(N) HAS_SOLENOID_##N
+// PARKING_EXTRUDER options alter the default behavior of solenoids, this ensures compliance of M380-381
+
+#if ENABLED(PARKING_EXTRUDER)
+  #include "../module/tool_change.h"
+  #define SOLENOID_MAGNETIZED_STATE (TERN_(PARKING_EXTRUDER_SOLENOIDS_INVERT,!)PARKING_EXTRUDER_SOLENOIDS_PINS_ACTIVE)
 #else
-  #define HAS_SOLENOID(N) (HAS_SOLENOID_##N && EXTRUDERS > N)
+  #define SOLENOID_MAGNETIZED_STATE HIGH
 #endif
 
+#define HAS_SOLENOID(N) (HAS_SOLENOID_##N && TERN(MANUAL_SOLENOID_CONTROL, true, EXTRUDERS > N))
+
 // Used primarily with MANUAL_SOLENOID_CONTROL
 static void set_solenoid(const uint8_t num, const bool active) {
-  const uint8_t value = active ? HIGH : LOW;
+  const uint8_t value = active ? SOLENOID_MAGNETIZED_STATE : !SOLENOID_MAGNETIZED_STATE;
   switch (num) {
     case 0:
       OUT_WRITE(SOL0_PIN, value);
+      TERN_(PARKING_EXTRUDER, if (!active && active_extruder == 0) parking_extruder_set_parked()); // If active extruder's solenoid is disabled, carriage is considered parked
       break;
     #if HAS_SOLENOID(1)
       case 1:
         OUT_WRITE(SOL1_PIN, value);
+        TERN_(PARKING_EXTRUDER, if (!active && active_extruder == 1) parking_extruder_set_parked()); // If active extruder's solenoid is disabled, carriage is considered parked
         break;
     #endif
     #if HAS_SOLENOID(2)
diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp
index e63f60994f8..7c13587a670 100644
--- a/Marlin/src/gcode/calibrate/G28.cpp
+++ b/Marlin/src/gcode/calibrate/G28.cpp
@@ -290,6 +290,10 @@ void GcodeSuite::G28() {
     #if DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE)
       const uint8_t old_tool_index = active_extruder;
     #endif
+    // PARKING_EXTRUDER homing requires different handling of movement / solenoid activation, depending on the side of homing
+    #if ENABLED(PARKING_EXTRUDER)
+      const bool pe_final_change_must_unpark = parking_extruder_unpark_after_homing(old_tool_index, X_HOME_DIR + 1 == old_tool_index * 2);
+    #endif
     tool_change(0, true);
   #endif
 
@@ -443,7 +447,7 @@ void GcodeSuite::G28() {
 
   // Restore the active tool after homing
   #if HAS_MULTI_HOTEND && (DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE))
-    tool_change(old_tool_index, NONE(PARKING_EXTRUDER, DUAL_X_CARRIAGE));   // Do move if one of these
+    tool_change(old_tool_index, TERN(PARKING_EXTRUDER, !pe_final_change_must_unpark, DISABLED(DUAL_X_CARRIAGE)));   // Do move if one of these
   #endif
 
   #if HAS_HOMING_CURRENT
diff --git a/Marlin/src/gcode/control/T.cpp b/Marlin/src/gcode/control/T.cpp
index 592b2b3dcef..3ce284f82e8 100644
--- a/Marlin/src/gcode/control/T.cpp
+++ b/Marlin/src/gcode/control/T.cpp
@@ -61,16 +61,10 @@ void GcodeSuite::T(const int8_t tool_index) {
     }
   #endif
 
-  #if EXTRUDERS < 2
-
-    tool_change(tool_index);
-
-  #else
-
-    tool_change(
-      tool_index,
-      (tool_index == active_extruder) || parser.boolval('S')
-    );
-
-  #endif
+  tool_change(tool_index
+    #if HAS_MULTI_EXTRUDER
+      ,  TERN(PARKING_EXTRUDER, false, tool_index == active_extruder) // For PARKING_EXTRUDER motion is decided in tool_change()
+      || parser.boolval('S')
+    #endif
+  );
 }
diff --git a/Marlin/src/module/tool_change.cpp b/Marlin/src/module/tool_change.cpp
index a9819319175..a310442126c 100644
--- a/Marlin/src/module/tool_change.cpp
+++ b/Marlin/src/module/tool_change.cpp
@@ -277,6 +277,28 @@ inline void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_a
     #endif
   }
 
+  bool extruder_parked = true, do_solenoid_activation = true;
+
+  // Modifies tool_change() behavior based on homing side
+  bool parking_extruder_unpark_after_homing(const uint8_t final_tool, bool homed_towards_final_tool) {
+    do_solenoid_activation = false; // Tell parking_extruder_tool_change to skip solenoid activation
+
+    if (!extruder_parked) return false; // nothing to do
+
+    if (homed_towards_final_tool) {
+      pe_deactivate_solenoid(1 - final_tool);
+      DEBUG_ECHOLNPAIR("Disengage magnet", (int)(1 - final_tool));
+      pe_activate_solenoid(final_tool);
+      DEBUG_ECHOLNPAIR("Engage magnet", (int)final_tool);
+      extruder_parked = false;
+      return false;
+    }
+
+    return true;
+  }
+
+  void parking_extruder_set_parked() { extruder_parked = true; }
+
   inline void parking_extruder_tool_change(const uint8_t new_tool, bool no_move) {
     if (!no_move) {
 
@@ -304,27 +326,30 @@ inline void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_a
 
       DEBUG_POS("Start PE Tool-Change", current_position);
 
-      current_position.x = parkingposx[active_extruder] + x_offset;
+      // Don't park the active_extruder unless unparked
+      if (!extruder_parked) {
+        current_position.x = parkingposx[active_extruder] + x_offset;
 
-      DEBUG_ECHOLNPAIR("(1) Park extruder ", int(active_extruder));
-      DEBUG_POS("Moving ParkPos", current_position);
+        DEBUG_ECHOLNPAIR("(1) Park extruder ", int(active_extruder));
+        DEBUG_POS("Moving ParkPos", current_position);
 
-      fast_line_to_current(X_AXIS);
+        fast_line_to_current(X_AXIS);
 
-      // STEP 2
+        // STEP 2
 
-      planner.synchronize();
-      DEBUG_ECHOLNPGM("(2) Disengage magnet");
-      pe_deactivate_solenoid(active_extruder);
+        planner.synchronize();
+        DEBUG_ECHOLNPGM("(2) Disengage magnet");
+        pe_deactivate_solenoid(active_extruder);
 
-      // STEP 3
+        // STEP 3
 
-      current_position.x += active_extruder ? -10 : 10; // move 10mm away from parked extruder
+        current_position.x += active_extruder ? -10 : 10; // move 10mm away from parked extruder
 
-      DEBUG_ECHOLNPGM("(3) Move near new extruder");
-      DEBUG_POS("Move away from parked extruder", current_position);
+        DEBUG_ECHOLNPGM("(3) Move near new extruder");
+        DEBUG_POS("Move away from parked extruder", current_position);
 
-      fast_line_to_current(X_AXIS);
+        fast_line_to_current(X_AXIS);
+      }
 
       // STEP 4
 
@@ -358,13 +383,16 @@ inline void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_a
       planner.synchronize(); // Always sync the final move
 
       DEBUG_POS("PE Tool-Change done.", current_position);
+      extruder_parked = false;
     }
-    else { // nomove == true
+    else if (do_solenoid_activation) { // && nomove == true
+      // Deactivate old extruder solenoid
+      TERN(PARKING_EXTRUDER_SOLENOIDS_INVERT, pe_activate_solenoid, pe_deactivate_solenoid)(active_extruder);
       // Only engage magnetic field for new extruder
-      pe_activate_solenoid(new_tool);
-      // Just save power for inverted magnets
-      TERN_(PARKING_EXTRUDER_SOLENOIDS_INVERT, pe_activate_solenoid(active_extruder));
+      TERN(PARKING_EXTRUDER_SOLENOIDS_INVERT, pe_deactivate_solenoid, pe_activate_solenoid)(new_tool);
     }
+
+    do_solenoid_activation = true; // Activate solenoid for subsequent tool_change()
   }
 
 #endif // PARKING_EXTRUDER
@@ -922,7 +950,7 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
       }
     #endif
 
-    if (new_tool != old_tool) {
+    if (new_tool != old_tool || TERN0(PARKING_EXTRUDER, extruder_parked)) { // PARKING_EXTRUDER may need to attach old_tool when homing
       destination = current_position;
 
       #if BOTH(TOOLCHANGE_FILAMENT_SWAP, HAS_FAN) && TOOLCHANGE_FS_FAN >= 0
diff --git a/Marlin/src/module/tool_change.h b/Marlin/src/module/tool_change.h
index 0b22f8b6f1c..fc953ddf8a0 100644
--- a/Marlin/src/module/tool_change.h
+++ b/Marlin/src/module/tool_change.h
@@ -93,6 +93,9 @@
 
   void pe_solenoid_init();
 
+  bool parking_extruder_unpark_after_homing(const uint8_t final_tool, bool homed_towards_final_tool);
+  void parking_extruder_set_parked();
+
 #elif ENABLED(MAGNETIC_PARKING_EXTRUDER)
 
   typedef struct MPESettings {