diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp
index 66d3c78ca4..b1aeaa40e1 100644
--- a/Marlin/src/MarlinCore.cpp
+++ b/Marlin/src/MarlinCore.cpp
@@ -1136,7 +1136,7 @@ void setup() {
   #endif
 
   #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
-    SETUP_RUN(init_closedloop());
+    SETUP_RUN(closedloop.init());
   #endif
 
   #ifdef STARTUP_COMMANDS
diff --git a/Marlin/src/feature/closedloop.cpp b/Marlin/src/feature/closedloop.cpp
index b777243481..5d302683a1 100644
--- a/Marlin/src/feature/closedloop.cpp
+++ b/Marlin/src/feature/closedloop.cpp
@@ -29,12 +29,14 @@
 
 #include "closedloop.h"
 
-void init_closedloop() {
+ClosedLoop closedloop;
+
+void ClosedLoop::init() {
   OUT_WRITE(CLOSED_LOOP_ENABLE_PIN, LOW);
   SET_INPUT_PULLUP(CLOSED_LOOP_MOVE_COMPLETE_PIN);
 }
 
-void set_closedloop(const byte val) {
+void ClosedLoop::set(const byte val) {
   OUT_WRITE(CLOSED_LOOP_ENABLE_PIN, val);
 }
 
diff --git a/Marlin/src/feature/closedloop.h b/Marlin/src/feature/closedloop.h
index 7e5594447d..75c7109ce6 100644
--- a/Marlin/src/feature/closedloop.h
+++ b/Marlin/src/feature/closedloop.h
@@ -21,5 +21,12 @@
  */
 #pragma once
 
-void init_closedloop();
-void set_closedloop(const byte val);
+class ClosedLoop {
+public:
+  static void init();
+  static void set(const byte val);
+};
+
+extern ClosedLoop closedloop;
+
+#define CLOSED_LOOP_WAITING() (READ(CLOSED_LOOP_ENABLE_PIN) && !READ(CLOSED_LOOP_MOVE_COMPLETE_PIN))
diff --git a/Marlin/src/gcode/calibrate/M12.cpp b/Marlin/src/gcode/calibrate/M12.cpp
index 26f15bb892..97d5bbc1ab 100644
--- a/Marlin/src/gcode/calibrate/M12.cpp
+++ b/Marlin/src/gcode/calibrate/M12.cpp
@@ -28,9 +28,12 @@
 #include "../../feature/closedloop.h"
 
 void GcodeSuite::M12() {
+
   planner.synchronize();
+
   if (parser.seenval('S'))
-    set_closedloop(parser.value_int()); // Force a CLC set
+    closedloop.set(parser.value_int()); // Force a CLC set
+
 }
 
 #endif
diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp
index 5715399e49..1beb63c1a4 100644
--- a/Marlin/src/module/planner.cpp
+++ b/Marlin/src/module/planner.cpp
@@ -91,6 +91,10 @@
   #include "../feature/power.h"
 #endif
 
+#if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
+  #include "../feature/closedloop.h"
+#endif
+
 #if ENABLED(BACKLASH_COMPENSATION)
   #include "../feature/backlash.h"
 #endif
@@ -1634,11 +1638,8 @@ float Planner::get_axis_position_mm(const AxisEnum axis) {
  * Block until all buffered steps are executed / cleaned
  */
 void Planner::synchronize() {
-  while (
-    has_blocks_queued() || cleaning_buffer_counter
-    #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
-      || (READ(CLOSED_LOOP_ENABLE_PIN) && !READ(CLOSED_LOOP_MOVE_COMPLETE_PIN))
-    #endif
+  while (has_blocks_queued() || cleaning_buffer_counter
+      || TERN0(EXTERNAL_CLOSED_LOOP_CONTROLLER, CLOSED_LOOP_WAITING())
   ) idle();
 }
 
diff --git a/buildroot/tests/mega1280-tests b/buildroot/tests/mega1280-tests
index 7f2872a0b4..c9caf7d852 100644
--- a/buildroot/tests/mega1280-tests
+++ b/buildroot/tests/mega1280-tests
@@ -20,8 +20,11 @@ opt_set LCD_LANGUAGE an
 opt_enable SPINDLE_FEATURE ULTIMAKERCONTROLLER LCD_BED_LEVELING \
            SENSORLESS_BACKOFF_MM HOMING_BACKOFF_POST_MM HOME_Y_BEFORE_X CODEPENDENT_XY_HOMING \
            MESH_BED_LEVELING ENABLE_LEVELING_FADE_HEIGHT MESH_G28_REST_ORIGIN \
-           G26_MESH_VALIDATION MESH_EDIT_MENU GCODE_QUOTED_STRINGS
-exec_test $1 $2 "Spindle, MESH_BED_LEVELING, and LCD"
+           G26_MESH_VALIDATION MESH_EDIT_MENU GCODE_QUOTED_STRINGS \
+           EXTERNAL_CLOSED_LOOP_CONTROLLER
+opt_set CLOSED_LOOP_ENABLE_PIN 44
+opt_set CLOSED_LOOP_MOVE_COMPLETE_PIN 45
+exec_test $1 $2 "Spindle, MESH_BED_LEVELING, closed loop, and LCD"
 
 #
 # Test DUAL_X_CARRIAGE