diff --git a/.travis.yml b/.travis.yml
index e901ec33640a4711a1470598b0df4d39d0d795c9..ea6aad9e6d592a2091e26c0f7a5f62b23f3ff4b8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -177,7 +177,7 @@ script:
   #
   - restore_configs
   - opt_enable ULTIMAKERCONTROLLER FILAMENT_LCD_DISPLAY FILAMENT_WIDTH_SENSOR SDSUPPORT
-  - opt_enable PRINTCOUNTER NOZZLE_PARK_FEATURE NOZZLE_CLEAN_FEATURE PCA9632
+  - opt_enable PRINTCOUNTER NOZZLE_PARK_FEATURE NOZZLE_CLEAN_FEATURE PCA9632 USE_XMAX_PLUG
   - opt_enable_adv Z_DUAL_STEPPER_DRIVERS Z_DUAL_ENDSTOPS BEZIER_CURVE_SUPPORT EXPERIMENTAL_I2CBUS
   - opt_set_adv I2C_SLAVE_ADDRESS 63
   - opt_enable_adv ADVANCED_PAUSE_FEATURE PARK_HEAD_ON_PAUSE LCD_INFO_MENU
@@ -434,7 +434,7 @@ script:
   - restore_configs
   - opt_enable_adv Z_DUAL_STEPPER_DRIVERS Z_DUAL_ENDSTOPS
   - pins_set RAMPS X_MAX_PIN -1
-  - opt_set_adv Z2_MAX_PIN 2
+  - opt_add_adv Z2_MAX_PIN 2
   - build_marlin_pio ${TRAVIS_BUILD_DIR} ${TEST_PLATFORM}
 
   #############################
diff --git a/Marlin/src/HAL/HAL_AVR/endstop_interrupts.h b/Marlin/src/HAL/HAL_AVR/endstop_interrupts.h
index d78ee97510c3cc4ddad31c599805411d8b5f88c7..8bd6f0081d2775461112eb0821530162fa61ab3a 100644
--- a/Marlin/src/HAL/HAL_AVR/endstop_interrupts.h
+++ b/Marlin/src/HAL/HAL_AVR/endstop_interrupts.h
@@ -161,6 +161,46 @@ void setup_endstop_interrupts( void ) {
     #endif
   #endif
 
+  #if HAS_X2_MAX
+    #if (digitalPinToInterrupt(X2_MAX_PIN) != NOT_AN_INTERRUPT)
+      attachInterrupt(digitalPinToInterrupt(X2_MAX_PIN), endstop_ISR, CHANGE);
+    #else
+      // Not all used endstop/probe -pins can raise interrupts. Please deactivate ENDSTOP_INTERRUPTS or change the pin configuration!
+      static_assert(digitalPinToPCICR(X2_MAX_PIN) != NULL, "X2_MAX_PIN is not interrupt-capable");
+      pciSetup(X2_MAX_PIN);
+    #endif
+  #endif
+
+  #if HAS_X2_MIN
+    #if (digitalPinToInterrupt(X2_MIN_PIN) != NOT_AN_INTERRUPT)
+      attachInterrupt(digitalPinToInterrupt(X2_MIN_PIN), endstop_ISR, CHANGE);
+    #else
+      // Not all used endstop/probe -pins can raise interrupts. Please deactivate ENDSTOP_INTERRUPTS or change the pin configuration!
+      static_assert(digitalPinToPCICR(X2_MIN_PIN) != NULL, "X2_MIN_PIN is not interrupt-capable");
+      pciSetup(X2_MIN_PIN);
+    #endif
+  #endif
+
+  #if HAS_Y2_MAX
+    #if (digitalPinToInterrupt(Y2_MAX_PIN) != NOT_AN_INTERRUPT)
+      attachInterrupt(digitalPinToInterrupt(Y2_MAX_PIN), endstop_ISR, CHANGE);
+    #else
+      // Not all used endstop/probe -pins can raise interrupts. Please deactivate ENDSTOP_INTERRUPTS or change the pin configuration!
+      static_assert(digitalPinToPCICR(Y2_MAX_PIN) != NULL, "Y2_MAX_PIN is not interrupt-capable");
+      pciSetup(Y2_MAX_PIN);
+    #endif
+  #endif
+
+  #if HAS_Y2_MIN
+    #if (digitalPinToInterrupt(Y2_MIN_PIN) != NOT_AN_INTERRUPT)
+      attachInterrupt(digitalPinToInterrupt(Y2_MIN_PIN), endstop_ISR, CHANGE);
+    #else
+      // Not all used endstop/probe -pins can raise interrupts. Please deactivate ENDSTOP_INTERRUPTS or change the pin configuration!
+      static_assert(digitalPinToPCICR(Y2_MIN_PIN) != NULL, "Y2_MIN_PIN is not interrupt-capable");
+      pciSetup(Y2_MIN_PIN);
+    #endif
+  #endif
+
   #if HAS_Z2_MAX
     #if (digitalPinToInterrupt(Z2_MAX_PIN) != NOT_AN_INTERRUPT)
       attachInterrupt(digitalPinToInterrupt(Z2_MAX_PIN), endstop_ISR, CHANGE);
diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h
index b485a3c7c0605e2800bb192e65aac05ee9653272..1cb6e02d8278fd0128a6ba4e8c134436d53edcb4 100644
--- a/Marlin/src/core/language.h
+++ b/Marlin/src/core/language.h
@@ -150,8 +150,12 @@
 #define MSG_ACTIVE_EXTRUDER                 "Active Extruder: "
 #define MSG_X_MIN                           "x_min: "
 #define MSG_X_MAX                           "x_max: "
+#define MSG_X2_MIN                          "x2_min: "
+#define MSG_X2_MAX                          "x2_max: "
 #define MSG_Y_MIN                           "y_min: "
 #define MSG_Y_MAX                           "y_max: "
+#define MSG_Y2_MIN                          "y2_min: "
+#define MSG_Y2_MAX                          "y2_max: "
 #define MSG_Z_MIN                           "z_min: "
 #define MSG_Z_MAX                           "z_max: "
 #define MSG_Z2_MIN                          "z2_min: "
diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h
index a89b6dafcead6554cdab8a9eea00db8cfb4cf7f8..1265ddbbc83e521a83170603d883df7c5b29d196 100644
--- a/Marlin/src/core/macros.h
+++ b/Marlin/src/core/macros.h
@@ -28,6 +28,13 @@
 #define ABC  3
 #define XYZ  3
 
+#define _XMIN_ 100
+#define _YMIN_ 200
+#define _ZMIN_ 300
+#define _XMAX_ 101
+#define _YMAX_ 201
+#define _ZMAX_ 301
+
 #define FORCE_INLINE __attribute__((always_inline)) inline
 #define _UNUSED      __attribute__((unused))
 #define _O0          __attribute__((optimize("O0")))
diff --git a/Marlin/src/gcode/calibrate/M666.cpp b/Marlin/src/gcode/calibrate/M666.cpp
index bba26614104736ba4d593bb8ff7c4cd7dda0c07f..96688f2bbf435f77960b0c74caf7636e3ad52ad4 100644
--- a/Marlin/src/gcode/calibrate/M666.cpp
+++ b/Marlin/src/gcode/calibrate/M666.cpp
@@ -64,11 +64,23 @@
   #include "../../module/endstops.h"
 
   /**
-   * M666: For Z Dual Endstop setup, set z axis offset to the z2 axis.
+   * M666: For a Dual Endstop setup, set offsets for any 2nd endstops.
    */
   void GcodeSuite::M666() {
-    if (parser.seen('Z')) endstops.z_endstop_adj = parser.value_linear_units();
-    SERIAL_ECHOLNPAIR("Z Endstop Adjustment set to (mm):", endstops.z_endstop_adj);
+    SERIAL_ECHOPGM("Dual Endstop Adjustment (mm): ");
+    #if ENABLED(X_DUAL_ENDSTOPS)
+      if (parser.seen('X')) endstops.x_endstop_adj = parser.value_linear_units();
+      SERIAL_ECHOPAIR(" X", endstops.x_endstop_adj);
+    #endif
+    #if ENABLED(Y_DUAL_ENDSTOPS)
+      if (parser.seen('Y')) endstops.y_endstop_adj = parser.value_linear_units();
+      SERIAL_ECHOPAIR(" Y", endstops.y_endstop_adj);
+    #endif
+    #if ENABLED(Z_DUAL_ENDSTOPS)
+      if (parser.seen('Z')) endstops.z_endstop_adj = parser.value_linear_units();
+      SERIAL_ECHOPAIR(" Z", endstops.z_endstop_adj);
+    #endif
+    SERIAL_EOL();
   }
 
 #endif
diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h
index 8a71a9d4a62f7b5957d1eb1bebe1f5c5357d8247..06f9a545943d125be33d8fcc3ba673a4344d11fc 100644
--- a/Marlin/src/inc/Conditionals_post.h
+++ b/Marlin/src/inc/Conditionals_post.h
@@ -422,29 +422,122 @@
 #define ARRAY_BY_HOTENDS(...) ARRAY_N(HOTENDS, __VA_ARGS__)
 #define ARRAY_BY_HOTENDS1(v1) ARRAY_BY_HOTENDS(v1, v1, v1, v1, v1, v1)
 
+/**
+ * X_DUAL_ENDSTOPS endstop reassignment
+ */
+#if ENABLED(X_DUAL_ENDSTOPS)
+  #if X_HOME_DIR > 0
+    #if X2_USE_ENDSTOP == _XMIN_
+      #define X2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+      #define X2_MAX_PIN X_MIN_PIN
+    #elif X2_USE_ENDSTOP == _XMAX_
+      #define X2_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+      #define X2_MAX_PIN X_MAX_PIN
+    #elif X2_USE_ENDSTOP == _YMIN_
+      #define X2_MAX_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+      #define X2_MAX_PIN Y_MIN_PIN
+    #elif X2_USE_ENDSTOP == _YMAX_
+      #define X2_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+      #define X2_MAX_PIN Y_MAX_PIN
+    #elif X2_USE_ENDSTOP == _ZMIN_
+      #define X2_MAX_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+      #define X2_MAX_PIN Z_MIN_PIN
+    #elif X2_USE_ENDSTOP == _ZMAX_
+      #define X2_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+      #define X2_MAX_PIN Z_MAX_PIN
+    #else
+      #define X2_MAX_ENDSTOP_INVERTING false
+    #endif
+    #define X2_MIN_ENDSTOP_INVERTING false
+  #else
+    #if X2_USE_ENDSTOP == _XMIN_
+      #define X2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+      #define X2_MIN_PIN X_MIN_PIN
+    #elif X2_USE_ENDSTOP == _XMAX_
+      #define X2_MIN_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+      #define X2_MIN_PIN X_MAX_PIN
+    #elif X2_USE_ENDSTOP == _YMIN_
+      #define X2_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+      #define X2_MIN_PIN Y_MIN_PIN
+    #elif X2_USE_ENDSTOP == _YMAX_
+      #define X2_MIN_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+      #define X2_MIN_PIN Y_MAX_PIN
+    #elif X2_USE_ENDSTOP == _ZMIN_
+      #define X2_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+      #define X2_MIN_PIN Z_MIN_PIN
+    #elif X2_USE_ENDSTOP == _ZMAX_
+      #define X2_MIN_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+      #define X2_MIN_PIN Z_MAX_PIN
+    #else
+      #define X2_MIN_ENDSTOP_INVERTING false
+    #endif
+    #define X2_MAX_ENDSTOP_INVERTING false
+  #endif
+#endif
+
+// Is an endstop plug used for the X2 endstop?
+#define IS_X2_ENDSTOP(A,M) (ENABLED(X_DUAL_ENDSTOPS) && X2_USE_ENDSTOP == _##A##M##_)
+
+/**
+ * Y_DUAL_ENDSTOPS endstop reassignment
+ */
+#if ENABLED(Y_DUAL_ENDSTOPS)
+  #if Y_HOME_DIR > 0
+    #if Y2_USE_ENDSTOP == _XMIN_
+      #define Y2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+      #define Y2_MAX_PIN X_MIN_PIN
+    #elif Y2_USE_ENDSTOP == _XMAX_
+      #define Y2_MAX_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+      #define Y2_MAX_PIN X_MAX_PIN
+    #elif Y2_USE_ENDSTOP == _YMIN_
+      #define Y2_MAX_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+      #define Y2_MAX_PIN Y_MIN_PIN
+    #elif Y2_USE_ENDSTOP == _YMAX_
+      #define Y2_MAX_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+      #define Y2_MAX_PIN Y_MAX_PIN
+    #elif Y2_USE_ENDSTOP == _ZMIN_
+      #define Y2_MAX_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+      #define Y2_MAX_PIN Z_MIN_PIN
+    #elif Y2_USE_ENDSTOP == _ZMAX_
+      #define Y2_MAX_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+      #define Y2_MAX_PIN Z_MAX_PIN
+    #else
+      #define Y2_MAX_ENDSTOP_INVERTING false
+    #endif
+    #define Y2_MIN_ENDSTOP_INVERTING false
+  #else
+    #if Y2_USE_ENDSTOP == _XMIN_
+      #define Y2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
+      #define Y2_MIN_PIN X_MIN_PIN
+    #elif Y2_USE_ENDSTOP == _XMAX_
+      #define Y2_MIN_ENDSTOP_INVERTING X_MAX_ENDSTOP_INVERTING
+      #define Y2_MIN_PIN X_MAX_PIN
+    #elif Y2_USE_ENDSTOP == _YMIN_
+      #define Y2_MIN_ENDSTOP_INVERTING Y_MIN_ENDSTOP_INVERTING
+      #define Y2_MIN_PIN Y_MIN_PIN
+    #elif Y2_USE_ENDSTOP == _YMAX_
+      #define Y2_MIN_ENDSTOP_INVERTING Y_MAX_ENDSTOP_INVERTING
+      #define Y2_MIN_PIN Y_MAX_PIN
+    #elif Y2_USE_ENDSTOP == _ZMIN_
+      #define Y2_MIN_ENDSTOP_INVERTING Z_MIN_ENDSTOP_INVERTING
+      #define Y2_MIN_PIN Z_MIN_PIN
+    #elif Y2_USE_ENDSTOP == _ZMAX_
+      #define Y2_MIN_ENDSTOP_INVERTING Z_MAX_ENDSTOP_INVERTING
+      #define Y2_MIN_PIN Z_MAX_PIN
+    #else
+      #define Y2_MIN_ENDSTOP_INVERTING false
+    #endif
+    #define Y2_MAX_ENDSTOP_INVERTING false
+  #endif
+#endif
+
+// Is an endstop plug used for the Y2 endstop or the bed probe?
+#define IS_Y2_ENDSTOP(A,M) (ENABLED(Y_DUAL_ENDSTOPS) && Y2_USE_ENDSTOP == _##A##M##_)
+
 /**
  * Z_DUAL_ENDSTOPS endstop reassignment
  */
 #if ENABLED(Z_DUAL_ENDSTOPS)
-  #define _XMIN_ 100
-  #define _YMIN_ 200
-  #define _ZMIN_ 300
-  #define _XMAX_ 101
-  #define _YMAX_ 201
-  #define _ZMAX_ 301
-  #if Z2_USE_ENDSTOP == _XMIN_
-    #define USE_XMIN_PLUG
-  #elif Z2_USE_ENDSTOP == _XMAX_
-    #define USE_XMAX_PLUG
-  #elif Z2_USE_ENDSTOP == _YMIN_
-    #define USE_YMIN_PLUG
-  #elif Z2_USE_ENDSTOP == _YMAX_
-    #define USE_YMAX_PLUG
-  #elif Z2_USE_ENDSTOP == _ZMIN_
-    #define USE_ZMIN_PLUG
-  #elif Z2_USE_ENDSTOP == _ZMAX_
-    #define USE_ZMAX_PLUG
-  #endif
   #if Z_HOME_DIR > 0
     #if Z2_USE_ENDSTOP == _XMIN_
       #define Z2_MAX_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
@@ -467,6 +560,7 @@
     #else
       #define Z2_MAX_ENDSTOP_INVERTING false
     #endif
+    #define Z2_MIN_ENDSTOP_INVERTING false
   #else
     #if Z2_USE_ENDSTOP == _XMIN_
       #define Z2_MIN_ENDSTOP_INVERTING X_MIN_ENDSTOP_INVERTING
@@ -489,6 +583,7 @@
     #else
       #define Z2_MIN_ENDSTOP_INVERTING false
     #endif
+    #define Z2_MAX_ENDSTOP_INVERTING false
   #endif
 #endif
 
@@ -585,12 +680,16 @@
 #define HAS_SOLENOID_4    (PIN_EXISTS(SOL4))
 
 // Endstops and bed probe
-#define HAS_X_MIN (PIN_EXISTS(X_MIN) && !IS_Z2_OR_PROBE(X,MIN))
-#define HAS_X_MAX (PIN_EXISTS(X_MAX) && !IS_Z2_OR_PROBE(X,MAX))
-#define HAS_Y_MIN (PIN_EXISTS(Y_MIN) && !IS_Z2_OR_PROBE(Y,MIN))
-#define HAS_Y_MAX (PIN_EXISTS(Y_MAX) && !IS_Z2_OR_PROBE(Y,MAX))
-#define HAS_Z_MIN (PIN_EXISTS(Z_MIN) && !IS_Z2_OR_PROBE(Z,MIN))
-#define HAS_Z_MAX (PIN_EXISTS(Z_MAX) && !IS_Z2_OR_PROBE(Z,MAX))
+#define HAS_X_MIN (PIN_EXISTS(X_MIN) && !IS_X2_ENDSTOP(X,MIN) && !IS_Y2_ENDSTOP(X,MIN) && !IS_Z2_OR_PROBE(X,MIN))
+#define HAS_X_MAX (PIN_EXISTS(X_MAX) && !IS_X2_ENDSTOP(X,MAX) && !IS_Y2_ENDSTOP(X,MAX) && !IS_Z2_OR_PROBE(X,MAX))
+#define HAS_Y_MIN (PIN_EXISTS(Y_MIN) && !IS_X2_ENDSTOP(Y,MIN) && !IS_Y2_ENDSTOP(Y,MIN) && !IS_Z2_OR_PROBE(Y,MIN))
+#define HAS_Y_MAX (PIN_EXISTS(Y_MAX) && !IS_X2_ENDSTOP(Y,MAX) && !IS_Y2_ENDSTOP(Y,MAX) && !IS_Z2_OR_PROBE(Y,MAX))
+#define HAS_Z_MIN (PIN_EXISTS(Z_MIN) && !IS_X2_ENDSTOP(Z,MIN) && !IS_Y2_ENDSTOP(Z,MIN) && !IS_Z2_OR_PROBE(Z,MIN))
+#define HAS_Z_MAX (PIN_EXISTS(Z_MAX) && !IS_X2_ENDSTOP(Z,MAX) && !IS_Y2_ENDSTOP(Z,MAX) && !IS_Z2_OR_PROBE(Z,MAX))
+#define HAS_X2_MIN (PIN_EXISTS(X2_MIN))
+#define HAS_X2_MAX (PIN_EXISTS(X2_MAX))
+#define HAS_Y2_MIN (PIN_EXISTS(Y2_MIN))
+#define HAS_Y2_MAX (PIN_EXISTS(Y2_MAX))
 #define HAS_Z2_MIN (PIN_EXISTS(Z2_MIN))
 #define HAS_Z2_MAX (PIN_EXISTS(Z2_MAX))
 #define HAS_Z_MIN_PROBE_PIN (PIN_EXISTS(Z_MIN_PROBE))
diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h
index 0da0f42684017979bcb59e60134c01c79939d798..fec4223a257bc0b7654505dbde1c5243e273b0f3 100644
--- a/Marlin/src/inc/SanityCheck.h
+++ b/Marlin/src/inc/SanityCheck.h
@@ -81,8 +81,6 @@
   #error "FILAMENT_SENSOR is deprecated. Use FILAMENT_WIDTH_SENSOR instead."
 #elif defined(DISABLE_MAX_ENDSTOPS) || defined(DISABLE_MIN_ENDSTOPS)
   #error "DISABLE_MAX_ENDSTOPS and DISABLE_MIN_ENDSTOPS deprecated. Use individual USE_*_PLUG options instead."
-#elif ENABLED(Z_DUAL_ENDSTOPS) && !defined(Z2_USE_ENDSTOP)
-  #error "Z_DUAL_ENDSTOPS settings are simplified. Just set Z2_USE_ENDSTOP to the endstop you want to repurpose for Z2."
 #elif defined(LANGUAGE_INCLUDE)
   #error "LANGUAGE_INCLUDE has been replaced by LCD_LANGUAGE. Please update your configuration."
 #elif defined(EXTRUDER_OFFSET_X) || defined(EXTRUDER_OFFSET_Y)
@@ -1081,23 +1079,25 @@ static_assert(1 >= 0
 #endif
 
 /**
- * Endstops
+ * Endstop Tests
  */
-#if DISABLED(USE_XMIN_PLUG) && DISABLED(USE_XMAX_PLUG) && !(ENABLED(Z_DUAL_ENDSTOPS) && WITHIN(Z2_USE_ENDSTOP, _XMAX_, _XMIN_))
- #error "You must enable USE_XMIN_PLUG or USE_XMAX_PLUG."
-#elif DISABLED(USE_YMIN_PLUG) && DISABLED(USE_YMAX_PLUG) && !(ENABLED(Z_DUAL_ENDSTOPS) && WITHIN(Z2_USE_ENDSTOP, _YMAX_, _YMIN_))
- #error "You must enable USE_YMIN_PLUG or USE_YMAX_PLUG."
-#elif DISABLED(USE_ZMIN_PLUG) && DISABLED(USE_ZMAX_PLUG) && !(ENABLED(Z_DUAL_ENDSTOPS) && WITHIN(Z2_USE_ENDSTOP, _ZMAX_, _ZMIN_))
- #error "You must enable USE_ZMIN_PLUG or USE_ZMAX_PLUG."
-#elif ENABLED(Z_DUAL_ENDSTOPS)
-  #if !Z2_USE_ENDSTOP
-    #error "You must set Z2_USE_ENDSTOP with Z_DUAL_ENDSTOPS."
-  #elif Z2_MAX_PIN == 0 && Z2_MIN_PIN == 0
-    #error "Z2_USE_ENDSTOP has been assigned to a nonexistent endstop!"
-  #elif ENABLED(DELTA)
-    #error "Z_DUAL_ENDSTOPS is not compatible with DELTA."
-  #endif
-#elif !IS_SCARA
+
+#define _PLUG_UNUSED_TEST(AXIS,PLUG) (DISABLED(USE_##PLUG##MIN_PLUG) && DISABLED(USE_##PLUG##MAX_PLUG) && !(ENABLED(AXIS##_DUAL_ENDSTOPS) && WITHIN(AXIS##2_USE_ENDSTOP, _##PLUG##MAX_, _##PLUG##MIN_)))
+#define _AXIS_PLUG_UNUSED_TEST(AXIS) (_PLUG_UNUSED_TEST(AXIS,X) && _PLUG_UNUSED_TEST(AXIS,Y) && _PLUG_UNUSED_TEST(AXIS,Z))
+
+// At least 3 endstop plugs must be used
+#if _AXIS_PLUG_UNUSED_TEST(X)
+  #error "You must enable USE_XMIN_PLUG or USE_XMAX_PLUG."
+#endif
+#if _AXIS_PLUG_UNUSED_TEST(Y)
+  #error "You must enable USE_YMIN_PLUG or USE_YMAX_PLUG."
+#endif
+#if _AXIS_PLUG_UNUSED_TEST(Z)
+  #error "You must enable USE_ZMIN_PLUG or USE_ZMAX_PLUG."
+#endif
+
+// Delta and Cartesian use 3 homing endstops
+#if !IS_SCARA
   #if X_HOME_DIR < 0 && DISABLED(USE_XMIN_PLUG)
     #error "Enable USE_XMIN_PLUG when homing X to MIN."
   #elif X_HOME_DIR > 0 && DISABLED(USE_XMAX_PLUG)
@@ -1106,10 +1106,76 @@ static_assert(1 >= 0
     #error "Enable USE_YMIN_PLUG when homing Y to MIN."
   #elif Y_HOME_DIR > 0 && DISABLED(USE_YMAX_PLUG)
     #error "Enable USE_YMAX_PLUG when homing Y to MAX."
-  #elif Z_HOME_DIR < 0 && DISABLED(USE_ZMIN_PLUG)
-    #error "Enable USE_ZMIN_PLUG when homing Z to MIN."
-  #elif Z_HOME_DIR > 0 && DISABLED(USE_ZMAX_PLUG)
-    #error "Enable USE_ZMAX_PLUG when homing Z to MAX."
+  #endif
+#endif
+#if Z_HOME_DIR < 0 && DISABLED(USE_ZMIN_PLUG)
+  #error "Enable USE_ZMIN_PLUG when homing Z to MIN."
+#elif Z_HOME_DIR > 0 && DISABLED(USE_ZMAX_PLUG)
+  #error "Enable USE_ZMAX_PLUG when homing Z to MAX."
+#endif
+
+// Dual endstops requirements
+#if ENABLED(X_DUAL_ENDSTOPS)
+  #if !X2_USE_ENDSTOP
+    #error "You must set X2_USE_ENDSTOP with X_DUAL_ENDSTOPS."
+  #elif X2_USE_ENDSTOP == _X_MIN_ && DISABLED(USE_XMIN_PLUG)
+    #error "USE_XMIN_PLUG is required when X2_USE_ENDSTOP is _X_MIN_."
+  #elif X2_USE_ENDSTOP == _X_MAX_ && DISABLED(USE_XMAX_PLUG)
+    #error "USE_XMAX_PLUG is required when X2_USE_ENDSTOP is _X_MAX_."
+  #elif X2_USE_ENDSTOP == _Y_MIN_ && DISABLED(USE_YMIN_PLUG)
+    #error "USE_YMIN_PLUG is required when X2_USE_ENDSTOP is _Y_MIN_."
+  #elif X2_USE_ENDSTOP == _Y_MAX_ && DISABLED(USE_YMAX_PLUG)
+    #error "USE_YMAX_PLUG is required when X2_USE_ENDSTOP is _Y_MAX_."
+  #elif X2_USE_ENDSTOP == _Z_MIN_ && DISABLED(USE_ZMIN_PLUG)
+    #error "USE_ZMIN_PLUG is required when X2_USE_ENDSTOP is _Z_MIN_."
+  #elif X2_USE_ENDSTOP == _Z_MAX_ && DISABLED(USE_ZMAX_PLUG)
+    #error "USE_ZMAX_PLUG is required when X2_USE_ENDSTOP is _Z_MAX_."
+  #elif !HAS_X2_MIN && !HAS_X2_MAX
+    #error "X2_USE_ENDSTOP has been assigned to a nonexistent endstop!"
+  #elif ENABLED(DELTA)
+    #error "X_DUAL_ENDSTOPS is not compatible with DELTA."
+  #endif
+#endif
+#if ENABLED(Y_DUAL_ENDSTOPS)
+  #if !Y2_USE_ENDSTOP
+    #error "You must set Y2_USE_ENDSTOP with Y_DUAL_ENDSTOPS."
+  #elif Y2_USE_ENDSTOP == _X_MIN_ && DISABLED(USE_XMIN_PLUG)
+    #error "USE_XMIN_PLUG is required when Y2_USE_ENDSTOP is _X_MIN_."
+  #elif Y2_USE_ENDSTOP == _X_MAX_ && DISABLED(USE_XMAX_PLUG)
+    #error "USE_XMAX_PLUG is required when Y2_USE_ENDSTOP is _X_MAX_."
+  #elif Y2_USE_ENDSTOP == _Y_MIN_ && DISABLED(USE_YMIN_PLUG)
+    #error "USE_YMIN_PLUG is required when Y2_USE_ENDSTOP is _Y_MIN_."
+  #elif Y2_USE_ENDSTOP == _Y_MAX_ && DISABLED(USE_YMAX_PLUG)
+    #error "USE_YMAX_PLUG is required when Y2_USE_ENDSTOP is _Y_MAX_."
+  #elif Y2_USE_ENDSTOP == _Z_MIN_ && DISABLED(USE_ZMIN_PLUG)
+    #error "USE_ZMIN_PLUG is required when Y2_USE_ENDSTOP is _Z_MIN_."
+  #elif Y2_USE_ENDSTOP == _Z_MAX_ && DISABLED(USE_ZMAX_PLUG)
+    #error "USE_ZMAX_PLUG is required when Y2_USE_ENDSTOP is _Z_MAX_."
+  #elif !HAS_Y2_MIN && !HAS_Y2_MAX
+    #error "Y2_USE_ENDSTOP has been assigned to a nonexistent endstop!"
+  #elif ENABLED(DELTA)
+    #error "Y_DUAL_ENDSTOPS is not compatible with DELTA."
+  #endif
+#endif
+#if ENABLED(Z_DUAL_ENDSTOPS)
+  #if !Z2_USE_ENDSTOP
+    #error "You must set Z2_USE_ENDSTOP with Z_DUAL_ENDSTOPS."
+  #elif Z2_USE_ENDSTOP == _X_MIN_ && DISABLED(USE_XMIN_PLUG)
+    #error "USE_XMIN_PLUG is required when Z2_USE_ENDSTOP is _X_MIN_."
+  #elif Z2_USE_ENDSTOP == _X_MAX_ && DISABLED(USE_XMAX_PLUG)
+    #error "USE_XMAX_PLUG is required when Z2_USE_ENDSTOP is _X_MAX_."
+  #elif Z2_USE_ENDSTOP == _Y_MIN_ && DISABLED(USE_YMIN_PLUG)
+    #error "USE_YMIN_PLUG is required when Z2_USE_ENDSTOP is _Y_MIN_."
+  #elif Z2_USE_ENDSTOP == _Y_MAX_ && DISABLED(USE_YMAX_PLUG)
+    #error "USE_YMAX_PLUG is required when Z2_USE_ENDSTOP is _Y_MAX_."
+  #elif Z2_USE_ENDSTOP == _Z_MIN_ && DISABLED(USE_ZMIN_PLUG)
+    #error "USE_ZMIN_PLUG is required when Z2_USE_ENDSTOP is _Z_MIN_."
+  #elif Z2_USE_ENDSTOP == _Z_MAX_ && DISABLED(USE_ZMAX_PLUG)
+    #error "USE_ZMAX_PLUG is required when Z2_USE_ENDSTOP is _Z_MAX_."
+  #elif !HAS_Z2_MIN && !HAS_Z2_MAX
+    #error "Z2_USE_ENDSTOP has been assigned to a nonexistent endstop!"
+  #elif ENABLED(DELTA)
+    #error "Z_DUAL_ENDSTOPS is not compatible with DELTA."
   #endif
 #endif
 
diff --git a/Marlin/src/module/configuration_store.cpp b/Marlin/src/module/configuration_store.cpp
index 99d9eb9f4dfa7b34324aa016274cb1432f81aa00..2b67ad5024773976b7cd8598628fe0aaa4bf4efd 100644
--- a/Marlin/src/module/configuration_store.cpp
+++ b/Marlin/src/module/configuration_store.cpp
@@ -36,13 +36,13 @@
  *
  */
 
-#define EEPROM_VERSION "V42"
+#define EEPROM_VERSION "V43"
 
 // Change EEPROM version if these are changed:
 #define EEPROM_OFFSET 100
 
 /**
- * V42 EEPROM Layout:
+ * V43 EEPROM Layout:
  *
  *  100  Version                                    (char x4)
  *  104  EEPROM CRC16                               (uint16_t)
@@ -68,7 +68,7 @@
  *  219            z_fade_height                    (float)
  *
  * MESH_BED_LEVELING:                               43 bytes
- *  223  M420 S    planner.leveling_active         (bool)
+ *  223  M420 S    planner.leveling_active          (bool)
  *  224            mbl.z_offset                     (float)
  *  228            GRID_MAX_POINTS_X                (uint8_t)
  *  229            GRID_MAX_POINTS_Y                (uint8_t)
@@ -91,78 +91,79 @@
  *  324  G29 A     planner.leveling_active          (bool)
  *  325  G29 S     ubl.storage_slot                 (int8_t)
  *
- * DELTA:                                           48 bytes
- *  344  M666 XYZ  delta_endstop_adj                (float x3)
- *  360  M665 R    delta_radius                     (float)
- *  364  M665 L    delta_diagonal_rod               (float)
- *  368  M665 S    delta_segments_per_second        (float)
- *  372  M665 B    delta_calibration_radius         (float)
- *  376  M665 X    delta_tower_angle_trim[A]        (float)
- *  380  M665 Y    delta_tower_angle_trim[B]        (float)
- *  384  M665 Z    delta_tower_angle_trim[C]        (float)
+ * DELTA:                                           40 bytes
+ *  352  M666 XYZ  delta_endstop_adj                (float x3)
+ *  364  M665 R    delta_radius                     (float)
+ *  368  M665 L    delta_diagonal_rod               (float)
+ *  372  M665 S    delta_segments_per_second        (float)
+ *  376  M665 B    delta_calibration_radius         (float)
+ *  380  M665 X    delta_tower_angle_trim[A]        (float)
+ *  384  M665 Y    delta_tower_angle_trim[B]        (float)
+ *  388  M665 Z    delta_tower_angle_trim[C]        (float)
  *
- * Z_DUAL_ENDSTOPS:                                 48 bytes
- *  348  M666 Z    endstops.z_endstop_adj           (float)
- *  ---            dummy data                       (float x11)
+ * [XYZ]_DUAL_ENDSTOPS:                             12 bytes
+ *  352  M666 X    endstops.x_endstop_adj           (float)
+ *  356  M666 Y    endstops.y_endstop_adj           (float)
+ *  360  M666 Z    endstops.z_endstop_adj           (float)
  *
  * ULTIPANEL:                                       6 bytes
- *  396  M145 S0 H lcd_preheat_hotend_temp          (int x2)
- *  400  M145 S0 B lcd_preheat_bed_temp             (int x2)
- *  404  M145 S0 F lcd_preheat_fan_speed            (int x2)
+ *  392  M145 S0 H lcd_preheat_hotend_temp          (int x2)
+ *  396  M145 S0 B lcd_preheat_bed_temp             (int x2)
+ *  400  M145 S0 F lcd_preheat_fan_speed            (int x2)
  *
- * PIDTEMP:                                         66 bytes
- *  408  M301 E0 PIDC  Kp[0], Ki[0], Kd[0], Kc[0]   (float x4)
- *  424  M301 E1 PIDC  Kp[1], Ki[1], Kd[1], Kc[1]   (float x4)
- *  440  M301 E2 PIDC  Kp[2], Ki[2], Kd[2], Kc[2]   (float x4)
- *  456  M301 E3 PIDC  Kp[3], Ki[3], Kd[3], Kc[3]   (float x4)
- *  472  M301 E4 PIDC  Kp[3], Ki[3], Kd[3], Kc[3]   (float x4)
- *  488  M301 L        lpq_len                      (int)
+ * PIDTEMP:                                         82 bytes
+ *  404  M301 E0 PIDC  Kp[0], Ki[0], Kd[0], Kc[0]   (float x4)
+ *  420  M301 E1 PIDC  Kp[1], Ki[1], Kd[1], Kc[1]   (float x4)
+ *  436  M301 E2 PIDC  Kp[2], Ki[2], Kd[2], Kc[2]   (float x4)
+ *  452  M301 E3 PIDC  Kp[3], Ki[3], Kd[3], Kc[3]   (float x4)
+ *  468  M301 E4 PIDC  Kp[3], Ki[3], Kd[3], Kc[3]   (float x4)
+ *  484  M301 L        lpq_len                      (int)
  *
  * PIDTEMPBED:                                      12 bytes
- *  490  M304 PID  thermalManager.bedKp, .bedKi, .bedKd (float x3)
+ *  486  M304 PID  thermalManager.bedKp, .bedKi, .bedKd (float x3)
  *
  * DOGLCD:                                          2 bytes
- *  502  M250 C    lcd_contrast                     (uint16_t)
+ *  498  M250 C    lcd_contrast                     (uint16_t)
  *
  * FWRETRACT:                                       33 bytes
- *  504  M209 S    autoretract_enabled              (bool)
- *  505  M207 S    retract_length                   (float)
- *  509  M207 F    retract_feedrate_mm_s            (float)
- *  513  M207 Z    retract_zlift                    (float)
- *  517  M208 S    retract_recover_length           (float)
- *  521  M208 F    retract_recover_feedrate_mm_s    (float)
- *  525  M207 W    swap_retract_length              (float)
- *  529  M208 W    swap_retract_recover_length      (float)
- *  533  M208 R    swap_retract_recover_feedrate_mm_s (float)
+ *  500  M209 S    autoretract_enabled              (bool)
+ *  501  M207 S    retract_length                   (float)
+ *  505  M207 F    retract_feedrate_mm_s            (float)
+ *  509  M207 Z    retract_zlift                    (float)
+ *  513  M208 S    retract_recover_length           (float)
+ *  517  M208 F    retract_recover_feedrate_mm_s    (float)
+ *  521  M207 W    swap_retract_length              (float)
+ *  525  M208 W    swap_retract_recover_length      (float)
+ *  529  M208 R    swap_retract_recover_feedrate_mm_s (float)
  *
  * Volumetric Extrusion:                            21 bytes
- *  537  M200 D    parser.volumetric_enabled        (bool)
- *  538  M200 T D  planner.filament_size            (float x5) (T0..3)
+ *  533  M200 D    volumetric_enabled               (bool)
+ *  534  M200 T D  filament_size                    (float x5) (T0..3)
  *
- * HAVE_TMC2130:                                    20 bytes
- *  558  M906 X    Stepper X current                (uint16_t)
- *  560  M906 Y    Stepper Y current                (uint16_t)
- *  562  M906 Z    Stepper Z current                (uint16_t)
- *  564  M906 X2   Stepper X2 current               (uint16_t)
- *  566  M906 Y2   Stepper Y2 current               (uint16_t)
- *  568  M906 Z2   Stepper Z2 current               (uint16_t)
- *  570  M906 E0   Stepper E0 current               (uint16_t)
- *  572  M906 E1   Stepper E1 current               (uint16_t)
- *  574  M906 E2   Stepper E2 current               (uint16_t)
- *  576  M906 E3   Stepper E3 current               (uint16_t)
- *  580  M906 E4   Stepper E4 current               (uint16_t)
+ * HAVE_TMC2130:                                    22 bytes
+ *  554  M906 X    Stepper X current                (uint16_t)
+ *  556  M906 Y    Stepper Y current                (uint16_t)
+ *  558  M906 Z    Stepper Z current                (uint16_t)
+ *  560  M906 X2   Stepper X2 current               (uint16_t)
+ *  562  M906 Y2   Stepper Y2 current               (uint16_t)
+ *  564  M906 Z2   Stepper Z2 current               (uint16_t)
+ *  566  M906 E0   Stepper E0 current               (uint16_t)
+ *  568  M906 E1   Stepper E1 current               (uint16_t)
+ *  570  M906 E2   Stepper E2 current               (uint16_t)
+ *  572  M906 E3   Stepper E3 current               (uint16_t)
+ *  574  M906 E4   Stepper E4 current               (uint16_t)
  *
  * LIN_ADVANCE:                                     8 bytes
- *  584  M900 K    extruder_advance_k               (float)
- *  588  M900 WHD  advance_ed_ratio                 (float)
+ *  576  M900 K    extruder_advance_k               (float)
+ *  580  M900 WHD  advance_ed_ratio                 (float)
  *
  * HAS_MOTOR_CURRENT_PWM:
- *  592  M907 X    Stepper XY current               (uint32_t)
- *  596  M907 Z    Stepper Z current                (uint32_t)
- *  600  M907 E    Stepper E current                (uint32_t)
+ *  584  M907 X    Stepper XY current               (uint32_t)
+ *  588  M907 Z    Stepper Z current                (uint32_t)
+ *  592  M907 E    Stepper E current                (uint32_t)
  *
- *  604                                Minimum end-point
- * 1925 (604 + 36 + 9 + 288 + 988)     Maximum end-point
+ *  596                                Minimum end-point
+ * 1917 (596 + 36 + 9 + 288 + 988)     Maximum end-point
  *
  * ========================================================================
  * meshes_begin (between max and min end-point, directly above)
@@ -419,7 +420,7 @@ void MarlinSettings::postprocess() {
       EEPROM_WRITE(storage_slot);
     #endif // AUTO_BED_LEVELING_UBL
 
-    // 10 floats for DELTA / Z_DUAL_ENDSTOPS
+    // 10 floats for DELTA / [XYZ]_DUAL_ENDSTOPS
     #if ENABLED(DELTA)
       EEPROM_WRITE(delta_endstop_adj);         // 3 floats
       EEPROM_WRITE(delta_radius);              // 1 float
@@ -427,15 +428,33 @@ void MarlinSettings::postprocess() {
       EEPROM_WRITE(delta_segments_per_second); // 1 float
       EEPROM_WRITE(delta_calibration_radius);  // 1 float
       EEPROM_WRITE(delta_tower_angle_trim);    // 3 floats
+
+    #elif ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
+      // Write dual endstops in X, Y, Z order. Unused = 0.0
       dummy = 0.0f;
-      for (uint8_t q = 2; q--;) EEPROM_WRITE(dummy);
-    #elif ENABLED(Z_DUAL_ENDSTOPS)
-      EEPROM_WRITE(endstops.z_endstop_adj);    // 1 float
-      dummy = 0.0f;
-      for (uint8_t q = 11; q--;) EEPROM_WRITE(dummy);
+      #if ENABLED(X_DUAL_ENDSTOPS)
+        EEPROM_WRITE(endstops.x_endstop_adj);   // 1 float
+      #else
+        EEPROM_WRITE(dummy);
+      #endif
+
+      #if ENABLED(Y_DUAL_ENDSTOPS)
+        EEPROM_WRITE(endstops.y_endstop_adj);   // 1 float
+      #else
+        EEPROM_WRITE(dummy);
+      #endif
+
+      #if ENABLED(Z_DUAL_ENDSTOPS)
+        EEPROM_WRITE(endstops.z_endstop_adj);   // 1 float
+      #else
+        EEPROM_WRITE(dummy);
+      #endif
+
+      for (uint8_t q = 7; q--;) EEPROM_WRITE(dummy);
+
     #else
       dummy = 0.0f;
-      for (uint8_t q = 12; q--;) EEPROM_WRITE(dummy);
+      for (uint8_t q = 10; q--;) EEPROM_WRITE(dummy);
     #endif
 
     #if DISABLED(ULTIPANEL)
@@ -638,6 +657,7 @@ void MarlinSettings::postprocess() {
       if (ubl.storage_slot >= 0)
         store_mesh(ubl.storage_slot);
     #endif
+
     return !eeprom_error;
   }
 
@@ -814,13 +834,31 @@ void MarlinSettings::postprocess() {
         EEPROM_READ(delta_tower_angle_trim);    // 3 floats
         dummy = 0.0f;
         for (uint8_t q=2; q--;) EEPROM_READ(dummy);
-      #elif ENABLED(Z_DUAL_ENDSTOPS)
-        EEPROM_READ(endstops.z_endstop_adj);    // 1 float
-        dummy = 0.0f;
-        for (uint8_t q=11; q--;) EEPROM_READ(dummy);
+
+      #elif ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
+
+        #if ENABLED(X_DUAL_ENDSTOPS)
+          EEPROM_READ(endstops.x_endstop_adj);  // 1 float
+        #else
+          EEPROM_READ(dummy);
+        #endif
+        #if ENABLED(Y_DUAL_ENDSTOPS)
+          EEPROM_READ(endstops.y_endstop_adj);  // 1 float
+        #else
+          EEPROM_READ(dummy);
+        #endif
+        #if ENABLED(Z_DUAL_ENDSTOPS)
+          EEPROM_READ(endstops.z_endstop_adj); // 1 float
+        #else
+          EEPROM_READ(dummy);
+        #endif
+
+        for (uint8_t q=7; q--;) EEPROM_READ(dummy);
+
       #else
-        dummy = 0.0f;
-        for (uint8_t q=12; q--;) EEPROM_READ(dummy);
+
+        for (uint8_t q=10; q--;) EEPROM_READ(dummy);
+
       #endif
 
       #if DISABLED(ULTIPANEL)
@@ -1218,15 +1256,35 @@ void MarlinSettings::reset() {
     COPY(delta_tower_angle_trim, dta);
     home_offset[Z_AXIS] = 0;
 
-  #elif ENABLED(Z_DUAL_ENDSTOPS)
+  #elif ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
 
-    endstops.z_endstop_adj =
-      #ifdef Z_DUAL_ENDSTOPS_ADJUSTMENT
-        Z_DUAL_ENDSTOPS_ADJUSTMENT
-      #else
-        0
-      #endif
-    ;
+    #if ENABLED(X_DUAL_ENDSTOPS)
+      endstops.x_endstop_adj = (
+        #ifdef X_DUAL_ENDSTOPS_ADJUSTMENT
+          X_DUAL_ENDSTOPS_ADJUSTMENT
+        #else
+          0
+        #endif
+      );
+    #endif
+    #if ENABLED(Y_DUAL_ENDSTOPS)
+      endstops.y_endstop_adj = (
+        #ifdef Y_DUAL_ENDSTOPS_ADJUSTMENT
+          Y_DUAL_ENDSTOPS_ADJUSTMENT
+        #else
+          0
+        #endif
+      );
+    #endif
+    #if ENABLED(Z_DUAL_ENDSTOPS)
+      endstops.z_endstop_adj = (
+        #ifdef Z_DUAL_ENDSTOPS_ADJUSTMENT
+          Z_DUAL_ENDSTOPS_ADJUSTMENT
+        #else
+          0
+        #endif
+      );
+    #endif
 
   #endif
 
@@ -1627,13 +1685,24 @@ void MarlinSettings::reset() {
       SERIAL_ECHOPAIR(" Y", LINEAR_UNIT(delta_tower_angle_trim[B_AXIS]));
       SERIAL_ECHOPAIR(" Z", LINEAR_UNIT(delta_tower_angle_trim[C_AXIS]));
       SERIAL_EOL();
-    #elif ENABLED(Z_DUAL_ENDSTOPS)
+
+    #elif ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
       if (!forReplay) {
         CONFIG_ECHO_START;
-        SERIAL_ECHOLNPGM("Z2 Endstop adjustment:");
+        SERIAL_ECHOLNPGM("Endstop adjustment:");
       }
       CONFIG_ECHO_START;
-      SERIAL_ECHOLNPAIR("  M666 Z", LINEAR_UNIT(endstops.z_endstop_adj));
+      SERIAL_ECHOPGM("  M666");
+      #if ENABLED(X_DUAL_ENDSTOPS)
+        SERIAL_ECHOPAIR(" X", LINEAR_UNIT(endstops.x_endstop_adj));
+      #endif
+      #if ENABLED(Y_DUAL_ENDSTOPS)
+        SERIAL_ECHOPAIR(" Y", LINEAR_UNIT(endstops.y_endstop_adj));
+      #endif
+      #if ENABLED(Z_DUAL_ENDSTOPS)
+        SERIAL_ECHOPAIR(" Z", LINEAR_UNIT(endstops.z_endstop_adj));
+      #endif
+      SERIAL_EOL();
     #endif // DELTA
 
     #if ENABLED(ULTIPANEL)
@@ -1738,7 +1807,7 @@ void MarlinSettings::reset() {
     #endif // FWRETRACT
 
     /**
-     * Auto Bed Leveling
+     * Probe Offset
      */
     #if HAS_BED_PROBE
       if (!forReplay) {
diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp
index b194abd5df06f62ca5233b7a0398251adc3d3144..dae888611c4f2e3bf1f3b00ae70245f125791909 100644
--- a/Marlin/src/module/endstops.cpp
+++ b/Marlin/src/module/endstops.cpp
@@ -42,7 +42,7 @@ Endstops endstops;
 bool Endstops::enabled, Endstops::enabled_globally; // Initialized by settings.load()
 volatile char Endstops::endstop_hit_bits; // use X_MIN, Y_MIN, Z_MIN and Z_MIN_PROBE as BIT value
 
-#if ENABLED(Z_DUAL_ENDSTOPS)
+#if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
   uint16_t
 #else
   byte
@@ -54,8 +54,14 @@ volatile char Endstops::endstop_hit_bits; // use X_MIN, Y_MIN, Z_MIN and Z_MIN_P
   volatile bool Endstops::z_probe_enabled = false;
 #endif
 
+#if ENABLED(X_DUAL_ENDSTOPS)
+  float Endstops::x_endstop_adj; // Initialized by settings.load()
+#endif
+#if ENABLED(Y_DUAL_ENDSTOPS)
+  float Endstops::y_endstop_adj; // Initialized by settings.load()
+#endif
 #if ENABLED(Z_DUAL_ENDSTOPS)
-  float Endstops::z_endstop_adj;
+  float Endstops::z_endstop_adj; // Initialized by settings.load()
 #endif
 
 /**
@@ -72,6 +78,14 @@ void Endstops::init() {
     #endif
   #endif
 
+  #if HAS_X2_MIN
+    #if ENABLED(ENDSTOPPULLUP_XMIN)
+      SET_INPUT_PULLUP(X2_MIN_PIN);
+    #else
+      SET_INPUT(X2_MIN_PIN);
+    #endif
+  #endif
+
   #if HAS_Y_MIN
     #if ENABLED(ENDSTOPPULLUP_YMIN)
       SET_INPUT_PULLUP(Y_MIN_PIN);
@@ -80,6 +94,14 @@ void Endstops::init() {
     #endif
   #endif
 
+  #if HAS_Y2_MIN
+    #if ENABLED(ENDSTOPPULLUP_YMIN)
+      SET_INPUT_PULLUP(Y2_MIN_PIN);
+    #else
+      SET_INPUT(Y2_MIN_PIN);
+    #endif
+  #endif
+
   #if HAS_Z_MIN
     #if ENABLED(ENDSTOPPULLUP_ZMIN)
       SET_INPUT_PULLUP(Z_MIN_PIN);
@@ -104,6 +126,14 @@ void Endstops::init() {
     #endif
   #endif
 
+  #if HAS_X2_MAX
+    #if ENABLED(ENDSTOPPULLUP_XMAX)
+      SET_INPUT_PULLUP(X2_MAX_PIN);
+    #else
+      SET_INPUT(X2_MAX_PIN);
+    #endif
+  #endif
+
   #if HAS_Y_MAX
     #if ENABLED(ENDSTOPPULLUP_YMAX)
       SET_INPUT_PULLUP(Y_MAX_PIN);
@@ -112,6 +142,14 @@ void Endstops::init() {
     #endif
   #endif
 
+  #if HAS_Y2_MAX
+    #if ENABLED(ENDSTOPPULLUP_YMAX)
+      SET_INPUT_PULLUP(Y2_MAX_PIN);
+    #else
+      SET_INPUT(Y2_MAX_PIN);
+    #endif
+  #endif
+
   #if HAS_Z_MAX
     #if ENABLED(ENDSTOPPULLUP_ZMAX)
       SET_INPUT_PULLUP(Z_MAX_PIN);
@@ -190,37 +228,45 @@ void Endstops::report_state() {
 
 void Endstops::M119() {
   SERIAL_PROTOCOLLNPGM(MSG_M119_REPORT);
+  #define ES_REPORT(AXIS) do{ \
+    SERIAL_PROTOCOLPGM(MSG_##AXIS); \
+    SERIAL_PROTOCOLLN(((READ(AXIS##_PIN)^AXIS##_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN)); \
+  }while(0)
   #if HAS_X_MIN
-    SERIAL_PROTOCOLPGM(MSG_X_MIN);
-    SERIAL_PROTOCOLLN(((READ(X_MIN_PIN)^X_MIN_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN));
+    ES_REPORT(X_MIN);
+  #endif
+  #if HAS_X2_MIN
+    ES_REPORT(X2_MIN);
   #endif
   #if HAS_X_MAX
-    SERIAL_PROTOCOLPGM(MSG_X_MAX);
-    SERIAL_PROTOCOLLN(((READ(X_MAX_PIN)^X_MAX_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN));
+    ES_REPORT(X_MAX);
+  #endif
+  #if HAS_X2_MAX
+    ES_REPORT(X2_MAX);
   #endif
   #if HAS_Y_MIN
-    SERIAL_PROTOCOLPGM(MSG_Y_MIN);
-    SERIAL_PROTOCOLLN(((READ(Y_MIN_PIN)^Y_MIN_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN));
+    ES_REPORT(Y_MIN);
+  #endif
+  #if HAS_Y2_MIN
+    ES_REPORT(Y2_MIN);
   #endif
   #if HAS_Y_MAX
-    SERIAL_PROTOCOLPGM(MSG_Y_MAX);
-    SERIAL_PROTOCOLLN(((READ(Y_MAX_PIN)^Y_MAX_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN));
+    ES_REPORT(Y_MAX);
+  #endif
+  #if HAS_Y2_MAX
+    ES_REPORT(Y2_MAX);
   #endif
   #if HAS_Z_MIN
-    SERIAL_PROTOCOLPGM(MSG_Z_MIN);
-    SERIAL_PROTOCOLLN(((READ(Z_MIN_PIN)^Z_MIN_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN));
+    ES_REPORT(Z_MIN);
   #endif
   #if HAS_Z2_MIN
-    SERIAL_PROTOCOLPGM(MSG_Z2_MIN);
-    SERIAL_PROTOCOLLN(((READ(Z2_MIN_PIN)^Z2_MIN_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN));
+    ES_REPORT(Z2_MIN);
   #endif
   #if HAS_Z_MAX
-    SERIAL_PROTOCOLPGM(MSG_Z_MAX);
-    SERIAL_PROTOCOLLN(((READ(Z_MAX_PIN)^Z_MAX_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN));
+    ES_REPORT(Z_MAX);
   #endif
   #if HAS_Z2_MAX
-    SERIAL_PROTOCOLPGM(MSG_Z2_MAX);
-    SERIAL_PROTOCOLLN(((READ(Z2_MAX_PIN)^Z2_MAX_ENDSTOP_INVERTING) ? MSG_ENDSTOP_HIT : MSG_ENDSTOP_OPEN));
+    ES_REPORT(Z2_MAX);
   #endif
   #if ENABLED(Z_MIN_PROBE_ENDSTOP)
     SERIAL_PROTOCOLPGM(MSG_Z_PROBE);
@@ -232,18 +278,35 @@ void Endstops::M119() {
   #endif
 } // Endstops::M119
 
+#if ENABLED(X_DUAL_ENDSTOPS)
+  void Endstops::test_dual_x_endstops(const EndstopEnum es1, const EndstopEnum es2) {
+    const byte x_test = TEST_ENDSTOP(es1) | (TEST_ENDSTOP(es2) << 1); // bit 0 for X, bit 1 for X2
+    if (x_test && stepper.current_block->steps[X_AXIS] > 0) {
+      SBI(endstop_hit_bits, X_MIN);
+      if (!stepper.performing_homing || (x_test == 0x3))  //if not performing home or if both endstops were trigged during homing...
+        stepper.kill_current_block();
+    }
+  }
+#endif
+#if ENABLED(Y_DUAL_ENDSTOPS)
+  void Endstops::test_dual_y_endstops(const EndstopEnum es1, const EndstopEnum es2) {
+    const byte y_test = TEST_ENDSTOP(es1) | (TEST_ENDSTOP(es2) << 1); // bit 0 for Y, bit 1 for Y2
+    if (y_test && stepper.current_block->steps[Y_AXIS] > 0) {
+      SBI(endstop_hit_bits, Y_MIN);
+      if (!stepper.performing_homing || (y_test == 0x3))  //if not performing home or if both endstops were trigged during homing...
+        stepper.kill_current_block();
+    }
+  }
+#endif
 #if ENABLED(Z_DUAL_ENDSTOPS)
-
-  // Pass the result of the endstop test
   void Endstops::test_dual_z_endstops(const EndstopEnum es1, const EndstopEnum es2) {
-    byte z_test = TEST_ENDSTOP(es1) | (TEST_ENDSTOP(es2) << 1); // bit 0 for Z, bit 1 for Z2
+    const byte z_test = TEST_ENDSTOP(es1) | (TEST_ENDSTOP(es2) << 1); // bit 0 for Z, bit 1 for Z2
     if (z_test && stepper.current_block->steps[Z_AXIS] > 0) {
       SBI(endstop_hit_bits, Z_MIN);
       if (!stepper.performing_homing || (z_test == 0x3))  //if not performing home or if both endstops were trigged during homing...
         stepper.kill_current_block();
     }
   }
-
 #endif
 
 // Check endstops - Called from ISR!
@@ -364,16 +427,35 @@ void Endstops::update() {
    */
 
   if (X_MOVE_TEST) {
-    if (stepper.motor_direction(X_AXIS_HEAD)) {
-      if (X_MIN_TEST) { // -direction
-        #if HAS_X_MIN
-          UPDATE_ENDSTOP(X, MIN);
+    if (stepper.motor_direction(X_AXIS_HEAD)) { // -direction
+      #if HAS_X_MIN
+        #if ENABLED(X_DUAL_ENDSTOPS)
+          UPDATE_ENDSTOP_BIT(X, MIN);
+          #if HAS_X2_MIN
+            UPDATE_ENDSTOP_BIT(X2, MIN);
+          #else
+            COPY_BIT(current_endstop_bits, X_MIN, X2_MIN);
+          #endif
+          test_dual_x_endstops(X_MIN, X2_MIN);
+        #else
+          if (X_MIN_TEST) UPDATE_ENDSTOP(X, MIN);
         #endif
-      }
+      #endif
     }
-    else if (X_MAX_TEST) { // +direction
+    else { // +direction
       #if HAS_X_MAX
-        UPDATE_ENDSTOP(X, MAX);
+        #if ENABLED(X_DUAL_ENDSTOPS)
+          UPDATE_ENDSTOP_BIT(X, MAX);
+          #if HAS_X2_MAX
+            UPDATE_ENDSTOP_BIT(X2, MAX);
+          #else
+            COPY_BIT(current_endstop_bits, X_MAX, X2_MAX);
+          #endif
+          test_dual_x_endstops(X_MAX, X2_MAX);
+        #else
+          if (X_MIN_TEST) UPDATE_ENDSTOP(X, MAX);
+        #endif
+
       #endif
     }
   }
@@ -381,12 +463,32 @@ void Endstops::update() {
   if (Y_MOVE_TEST) {
     if (stepper.motor_direction(Y_AXIS_HEAD)) { // -direction
       #if HAS_Y_MIN
-        UPDATE_ENDSTOP(Y, MIN);
+        #if ENABLED(Y_DUAL_ENDSTOPS)
+          UPDATE_ENDSTOP_BIT(Y, MIN);
+          #if HAS_Y2_MIN
+            UPDATE_ENDSTOP_BIT(Y2, MIN);
+          #else
+            COPY_BIT(current_endstop_bits, Y_MIN, Y2_MIN);
+          #endif
+          test_dual_y_endstops(Y_MIN, Y2_MIN);
+        #else
+          UPDATE_ENDSTOP(Y, MIN);
+        #endif
       #endif
     }
     else { // +direction
       #if HAS_Y_MAX
-        UPDATE_ENDSTOP(Y, MAX);
+        #if ENABLED(Y_DUAL_ENDSTOPS)
+          UPDATE_ENDSTOP_BIT(Y, MAX);
+          #if HAS_Y2_MAX
+            UPDATE_ENDSTOP_BIT(Y2, MAX);
+          #else
+            COPY_BIT(current_endstop_bits, Y_MAX, Y2_MAX);
+          #endif
+          test_dual_y_endstops(Y_MAX, Y2_MAX);
+        #else
+          UPDATE_ENDSTOP(Y, MAX);
+        #endif
       #endif
     }
   }
@@ -395,27 +497,21 @@ void Endstops::update() {
     if (stepper.motor_direction(Z_AXIS_HEAD)) { // Z -direction. Gantry down, bed up.
       #if HAS_Z_MIN
         #if ENABLED(Z_DUAL_ENDSTOPS)
-
           UPDATE_ENDSTOP_BIT(Z, MIN);
           #if HAS_Z2_MIN
             UPDATE_ENDSTOP_BIT(Z2, MIN);
           #else
             COPY_BIT(current_endstop_bits, Z_MIN, Z2_MIN);
           #endif
-
           test_dual_z_endstops(Z_MIN, Z2_MIN);
-
-        #else // !Z_DUAL_ENDSTOPS
-
+        #else
           #if ENABLED(Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN)
             if (z_probe_enabled) UPDATE_ENDSTOP(Z, MIN);
           #else
             UPDATE_ENDSTOP(Z, MIN);
           #endif
-
-        #endif // !Z_DUAL_ENDSTOPS
-
-      #endif // HAS_Z_MIN
+        #endif
+      #endif
 
       // When closing the gap check the enabled probe
       #if ENABLED(Z_MIN_PROBE_ENDSTOP)
@@ -427,27 +523,21 @@ void Endstops::update() {
     }
     else { // Z +direction. Gantry up, bed down.
       #if HAS_Z_MAX
-
         // Check both Z dual endstops
         #if ENABLED(Z_DUAL_ENDSTOPS)
-
           UPDATE_ENDSTOP_BIT(Z, MAX);
           #if HAS_Z2_MAX
             UPDATE_ENDSTOP_BIT(Z2, MAX);
           #else
             COPY_BIT(current_endstop_bits, Z_MAX, Z2_MAX);
           #endif
-
           test_dual_z_endstops(Z_MAX, Z2_MAX);
-
         // If this pin is not hijacked for the bed probe
         // then it belongs to the Z endstop
         #elif DISABLED(Z_MIN_PROBE_ENDSTOP) || Z_MAX_PIN != Z_MIN_PROBE_PIN
-
           UPDATE_ENDSTOP(Z, MAX);
-
-        #endif // !Z_MIN_PROBE_PIN...
-      #endif // Z_MAX_PIN
+        #endif
+      #endif
     }
   }
 
@@ -496,6 +586,18 @@ void Endstops::update() {
     #if HAS_Z_MIN_PROBE_PIN
       if (READ(Z_MIN_PROBE_PIN)) SBI(current_endstop_bits_local, Z_MIN_PROBE);
     #endif
+    #if HAS_X2_MIN
+      if (READ(X2_MIN_PIN)) SBI(current_endstop_bits_local, X2_MIN);
+    #endif
+    #if HAS_X2_MAX
+      if (READ(X2_MAX_PIN)) SBI(current_endstop_bits_local, X2_MAX);
+    #endif
+    #if HAS_Y2_MIN
+      if (READ(Y2_MIN_PIN)) SBI(current_endstop_bits_local, Y2_MIN);
+    #endif
+    #if HAS_Y2_MAX
+      if (READ(Y2_MAX_PIN)) SBI(current_endstop_bits_local, Y2_MAX);
+    #endif
     #if HAS_Z2_MIN
       if (READ(Z2_MIN_PIN)) SBI(current_endstop_bits_local, Z2_MIN);
     #endif
@@ -527,6 +629,18 @@ void Endstops::update() {
       #if HAS_Z_MIN_PROBE_PIN
         if (TEST(endstop_change, Z_MIN_PROBE)) SERIAL_PROTOCOLPAIR("  PROBE:", !!TEST(current_endstop_bits_local, Z_MIN_PROBE));
       #endif
+      #if HAS_X2_MIN
+        if (TEST(endstop_change, X2_MIN)) SERIAL_PROTOCOLPAIR("  X2_MIN:", !!TEST(current_endstop_bits_local, X2_MIN));
+      #endif
+      #if HAS_X2_MAX
+        if (TEST(endstop_change, X2_MAX)) SERIAL_PROTOCOLPAIR("  X2_MAX:", !!TEST(current_endstop_bits_local, X2_MAX));
+      #endif
+      #if HAS_Y2_MIN
+        if (TEST(endstop_change, Y2_MIN)) SERIAL_PROTOCOLPAIR("  Y2_MIN:", !!TEST(current_endstop_bits_local, Y2_MIN));
+      #endif
+      #if HAS_Y2_MAX
+        if (TEST(endstop_change, Y2_MAX)) SERIAL_PROTOCOLPAIR("  Y2_MAX:", !!TEST(current_endstop_bits_local, Y2_MAX));
+      #endif
       #if HAS_Z2_MIN
         if (TEST(endstop_change, Z2_MIN)) SERIAL_PROTOCOLPAIR("  Z2_MIN:", !!TEST(current_endstop_bits_local, Z2_MIN));
       #endif
diff --git a/Marlin/src/module/endstops.h b/Marlin/src/module/endstops.h
index c0cef47655ffac75231e52bdbdcef0c4529968cc..24b498d490498eb96ee6009781df54c5283d9f52 100644
--- a/Marlin/src/module/endstops.h
+++ b/Marlin/src/module/endstops.h
@@ -38,6 +38,10 @@ enum EndstopEnum {
   X_MAX,
   Y_MAX,
   Z_MAX,
+  X2_MIN,
+  X2_MAX,
+  Y2_MIN,
+  Y2_MAX,
   Z2_MIN,
   Z2_MAX
 };
@@ -49,8 +53,16 @@ class Endstops {
     static bool enabled, enabled_globally;
     static volatile char endstop_hit_bits; // use X_MIN, Y_MIN, Z_MIN and Z_MIN_PROBE as BIT value
 
+    #if ENABLED(X_DUAL_ENDSTOPS)
+      static float x_endstop_adj;
+    #endif
+    #if ENABLED(Y_DUAL_ENDSTOPS)
+      static float y_endstop_adj;
+    #endif
     #if ENABLED(Z_DUAL_ENDSTOPS)
       static float z_endstop_adj;
+    #endif
+    #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
       typedef uint16_t esbits_t;
     #else
       typedef byte esbits_t;
@@ -113,6 +125,12 @@ class Endstops {
 
   private:
 
+    #if ENABLED(X_DUAL_ENDSTOPS)
+      static void test_dual_x_endstops(const EndstopEnum es1, const EndstopEnum es2);
+    #endif
+    #if ENABLED(Y_DUAL_ENDSTOPS)
+      static void test_dual_y_endstops(const EndstopEnum es1, const EndstopEnum es2);
+    #endif
     #if ENABLED(Z_DUAL_ENDSTOPS)
       static void test_dual_z_endstops(const EndstopEnum es1, const EndstopEnum es2);
     #endif
diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp
index f887429b8350ed3ed8b00100cdc1b8b4e90ea31e..e7cd631ff34b0de9d864cbd47a326f2183132007 100644
--- a/Marlin/src/module/motion.cpp
+++ b/Marlin/src/module/motion.cpp
@@ -1043,9 +1043,15 @@ void homeaxis(const AxisEnum axis) {
     if (axis == Z_AXIS && DEPLOY_PROBE()) return;
   #endif
 
-  // Set a flag for Z motor locking
+  // Set flags for X, Y, Z motor locking
+  #if ENABLED(X_DUAL_ENDSTOPS)
+    if (axis == X_AXIS) stepper.set_homing_flag_x(true);
+  #endif
+  #if ENABLED(Y_DUAL_ENDSTOPS)
+    if (axis == Y_AXIS) stepper.set_homing_flag_y(true);
+  #endif
   #if ENABLED(Z_DUAL_ENDSTOPS)
-    if (axis == Z_AXIS) stepper.set_homing_flag(true);
+    if (axis == Z_AXIS) stepper.set_homing_flag_z(true);
   #endif
 
   // Disable stealthChop if used. Enable diag1 pin on driver.
@@ -1087,25 +1093,41 @@ void homeaxis(const AxisEnum axis) {
     do_homing_move(axis, 2 * bump, get_homing_bump_feedrate(axis));
   }
 
-  #if ENABLED(Z_DUAL_ENDSTOPS)
-    if (axis == Z_AXIS) {
-      float adj = FABS(endstops.z_endstop_adj);
-      bool lockZ1;
-      if (axis_home_dir > 0) {
-        adj = -adj;
-        lockZ1 = (endstops.z_endstop_adj > 0);
+  #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
+    const bool pos_dir = axis_home_dir > 0;
+    #if ENABLED(X_DUAL_ENDSTOPS)
+      if (axis == X_AXIS) {
+        const bool lock_x1 = pos_dir ? (endstops.x_endstop_adj > 0) : (endstops.x_endstop_adj < 0);
+        float adj = FABS(endstops.x_endstop_adj);
+        if (pos_dir) adj = -adj;
+        if (lock_x1) stepper.set_x_lock(true); else stepper.set_x2_lock(true);
+        do_homing_move(axis, adj);
+        if (lock_x1) stepper.set_x_lock(false); else stepper.set_x2_lock(false);
+        stepper.set_homing_flag_x(false);
       }
-      else
-        lockZ1 = (endstops.z_endstop_adj < 0);
-
-      if (lockZ1) stepper.set_z_lock(true); else stepper.set_z2_lock(true);
-
-      // Move to the adjusted endstop height
-      do_homing_move(axis, adj);
-
-      if (lockZ1) stepper.set_z_lock(false); else stepper.set_z2_lock(false);
-      stepper.set_homing_flag(false);
-    } // Z_AXIS
+    #endif
+    #if ENABLED(Y_DUAL_ENDSTOPS)
+      if (axis == Y_AXIS) {
+        const bool lock_y1 = pos_dir ? (endstops.y_endstop_adj > 0) : (endstops.y_endstop_adj < 0);
+        float adj = FABS(endstops.y_endstop_adj);
+        if (pos_dir) adj = -adj;
+        if (lock_y1) stepper.set_y_lock(true); else stepper.set_y2_lock(true);
+        do_homing_move(axis, adj);
+        if (lock_y1) stepper.set_y_lock(false); else stepper.set_y2_lock(false);
+        stepper.set_homing_flag_y(false);
+      }
+    #endif
+    #if ENABLED(Z_DUAL_ENDSTOPS)
+      if (axis == Z_AXIS) {
+        const bool lock_z1 = pos_dir ? (endstops.z_endstop_adj > 0) : (endstops.z_endstop_adj < 0);
+        float adj = FABS(endstops.z_endstop_adj);
+        if (pos_dir) adj = -adj;
+        if (lock_z1) stepper.set_z_lock(true); else stepper.set_z2_lock(true);
+        do_homing_move(axis, adj);
+        if (lock_z1) stepper.set_z_lock(false); else stepper.set_z2_lock(false);
+        stepper.set_homing_flag_z(false);
+      }
+    #endif
   #endif
 
   #if IS_SCARA
diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp
index 22c9ad3d92142ae9f2254d8c868933682835a2e9..c86575edee8519827b56b2b5277a8df0e44e1331 100644
--- a/Marlin/src/module/stepper.cpp
+++ b/Marlin/src/module/stepper.cpp
@@ -83,7 +83,7 @@ block_t* Stepper::current_block = NULL;  // A pointer to the block currently bei
   bool Stepper::abort_on_endstop_hit = false;
 #endif
 
-#if ENABLED(Z_DUAL_ENDSTOPS)
+#if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
   bool Stepper::performing_homing = false;
 #endif
 
@@ -96,6 +96,16 @@ block_t* Stepper::current_block = NULL;  // A pointer to the block currently bei
 uint8_t Stepper::last_direction_bits = 0;        // The next stepping-bits to be output
 uint16_t Stepper::cleaning_buffer_counter = 0;
 
+#if ENABLED(X_DUAL_ENDSTOPS)
+  bool Stepper::locked_x_motor = false;
+  bool Stepper::locked_x2_motor = false;
+#endif
+
+#if ENABLED(Y_DUAL_ENDSTOPS)
+  bool Stepper::locked_y_motor = false;
+  bool Stepper::locked_y2_motor = false;
+#endif
+
 #if ENABLED(Z_DUAL_ENDSTOPS)
   bool Stepper::locked_z_motor = false;
   bool Stepper::locked_z2_motor = false;
@@ -153,26 +163,54 @@ timer_t Stepper::OCR1A_nominal;
 
 volatile long Stepper::endstops_trigsteps[XYZ];
 
+#if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
+  #define LOCKED_X_MOTOR  locked_x_motor
+  #define LOCKED_Y_MOTOR  locked_y_motor
+  #define LOCKED_Z_MOTOR  locked_z_motor
+  #define LOCKED_X2_MOTOR locked_x2_motor
+  #define LOCKED_Y2_MOTOR locked_y2_motor
+  #define LOCKED_Z2_MOTOR locked_z2_motor
+  #define DUAL_ENDSTOP_APPLY_STEP(AXIS,v)                                                                                                             \
+    if (performing_homing) {                                                                                                                          \
+      if (AXIS##_HOME_DIR < 0) {                                                                                                                      \
+        if (!(TEST(endstops.old_endstop_bits, AXIS##_MIN) && (count_direction[AXIS##_AXIS] < 0)) && !LOCKED_##AXIS##_MOTOR) AXIS##_STEP_WRITE(v);     \
+        if (!(TEST(endstops.old_endstop_bits, AXIS##2_MIN) && (count_direction[AXIS##_AXIS] < 0)) && !LOCKED_##AXIS##2_MOTOR) AXIS##2_STEP_WRITE(v);  \
+      }                                                                                                                                               \
+      else {                                                                                                                                          \
+        if (!(TEST(endstops.old_endstop_bits, AXIS##_MAX) && (count_direction[AXIS##_AXIS] > 0)) && !LOCKED_##AXIS##_MOTOR) AXIS##_STEP_WRITE(v);     \
+        if (!(TEST(endstops.old_endstop_bits, AXIS##2_MAX) && (count_direction[AXIS##_AXIS] > 0)) && !LOCKED_##AXIS##2_MOTOR) AXIS##2_STEP_WRITE(v);  \
+      }                                                                                                                                               \
+    }                                                                                                                                                 \
+    else {                                                                                                                                            \
+      AXIS##_STEP_WRITE(v);                                                                                                                           \
+      AXIS##2_STEP_WRITE(v);                                                                                                                          \
+    }
+#endif
+
 #if ENABLED(X_DUAL_STEPPER_DRIVERS)
   #define X_APPLY_DIR(v,Q) do{ X_DIR_WRITE(v); X2_DIR_WRITE((v) != INVERT_X2_VS_X_DIR); }while(0)
-  #define X_APPLY_STEP(v,Q) do{ X_STEP_WRITE(v); X2_STEP_WRITE(v); }while(0)
-#elif ENABLED(DUAL_X_CARRIAGE)
-  #define X_APPLY_DIR(v,ALWAYS) \
-    if (extruder_duplication_enabled || ALWAYS) { \
-      X_DIR_WRITE(v); \
-      X2_DIR_WRITE(v); \
-    } \
-    else { \
-      if (current_block->active_extruder) X2_DIR_WRITE(v); else X_DIR_WRITE(v); \
-    }
-  #define X_APPLY_STEP(v,ALWAYS) \
-    if (extruder_duplication_enabled || ALWAYS) { \
-      X_STEP_WRITE(v); \
-      X2_STEP_WRITE(v); \
-    } \
-    else { \
-      if (current_block->active_extruder) X2_STEP_WRITE(v); else X_STEP_WRITE(v); \
-    }
+  #if ENABLED(DUAL_X_CARRIAGE)
+    #define X_APPLY_DIR(v,ALWAYS) \
+      if (extruder_duplication_enabled || ALWAYS) { \
+        X_DIR_WRITE(v); \
+        X2_DIR_WRITE(v); \
+      } \
+      else { \
+        if (current_block->active_extruder) X2_DIR_WRITE(v); else X_DIR_WRITE(v); \
+      }
+    #define X_APPLY_STEP(v,ALWAYS) \
+      if (extruder_duplication_enabled || ALWAYS) { \
+        X_STEP_WRITE(v); \
+        X2_STEP_WRITE(v); \
+      } \
+      else { \
+        if (current_block->active_extruder) X2_STEP_WRITE(v); else X_STEP_WRITE(v); \
+      }
+  #elif ENABLED(X_DUAL_ENDSTOPS)
+    #define X_APPLY_STEP(v,Q) DUAL_ENDSTOP_APPLY_STEP(X,v)
+  #else
+    #define X_APPLY_STEP(v,Q) do{ X_STEP_WRITE(v); X2_STEP_WRITE(v); }while(0)
+  #endif
 #else
   #define X_APPLY_DIR(v,Q) X_DIR_WRITE(v)
   #define X_APPLY_STEP(v,Q) X_STEP_WRITE(v)
@@ -180,7 +218,11 @@ volatile long Stepper::endstops_trigsteps[XYZ];
 
 #if ENABLED(Y_DUAL_STEPPER_DRIVERS)
   #define Y_APPLY_DIR(v,Q) do{ Y_DIR_WRITE(v); Y2_DIR_WRITE((v) != INVERT_Y2_VS_Y_DIR); }while(0)
-  #define Y_APPLY_STEP(v,Q) do{ Y_STEP_WRITE(v); Y2_STEP_WRITE(v); }while(0)
+  #if ENABLED(Y_DUAL_ENDSTOPS)
+    #define Y_APPLY_STEP(v,Q) DUAL_ENDSTOP_APPLY_STEP(Y,v)
+  #else
+    #define Y_APPLY_STEP(v,Q) do{ Y_STEP_WRITE(v); Y2_STEP_WRITE(v); }while(0)
+  #endif
 #else
   #define Y_APPLY_DIR(v,Q) Y_DIR_WRITE(v)
   #define Y_APPLY_STEP(v,Q) Y_STEP_WRITE(v)
@@ -189,21 +231,7 @@ volatile long Stepper::endstops_trigsteps[XYZ];
 #if ENABLED(Z_DUAL_STEPPER_DRIVERS)
   #define Z_APPLY_DIR(v,Q) do{ Z_DIR_WRITE(v); Z2_DIR_WRITE(v); }while(0)
   #if ENABLED(Z_DUAL_ENDSTOPS)
-    #define Z_APPLY_STEP(v,Q) \
-    if (performing_homing) { \
-      if (Z_HOME_DIR < 0) { \
-        if (!(TEST(endstops.old_endstop_bits, Z_MIN) && (count_direction[Z_AXIS] < 0)) && !locked_z_motor) Z_STEP_WRITE(v); \
-        if (!(TEST(endstops.old_endstop_bits, Z2_MIN) && (count_direction[Z_AXIS] < 0)) && !locked_z2_motor) Z2_STEP_WRITE(v); \
-      } \
-      else { \
-        if (!(TEST(endstops.old_endstop_bits, Z_MAX) && (count_direction[Z_AXIS] > 0)) && !locked_z_motor) Z_STEP_WRITE(v); \
-        if (!(TEST(endstops.old_endstop_bits, Z2_MAX) && (count_direction[Z_AXIS] > 0)) && !locked_z2_motor) Z2_STEP_WRITE(v); \
-      } \
-    } \
-    else { \
-      Z_STEP_WRITE(v); \
-      Z2_STEP_WRITE(v); \
-    }
+    #define Z_APPLY_STEP(v,Q) DUAL_ENDSTOP_APPLY_STEP(Z,v)
   #else
     #define Z_APPLY_STEP(v,Q) do{ Z_STEP_WRITE(v); Z2_STEP_WRITE(v); }while(0)
   #endif
diff --git a/Marlin/src/module/stepper.h b/Marlin/src/module/stepper.h
index aa11ef929bc99ddd211d7694e8aebacefff43223..614b648f906bf89840f7a730073a573e5d22decc 100644
--- a/Marlin/src/module/stepper.h
+++ b/Marlin/src/module/stepper.h
@@ -66,7 +66,7 @@ class Stepper {
       static bool abort_on_endstop_hit;
     #endif
 
-    #if ENABLED(Z_DUAL_ENDSTOPS)
+    #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
       static bool performing_homing;
     #endif
 
@@ -82,6 +82,12 @@ class Stepper {
     static uint8_t last_direction_bits;        // The next stepping-bits to be output
     static uint16_t cleaning_buffer_counter;
 
+    #if ENABLED(X_DUAL_ENDSTOPS)
+      static bool locked_x_motor, locked_x2_motor;
+    #endif
+    #if ENABLED(Y_DUAL_ENDSTOPS)
+      static bool locked_y_motor, locked_y2_motor;
+    #endif
     #if ENABLED(Z_DUAL_ENDSTOPS)
       static bool locked_z_motor, locked_z2_motor;
     #endif
@@ -227,8 +233,20 @@ class Stepper {
       static void microstep_readings();
     #endif
 
+    #if ENABLED(X_DUAL_ENDSTOPS)
+      static FORCE_INLINE void set_homing_flag_x(const bool state) { performing_homing = state; }
+      static FORCE_INLINE void set_x_lock(const bool state) { locked_x_motor = state; }
+      static FORCE_INLINE void set_x2_lock(const bool state) { locked_x2_motor = state; }
+    #endif
+
+    #if ENABLED(Y_DUAL_ENDSTOPS)
+      static FORCE_INLINE void set_homing_flag_y(const bool state) { performing_homing = state; }
+      static FORCE_INLINE void set_y_lock(const bool state) { locked_y_motor = state; }
+      static FORCE_INLINE void set_y2_lock(const bool state) { locked_y2_motor = state; }
+    #endif
+
     #if ENABLED(Z_DUAL_ENDSTOPS)
-      static FORCE_INLINE void set_homing_flag(const bool state) { performing_homing = state; }
+      static FORCE_INLINE void set_homing_flag_z(const bool state) { performing_homing = state; }
       static FORCE_INLINE void set_z_lock(const bool state) { locked_z_motor = state; }
       static FORCE_INLINE void set_z2_lock(const bool state) { locked_z2_motor = state; }
     #endif