diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h
index a424b1967e..6a96411863 100644
--- a/Marlin/Configuration.h
+++ b/Marlin/Configuration.h
@@ -2756,6 +2756,11 @@
 //
 //#define DWIN_CREALITY_LCD
 
+//
+// Ender-3 v2 OEM display, enhanced.
+//
+//#define DWIN_CREALITY_LCD_ENHANCED
+
 //
 // Ender-3 v2 OEM display with enhancements by Jacob Myers
 //
diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp
index d68e87eb89..49db8c61b9 100644
--- a/Marlin/src/MarlinCore.cpp
+++ b/Marlin/src/MarlinCore.cpp
@@ -77,6 +77,9 @@
 #if ENABLED(DWIN_CREALITY_LCD)
   #include "lcd/e3v2/creality/dwin.h"
   #include "lcd/e3v2/creality/rotary_encoder.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "lcd/e3v2/enhanced/dwin.h"
+  #include "lcd/e3v2/enhanced/rotary_encoder.h"
 #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
   #include "lcd/e3v2/jyersui/dwin.h"
   #include "lcd/e3v2/jyersui/rotary_encoder.h"
@@ -849,7 +852,7 @@ void idle(bool no_stepper_sleep/*=false*/) {
   TERN_(USE_BEEPER, buzzer.tick());
 
   // Handle UI input / draw events
-  TERN(DWIN_CREALITY_LCD, DWIN_Update(), ui.update());
+  TERN(HAS_DWIN_E3V2_BASIC, DWIN_Update(), ui.update());
 
   // Run i2c Position Encoders
   #if ENABLED(I2C_POSITION_ENCODERS)
@@ -904,7 +907,7 @@ void kill(PGM_P const lcd_error/*=nullptr*/, PGM_P const lcd_component/*=nullptr
   // Echo the LCD message to serial for extra context
   if (lcd_error) { SERIAL_ECHO_START(); SERIAL_ECHOLNPGM_P(lcd_error); }
 
-  #if HAS_DISPLAY
+  #if EITHER(HAS_DISPLAY, DWIN_CREALITY_LCD_ENHANCED)
     ui.kill_screen(lcd_error ?: GET_TEXT(MSG_KILLED), lcd_component ?: NUL_STR);
   #else
     UNUSED(lcd_error); UNUSED(lcd_component);
@@ -1315,7 +1318,7 @@ void setup() {
   // UI must be initialized before EEPROM
   // (because EEPROM code calls the UI).
 
-  #if ENABLED(DWIN_CREALITY_LCD)
+  #if HAS_DWIN_E3V2_BASIC
     SETUP_RUN(DWIN_Startup());
   #else
     SETUP_RUN(ui.init());
@@ -1590,7 +1593,7 @@ void setup() {
     SERIAL_ECHO_TERNARY(err, "BL24CXX Check ", "failed", "succeeded", "!\n");
   #endif
 
-  #if ENABLED(DWIN_CREALITY_LCD)
+  #if HAS_DWIN_E3V2_BASIC
     Encoder_Configuration();
     HMI_Init();
     HMI_SetLanguageCache();
@@ -1598,7 +1601,7 @@ void setup() {
     DWIN_StatusChanged_P(GET_TEXT(WELCOME_MSG));
   #endif
 
-  #if HAS_SERVICE_INTERVALS && DISABLED(DWIN_CREALITY_LCD)
+  #if HAS_SERVICE_INTERVALS && !HAS_DWIN_E3V2_BASIC
     ui.reset_status(true);  // Show service messages or keep current status
   #endif
 
diff --git a/Marlin/src/feature/pause.cpp b/Marlin/src/feature/pause.cpp
index 9a402141e6..f1d6dbb985 100644
--- a/Marlin/src/feature/pause.cpp
+++ b/Marlin/src/feature/pause.cpp
@@ -53,6 +53,8 @@
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #include "../lcd/marlinui.h"
@@ -242,6 +244,7 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load
 
     TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_FILAMENT_CHANGE_PURGE)));
     TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_FILAMENT_CHANGE_PURGE), CONTINUE_STR));
+    TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_Popup_Confirm(ICON_BLTouch, GET_TEXT(MSG_FILAMENT_CHANGE_PURGE), CONTINUE_STR));
     wait_for_user = true; // A click or M108 breaks the purge_length loop
     for (float purge_count = purge_length; purge_count > 0 && wait_for_user; --purge_count)
       unscaled_e_move(1, ADVANCED_PAUSE_PURGE_FEEDRATE);
@@ -265,7 +268,7 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load
           // Show "Purge More" / "Resume" menu and wait for reply
           KEEPALIVE_STATE(PAUSED_FOR_USER);
           wait_for_user = false;
-          #if HAS_LCD_MENU
+          #if EITHER(HAS_LCD_MENU, DWIN_CREALITY_LCD_ENHANCED)
             ui.pause_show_message(PAUSE_MESSAGE_OPTION); // Also sets PAUSE_RESPONSE_WAIT_FOR
           #else
             pause_menu_response = PAUSE_RESPONSE_WAIT_FOR;
@@ -525,6 +528,8 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
 
       TERN_(EXTENSIBLE_UI, ExtUI::onStatusChanged_P(GET_TEXT(MSG_REHEATING)));
 
+      TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status_P(GET_TEXT(MSG_REHEATING)));
+
       // Re-enable the heaters if they timed out
       HOTEND_LOOP() thermalManager.reset_hotend_idle_timer(e);
 
@@ -538,8 +543,13 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep
       const millis_t nozzle_timeout = SEC_TO_MS(PAUSE_PARK_NOZZLE_TIMEOUT);
 
       HOTEND_LOOP() thermalManager.heater_idle[e].start(nozzle_timeout);
+
       TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_REHEATDONE), CONTINUE_STR));
+
       TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_REHEATDONE)));
+
+      TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status_P(GET_TEXT(MSG_REHEATDONE)));
+
       wait_for_user = true;
       nozzle_timed_out = false;
 
@@ -675,6 +685,7 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_
 
   TERN_(HAS_STATUS_MESSAGE, ui.reset_status());
   TERN_(HAS_LCD_MENU, ui.return_to_status());
+  TERN_(DWIN_CREALITY_LCD_ENHANCED, HMI_ReturnScreen());
 }
 
 #endif // ADVANCED_PAUSE_FEATURE
diff --git a/Marlin/src/feature/powerloss.cpp b/Marlin/src/feature/powerloss.cpp
index 3409e6714d..c86cb4f0d6 100644
--- a/Marlin/src/feature/powerloss.cpp
+++ b/Marlin/src/feature/powerloss.cpp
@@ -40,7 +40,7 @@ uint8_t PrintJobRecovery::queue_index_r;
 uint32_t PrintJobRecovery::cmd_sdpos, // = 0
          PrintJobRecovery::sdpos[BUFSIZE];
 
-#if ENABLED(DWIN_CREALITY_LCD)
+#if HAS_DWIN_E3V2_BASIC
   bool PrintJobRecovery::dwin_flag; // = false
 #endif
 
diff --git a/Marlin/src/feature/powerloss.h b/Marlin/src/feature/powerloss.h
index d3ecc6c9cc..6a13c92df7 100644
--- a/Marlin/src/feature/powerloss.h
+++ b/Marlin/src/feature/powerloss.h
@@ -145,7 +145,7 @@ class PrintJobRecovery {
     static uint32_t cmd_sdpos,        //!< SD position of the next command
                     sdpos[BUFSIZE];   //!< SD positions of queued commands
 
-    #if ENABLED(DWIN_CREALITY_LCD)
+    #if HAS_DWIN_E3V2_BASIC
       static bool dwin_flag;
     #endif
 
diff --git a/Marlin/src/feature/runout.cpp b/Marlin/src/feature/runout.cpp
index 531ca1081f..ef1f876bdf 100644
--- a/Marlin/src/feature/runout.cpp
+++ b/Marlin/src/feature/runout.cpp
@@ -68,6 +68,8 @@ bool FilamentMonitorBase::enabled = true,
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 void event_filament_runout(const uint8_t extruder) {
@@ -86,6 +88,7 @@ void event_filament_runout(const uint8_t extruder) {
   #endif
 
   TERN_(EXTENSIBLE_UI, ExtUI::onFilamentRunout(ExtUI::getTool(extruder)));
+  TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_FilamentRunout(extruder));
 
   #if ANY(HOST_PROMPT_SUPPORT, HOST_ACTION_COMMANDS, MULTI_FILAMENT_SENSOR)
     const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, extruder);
diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp
index ca36f6d46e..f756aa89df 100644
--- a/Marlin/src/gcode/bedlevel/abl/G29.cpp
+++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp
@@ -58,10 +58,10 @@
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../../../lcd/extui/ui_api.h"
-#endif
-
-#if ENABLED(DWIN_CREALITY_LCD)
+#elif ENABLED(DWIN_CREALITY_LCD)
   #include "../../../lcd/e3v2/creality/dwin.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #if HAS_MULTI_HOTEND
@@ -403,10 +403,9 @@ G29_TYPE GcodeSuite::G29() {
     #if ENABLED(AUTO_BED_LEVELING_3POINT)
       if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> 3-point Leveling");
       points[0].z = points[1].z = points[2].z = 0;  // Probe at 3 arbitrary points
-    #endif
-
-    #if BOTH(AUTO_BED_LEVELING_BILINEAR, EXTENSIBLE_UI)
-      ExtUI::onMeshLevelingStart();
+    #elif ENABLED(AUTO_BED_LEVELING_BILINEAR)
+      TERN_(EXTENSIBLE_UI, ExtUI::onMeshLevelingStart());
+      TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_MeshLevelingStart());
     #endif
 
     if (!faux) {
@@ -886,9 +885,7 @@ G29_TYPE GcodeSuite::G29() {
     process_subcommands_now_P(PSTR(Z_PROBE_END_SCRIPT));
   #endif
 
-  #if ENABLED(DWIN_CREALITY_LCD)
-    DWIN_CompletedLeveling();
-  #endif
+  TERN_(HAS_DWIN_E3V2_BASIC, DWIN_CompletedLeveling());
 
   report_current_position();
 
diff --git a/Marlin/src/gcode/bedlevel/mbl/G29.cpp b/Marlin/src/gcode/bedlevel/mbl/G29.cpp
index b8ca8bdee5..adfe61d3d2 100644
--- a/Marlin/src/gcode/bedlevel/mbl/G29.cpp
+++ b/Marlin/src/gcode/bedlevel/mbl/G29.cpp
@@ -40,6 +40,8 @@
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../../../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
@@ -191,6 +193,7 @@ void GcodeSuite::G29() {
       if (parser.seenval('Z')) {
         mbl.z_values[ix][iy] = parser.value_linear_units();
         TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ix, iy, mbl.z_values[ix][iy]));
+        TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_MeshUpdate(ix, iy, mbl.z_values[ix][iy]));
       }
       else
         return echo_not_entered('Z');
diff --git a/Marlin/src/gcode/bedlevel/ubl/M421.cpp b/Marlin/src/gcode/bedlevel/ubl/M421.cpp
index f1e1b76126..e6f0ef1f89 100644
--- a/Marlin/src/gcode/bedlevel/ubl/M421.cpp
+++ b/Marlin/src/gcode/bedlevel/ubl/M421.cpp
@@ -33,6 +33,8 @@
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../../../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 /**
@@ -67,6 +69,7 @@ void GcodeSuite::M421() {
     float &zval = ubl.z_values[ij.x][ij.y];                               // Altering this Mesh Point
     zval = hasN ? NAN : parser.value_linear_units() + (hasQ ? zval : 0);  // N=NAN, Z=NEWVAL, or Q=ADDVAL
     TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ij.x, ij.y, zval));          // Ping ExtUI in case it's showing the mesh
+    TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_MeshUpdate(ij.x, ij.y, zval));
   }
 }
 
diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp
index 89ad20d906..d85c0306d4 100644
--- a/Marlin/src/gcode/calibrate/G28.cpp
+++ b/Marlin/src/gcode/calibrate/G28.cpp
@@ -46,12 +46,13 @@
 #endif
 
 #include "../../lcd/marlinui.h"
-#if ENABLED(DWIN_CREALITY_LCD)
-  #include "../../lcd/e3v2/creality/dwin.h"
-#endif
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD)
+  #include "../../lcd/e3v2/creality/dwin.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #if HAS_L64XX                         // set L6470 absolute position registers to counts
@@ -238,7 +239,7 @@ void GcodeSuite::G28() {
     return;
   }
 
-  TERN_(DWIN_CREALITY_LCD, DWIN_StartHoming());
+  TERN_(HAS_DWIN_E3V2_BASIC, DWIN_StartHoming());
   TERN_(EXTENSIBLE_UI, ExtUI::onHomingStart());
 
   planner.synchronize();          // Wait for planner moves to finish!
@@ -522,7 +523,7 @@ void GcodeSuite::G28() {
 
   ui.refresh();
 
-  TERN_(DWIN_CREALITY_LCD, DWIN_CompletedHoming());
+  TERN_(HAS_DWIN_E3V2_BASIC, DWIN_CompletedHoming());
   TERN_(EXTENSIBLE_UI, ExtUI::onHomingComplete());
 
   report_current_position();
diff --git a/Marlin/src/gcode/control/M997.cpp b/Marlin/src/gcode/control/M997.cpp
index cdff96f1ac..73d795bcef 100644
--- a/Marlin/src/gcode/control/M997.cpp
+++ b/Marlin/src/gcode/control/M997.cpp
@@ -24,11 +24,17 @@
 
 #if ENABLED(PLATFORM_M997_SUPPORT)
 
+#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../lcd/e3v2/enhanced/dwin.h"
+#endif
+
 /**
  * M997: Perform in-application firmware update
  */
 void GcodeSuite::M997() {
 
+  TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_RebootScreen());
+
   flashFirmware(parser.intval('S'));
 
 }
diff --git a/Marlin/src/gcode/feature/powerloss/M1000.cpp b/Marlin/src/gcode/feature/powerloss/M1000.cpp
index 0e731016dd..3ebb286b57 100644
--- a/Marlin/src/gcode/feature/powerloss/M1000.cpp
+++ b/Marlin/src/gcode/feature/powerloss/M1000.cpp
@@ -27,9 +27,14 @@
 #include "../../gcode.h"
 #include "../../../feature/powerloss.h"
 #include "../../../module/motion.h"
+
 #include "../../../lcd/marlinui.h"
 #if ENABLED(EXTENSIBLE_UI)
   #include "../../../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD)
+  #include "../../../lcd/e3v2/creality/dwin.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../../lcd/e3v2/enhanced/dwin.h"
 #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
   #include "../../../lcd/e3v2/jyersui/dwin.h" // Temporary fix until it can be better implemented
 #endif
@@ -64,7 +69,7 @@ void GcodeSuite::M1000() {
     if (parser.seen_test('S')) {
       #if HAS_LCD_MENU
         ui.goto_screen(menu_job_recovery);
-      #elif ENABLED(DWIN_CREALITY_LCD)
+      #elif HAS_DWIN_E3V2_BASIC
         recovery.dwin_flag = true;
       #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI) // Temporary fix until it can be better implemented
         CrealityDWIN.Popup_Handler(Resume);
diff --git a/Marlin/src/gcode/lcd/M0_M1.cpp b/Marlin/src/gcode/lcd/M0_M1.cpp
index 414c4ce023..cb37bfec24 100644
--- a/Marlin/src/gcode/lcd/M0_M1.cpp
+++ b/Marlin/src/gcode/lcd/M0_M1.cpp
@@ -35,6 +35,8 @@
   #include "../../lcd/marlinui.h"
 #elif ENABLED(EXTENSIBLE_UI)
   #include "../../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #if ENABLED(HOST_PROMPT_SUPPORT)
@@ -68,6 +70,8 @@ void GcodeSuite::M0_M1() {
       ExtUI::onUserConfirmRequired(parser.string_arg); // Can this take an SRAM string??
     else
       ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_USERWAIT));
+  #elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+    DWIN_Popup_Confirm(ICON_BLTouch, parser.string_arg ?: GET_TEXT(MSG_STOPPED), GET_TEXT(MSG_USERWAIT));
   #else
 
     if (parser.string_arg) {
diff --git a/Marlin/src/gcode/lcd/M73.cpp b/Marlin/src/gcode/lcd/M73.cpp
index 8996e5c88e..b7a9b3459e 100644
--- a/Marlin/src/gcode/lcd/M73.cpp
+++ b/Marlin/src/gcode/lcd/M73.cpp
@@ -28,6 +28,10 @@
 #include "../../lcd/marlinui.h"
 #include "../../sd/cardreader.h"
 
+#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../lcd/e3v2/enhanced/dwin.h"
+#endif
+
 /**
  * M73: Set percentage complete (for display on LCD)
  *
@@ -35,13 +39,23 @@
  *   M73 P25 ; Set progress to 25%
  */
 void GcodeSuite::M73() {
-  if (parser.seenval('P'))
-    ui.set_progress((PROGRESS_SCALE) > 1
-      ? parser.value_float() * (PROGRESS_SCALE)
-      : parser.value_byte()
-    );
-  #if ENABLED(USE_M73_REMAINING_TIME)
-    if (parser.seenval('R')) ui.set_remaining_time(60 * parser.value_ulong());
+
+  #if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+
+    DWIN_Progress_Update();
+
+  #else
+
+    if (parser.seenval('P'))
+      ui.set_progress((PROGRESS_SCALE) > 1
+        ? parser.value_float() * (PROGRESS_SCALE)
+        : parser.value_byte()
+      );
+
+    #if ENABLED(USE_M73_REMAINING_TIME)
+      if (parser.seenval('R')) ui.set_remaining_time(60 * parser.value_ulong());
+    #endif
+
   #endif
 }
 
diff --git a/Marlin/src/gcode/sd/M1001.cpp b/Marlin/src/gcode/sd/M1001.cpp
index cd4933ff27..14bd712d27 100644
--- a/Marlin/src/gcode/sd/M1001.cpp
+++ b/Marlin/src/gcode/sd/M1001.cpp
@@ -48,6 +48,8 @@
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #if ENABLED(HOST_ACTION_COMMANDS)
@@ -106,6 +108,7 @@ void GcodeSuite::M1001() {
   #endif
 
   TERN_(EXTENSIBLE_UI, ExtUI::onPrintFinished());
+  TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_Print_Finished());
 
   // Re-select the last printed file in the UI
   TERN_(SD_REPRINT_LAST_SELECTED_FILE, ui.reselect_last_file());
diff --git a/Marlin/src/gcode/stats/M75-M78.cpp b/Marlin/src/gcode/stats/M75-M78.cpp
index 66f9f8eb8d..b55409946e 100644
--- a/Marlin/src/gcode/stats/M75-M78.cpp
+++ b/Marlin/src/gcode/stats/M75-M78.cpp
@@ -29,11 +29,19 @@
 
 #include "../../MarlinCore.h" // for startOrResumeJob
 
+#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../lcd/e3v2/enhanced/dwin.h"
+#endif
+
 /**
  * M75: Start print timer
  */
 void GcodeSuite::M75() {
   startOrResumeJob();
+  #if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+    DWIN_Print_Header(parser.string_arg && parser.string_arg[0] ? parser.string_arg : GET_TEXT(MSG_HOST_START_PRINT));
+    DWIN_Print_Started(false);
+  #endif
 }
 
 /**
@@ -49,29 +57,30 @@ void GcodeSuite::M76() {
  */
 void GcodeSuite::M77() {
   print_job_timer.stop();
+  TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_Print_Finished());
 }
 
 #if ENABLED(PRINTCOUNTER)
 
-/**
+  /**
    * M78: Show print statistics
    */
-void GcodeSuite::M78() {
-  if (parser.intval('S') == 78) {  // "M78 S78" will reset the statistics
+  void GcodeSuite::M78() {
+    if (parser.intval('S') == 78) {  // "M78 S78" will reset the statistics
       print_job_timer.initStats();
       ui.reset_status();
-    return;
+      return;
     }
 
     #if HAS_SERVICE_INTERVALS
-    if (parser.seenval('R')) {
+      if (parser.seenval('R')) {
         print_job_timer.resetServiceInterval(parser.value_int());
         ui.reset_status();
-      return;
+        return;
       }
     #endif
 
     print_job_timer.showStats();
-}
+  }
 
 #endif // PRINTCOUNTER
diff --git a/Marlin/src/gcode/temp/M303.cpp b/Marlin/src/gcode/temp/M303.cpp
index ad3afe6e46..0d0ce478ee 100644
--- a/Marlin/src/gcode/temp/M303.cpp
+++ b/Marlin/src/gcode/temp/M303.cpp
@@ -30,6 +30,8 @@
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 /**
@@ -71,6 +73,7 @@ void GcodeSuite::M303() {
     default:
       SERIAL_ECHOLNPGM(STR_PID_BAD_HEATER_ID);
       TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_BAD_EXTRUDER_NUM));
+      TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_PidTuning(PID_BAD_EXTRUDER_NUM));
       return;
   }
 
diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h
index 825ad58f9a..a7bde803ba 100644
--- a/Marlin/src/inc/Conditionals_LCD.h
+++ b/Marlin/src/inc/Conditionals_LCD.h
@@ -493,7 +493,10 @@
 #endif
 
 // Aliases for LCD features
-#if ANY(DWIN_CREALITY_LCD, DWIN_CREALITY_LCD_JYERSUI)
+#if EITHER(DWIN_CREALITY_LCD, DWIN_CREALITY_LCD_ENHANCED)
+  #define HAS_DWIN_E3V2_BASIC 1
+#endif
+#if EITHER(HAS_DWIN_E3V2_BASIC, DWIN_CREALITY_LCD_JYERSUI)
   #define HAS_DWIN_E3V2 1
 #endif
 
diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h
index 6de8dc9860..61a9ea2c9a 100644
--- a/Marlin/src/inc/Conditionals_post.h
+++ b/Marlin/src/inc/Conditionals_post.h
@@ -421,7 +421,7 @@
   #endif
 #endif
 
-#if ENABLED(DWIN_CREALITY_LCD_JYERSUI)
+#if EITHER(DWIN_CREALITY_LCD_ENHANCED, DWIN_CREALITY_LCD_JYERSUI)
   #define HAS_LCD_BRIGHTNESS 1
 #endif
 
@@ -2953,7 +2953,7 @@
  * Advanced Pause - Filament Change
  */
 #if ENABLED(ADVANCED_PAUSE_FEATURE)
-  #if ANY(HAS_LCD_MENU, EXTENSIBLE_UI, DWIN_CREALITY_LCD_JYERSUI) || BOTH(EMERGENCY_PARSER, HOST_PROMPT_SUPPORT)
+  #if ANY(HAS_LCD_MENU, EXTENSIBLE_UI, DWIN_CREALITY_LCD_ENHANCED, DWIN_CREALITY_LCD_JYERSUI) || BOTH(EMERGENCY_PARSER, HOST_PROMPT_SUPPORT)
     #define M600_PURGE_MORE_RESUMABLE 1
   #endif
   #ifndef FILAMENT_CHANGE_SLOW_LOAD_LENGTH
diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h
index 59b4a68ff1..3c00d84142 100644
--- a/Marlin/src/inc/SanityCheck.h
+++ b/Marlin/src/inc/SanityCheck.h
@@ -808,7 +808,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
     #error "PROGRESS_MSG_EXPIRE must be greater than or equal to 0."
   #endif
 #elif ENABLED(LCD_SET_PROGRESS_MANUALLY) && NONE(HAS_MARLINUI_U8GLIB, HAS_GRAPHICAL_TFT, HAS_MARLINUI_HD44780, EXTENSIBLE_UI, HAS_DWIN_E3V2, IS_DWIN_MARLINUI)
-  #error "LCD_SET_PROGRESS_MANUALLY requires LCD_PROGRESS_BAR, Character LCD, Graphical LCD, TFT, DWIN_CREALITY_LCD, DWIN_CREALITY_LCD_JYERSUI, DWIN_MARLINUI_*, or EXTENSIBLE_UI."
+  #error "LCD_SET_PROGRESS_MANUALLY requires LCD_PROGRESS_BAR, Character LCD, Graphical LCD, TFT, DWIN_CREALITY_LCD, DWIN_CREALITY_LCD_ENHANCED, DWIN_CREALITY_LCD_JYERSUI, DWIN_MARLINUI_*, OR EXTENSIBLE_UI."
 #endif
 
 #if ENABLED(USE_M73_REMAINING_TIME) && DISABLED(LCD_SET_PROGRESS_MANUALLY)
@@ -1748,7 +1748,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
  * LCD_BED_LEVELING requirements
  */
 #if ENABLED(LCD_BED_LEVELING)
-  #if NONE(HAS_LCD_MENU, DWIN_CREALITY_LCD)
+  #if NONE(HAS_LCD_MENU, DWIN_CREALITY_LCD, DWIN_CREALITY_LCD_ENHANCED)
     #error "LCD_BED_LEVELING is not supported by the selected LCD controller."
   #elif !(ENABLED(MESH_BED_LEVELING) || HAS_ABL_NOT_UBL)
     #error "LCD_BED_LEVELING requires MESH_BED_LEVELING or AUTO_BED_LEVELING."
@@ -2655,7 +2655,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
   + COUNT_ENABLED(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON, ANYCUBIC_TFT35) \
   + COUNT_ENABLED(DGUS_LCD_UI_ORIGIN, DGUS_LCD_UI_FYSETC, DGUS_LCD_UI_HIPRECY, DGUS_LCD_UI_MKS, DGUS_LCD_UI_RELOADED) \
   + COUNT_ENABLED(ENDER2_STOCKDISPLAY, CR10_STOCKDISPLAY) \
-  + COUNT_ENABLED(DWIN_CREALITY_LCD, DWIN_CREALITY_LCD_JYERSUI, DWIN_MARLINUI_PORTRAIT, DWIN_MARLINUI_LANDSCAPE) \
+  + COUNT_ENABLED(DWIN_CREALITY_LCD, DWIN_CREALITY_LCD_ENHANCED, DWIN_CREALITY_LCD_JYERSUI, DWIN_MARLINUI_PORTRAIT, DWIN_MARLINUI_LANDSCAPE) \
   + COUNT_ENABLED(FYSETC_MINI_12864_X_X, FYSETC_MINI_12864_1_2, FYSETC_MINI_12864_2_0, FYSETC_MINI_12864_2_1, FYSETC_GENERIC_12864_1_1) \
   + COUNT_ENABLED(LCD_SAINSMART_I2C_1602, LCD_SAINSMART_I2C_2004) \
   + COUNT_ENABLED(MKS_12864OLED, MKS_12864OLED_SSD1306) \
@@ -2763,6 +2763,18 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
   #elif BOTH(LCD_BED_LEVELING, PROBE_MANUALLY)
     #error "DWIN_CREALITY_LCD does not support LCD_BED_LEVELING with PROBE_MANUALLY."
   #endif
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #if DISABLED(SDSUPPORT)
+    #error "DWIN_CREALITY_LCD_ENHANCED requires SDSUPPORT to be enabled."
+  #elif ENABLED(PID_EDIT_MENU)
+    #error "DWIN_CREALITY_LCD_ENHANCED does not support PID_EDIT_MENU."
+  #elif ENABLED(PID_AUTOTUNE_MENU)
+    #error "DWIN_CREALITY_LCD_ENHANCED does not support PID_AUTOTUNE_MENU."
+  #elif ENABLED(LEVEL_BED_CORNERS)
+    #error "DWIN_CREALITY_LCD_ENHANCED does not support LEVEL_BED_CORNERS."
+  #elif BOTH(LCD_BED_LEVELING, PROBE_MANUALLY)
+    #error "DWIN_CREALITY_LCD_ENHANCED does not support LCD_BED_LEVELING with PROBE_MANUALLY."
+  #endif
 #endif
 
 /**
diff --git a/Marlin/src/lcd/e3v2/creality/README.md b/Marlin/src/lcd/e3v2/README.md
similarity index 100%
rename from Marlin/src/lcd/e3v2/creality/README.md
rename to Marlin/src/lcd/e3v2/README.md
diff --git a/Marlin/src/lcd/e3v2/creality/rotary_encoder.h b/Marlin/src/lcd/e3v2/creality/rotary_encoder.h
index f73577b3b0..48e13b7ece 100644
--- a/Marlin/src/lcd/e3v2/creality/rotary_encoder.h
+++ b/Marlin/src/lcd/e3v2/creality/rotary_encoder.h
@@ -22,12 +22,12 @@
 #pragma once
 
 /*****************************************************************************
-  * @file     lcd/e3v2/creality/rotary_encoder.h
-  * @author   LEO / Creality3D
-  * @date     2019/07/06
-  * @version  2.0.1
-  * @brief    Rotary encoder functions
-  ****************************************************************************/
+ * @file     lcd/e3v2/creality/rotary_encoder.h
+ * @author   LEO / Creality3D
+ * @date     2019/07/06
+ * @version  2.0.1
+ * @brief    Rotary encoder functions
+ ****************************************************************************/
 
 #include "../../../inc/MarlinConfig.h"
 
diff --git a/Marlin/src/lcd/e3v2/enhanced/dwin.cpp b/Marlin/src/lcd/e3v2/enhanced/dwin.cpp
new file mode 100644
index 0000000000..0143d62bd2
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/dwin.cpp
@@ -0,0 +1,3657 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.1
+ * Date: 2021/08/29
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+
+#include "dwin.h"
+
+#include "../../fontutils.h"
+#include "../../marlinui.h"
+
+#include "../../../sd/cardreader.h"
+
+#include "../../../MarlinCore.h"
+#include "../../../core/serial.h"
+#include "../../../core/macros.h"
+
+#include "../../../module/temperature.h"
+#include "../../../module/printcounter.h"
+#include "../../../module/motion.h"
+#include "../../../module/planner.h"
+
+#include "../../../gcode/gcode.h"
+#include "../../../gcode/queue.h"
+
+#if HAS_FILAMENT_SENSOR
+  #include "../../../feature/runout.h"
+#endif
+
+#if ENABLED(EEPROM_SETTINGS)
+  #include "../../../module/settings.h"
+#endif
+
+#if ENABLED(HOST_ACTION_COMMANDS)
+  #include "../../../feature/host_actions.h"
+#endif
+
+#if HAS_ONESTEP_LEVELING
+  #include "../../../feature/bedlevel/bedlevel.h"
+#endif
+
+#if HAS_BED_PROBE
+  #include "../../../module/probe.h"
+#endif
+
+#if EITHER(BABYSTEP_ZPROBE_OFFSET, JUST_BABYSTEP)
+  #include "../../../feature/babystep.h"
+#endif
+
+#if ENABLED(POWER_LOSS_RECOVERY)
+  #include "../../../feature/powerloss.h"
+#endif
+
+#include <WString.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef MACHINE_SIZE
+  #define MACHINE_SIZE STRINGIFY(X_BED_SIZE) "x" STRINGIFY(Y_BED_SIZE) "x" STRINGIFY(Z_MAX_POS)
+#endif
+
+#include "lockscreen.h"
+
+#ifndef CORP_WEBSITE
+  #define CORP_WEBSITE WEBSITE_URL
+#endif
+
+#define PAUSE_HEAT
+
+#define USE_STRING_HEADINGS
+#define USE_STRING_TITLES
+
+#define MENU_CHAR_LIMIT  24
+
+// Print speed limit
+#define MIN_PRINT_SPEED  10
+#define MAX_PRINT_SPEED 999
+
+// Print flow limit
+#define MIN_PRINT_FLOW   10
+#define MAX_PRINT_FLOW   299
+
+// Load and Unload limits
+#define MAX_LOAD_UNLOAD  500
+
+// Feedspeed limit (max feedspeed = DEFAULT_MAX_FEEDRATE * 2)
+#define MIN_MAXFEEDSPEED      1
+#define MIN_MAXACCELERATION   1
+#define MIN_MAXJERK           0.1
+#define MIN_STEP              1
+#define MAX_STEP              999.9
+
+// Extruder's temperature limits
+#define MIN_ETEMP  HEATER_0_MINTEMP
+#define MAX_ETEMP  (HEATER_0_MAXTEMP - HOTEND_OVERSHOOT)
+
+#define FEEDRATE_E      (60)
+
+// Minimum unit (0.1) : multiple (10)
+#define UNITFDIGITS 1
+#define MINUNITMULT POW(10, UNITFDIGITS)
+
+#define ENCODER_WAIT_MS                  20
+#define DWIN_VAR_UPDATE_INTERVAL         1024
+#define DWIN_SCROLL_UPDATE_INTERVAL      SEC_TO_MS(2)
+#define DWIN_REMAIN_TIME_UPDATE_INTERVAL SEC_TO_MS(20)
+
+#define BABY_Z_VAR TERN(HAS_BED_PROBE, probe.offset.z, dwin_zoffset)
+
+// Structs
+HMI_value_t HMI_value;
+HMI_flag_t HMI_flag{0};
+HMI_data_t HMI_data;
+
+millis_t dwin_heat_time = 0;
+
+uint8_t checkkey = MainMenu;
+uint8_t last_checkkey = MainMenu;
+
+typedef struct {
+  uint8_t now, last;
+  void set(uint8_t v) { now = last = v; }
+  void reset() { set(0); }
+  bool changed() { bool c = (now != last); if (c) last = now; return c; }
+  bool dec() { if (now) now--; return changed(); }
+  bool inc(uint8_t v) { if (now < (v - 1)) now++; else now = (v - 1); return changed(); }
+} select_t;
+
+select_t select_page{0}, select_file{0}, select_print{0};
+uint8_t index_file     = MROWS;
+
+bool dwin_abort_flag = false; // Flag to reset feedrate, return to Home
+
+constexpr float default_max_feedrate[]        = DEFAULT_MAX_FEEDRATE;
+constexpr float default_max_acceleration[]    = DEFAULT_MAX_ACCELERATION;
+
+#if HAS_CLASSIC_JERK
+  constexpr float default_max_jerk[]          = { DEFAULT_XJERK, DEFAULT_YJERK, DEFAULT_ZJERK, DEFAULT_EJERK };
+#endif
+
+static uint8_t _percent_done = 0;
+static uint32_t _remain_time = 0;
+
+// Additional Aux Host Support
+static bool sdprint = false;
+
+#if ENABLED(PAUSE_HEAT)
+  #if HAS_HOTEND
+    celsius_t resume_hotend_temp = 0;
+  #endif
+  #if HAS_HEATED_BED
+    celsius_t resume_bed_temp = 0;
+  #endif
+  #if HAS_FAN
+    uint16_t resume_fan = 0;
+  #endif
+#endif
+
+#if HAS_ZOFFSET_ITEM
+  float dwin_zoffset = 0, last_zoffset = 0;
+#endif
+
+#if HAS_HOTEND
+  float last_E = 0;
+#endif
+
+// New menu system pointers
+MenuClass *PrepareMenu = nullptr;
+MenuClass *LevBedMenu = nullptr;
+MenuClass *MoveMenu = nullptr;
+MenuClass *ControlMenu = nullptr;
+MenuClass *AdvancedSettings = nullptr;
+#if HAS_HOME_OFFSET
+  MenuClass *HomeOffMenu = nullptr;
+#endif
+#if HAS_BED_PROBE
+  MenuClass *ProbeSetMenu = nullptr;
+#endif
+MenuClass *FilSetMenu = nullptr;
+MenuClass *SelectColorMenu = nullptr;
+MenuClass *GetColorMenu = nullptr;
+MenuClass *TuneMenu = nullptr;
+MenuClass *MotionMenu = nullptr;
+MenuClass *FilamentMenu = nullptr;
+#if ENABLED(MESH_BED_LEVELING)
+  MenuClass *ManualMesh = nullptr;
+#endif
+#if HAS_HOTEND
+  MenuClass *PreheatMenu = nullptr;
+#endif
+MenuClass *TemperatureMenu = nullptr;
+MenuClass *MaxSpeedMenu = nullptr;
+MenuClass *MaxAccelMenu = nullptr;
+MenuClass *MaxJerkMenu = nullptr;
+MenuClass *StepsMenu = nullptr;
+MenuClass *HotendPIDMenu = nullptr;
+MenuClass *BedPIDMenu = nullptr;
+#if EITHER(HAS_BED_PROBE, BABYSTEPPING)
+  MenuClass *ZOffsetWizMenu = nullptr;
+#endif
+
+// Updatable menuitems pointers
+MenuItemClass *HotendTargetItem = nullptr;
+MenuItemClass *BedTargetItem = nullptr;
+MenuItemClass *FanSpeedItem = nullptr;
+MenuItemClass *MMeshMoveZItem = nullptr;
+
+#define DWIN_LANGUAGE_EEPROM_ADDRESS 0x01   // Between 0x01 and 0x63 (EEPROM_OFFSET-1)
+                                            // BL24CXX::check() uses 0x00
+
+inline bool HMI_IsChinese() { return HMI_flag.language == DWIN_CHINESE; }
+
+void HMI_SetLanguageCache() {
+  DWIN_JPG_CacheTo1(HMI_IsChinese() ? Language_Chinese : Language_English);
+}
+
+void HMI_SetLanguage() {
+  #if BOTH(EEPROM_SETTINGS, IIC_BL24CXX_EEPROM)
+    BL24CXX::read(DWIN_LANGUAGE_EEPROM_ADDRESS, (uint8_t*)&HMI_flag.language, sizeof(HMI_flag.language));
+  #endif
+  HMI_SetLanguageCache();
+}
+
+void HMI_ToggleLanguage() {
+  HMI_flag.language = HMI_IsChinese() ? DWIN_ENGLISH : DWIN_CHINESE;
+  HMI_SetLanguageCache();
+  #if BOTH(EEPROM_SETTINGS, IIC_BL24CXX_EEPROM)
+    BL24CXX::write(DWIN_LANGUAGE_EEPROM_ADDRESS, (uint8_t*)&HMI_flag.language, sizeof(HMI_flag.language));
+  #endif
+}
+
+typedef struct { uint16_t x, y[2], w, h; } text_info_t;
+
+void ICON_Button(const bool here, const int iconid, const frame_rect_t &ico, const text_info_t (&txt)[2]) {
+  const bool cn = HMI_IsChinese();
+  DWIN_ICON_Show(1, 0, 0, ICON, iconid + here, ico.x, ico.y);
+  if (here) DWIN_Draw_Rectangle(0, HMI_data.Highlight_Color, ico.x, ico.y, ico.x + ico.w - 1, ico.y + ico.h - 1);
+  DWIN_Frame_AreaCopy(1, txt[cn].x, txt[cn].y[here], txt[cn].x + txt[cn].w - 1, txt[cn].y[here] + txt[cn].h - 1, ico.x + (ico.w - txt[cn].w) / 2, (ico.y + ico.h - 28) - txt[cn].h/2);
+}
+
+//
+// Main Menu: "Print"
+//
+void ICON_Print() {
+  constexpr frame_rect_t ico = { 17, 110, 110, 100 };
+  constexpr text_info_t txt[2] = {
+    { 1, { 417, 449 }, 30, 14 },
+    { 1, { 405, 447 }, 27, 15 }
+  };
+  ICON_Button(select_page.now == 0, ICON_Print_0, ico, txt);
+}
+
+//
+// Main Menu: "Prepare"
+//
+void ICON_Prepare() {
+  constexpr frame_rect_t ico = { 145, 110, 110, 100 };
+  constexpr text_info_t txt[2] = {
+    { 33, { 417, 449 }, 51, 14 },
+    { 31, { 405, 447 }, 27, 15 }
+  };
+  ICON_Button(select_page.now == 1, ICON_Prepare_0, ico, txt);
+}
+
+//
+// Main Menu: "Control"
+//
+void ICON_Control() {
+  constexpr frame_rect_t ico = { 17, 226, 110, 100 };
+  constexpr text_info_t txt[2] = {
+    { 85, { 417, 449 }, 46, 14 },
+    { 61, { 405, 447 }, 27, 15 }
+  };
+  ICON_Button(select_page.now == 2, ICON_Control_0, ico, txt);
+}
+
+//
+// Main Menu: "Info"
+//
+void ICON_StartInfo() {
+  constexpr frame_rect_t ico = { 145, 226, 110, 100 };
+  constexpr text_info_t txt[2] = {
+    { 133, { 417, 449 }, 23, 14 },
+    {  91, { 405, 447 }, 27, 15 }
+  };
+  ICON_Button(select_page.now == 3, ICON_Info_0, ico, txt);
+}
+
+//
+// Main Menu: "Level"
+//
+void ICON_Leveling() {
+  constexpr frame_rect_t ico = { 145, 226, 110, 100 };
+  constexpr text_info_t txt[2] = {
+    {  88, { 433, 464 }, 36, 14 },
+    { 211, { 405, 447 }, 27, 15 }
+  };
+  ICON_Button(select_page.now == 3, ICON_Leveling_0, ico, txt);
+}
+
+//
+// Printing: "Tune"
+//
+void ICON_Tune() {
+  constexpr frame_rect_t ico = { 8, 232, 80, 100 };
+  constexpr text_info_t txt[2] = {
+    {   0, { 433, 464 }, 32, 14 },
+    { 121, { 405, 447 }, 27, 15 }
+  };
+  ICON_Button(select_print.now == 0, ICON_Setup_0, ico, txt);
+}
+
+//
+// Printing: "Pause"
+//
+void ICON_Pause() {
+  constexpr frame_rect_t ico = { 96, 232, 80, 100 };
+  constexpr text_info_t txt[2] = {
+    { 157, { 417, 449 }, 39, 14 },
+    { 181, { 405, 447 }, 27, 15 }
+  };
+  ICON_Button(select_print.now == 1, ICON_Pause_0, ico, txt);
+}
+
+//
+// Printing: "Resume"
+//
+void ICON_Resume() {
+  constexpr frame_rect_t ico = { 96, 232, 80, 100 };
+  constexpr text_info_t txt[2] = {
+    { 33, { 433, 464 }, 53, 14 },
+    {  1, { 405, 447 }, 27, 15 }
+  };
+  ICON_Button(select_print.now == 1, ICON_Continue_0, ico, txt);
+}
+
+//
+// Printing: "Stop"
+//
+void ICON_Stop() {
+  constexpr frame_rect_t ico = { 184, 232, 80, 100 };
+  constexpr text_info_t txt[2] = {
+    { 196, { 417, 449 }, 29, 14 },
+    { 151, { 405, 447 }, 27, 12 }
+  };
+  ICON_Button(select_print.now == 2, ICON_Stop_0, ico, txt);
+}
+
+void Draw_Menu_Cursor(const uint8_t line) {
+  DWIN_Draw_Rectangle(1, HMI_data.Cursor_color, 0, MBASE(line) - 18, 14, MBASE(line + 1) - 20);
+}
+
+void Erase_Menu_Cursor(const uint8_t line) {
+  DWIN_Draw_Rectangle(1, HMI_data.Background_Color, 0, MBASE(line) - 18, 14, MBASE(line + 1) - 20);
+}
+
+void Move_Highlight(const int16_t from, const uint16_t newline) {
+  Erase_Menu_Cursor(newline - from);
+  Draw_Menu_Cursor(newline);
+}
+
+void Add_Menu_Line() {
+  Move_Highlight(1, MROWS);
+  DWIN_Draw_Line(HMI_data.SplitLine_Color, 16, MBASE(MROWS + 1) - 20, 256, MBASE(MROWS + 1) - 19);
+}
+
+void Scroll_Menu(const uint8_t dir) {
+  DWIN_Frame_AreaMove(1, dir, MLINE, HMI_data.Background_Color, 0, 31, DWIN_WIDTH, 349);
+  switch (dir) {
+    case DWIN_SCROLL_DOWN: Move_Highlight(-1, 0); break;
+    case DWIN_SCROLL_UP:   Add_Menu_Line(); break;
+  }
+}
+
+inline uint16_t nr_sd_menu_items() {
+  return card.get_num_Files() + !card.flag.workDirIsRoot;
+}
+
+void Erase_Menu_Text(const uint8_t line) {
+  DWIN_Draw_Rectangle(1, HMI_data.Background_Color, LBLX, MBASE(line) - 14, 271, MBASE(line) + 28);
+}
+
+void Draw_Menu_Line(const uint8_t line, const uint8_t icon=0, const char * const label=nullptr, bool more=false) {
+  if (label) DWINUI::Draw_String(LBLX, MBASE(line) - 1, (char*)label);
+  if (icon)  DWINUI::Draw_Icon(icon, 26, MBASE(line) - 3);
+  if (more)  DWINUI::Draw_Icon(ICON_More, 226, MBASE(line) - 3);
+  DWIN_Draw_Line(HMI_data.SplitLine_Color, 16, MBASE(line) + 33, 256, MBASE(line) + 33);
+}
+
+void Draw_Chkb_Line(const uint8_t line, const bool checked) {
+  DWINUI::Draw_Checkbox(HMI_data.Text_Color, HMI_data.Background_Color, VALX + 16, MBASE(line) - 1, checked);
+}
+
+void Draw_Menu_IntValue(uint16_t bcolor, const uint8_t line, uint8_t iNum, const uint16_t value=0) {
+  DWINUI::Draw_Int(HMI_data.Text_Color, bcolor, iNum , VALX, MBASE(line) - 1, value);
+}
+
+// The "Back" label is always on the first line
+void Draw_Back_Label() {
+  if (HMI_IsChinese())
+    DWIN_Frame_AreaCopy(1, 129, 72, 156, 84, LBLX, MBASE(0));
+  else
+    DWIN_Frame_AreaCopy(1, 223, 179, 254, 189, LBLX, MBASE(0));
+}
+
+// Draw "Back" line at the top
+void Draw_Back_First(const bool is_sel=true) {
+  Draw_Menu_Line(0, ICON_Back);
+  Draw_Back_Label();
+  if (is_sel) Draw_Menu_Cursor(0);
+}
+
+inline ENCODER_DiffState get_encoder_state() {
+  static millis_t Encoder_ms = 0;
+  const millis_t ms = millis();
+  if (PENDING(ms, Encoder_ms)) return ENCODER_DIFF_NO;
+  const ENCODER_DiffState state = Encoder_ReceiveAnalyze();
+  if (state != ENCODER_DIFF_NO) Encoder_ms = ms + ENCODER_WAIT_MS;
+  return state;
+}
+
+template<typename T>
+inline bool Apply_Encoder(const ENCODER_DiffState &encoder_diffState, T &valref) {
+  if (encoder_diffState == ENCODER_DIFF_CW)
+    valref += EncoderRate.encoderMoveValue;
+  else if (encoder_diffState == ENCODER_DIFF_CCW)
+    valref -= EncoderRate.encoderMoveValue;
+  return encoder_diffState == ENCODER_DIFF_ENTER;
+}
+
+//
+// Draw Popup Windows
+//
+
+inline void Draw_Popup_Bkgd_60() {
+  DWIN_Draw_Rectangle(1, HMI_data.PopupBg_color,   14, 60, 258, 330);
+  DWIN_Draw_Rectangle(0, HMI_data.Highlight_Color, 14, 60, 258, 330);
+}
+
+inline void Draw_Popup_Bkgd_105() {
+  DWIN_Draw_Rectangle(1, HMI_data.PopupBg_color,   14, 105, 258, 374);
+  DWIN_Draw_Rectangle(0, HMI_data.Highlight_Color, 14, 105, 258, 374);
+}
+
+void Clear_Popup_Area() {
+  DWIN_Draw_Rectangle(1, HMI_data.Background_Color, 0, 31, DWIN_WIDTH, DWIN_HEIGHT);
+}
+
+void DWIN_Draw_Popup(uint8_t icon=0, const char * const msg1=nullptr, const char * const msg2=nullptr, uint8_t button=0) {
+  DWINUI::ClearMenuArea();
+  Draw_Popup_Bkgd_60();
+  if (icon) DWINUI::Draw_Icon(icon, 101, 105);
+  if (msg1) DWINUI::Draw_CenteredString(HMI_data.PopupTxt_Color, 210, msg1);
+  if (msg2) DWINUI::Draw_CenteredString(HMI_data.PopupTxt_Color, 240, msg2);
+  if (button) DWINUI::Draw_Icon(button, 86, 280);
+}
+
+void DWIN_Popup_Confirm(uint8_t icon, const char * const msg1, const char * const msg2) {
+  HMI_SaveProcessID(WaitResponse);
+  DWIN_Draw_Popup(icon, msg1, msg2, ICON_Confirm_E);  // Button Confirm
+  DWIN_UpdateLCD();
+}
+
+void DWIN_Popup_Continue(uint8_t icon, const char * const msg1, const char * const msg2) {
+  HMI_SaveProcessID(WaitResponse);
+  DWIN_Draw_Popup(icon, msg1, msg2, ICON_Continue_E);  // Button Continue
+  DWIN_UpdateLCD();
+}
+
+#if HAS_HOTEND
+
+  void Popup_Window_ETempTooLow() {
+    if (HMI_IsChinese()) {
+      HMI_SaveProcessID(WaitResponse);
+      DWINUI::ClearMenuArea();
+      Draw_Popup_Bkgd_60();
+      DWINUI::Draw_Icon(ICON_TempTooLow, 102, 105);
+      DWIN_Frame_AreaCopy(1, 103, 371, 136, 386,  69, 240);
+      DWIN_Frame_AreaCopy(1, 170, 371, 270, 386, 102, 240);
+      DWINUI::Draw_Icon(ICON_Confirm_C, 86, 280);
+      DWIN_UpdateLCD();
+    }
+    else
+      DWIN_Popup_Confirm(ICON_TempTooLow, "Nozzle is too cold", "Preheat the hotend");
+  }
+
+#endif
+
+void Popup_Window_Resume() {
+  Clear_Popup_Area();
+  Draw_Popup_Bkgd_105();
+  if (HMI_IsChinese()) {
+    DWIN_Frame_AreaCopy(1, 160, 338, 235, 354, 98, 135);
+    DWIN_Frame_AreaCopy(1, 103, 321, 271, 335, 52, 192);
+    DWINUI::Draw_Icon(ICON_Cancel_C,    26, 307);
+    DWINUI::Draw_Icon(ICON_Continue_C, 146, 307);
+  }
+  else {
+    DWINUI::Draw_CenteredString(HMI_data.PopupTxt_Color, 115, F("Continue Print"));
+    DWINUI::Draw_CenteredString(HMI_data.PopupTxt_Color, 192, F("It looks like the last"));
+    DWINUI::Draw_CenteredString(HMI_data.PopupTxt_Color, 212, F("file was interrupted."));
+    DWINUI::Draw_Icon(ICON_Cancel_E,    26, 307);
+    DWINUI::Draw_Icon(ICON_Continue_E, 146, 307);
+  }
+}
+
+void Draw_Select_Highlight(const bool sel) {
+  HMI_flag.select_flag = sel;
+  const uint16_t c1 = sel ? HMI_data.Highlight_Color : HMI_data.PopupBg_color,
+                 c2 = sel ? HMI_data.PopupBg_color : HMI_data.Highlight_Color;
+  DWIN_Draw_Rectangle(0, c1, 25, 279, 126, 318);
+  DWIN_Draw_Rectangle(0, c1, 24, 278, 127, 319);
+  DWIN_Draw_Rectangle(0, c2, 145, 279, 246, 318);
+  DWIN_Draw_Rectangle(0, c2, 144, 278, 247, 319);
+}
+
+void Popup_window_PauseOrStop() {
+  if (HMI_IsChinese()) {
+    DWINUI::ClearMenuArea();
+    Draw_Popup_Bkgd_60();
+         if (select_print.now == 1) DWIN_Frame_AreaCopy(1, 237, 338, 269, 356, 98, 150);
+    else if (select_print.now == 2) DWIN_Frame_AreaCopy(1, 221, 320, 253, 336, 98, 150);
+    DWIN_Frame_AreaCopy(1, 220, 304, 264, 319, 130, 150);
+    DWINUI::Draw_Icon(ICON_Confirm_C, 26, 280);
+    DWINUI::Draw_Icon(ICON_Cancel_C, 146, 280);
+  }
+  else {
+    DWIN_Draw_Popup(ICON_BLTouch, "Please confirm",(select_print.now == 1) ? GET_TEXT(MSG_PAUSE_PRINT) : GET_TEXT(MSG_STOP_PRINT));
+    DWINUI::Draw_Icon(ICON_Confirm_E, 26, 280);
+    DWINUI::Draw_Icon(ICON_Cancel_E, 146, 280);
+  }
+  Draw_Select_Highlight(true);
+}
+
+#if HAS_HOTEND || HAS_HEATED_BED
+  void DWIN_Popup_Temperature(const bool toohigh) {
+    Clear_Popup_Area();
+    Draw_Popup_Bkgd_105();
+    if (toohigh) {
+      DWINUI::Draw_Icon(ICON_TempTooHigh, 102, 165);
+      if (HMI_IsChinese()) {
+        DWIN_Frame_AreaCopy(1, 103, 371, 237, 386, 52, 285);
+        DWIN_Frame_AreaCopy(1, 151, 389, 185, 402, 187, 285);
+        DWIN_Frame_AreaCopy(1, 189, 389, 271, 402, 95, 310);
+      }
+      else {
+        DWINUI::Draw_String(HMI_data.PopupTxt_Color, 36, 300, F("Nozzle or Bed temperature"));
+        DWINUI::Draw_String(HMI_data.PopupTxt_Color, 92, 300, F("is too high"));
+      }
+    }
+    else {
+      DWINUI::Draw_Icon(ICON_TempTooLow, 102, 165);
+      if (HMI_IsChinese()) {
+        DWIN_Frame_AreaCopy(1, 103, 371, 270, 386, 52, 285);
+        DWIN_Frame_AreaCopy(1, 189, 389, 271, 402, 95, 310);
+      }
+      else {
+        DWINUI::Draw_String(HMI_data.PopupTxt_Color, 36, 300, F("Nozzle or Bed temperature"));
+        DWINUI::Draw_String(HMI_data.PopupTxt_Color, 92, 300, F("is too low"));
+      }
+    }
+  }
+
+#endif
+
+void Draw_Print_Labels() {
+  if (HMI_IsChinese()) {
+    Title.FrameCopy(30, 1, 42, 14);                     // "Printing"
+    DWIN_Frame_AreaCopy(1,  0, 72,  63, 86,  41, 173);  // Printing Time
+    DWIN_Frame_AreaCopy(1, 65, 72, 128, 86, 176, 173);  // Remain
+  }
+  else {
+    #ifdef USE_STRING_TITLES
+      Title.ShowCaption(GET_TEXT(MSG_PRINTING));
+      DWINUI::Draw_String( 46, 173, F("Print Time"));
+      DWINUI::Draw_String(181, 173, F("Remain"));
+    #else
+      const uint16_t y = 168;
+      Title.FrameCopy(42, 0, 47, 14);                   // "Printing"
+      DWIN_Frame_AreaCopy(1,  0, 44,  96, 58,  41, y);  // Printing Time
+      DWIN_Frame_AreaCopy(1, 98, 44, 152, 58, 176, y);  // Remain
+    #endif
+  }
+}
+
+void Draw_Print_ProgressBar() {
+  DWINUI::Draw_Icon(ICON_Bar, 15, 93);
+  DWIN_Draw_Rectangle(1, HMI_data.Barfill_Color, 16 + _percent_done * 240 / 100, 93, 256, 113);
+  DWINUI::Draw_Int(HMI_data.PercentTxt_Color, HMI_data.Background_Color, 3, 117, 133, _percent_done);
+  DWINUI::Draw_String(HMI_data.PercentTxt_Color, 142, 133, F("%"));
+}
+
+void Draw_Print_ProgressElapsed() {
+  char buf[10];
+  duration_t elapsed = print_job_timer.duration(); // print timer
+  sprintf_P(buf, PSTR("%02i:%02i"), (uint16_t)(elapsed.value / 3600), ((uint16_t)elapsed.value % 3600) / 60);
+  DWINUI::Draw_String(HMI_data.Text_Color, HMI_data.Background_Color, 47, 192, buf);
+}
+
+void Draw_Print_ProgressRemain() {
+  char buf[10];
+  sprintf_P(buf, PSTR("%02i:%02i"), (uint16_t)(_remain_time / 3600), ((uint16_t)_remain_time % 3600) / 60);
+  DWINUI::Draw_String(HMI_data.Text_Color, HMI_data.Background_Color, 181, 192, buf);
+}
+
+void ICON_ResumeOrPause() {
+  if (printingIsPaused() || HMI_flag.pause_flag || HMI_flag.pause_action)
+    ICON_Resume();
+  else
+    ICON_Pause();
+}
+
+void Draw_PrintProcess() {
+  DWINUI::ClearMenuArea();
+  Draw_Print_Labels();
+
+  ICON_Tune();
+  ICON_ResumeOrPause();
+  ICON_Stop();
+
+  DWIN_Print_Header(sdprint ? card.longest_filename() : nullptr);
+
+  DWINUI::Draw_Icon(ICON_PrintTime, 15, 173);
+  DWINUI::Draw_Icon(ICON_RemainTime, 150, 171);
+
+  Draw_Print_ProgressBar();
+  Draw_Print_ProgressElapsed();
+  Draw_Print_ProgressRemain();
+
+  DWIN_UpdateLCD();
+}
+
+void Goto_PrintProcess() {
+  checkkey = PrintProcess;
+  Draw_PrintProcess();
+}
+
+void Draw_PrintDone() {
+  // show percent bar and value
+  _percent_done = 100;
+  _remain_time = 0;
+
+  DWINUI::ClearMenuArea();
+  DWIN_Print_Header(nullptr);
+  Draw_Print_Labels();
+  DWINUI::Draw_Icon(ICON_PrintTime, 15, 173);
+  DWINUI::Draw_Icon(ICON_RemainTime, 150, 171);
+  Draw_Print_ProgressBar();
+  Draw_Print_ProgressElapsed();
+  Draw_Print_ProgressRemain();
+
+  // show print done confirm
+  DWIN_Draw_Rectangle(1, HMI_data.Background_Color, 0, 240, DWIN_WIDTH - 1, STATUS_Y - 1);
+  DWINUI::Draw_Icon(HMI_IsChinese() ? ICON_Confirm_C : ICON_Confirm_E, 86, 283);
+}
+
+void Draw_Main_Menu() {
+  DWINUI::ClearMenuArea();
+
+  if (HMI_IsChinese())
+    Title.FrameCopy(2, 2, 26, 13);   // "Home" etc
+  else {
+    #ifdef USE_STRING_HEADINGS
+      Title.ShowCaption(MACHINE_NAME);
+    #else
+      Title.FrameCopy(0, 2, 40, 11); // "Home"
+    #endif
+  }
+
+  DWINUI::Draw_Icon(ICON_LOGO, 71, 52);  // CREALITY logo
+
+  ICON_Print();
+  ICON_Prepare();
+  ICON_Control();
+  TERN(HAS_ONESTEP_LEVELING, ICON_Leveling, ICON_StartInfo)();
+  DWIN_UpdateLCD();
+}
+
+void Goto_Main_Menu() {
+  checkkey = MainMenu;
+  DWIN_StatusChanged(nullptr);
+  Draw_Main_Menu();
+}
+
+// Draw X, Y, Z and blink if in an un-homed or un-trusted state
+void _update_axis_value(const AxisEnum axis, const uint16_t x, const uint16_t y, const bool blink, const bool force) {
+  const bool draw_qmark = axis_should_home(axis),
+             draw_empty = NONE(HOME_AFTER_DEACTIVATE, DISABLE_REDUCED_ACCURACY_WARNING) && !draw_qmark && !axis_is_trusted(axis);
+
+  // Check for a position change
+  static xyz_pos_t oldpos = { -1, -1, -1 };
+  const float p = current_position[axis];
+  const bool changed = oldpos[axis] != p;
+  if (changed) oldpos[axis] = p;
+
+  if (force || changed || draw_qmark || draw_empty) {
+    if (blink && draw_qmark)
+      DWINUI::Draw_String(HMI_data.Coordinate_Color, HMI_data.Background_Color, x, y, F("--?--"));
+    else if (blink && draw_empty)
+      DWINUI::Draw_String(HMI_data.Coordinate_Color, HMI_data.Background_Color, x, y, F("     "));
+    else
+      DWINUI::Draw_Signed_Float(HMI_data.Coordinate_Color, HMI_data.Background_Color, 3, 1, x, y, p);
+  }
+}
+
+void _draw_xyz_position(const bool force) {
+  //SERIAL_ECHOPGM("Draw XYZ:");
+  static bool _blink = false;
+  const bool blink = !!(millis() & 0x400UL);
+  if (force || blink != _blink) {
+    _blink = blink;
+    //SERIAL_ECHOPGM(" (blink)");
+    _update_axis_value(X_AXIS,  35, 459, blink, true);
+    _update_axis_value(Y_AXIS, 120, 459, blink, true);
+    _update_axis_value(Z_AXIS, 205, 459, blink, true);
+  }
+  //SERIAL_EOL();
+}
+
+void update_variable() {
+  #if HAS_HOTEND
+    static celsius_t _hotendtemp = 0, _hotendtarget = 0;
+    const celsius_t hc = thermalManager.wholeDegHotend(0),
+                    ht = thermalManager.degTargetHotend(0);
+    const bool _new_hotend_temp = _hotendtemp != hc,
+               _new_hotend_target = _hotendtarget != ht;
+    if (_new_hotend_temp) _hotendtemp = hc;
+    if (_new_hotend_target) _hotendtarget = ht;
+  #endif
+  #if HAS_HEATED_BED
+    static celsius_t _bedtemp = 0, _bedtarget = 0;
+    const celsius_t bc = thermalManager.wholeDegBed(),
+                    bt = thermalManager.degTargetBed();
+    const bool _new_bed_temp = _bedtemp != bc,
+               _new_bed_target = _bedtarget != bt;
+    if (_new_bed_temp) _bedtemp = bc;
+    if (_new_bed_target) _bedtarget = bt;
+  #endif
+  #if HAS_FAN
+    static uint8_t _fanspeed = 0;
+    const bool _new_fanspeed = _fanspeed != thermalManager.fan_speed[0];
+    if (_new_fanspeed) _fanspeed = thermalManager.fan_speed[0];
+  #endif
+
+  if (checkkey == Menu && (CurrentMenu == TuneMenu || CurrentMenu == TemperatureMenu)) {
+    // Tune page temperature update
+    #if HAS_HOTEND
+      if (_new_hotend_target)
+        HotendTargetItem->Draw(CurrentMenu->line(HotendTargetItem->pos));
+    #endif
+    #if HAS_HEATED_BED
+      if (_new_bed_target)
+        BedTargetItem->Draw(CurrentMenu->line(BedTargetItem->pos));
+    #endif
+    #if HAS_FAN
+      if (_new_fanspeed)
+        FanSpeedItem->Draw(CurrentMenu->line(FanSpeedItem->pos));
+    #endif
+  }
+
+  // Bottom temperature update
+
+  #if HAS_HOTEND
+    if (_new_hotend_temp)
+      DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 28, 384, _hotendtemp);
+    if (_new_hotend_target)
+      DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 25 + 4 * STAT_CHR_W + 6, 384, _hotendtarget);
+
+    static int16_t _flow = planner.flow_percentage[0];
+    if (_flow != planner.flow_percentage[0]) {
+      _flow = planner.flow_percentage[0];
+      DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 116 + 2 * STAT_CHR_W, 417, _flow);
+    }
+  #endif
+
+  #if HAS_HEATED_BED
+    if (_new_bed_temp)
+      DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 28, 417, _bedtemp);
+    if (_new_bed_target)
+      DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 25 + 4 * STAT_CHR_W + 6, 417, _bedtarget);
+  #endif
+
+  static int16_t _feedrate = 100;
+  if (_feedrate != feedrate_percentage) {
+    _feedrate = feedrate_percentage;
+    DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 116 + 2 * STAT_CHR_W, 384, _feedrate);
+  }
+
+  #if HAS_FAN
+    if (_new_fanspeed) {
+      _fanspeed = thermalManager.fan_speed[0];
+      DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 195 + 2 * STAT_CHR_W, 384, _fanspeed);
+    }
+  #endif
+
+  static float _offset = 0;
+  if (BABY_Z_VAR != _offset) {
+    _offset = BABY_Z_VAR;
+    DWINUI::Draw_Signed_Float(DWIN_FONT_STAT, HMI_data.Indicator_Color,  HMI_data.Background_Color, 2, 2, 210, 417, _offset);
+  }
+
+  _draw_xyz_position(false);
+}
+
+/**
+ * Read and cache the working directory.
+ *
+ * TODO: New code can follow the pattern of menu_media.cpp
+ * and rely on Marlin caching for performance. No need to
+ * cache files here.
+ */
+
+#ifndef strcasecmp_P
+  #define strcasecmp_P(a, b) strcasecmp((a), (b))
+#endif
+
+void make_name_without_ext(char *dst, char *src, size_t maxlen=MENU_CHAR_LIMIT) {
+  char * const name = card.longest_filename();
+  size_t pos        = strlen(name); // index of ending nul
+
+  // For files, remove the extension
+  // which may be .gcode, .gco, or .g
+  if (!card.flag.filenameIsDir)
+    while (pos && src[pos] != '.') pos--; // find last '.' (stop at 0)
+
+  size_t len = pos;   // nul or '.'
+  if (len > maxlen) { // Keep the name short
+    pos        = len = maxlen; // move nul down
+    dst[--pos] = '.'; // insert dots
+    dst[--pos] = '.';
+    dst[--pos] = '.';
+  }
+
+  dst[len] = '\0';    // end it
+
+  // Copy down to 0
+  while (pos--) dst[pos] = src[pos];
+}
+
+void HMI_SDCardInit() { card.cdroot(); }
+
+void MarlinUI::refresh() { /* Nothing to see here */ }
+
+#define ICON_Folder ICON_More
+
+#if ENABLED(SCROLL_LONG_FILENAMES)
+
+  char shift_name[LONG_FILENAME_LENGTH + 1];
+  int8_t shift_amt; // = 0
+  millis_t shift_ms; // = 0
+
+  // Init the shift name based on the highlighted item
+  void Init_Shift_Name() {
+    const bool is_subdir = !card.flag.workDirIsRoot;
+    const int8_t filenum = select_file.now - 1 - is_subdir; // Skip "Back" and ".."
+    const uint16_t fileCnt = card.get_num_Files();
+    if (WITHIN(filenum, 0, fileCnt - 1)) {
+      card.getfilename_sorted(SD_ORDER(filenum, fileCnt));
+      char * const name = card.longest_filename();
+      make_name_without_ext(shift_name, name, 100);
+    }
+  }
+
+  void Init_SDItem_Shift() {
+    shift_amt = 0;
+    shift_ms = select_file.now > 0 && strlen(shift_name) > MENU_CHAR_LIMIT ? millis() + 750UL : 0;
+  }
+
+#endif
+
+/**
+ * Display an SD item, adding a CDUP for subfolders.
+ */
+void Draw_SDItem(const uint16_t item, int16_t row=-1) {
+  if (row < 0) row = item + 1 + MROWS - index_file;
+  const bool is_subdir = !card.flag.workDirIsRoot;
+  if (is_subdir && item == 0)
+    return Draw_Menu_Line(row, ICON_Folder, "..");
+
+  card.getfilename_sorted(SD_ORDER(item - is_subdir, card.get_num_Files()));
+  char * const name = card.longest_filename();
+
+  #if ENABLED(SCROLL_LONG_FILENAMES)
+    // Init the current selected name
+    // This is used during scroll drawing
+    if (item == select_file.now - 1) {
+      make_name_without_ext(shift_name, name, 100);
+      Init_SDItem_Shift();
+    }
+  #endif
+
+  // Draw the file/folder with name aligned left
+  char str[strlen(name) + 1];
+  make_name_without_ext(str, name);
+  Draw_Menu_Line(row, card.flag.filenameIsDir ? ICON_Folder : ICON_File, str);
+}
+
+#if ENABLED(SCROLL_LONG_FILENAMES)
+
+  void Draw_SDItem_Shifted(uint8_t &shift) {
+    // Limit to the number of chars past the cutoff
+    const size_t len = strlen(shift_name);
+    NOMORE(shift, _MAX(len - MENU_CHAR_LIMIT, 0U));
+
+    // Shorten to the available space
+    const size_t lastchar = _MIN((signed)len, shift + MENU_CHAR_LIMIT);
+
+    const char c = shift_name[lastchar];
+    shift_name[lastchar] = '\0';
+
+    const uint8_t row = select_file.now + MROWS - index_file; // skip "Back" and scroll
+    Erase_Menu_Text(row);
+    Draw_Menu_Line(row, 0, &shift_name[shift]);
+
+    shift_name[lastchar] = c;
+  }
+
+#endif
+
+// Redraw the first set of SD Files
+void Redraw_SD_List() {
+  select_file.reset();
+  index_file = MROWS;
+
+  DWINUI::ClearMenuArea(); // Leave title bar unchanged
+
+  Draw_Back_First();
+
+  if (card.isMounted()) {
+    // As many files as will fit
+    LOOP_L_N(i, _MIN(nr_sd_menu_items(), MROWS))
+      Draw_SDItem(i, i + 1);
+
+    TERN_(SCROLL_LONG_FILENAMES, Init_SDItem_Shift());
+  }
+  else {
+    DWIN_Draw_Rectangle(1, HMI_data.AlertBg_Color, 10, MBASE(3) - 10, DWIN_WIDTH - 10, MBASE(4));
+    DWINUI::Draw_CenteredString(font16x32, HMI_data.AlertTxt_Color, MBASE(3), F("No Media"));
+  }
+}
+
+bool DWIN_lcd_sd_status = false;
+
+void SDCard_Up() {
+  card.cdup();
+  Redraw_SD_List();
+  DWIN_lcd_sd_status = false; // On next DWIN_Update
+}
+
+void SDCard_Folder(char * const dirname) {
+  card.cd(dirname);
+  Redraw_SD_List();
+  DWIN_lcd_sd_status = false; // On next DWIN_Update
+}
+
+//
+// Watch for media mount / unmount
+//
+void HMI_SDCardUpdate() {
+  if (HMI_flag.home_flag) return;
+  if (DWIN_lcd_sd_status != card.isMounted()) {
+    DWIN_lcd_sd_status = card.isMounted();
+    //SERIAL_ECHOLNPAIR("HMI_SDCardUpdate: ", DWIN_lcd_sd_status);
+    if (DWIN_lcd_sd_status) {
+      if (checkkey == SelectFile)
+        Redraw_SD_List();
+    }
+    else {
+      // clean file icon
+      if (checkkey == SelectFile) {
+        Redraw_SD_List();
+      }
+      else if (sdprint && card.isPrinting() && printingIsActive()) {
+        // TODO: Move card removed abort handling
+        //       to CardReader::manage_media.
+        card.abortFilePrintSoon();
+        wait_for_heatup = wait_for_user = false;
+        dwin_abort_flag = true; // Reset feedrate, return to Home
+      }
+    }
+    DWIN_UpdateLCD();
+  }
+}
+
+//
+// The status area is always on-screen, except during
+// full-screen modal dialogs. (TODO: Keep alive during dialogs)
+//
+void Draw_Status_Area(const bool with_update) {
+
+  DWIN_Draw_Rectangle(1, HMI_data.Background_Color, 0, STATUS_Y + 21, DWIN_WIDTH, DWIN_HEIGHT - 1);
+
+  #if HAS_HOTEND
+    DWINUI::Draw_Icon(ICON_HotendTemp, 10, 383);
+    DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 28, 384, thermalManager.wholeDegHotend(0));
+    DWINUI::Draw_String(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 25 + 3 * STAT_CHR_W + 5, 384, F("/"));
+    DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 25 + 4 * STAT_CHR_W + 6, 384, thermalManager.degTargetHotend(0));
+
+    DWINUI::Draw_Icon(ICON_StepE, 112, 417);
+    DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 116 + 2 * STAT_CHR_W, 417, planner.flow_percentage[0]);
+    DWINUI::Draw_String(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 116 + 5 * STAT_CHR_W + 2, 417, F("%"));
+  #endif
+
+  #if HAS_HEATED_BED
+    DWINUI::Draw_Icon(ICON_BedTemp, 10, 416);
+    DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 28, 417, thermalManager.wholeDegBed());
+    DWINUI::Draw_String(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 25 + 3 * STAT_CHR_W + 5, 417, F("/"));
+    DWINUI::Draw_Int(true, true, 0, DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 25 + 4 * STAT_CHR_W + 6, 417, thermalManager.degTargetBed());
+  #endif
+
+  DWINUI::Draw_Icon(ICON_Speed, 113, 383);
+  DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 116 + 2 * STAT_CHR_W, 384, feedrate_percentage);
+  DWINUI::Draw_String(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 116 + 5 * STAT_CHR_W + 2, 384, F("%"));
+
+  #if HAS_FAN
+    DWINUI::Draw_Icon(ICON_FanSpeed, 187, 383);
+    DWINUI::Draw_Int(DWIN_FONT_STAT, HMI_data.Indicator_Color, HMI_data.Background_Color, 3, 195 + 2 * STAT_CHR_W, 384, thermalManager.fan_speed[0]);
+  #endif
+
+  #if HAS_ZOFFSET_ITEM
+    DWINUI::Draw_Icon(ICON_Zoffset, 187, 416);
+  #endif
+
+  DWINUI::Draw_Signed_Float(DWIN_FONT_STAT, HMI_data.Indicator_Color,  HMI_data.Background_Color, 2, 2, 210, 417, BABY_Z_VAR);
+
+  DWIN_Draw_Rectangle(1, HMI_data.SplitLine_Color, 0, 449, DWIN_WIDTH, 451);
+
+  DWINUI::Draw_Icon(ICON_MaxSpeedX,  10, 456);
+  DWINUI::Draw_Icon(ICON_MaxSpeedY,  95, 456);
+  DWINUI::Draw_Icon(ICON_MaxSpeedZ, 180, 456);
+  _draw_xyz_position(true);
+
+  if (with_update) {
+    DWIN_UpdateLCD();
+    delay(5);
+  }
+}
+
+void HMI_StartFrame(const bool with_update) {
+  Goto_Main_Menu();
+  Draw_Status_Area(with_update);
+}
+
+void Draw_Info_Menu() {
+  DWINUI::ClearMenuArea();
+  Draw_Back_First();
+
+  DWINUI::Draw_CenteredString(122, F(MACHINE_SIZE));
+  DWINUI::Draw_CenteredString(195, F(SHORT_BUILD_VERSION));
+
+  if (HMI_IsChinese()) {
+    Title.FrameCopy(30, 17, 28, 13);                        // "Info"
+
+    DWIN_Frame_AreaCopy(1, 197, 149, 252, 161, 108, 102);   // "Size"
+    DWIN_Frame_AreaCopy(1,   1, 164,  56, 176, 108, 175);   // "Firmware Version"
+    DWIN_Frame_AreaCopy(1,  58, 164, 113, 176, 105, 248);   // "Contact Details"
+  }
+  else {
+    #ifdef USE_STRING_HEADINGS
+      Title.ShowCaption(GET_TEXT_F(MSG_INFO_SCREEN));
+    #else
+      Title.FrameCopy(192, 15, 23, 12);                     // "Info"
+    #endif
+
+    DWIN_Frame_AreaCopy(1, 120, 150, 146, 161, 124, 102);   // "Size"
+    DWIN_Frame_AreaCopy(1, 146, 151, 254, 161,  82, 175);   // "Firmware Version"
+    DWIN_Frame_AreaCopy(1,   1, 164,  96, 175,  89, 248);   // "Contact details"
+  }
+  DWINUI::Draw_CenteredString(268, F(CORP_WEBSITE));
+
+  LOOP_L_N(i, 3) {
+    DWINUI::Draw_Icon(ICON_PrintSize + i, 26, 99 + i * 73);
+    DWIN_Draw_Line(HMI_data.SplitLine_Color, 16, MBASE(2) + i * 73, 256, 156 + i * 73);
+  }
+
+  DWIN_UpdateLCD();
+}
+
+void Draw_Print_File_Menu() {
+  if (HMI_IsChinese())
+    Title.FrameCopy(0, 31, 56, 14);    // "Print file"
+  else {
+    #ifdef USE_STRING_HEADINGS
+      Title.ShowCaption(GET_TEXT_F(MSG_MEDIA_MENU));
+    #else
+      Title.FrameCopy(52, 31, 86, 11); // "Print file"
+    #endif
+  }
+  Redraw_SD_List();
+}
+
+// Main Process
+void HMI_MainMenu() {
+  ENCODER_DiffState encoder_diffState = get_encoder_state();
+  if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+  if (encoder_diffState == ENCODER_DIFF_CW) {
+    if (select_page.inc(4)) {
+      switch (select_page.now) {
+        case 0: ICON_Print(); break;
+        case 1: ICON_Print(); ICON_Prepare(); break;
+        case 2: ICON_Prepare(); ICON_Control(); break;
+        case 3: ICON_Control(); TERN(HAS_ONESTEP_LEVELING, ICON_Leveling, ICON_StartInfo)(); break;
+      }
+    }
+  }
+  else if (encoder_diffState == ENCODER_DIFF_CCW) {
+    if (select_page.dec()) {
+      switch (select_page.now) {
+        case 0: ICON_Print(); ICON_Prepare(); break;
+        case 1: ICON_Prepare(); ICON_Control(); break;
+        case 2: ICON_Control(); TERN(HAS_ONESTEP_LEVELING, ICON_Leveling, ICON_StartInfo)(); break;
+        case 3: TERN(HAS_ONESTEP_LEVELING, ICON_Leveling, ICON_StartInfo)(); break;
+      }
+    }
+  }
+  else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+    switch (select_page.now) {
+      case 0: // Print File
+        checkkey = SelectFile;
+        Draw_Print_File_Menu();
+        break;
+
+      case 1: // Prepare
+        Draw_Prepare_Menu();
+        break;
+
+      case 2: // Control
+        Draw_Control_Menu();
+        break;
+
+      case 3: // Leveling or Info
+        #if HAS_ONESTEP_LEVELING
+          queue.inject_P(PSTR("G28XYO\nG28Z\nG29"));
+        #else
+          checkkey = Info;
+          Draw_Info_Menu();
+        #endif
+        break;
+    }
+  }
+  DWIN_UpdateLCD();
+}
+
+// Select (and Print) File
+void HMI_SelectFile() {
+  ENCODER_DiffState encoder_diffState = get_encoder_state();
+
+  const uint16_t hasUpDir = !card.flag.workDirIsRoot;
+
+  if (encoder_diffState == ENCODER_DIFF_NO) {
+    #if ENABLED(SCROLL_LONG_FILENAMES)
+      if (shift_ms && select_file.now >= 1 + hasUpDir) {
+        // Scroll selected filename every second
+        const millis_t ms = millis();
+        if (ELAPSED(ms, shift_ms)) {
+          const bool was_reset = shift_amt < 0;
+          shift_ms = ms + 375UL + was_reset * 250UL;  // ms per character
+          uint8_t shift_new = shift_amt + 1;          // Try to shift by...
+          Draw_SDItem_Shifted(shift_new);             // Draw the item
+          if (!was_reset && shift_new == 0)           // Was it limited to 0?
+            shift_ms = 0;                             // No scrolling needed
+          else if (shift_new == shift_amt)            // Scroll reached the end
+            shift_new = -1;                           // Reset
+          shift_amt = shift_new;                      // Set new scroll
+        }
+      }
+    #endif
+    return;
+  }
+
+  // First pause is long. Easy.
+  // On reset, long pause must be after 0.
+
+  const uint16_t fullCnt = nr_sd_menu_items();
+
+  if (encoder_diffState == ENCODER_DIFF_CW && fullCnt) {
+    if (select_file.inc(1 + fullCnt)) {
+      const uint8_t itemnum = select_file.now - 1;              // -1 for "Back"
+      if (TERN0(SCROLL_LONG_FILENAMES, shift_ms)) {             // If line was shifted
+        Erase_Menu_Text(itemnum + MROWS - index_file);          // Erase and
+        Draw_SDItem(itemnum - 1);                               // redraw
+      }
+      if (select_file.now > MROWS && select_file.now > index_file) { // Cursor past the bottom
+        index_file = select_file.now;                           // New bottom line
+        Scroll_Menu(DWIN_SCROLL_UP);
+        Draw_SDItem(itemnum, MROWS);                            // Draw and init the shift name
+      }
+      else {
+        Move_Highlight(1, select_file.now + MROWS - index_file); // Just move highlight
+        TERN_(SCROLL_LONG_FILENAMES, Init_Shift_Name());         // ...and init the shift name
+      }
+      TERN_(SCROLL_LONG_FILENAMES, Init_SDItem_Shift());
+    }
+  }
+  else if (encoder_diffState == ENCODER_DIFF_CCW && fullCnt) {
+    if (select_file.dec()) {
+      const uint8_t itemnum = select_file.now - 1;              // -1 for "Back"
+      if (TERN0(SCROLL_LONG_FILENAMES, shift_ms)) {             // If line was shifted
+        Erase_Menu_Text(select_file.now + 1 + MROWS - index_file); // Erase and
+        Draw_SDItem(itemnum + 1);                               // redraw
+      }
+      if (select_file.now < index_file - MROWS) {               // Cursor past the top
+        index_file--;                                           // New bottom line
+        Scroll_Menu(DWIN_SCROLL_DOWN);
+        if (index_file == MROWS) {
+          Draw_Back_First();
+          TERN_(SCROLL_LONG_FILENAMES, shift_ms = 0);
+        }
+        else {
+          Draw_SDItem(itemnum, 0);                              // Draw the item (and init shift name)
+        }
+      }
+      else {
+        Move_Highlight(-1, select_file.now + MROWS - index_file); // Just move highlight
+        TERN_(SCROLL_LONG_FILENAMES, Init_Shift_Name());        // ...and init the shift name
+      }
+      TERN_(SCROLL_LONG_FILENAMES, Init_SDItem_Shift());        // Reset left. Init timer.
+    }
+  }
+  else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+    if (select_file.now == 0) { // Back
+      select_page.set(0);
+      Goto_Main_Menu();
+    }
+    else if (hasUpDir && select_file.now == 1) { // CD-Up
+      SDCard_Up();
+      goto HMI_SelectFileExit;
+    }
+    else {
+      const uint16_t filenum = select_file.now - 1 - hasUpDir;
+      card.getfilename_sorted(SD_ORDER(filenum, card.get_num_Files()));
+
+      // Enter that folder!
+      if (card.flag.filenameIsDir) {
+        SDCard_Folder(card.filename);
+        goto HMI_SelectFileExit;
+      }
+
+      // Reset highlight for next entry
+      select_print.reset();
+      select_file.reset();
+
+      // Start choice and print SD file
+      HMI_flag.heat_flag = true;
+      HMI_flag.print_finish = false;
+
+      card.openAndPrintFile(card.filename);
+
+      #if HAS_FAN
+        // All fans on for Ender 3 v2 ?
+        // The slicer should manage this for us.
+        //for (uint8_t i = 0; i < FAN_COUNT; i++)
+        //  thermalManager.fan_speed[i] = 255;
+      #endif
+
+      DWIN_Print_Started(true);
+    }
+  }
+HMI_SelectFileExit:
+  DWIN_UpdateLCD();
+}
+
+// Printing
+void HMI_Printing() {
+  ENCODER_DiffState encoder_diffState = get_encoder_state();
+  if (encoder_diffState == ENCODER_DIFF_NO) return;
+  // Avoid flicker by updating only the previous menu
+  if (encoder_diffState == ENCODER_DIFF_CW) {
+    if (select_print.inc(3)) {
+      switch (select_print.now) {
+        case 0: ICON_Tune(); break;
+        case 1:
+          ICON_Tune();
+          ICON_ResumeOrPause();
+          break;
+        case 2:
+          ICON_ResumeOrPause();
+          ICON_Stop();
+          break;
+      }
+    }
+  }
+  else if (encoder_diffState == ENCODER_DIFF_CCW) {
+    if (select_print.dec()) {
+      switch (select_print.now) {
+        case 0:
+          ICON_Tune();
+          ICON_ResumeOrPause();
+          break;
+        case 1:
+          ICON_ResumeOrPause();
+          ICON_Stop();
+          break;
+        case 2: ICON_Stop(); break;
+      }
+    }
+  }
+  else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+    switch (select_print.now) {
+      case 0: // Tune
+        Draw_Tune_Menu();
+        break;
+      case 1: // Pause
+        if (HMI_flag.pause_flag) {
+          ICON_Pause();
+          #ifndef ADVANCED_PAUSE_FEATURE
+            char cmd[40];
+            cmd[0] = '\0';
+            #if BOTH(HAS_HEATED_BED, PAUSE_HEAT)
+              if (resume_bed_temp) sprintf_P(cmd, PSTR("M190 S%i\n"), resume_bed_temp);
+            #endif
+            #if BOTH(HAS_HOTEND, PAUSE_HEAT)
+              if (resume_hotend_temp) sprintf_P(&cmd[strlen(cmd)], PSTR("M109 S%i\n"), resume_hotend_temp);
+            #endif
+            #if HAS_FAN
+              if (resume_fan) thermalManager.fan_speed[0] = resume_fan;
+            #endif
+            strcat_P(cmd, M24_STR);
+            queue.inject(cmd);
+          #endif
+        }
+        else {
+          HMI_flag.select_flag = true;
+          checkkey = PauseOrStop;
+          Popup_window_PauseOrStop();
+        }
+        break;
+
+      case 2: // Stop
+        HMI_flag.select_flag = true;
+        checkkey = PauseOrStop;
+        Popup_window_PauseOrStop();
+        break;
+
+      default: break;
+    }
+  }
+  DWIN_UpdateLCD();
+}
+
+// Print done
+void HMI_PrintDone() {
+  ENCODER_DiffState encoder_diffState = get_encoder_state();
+  if (encoder_diffState == ENCODER_DIFF_NO) return;
+  if (encoder_diffState == ENCODER_DIFF_ENTER) {
+    dwin_abort_flag = true; // Reset feedrate, return to Home
+    Goto_Main_Menu(); // Return to Main menu after print done
+  }
+}
+
+// Pause or Stop popup
+void HMI_PauseOrStop() {
+  ENCODER_DiffState encoder_diffState = get_encoder_state();
+  if (encoder_diffState == ENCODER_DIFF_NO) return;
+
+  if (encoder_diffState == ENCODER_DIFF_CW)
+    Draw_Select_Highlight(false);
+  else if (encoder_diffState == ENCODER_DIFF_CCW)
+    Draw_Select_Highlight(true);
+  else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+    if (select_print.now == 1) { // pause window
+      if (HMI_flag.select_flag) {
+        HMI_flag.pause_action = true;
+        ICON_Resume();
+        queue.inject_P(PSTR("M25"));
+      }
+      else {
+        // cancel pause
+      }
+      Goto_PrintProcess();
+    }
+    else if (select_print.now == 2) { // stop window
+      if (HMI_flag.select_flag) {
+        checkkey = MainMenu;
+        if (HMI_flag.home_flag) planner.synchronize(); // Wait for planner moves to finish!
+        wait_for_heatup = wait_for_user = false;       // Stop waiting for heating/user
+        card.abortFilePrintSoon();                     // Let the main loop handle SD abort
+        dwin_abort_flag = true;                        // Reset feedrate, return to Home
+        #ifdef ACTION_ON_CANCEL
+          host_action_cancel();
+        #endif
+        DWIN_Draw_Popup(ICON_BLTouch, "Stopping..." , "Please wait until done.");
+      }
+      else
+        Goto_PrintProcess(); // cancel stop
+    }
+  }
+  DWIN_UpdateLCD();
+}
+
+#include "../../../libs/buzzer.h"
+
+void HMI_AudioFeedback(const bool success/*=true*/) {
+  #if HAS_BUZZER
+    if (success) {
+      BUZZ(100, 659);
+      BUZZ(10, 0);
+      BUZZ(100, 698);
+    }
+    else
+      BUZZ(40, 440);
+  #endif
+}
+
+void Draw_Main_Area() {
+  switch (checkkey) {
+    case MainMenu:               Draw_Main_Menu(); break;
+    case SelectFile:             Draw_Print_File_Menu(); break;
+    case PrintProcess:           Draw_PrintProcess(); break;
+    case PrintDone:              Draw_PrintDone(); break;
+    case Info:                   Draw_Info_Menu(); break;
+    case PauseOrStop:            Popup_window_PauseOrStop(); break;
+    #if ENABLED(ADVANCED_PAUSE_FEATURE)
+      case FilamentPurge:        Draw_Popup_FilamentPurge(); break;
+    #endif
+    case Locked:                 LockScreen.Draw(); break;
+    case Menu:
+    case SetInt:
+    case SetPInt:
+    case SetIntNoDraw:
+    case SetFloat:
+    case SetPFloat:              CurrentMenu->Draw(); break;
+    default: break;
+  }
+}
+
+void HMI_ReturnScreen() {
+  checkkey = last_checkkey;
+  Draw_Main_Area();
+  DWIN_UpdateLCD();
+  return;
+}
+
+void HMI_Popup() {
+  ENCODER_DiffState encoder_diffState = get_encoder_state();
+  if (encoder_diffState == ENCODER_DIFF_NO) return;
+  if (encoder_diffState == ENCODER_DIFF_ENTER) {
+    wait_for_user = false;
+    HMI_ReturnScreen();
+  }
+}
+
+void HMI_Init() {
+  HMI_SDCardInit();
+
+  for (uint16_t t = 0; t <= 100; t += 2) {
+    DWINUI::Draw_Icon(ICON_Bar, 15, 260);
+    DWIN_Draw_Rectangle(1, HMI_data.Background_Color, 15 + t * 242 / 100, 260, 257, 280);
+    DWIN_UpdateLCD();
+    delay(20);
+  }
+
+  HMI_SetLanguage();
+}
+
+void DWIN_Update() {
+  EachMomentUpdate();   // Status update
+  HMI_SDCardUpdate();   // SD card update
+  DWIN_HandleScreen();  // Rotary encoder update
+}
+
+void EachMomentUpdate() {
+  static millis_t next_var_update_ms = 0, next_rts_update_ms = 0;
+
+  const millis_t ms = millis();
+  if (ELAPSED(ms, next_var_update_ms)) {
+    next_var_update_ms = ms + DWIN_VAR_UPDATE_INTERVAL;
+    update_variable();
+  }
+
+  if (PENDING(ms, next_rts_update_ms)) return;
+  next_rts_update_ms = ms + DWIN_SCROLL_UPDATE_INTERVAL;
+
+  if (checkkey == PrintProcess) {
+    // if print done
+    if (HMI_flag.print_finish) {
+      HMI_flag.print_finish = false;
+      TERN_(POWER_LOSS_RECOVERY, recovery.cancel());
+      planner.finish_and_disable();
+      checkkey = PrintDone;
+      Draw_PrintDone();
+    }
+    else if (HMI_flag.pause_flag != printingIsPaused()) {
+      // print status update
+      HMI_flag.pause_flag = printingIsPaused();
+      ICON_ResumeOrPause();
+    }
+  }
+
+  // pause after homing
+  if (HMI_flag.pause_action && printingIsPaused() && !planner.has_blocks_queued()) {
+    HMI_flag.pause_action = false;
+    #if ENABLED(PAUSE_HEAT)
+      if (sdprint) {
+        TERN_(HAS_HOTEND, resume_hotend_temp = thermalManager.degTargetHotend(0));
+        TERN_(HAS_HEATED_BED, resume_bed_temp = thermalManager.degTargetBed());
+      }
+      else {
+        TERN_(HAS_HOTEND, resume_hotend_temp = thermalManager.wholeDegHotend(0));
+        TERN_(HAS_HEATED_BED, resume_bed_temp = thermalManager.wholeDegBed());
+      }
+      TERN_(HAS_FAN, resume_fan = thermalManager.fan_speed[0]);
+    #endif
+    #if DISABLED(ADVANCED_PAUSE_FEATURE)
+      thermalManager.disable_all_heaters();
+    #endif
+    #if DISABLED(PARK_HEAD_ON_PAUSE)
+      queue.inject_P(PSTR("G1 F1200 X0 Y0"));
+	#endif
+  }
+
+  if (checkkey == PrintProcess) { // print process
+
+    duration_t elapsed = print_job_timer.duration(); // print timer
+
+    if (sdprint && card.isPrinting()) {
+      uint8_t percentDone = card.percentDone();
+      static uint8_t last_percentValue = 101;
+      if (last_percentValue != percentDone) { // print percent
+        last_percentValue = percentDone;
+        if (percentDone) {
+          _percent_done = percentDone;
+          Draw_Print_ProgressBar();
+        }
+      }
+
+      // Estimate remaining time every 20 seconds
+      static millis_t next_remain_time_update = 0;
+      if (_percent_done > 1 && ELAPSED(ms, next_remain_time_update) && !HMI_flag.heat_flag) {
+        _remain_time = (elapsed.value - dwin_heat_time) / (_percent_done * 0.01f) - (elapsed.value - dwin_heat_time);
+        next_remain_time_update += DWIN_REMAIN_TIME_UPDATE_INTERVAL;
+        Draw_Print_ProgressRemain();
+      }
+    }
+
+    // Print time so far
+    static uint16_t last_Printtime = 0;
+    const uint16_t min = (elapsed.value % 3600) / 60;
+    if (last_Printtime != min) { // 1 minute update
+      last_Printtime = min;
+      Draw_Print_ProgressElapsed();
+    }
+
+  }
+  else if (dwin_abort_flag && !HMI_flag.home_flag) { // Print Stop
+    dwin_abort_flag = false;
+    dwin_zoffset = BABY_Z_VAR;
+    select_page.set(0);
+    Goto_Main_Menu();
+  }
+
+  #if ENABLED(POWER_LOSS_RECOVERY)
+    else if (DWIN_lcd_sd_status && recovery.dwin_flag) { // resume print before power off
+      static bool recovery_flag = false;
+
+      recovery.dwin_flag = false;
+      recovery_flag = true;
+
+      auto update_selection = [&](const bool sel) {
+        HMI_flag.select_flag = sel;
+        const uint16_t c1 = sel ? HMI_data.PopupBg_color : HMI_data.Highlight_Color;
+        DWIN_Draw_Rectangle(0, c1, 25, 306, 126, 345);
+        DWIN_Draw_Rectangle(0, c1, 24, 305, 127, 346);
+        const uint16_t c2 = sel ? HMI_data.Highlight_Color : HMI_data.PopupBg_color;
+        DWIN_Draw_Rectangle(0, c2, 145, 306, 246, 345);
+        DWIN_Draw_Rectangle(0, c2, 144, 305, 247, 346);
+      };
+
+      Popup_Window_Resume();
+      update_selection(true);
+
+      // TODO: Get the name of the current file from someplace
+      //
+      //(void)recovery.interrupted_file_exists();
+      SdFile *dir = nullptr;
+      const char * const filename = card.diveToFile(true, dir, recovery.info.sd_filename);
+      card.selectFileByName(filename);
+      DWINUI::Draw_CenteredString(HMI_data.PopupTxt_Color, 252, card.longest_filename());
+      DWIN_UpdateLCD();
+
+      while (recovery_flag) {
+        ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+        if (encoder_diffState != ENCODER_DIFF_NO) {
+          if (encoder_diffState == ENCODER_DIFF_ENTER) {
+            recovery_flag = false;
+            if (HMI_flag.select_flag) break;
+            TERN_(POWER_LOSS_RECOVERY, queue.inject_P(PSTR("M1000C")));
+            return HMI_StartFrame(true);
+          }
+          else
+            update_selection(encoder_diffState == ENCODER_DIFF_CW);
+
+          DWIN_UpdateLCD();
+        }
+        watchdog_refresh();
+      }
+
+      select_print.set(0);
+      queue.inject_P(PSTR("M1000"));
+      sdprint = true;
+      Goto_PrintProcess();
+      Draw_Status_Area(true);
+    }
+  #endif // POWER_LOSS_RECOVERY
+
+  DWIN_UpdateLCD();
+}
+
+void DWIN_HandleScreen() {
+  switch (checkkey) {
+    case MainMenu:        HMI_MainMenu(); break;
+    case Menu:            HMI_Menu(); break;
+    case SetInt:          HMI_SetInt(); break;
+    case SetPInt:         HMI_SetPInt(); break;
+    case SetIntNoDraw:    HMI_SetIntNoDraw(); break;
+    case SetFloat:        HMI_SetFloat(); break;
+    case SetPFloat:       HMI_SetPFloat(); break;
+    case SelectFile:      HMI_SelectFile(); break;
+    case Homing:          break;
+    case Leveling:        break;
+    case PrintProcess:    HMI_Printing(); break;
+    case PrintDone:       HMI_PrintDone(); break;
+    case PauseOrStop:     HMI_PauseOrStop(); break;
+    case Info:            HMI_Popup(); break;
+    case WaitResponse:    HMI_Popup(); break;
+    #if ENABLED(ADVANCED_PAUSE_FEATURE)
+      case FilamentPurge: HMI_FilamentPurge(); break;
+    #endif
+    case NothingToDo:     break;
+    case Locked:          HMI_LockScreen(); break;
+    default: break;
+  }
+}
+
+void HMI_SaveProcessID(const uint8_t id) {
+  if (checkkey != id) {
+    if ((checkkey != NothingToDo) &&
+        (checkkey != WaitResponse) &&
+        (checkkey != Homing) &&
+        (checkkey != Leveling) &&
+        (checkkey != PauseOrStop) &&
+        (checkkey != FilamentPurge)) last_checkkey = checkkey; // if not a popup
+    checkkey = id;
+  }
+}
+
+void DWIN_StartHoming() {
+  HMI_flag.home_flag = true;
+  HMI_SaveProcessID(Homing);
+  DWIN_Draw_Popup(ICON_BLTouch, "Axis Homing", "Please wait until done.");
+}
+
+void DWIN_CompletedHoming() {
+  HMI_flag.home_flag = false;
+  dwin_zoffset = TERN0(HAS_BED_PROBE, probe.offset.z);
+  if (dwin_abort_flag) {
+    planner.finish_and_disable();
+  }
+  HMI_ReturnScreen();
+}
+
+void DWIN_MeshLevelingStart() {
+  #if HAS_ONESTEP_LEVELING
+    HMI_SaveProcessID(Leveling);
+    DWIN_Draw_Popup(ICON_AutoLeveling, GET_TEXT(MSG_BED_LEVELING), "Please wait until done.");
+  #elif ENABLED(MESH_BED_LEVELING)
+    Draw_ManualMesh_Menu();
+  #endif
+}
+
+void DWIN_CompletedLeveling() { HMI_ReturnScreen(); }
+
+#if HAS_MESH
+  void DWIN_MeshUpdate(const int8_t xpos, const int8_t ypos, const float zval) {
+    char msg[33] = "";
+    char str_1[6] = "";
+    sprintf_P(msg, PSTR(S_FMT " %i/%i Z=%s"), GET_TEXT(MSG_PROBING_POINT), xpos, ypos,
+      dtostrf(zval, 1, 2, str_1));
+    ui.set_status(msg);
+  }
+#endif
+
+// PID process
+void DWIN_PidTuning(pidresult_t result) {
+  switch (result) {
+    case PID_BED_START:
+      HMI_SaveProcessID(NothingToDo);
+      DWIN_Draw_Popup(ICON_TempTooHigh, GET_TEXT(MSG_PID_AUTOTUNE), "for BED is running.");
+      break;
+    case PID_EXTR_START:
+      HMI_SaveProcessID(NothingToDo);
+      DWIN_Draw_Popup(ICON_TempTooHigh, GET_TEXT(MSG_PID_AUTOTUNE), "for Nozzle is running.");
+      break;
+    case PID_BAD_EXTRUDER_NUM:
+      checkkey = last_checkkey;
+      DWIN_Popup_Confirm(ICON_TempTooLow, "PID Autotune failed!", "Bad extruder");
+      break;
+    case PID_TUNING_TIMEOUT:
+      checkkey = last_checkkey;
+      DWIN_Popup_Confirm(ICON_TempTooHigh, "Error", GET_TEXT(MSG_PID_TIMEOUT));
+      break;
+    case PID_TEMP_TOO_HIGH:
+      checkkey = last_checkkey;
+      DWIN_Popup_Confirm(ICON_TempTooHigh, "PID Autotune failed!", "Temperature too high");
+      break;
+    case PID_DONE:
+      checkkey = last_checkkey;
+      DWIN_Popup_Confirm(ICON_TempTooLow, GET_TEXT(MSG_PID_AUTOTUNE), GET_TEXT(MSG_BUTTON_DONE));
+      break;
+    default:
+      checkkey = last_checkkey;
+      break;
+  }
+}
+
+// Update filename on print
+void DWIN_Print_Header(const char *text = nullptr) {
+
+  static char headertxt[31] = "";  // Print header text
+
+  if (text != nullptr) {
+    const int8_t size = _MIN((unsigned) 30, strlen_P(text));
+    LOOP_L_N(i, size) headertxt[i] = text[i];
+    headertxt[size] = '\0';
+  }
+  if (checkkey == PrintProcess || checkkey == PrintDone) {
+    DWIN_Draw_Rectangle(1, HMI_data.Background_Color, 0, 60, DWIN_WIDTH, 60+16);
+    DWINUI::Draw_CenteredString(60, headertxt);
+  }
+}
+
+void Draw_Title(TitleClass* title) {
+  DWIN_Draw_Rectangle(1, HMI_data.TitleBg_color, 0, 0, DWIN_WIDTH - 1, TITLE_HEIGHT - 1);
+  if (title->frameid)
+    DWIN_Frame_AreaCopy(title->frameid, title->frame.left, title->frame.top, title->frame.right, title->frame.bottom, 14, (TITLE_HEIGHT - (title->frame.bottom - title->frame.top)) / 2 - 1);
+  else
+    DWIN_Draw_String(false, false, DWIN_FONT_HEAD, HMI_data.TitleTxt_color, HMI_data.TitleBg_color, 14, (TITLE_HEIGHT - DWINUI::Get_font_height(DWIN_FONT_HEAD)) / 2 - 1, title->caption);
+}
+
+void Draw_Menu(MenuClass* menu) {
+  DWINUI::SetColors(HMI_data.Text_Color, HMI_data.Background_Color);
+  DWIN_Draw_Rectangle(1, DWINUI::backcolor, 0, TITLE_HEIGHT, DWIN_WIDTH - 1, STATUS_Y - 1);
+  ui.set_status("");
+}
+
+// Startup routines
+void DWIN_Startup() {
+  DWINUI::Init();
+  DWINUI::onCursorDraw = Draw_Menu_Cursor;
+  DWINUI::onCursorErase = Erase_Menu_Cursor;
+  DWINUI::onTitleDraw = Draw_Title;
+  DWINUI::onMenuDraw = Draw_Menu;
+  HMI_SetLanguage();
+}
+
+void DWIN_DrawStatusLine(const uint16_t color, const uint16_t bgcolor, const char *text) {
+  DWIN_Draw_Rectangle(1, bgcolor, 0, STATUS_Y, DWIN_WIDTH, STATUS_Y + 20);
+  if (text) DWINUI::Draw_CenteredString(color, STATUS_Y + 2, text);
+  DWIN_UpdateLCD();
+}
+
+// Update Status line
+void DWIN_StatusChanged(const char *text) {
+  DWIN_DrawStatusLine(HMI_data.StatusTxt_Color, HMI_data.StatusBg_Color, text);
+}
+
+void DWIN_StatusChanged_P(PGM_P const pstr) {
+  char str[strlen_P((const char*)pstr) + 1];
+  strcpy_P(str, (const char*)pstr);
+  DWIN_StatusChanged(str);
+}
+
+// Started a Print Job
+void DWIN_Print_Started(const bool sd) {
+  sdprint = card.isPrinting() || sd;
+  _percent_done = 0;
+  _remain_time = 0;
+  HMI_flag.print_finish = false;
+  Goto_PrintProcess();
+}
+
+// Ended print job
+void DWIN_Print_Finished() {
+  if (checkkey == PrintProcess || printingIsActive()) {
+    thermalManager.disable_all_heaters();
+    thermalManager.zero_fan_speeds();
+    HMI_flag.print_finish = true;
+  }
+}
+
+// Progress Bar update
+void DWIN_Progress_Update() {
+  if (parser.seenval('P')) _percent_done = parser.byteval('P');
+  if (parser.seenval('R')) _remain_time = parser.ulongval('R') * 60;
+  if (checkkey == PrintProcess) {
+    Draw_Print_ProgressBar();
+    Draw_Print_ProgressRemain();
+    Draw_Print_ProgressElapsed();
+  }
+}
+
+#if HAS_FILAMENT_SENSOR
+  // Filament Runout process
+  void DWIN_FilamentRunout(const uint8_t extruder) { ui.set_status_P(GET_TEXT(MSG_RUNOUT_SENSOR)); }
+#endif
+
+void DWIN_SetColorDefaults() {
+  HMI_data.Background_Color = Def_Background_Color;
+  HMI_data.Cursor_color     = Def_Cursor_color;
+  HMI_data.TitleBg_color    = Def_TitleBg_color;
+  HMI_data.TitleTxt_color   = Def_TitleTxt_color;
+  HMI_data.Text_Color       = Def_Text_Color;
+  HMI_data.Selected_Color   = Def_Selected_Color;
+  HMI_data.SplitLine_Color  = Def_SplitLine_Color;
+  HMI_data.Highlight_Color  = Def_Highlight_Color;
+  HMI_data.StatusBg_Color   = Def_StatusBg_Color;
+  HMI_data.StatusTxt_Color  = Def_StatusTxt_Color;
+  HMI_data.PopupBg_color    = Def_PopupBg_color;
+  HMI_data.PopupTxt_Color   = Def_PopupTxt_Color;
+  HMI_data.AlertBg_Color    = Def_AlertBg_Color;
+  HMI_data.AlertTxt_Color   = Def_AlertTxt_Color;
+  HMI_data.PercentTxt_Color = Def_PercentTxt_Color;
+  HMI_data.Barfill_Color    = Def_Barfill_Color;
+  HMI_data.Indicator_Color  = Def_Indicator_Color;
+  HMI_data.Coordinate_Color = Def_Coordinate_Color;
+}
+
+void DWIN_SetDataDefaults() {
+  DWIN_SetColorDefaults();
+  DWINUI::SetColors(HMI_data.Text_Color, HMI_data.Background_Color);
+  TERN_(HAS_HOTEND,             HMI_data.HotendPidT = PREHEAT_1_TEMP_HOTEND);
+  TERN_(HAS_HEATED_BED,         HMI_data.BedPidT    = PREHEAT_1_TEMP_BED);
+  TERN_(HAS_HOTEND,             HMI_data.PidCycles  = 5);
+  TERN_(PREVENT_COLD_EXTRUSION, HMI_data.ExtMinT    = EXTRUDE_MINTEMP);
+}
+
+void DWIN_StoreSettings(char *buff) {
+  memcpy(buff, &HMI_data, _MIN(sizeof(HMI_data), eeprom_data_size));
+}
+
+void DWIN_LoadSettings(const char *buff) {
+  memcpy(&HMI_data, buff, _MIN(sizeof(HMI_data), eeprom_data_size));
+  dwin_zoffset = TERN0(HAS_BED_PROBE, probe.offset.z);
+  if (HMI_data.Text_Color == HMI_data.Background_Color) DWIN_SetColorDefaults();
+  DWINUI::SetColors(HMI_data.Text_Color, HMI_data.Background_Color);
+  TERN_(PREVENT_COLD_EXTRUSION, ApplyExtMinT());
+  feedrate_percentage = 100;
+}
+
+void MarlinUI::kill_screen(PGM_P lcd_error, PGM_P lcd_component) {
+  DWIN_Draw_Popup(ICON_BLTouch, lcd_error, lcd_component);
+  DWIN_UpdateLCD();
+}
+
+void DWIN_RebootScreen() {
+  DWIN_Frame_Clear(Color_Bg_Black);
+  DWINUI::Draw_Icon(ICON_LOGO, 71, 150);  // CREALITY logo
+  DWINUI::Draw_CenteredString(Color_White, 200, F("Please wait until reboot."));
+  DWIN_UpdateLCD();
+  delay(500);
+}
+
+void DWIN_Redraw_screen() {
+  Draw_Main_Area();
+  DWIN_StatusChanged(ui.status_message);
+  Draw_Status_Area(false);
+}
+
+#if ENABLED(ADVANCED_PAUSE_FEATURE)
+
+  void DWIN_Popup_Pause(const char *msg, uint8_t button = 0) {
+    HMI_SaveProcessID(button ? WaitResponse : NothingToDo);
+    DWIN_Draw_Popup(ICON_BLTouch, "Advanced Pause", msg, button);
+    ui.reset_status(true);
+  }
+
+  void MarlinUI::pause_show_message(const PauseMessage message, const PauseMode mode/*=PAUSE_MODE_SAME*/, const uint8_t extruder/*=active_extruder*/) {
+    switch (message) {
+      case PAUSE_MESSAGE_PARKING:  DWIN_Popup_Pause(GET_TEXT(MSG_PAUSE_PRINT_PARKING));    break;
+      case PAUSE_MESSAGE_CHANGING: DWIN_Popup_Pause(GET_TEXT(MSG_FILAMENT_CHANGE_INIT));   break;
+      case PAUSE_MESSAGE_UNLOAD:   DWIN_Popup_Pause(GET_TEXT(MSG_FILAMENT_CHANGE_UNLOAD)); break;
+      case PAUSE_MESSAGE_WAITING:  DWIN_Popup_Pause(GET_TEXT(MSG_ADVANCED_PAUSE_WAITING), ICON_Continue_E); break;
+      case PAUSE_MESSAGE_INSERT:   DWIN_Popup_Continue(ICON_BLTouch, "Advanced Pause", GET_TEXT(MSG_FILAMENT_CHANGE_INSERT)); break;
+      case PAUSE_MESSAGE_LOAD:     DWIN_Popup_Pause(GET_TEXT(MSG_FILAMENT_CHANGE_LOAD));   break;
+      case PAUSE_MESSAGE_PURGE:    DWIN_Popup_Pause(GET_TEXT(MSG_FILAMENT_CHANGE_PURGE));  break;
+      case PAUSE_MESSAGE_OPTION:   DWIN_Popup_FilamentPurge(); break;
+      case PAUSE_MESSAGE_RESUME:   DWIN_Popup_Pause(GET_TEXT(MSG_FILAMENT_CHANGE_RESUME)); break;
+      case PAUSE_MESSAGE_HEAT:     DWIN_Popup_Pause(GET_TEXT(MSG_FILAMENT_CHANGE_HEAT), ICON_Continue_E);   break;
+      case PAUSE_MESSAGE_HEATING:  ui.set_status_P(GET_TEXT(MSG_FILAMENT_CHANGE_HEATING)); break;
+      case PAUSE_MESSAGE_STATUS:   HMI_ReturnScreen(); break;
+      default: break;
+    }
+  }
+
+  void Draw_Popup_FilamentPurge() {
+    DWIN_Draw_Popup(ICON_BLTouch, "Advanced Pause", "Purge or Continue?");
+    DWINUI::Draw_Icon(ICON_Confirm_E, 26, 280);
+    DWINUI::Draw_Icon(ICON_Continue_E, 146, 280);
+    Draw_Select_Highlight(true);
+  }
+
+  // Handle responses such as:
+  //  - Purge More, Continue
+  //  - General "Continue" response
+  void DWIN_Popup_FilamentPurge() {
+    HMI_SaveProcessID(FilamentPurge);
+    pause_menu_response = PAUSE_RESPONSE_WAIT_FOR;
+    Draw_Popup_FilamentPurge();
+  }
+
+  void HMI_FilamentPurge() {
+    ENCODER_DiffState encoder_diffState = get_encoder_state();
+    if (encoder_diffState == ENCODER_DIFF_NO) return;
+    if (encoder_diffState == ENCODER_DIFF_CW)
+      Draw_Select_Highlight(false);
+    else if (encoder_diffState == ENCODER_DIFF_CCW)
+      Draw_Select_Highlight(true);
+    else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+      if (HMI_flag.select_flag)
+        pause_menu_response = PAUSE_RESPONSE_EXTRUDE_MORE;  // "Purge More" button
+      else {
+        HMI_SaveProcessID(NothingToDo);
+        pause_menu_response = PAUSE_RESPONSE_RESUME_PRINT;  // "Continue" button
+      }
+    }
+    DWIN_UpdateLCD();
+  }
+
+#endif // ADVANCED_PAUSE_FEATURE
+
+void HMI_LockScreen() {
+  ENCODER_DiffState encoder_diffState = get_encoder_state();
+  if (encoder_diffState == ENCODER_DIFF_NO) return;
+  LockScreen.onEncoderState(encoder_diffState);
+  if (LockScreen.isUnlocked()) {
+    if (CurrentMenu == AdvancedSettings)
+      Draw_AdvancedSettings_Menu();
+    else
+      Draw_Tune_Menu();
+  }
+}
+
+void DWIN_LockScreen(const bool flag) {
+  HMI_flag.lock_flag = flag;
+  checkkey = Locked;
+  LockScreen.Init();
+}
+
+//
+// NEW MENU SUBSYSTEM =========================================================
+//
+
+// On click functions
+
+// Generic onclick event without draw anything
+//  process: process id HMI destiny
+//  lo: low limit
+//  hi: high limit
+//  dp: decimal places, 0 for integers
+//  val: value / scaled value
+//  LiveUpdate: live update function when the encoder changes
+//  Apply: update function when the encoder is pressed
+void SetOnClick(uint8_t process, const int32_t lo, const int32_t hi, uint8_t dp, const int32_t val, void (*Apply)() = nullptr, void (*LiveUpdate)() = nullptr) {
+  last_checkkey = Menu;
+  checkkey = process;
+  HMI_value.MinValue = lo;
+  HMI_value.MaxValue = hi;
+  HMI_value.dp = dp;
+  HMI_value.Apply = Apply;
+  HMI_value.LiveUpdate = LiveUpdate;
+  HMI_value.Value = val;
+  EncoderRate.enabled = true;
+}
+
+// Generic onclick event for integer values
+//  process: process id HMI destiny
+//  lo: scaled low limit
+//  hi: scaled high limit
+//  val: value
+//  LiveUpdate: live update function when the encoder changes
+//  Apply: update function when the encoder is pressed
+void SetValueOnClick(uint8_t process, const int32_t lo, const int32_t hi, const int32_t val, void (*Apply)() = nullptr, void (*LiveUpdate)() = nullptr) {
+  SetOnClick(process, lo, hi, 0, val, Apply, LiveUpdate);
+  Draw_Menu_IntValue(HMI_data.Selected_Color, CurrentMenu->line(), 4, HMI_value.Value);
+}
+
+// Generic onclick event for float values
+//  process: process id HMI destiny
+//  lo: scaled low limit
+//  hi: scaled high limit
+//  val: value
+//  LiveUpdate: live update function when the encoder changes
+//  Apply: update function when the encoder is pressed
+void SetValueOnClick(uint8_t process, const float lo, const float hi, uint8_t dp, const float val, void (*Apply)() = nullptr, void (*LiveUpdate)() = nullptr) {
+  const int32_t value =  round(val * POW(10, dp));
+  SetOnClick(process, lo * POW(10, dp), hi * POW(10, dp), dp, value, Apply, LiveUpdate);
+  DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Selected_Color, 3, dp, VALX - dp * DWINUI::Get_font_width(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), val);
+}
+
+// Generic onclick event for integer values
+//  lo: scaled low limit
+//  hi: scaled high limit
+//  val: value
+//  LiveUpdate: live update function when the encoder changes
+//  Apply: update function when the encoder is pressed
+inline void SetIntOnClick(const int32_t lo, const int32_t hi, const int32_t val, void (*Apply)() = nullptr, void (*LiveUpdate)() = nullptr) {
+  SetValueOnClick(SetInt, lo, hi, val, Apply, LiveUpdate);
+}
+
+// Generic onclick event for set pointer to 16 bit uinteger values
+//  lo: low limit
+//  hi: high limit
+//  LiveUpdate: live update function when the encoder changes
+//  Apply: update function when the encoder is pressed
+void SetPIntOnClick(const int32_t lo, const int32_t hi, void (*Apply)() = nullptr, void (*LiveUpdate)() = nullptr) {
+  HMI_value.P_Int = (int16_t*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
+  const int32_t value = *HMI_value.P_Int;
+  SetValueOnClick(SetPInt, lo, hi, value, Apply, LiveUpdate);
+}
+
+// Generic onclick event for float values
+//  process: process id HMI destiny
+//  lo: low limit
+//  hi: high limit
+//  dp: decimal places
+//  val: value
+inline void SetFloatOnClick(const float lo, const float hi, uint8_t dp, const float val, void (*Apply)() = nullptr, void (*LiveUpdate)() = nullptr) {
+  SetValueOnClick(SetFloat, lo, hi, dp, val, Apply, LiveUpdate);
+}
+
+// Generic onclick event for set pointer to float values
+//  lo: low limit
+//  hi: high limit
+//  LiveUpdate: live update function when the encoder changes
+//  Apply: update function when the encoder is pressed
+void SetPFloatOnClick(const float lo, const float hi, uint8_t dp, void (*Apply)() = nullptr, void (*LiveUpdate)() = nullptr) {
+  HMI_value.P_Float = (float*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
+  SetValueOnClick(SetPFloat, lo, hi, dp, *HMI_value.P_Float, Apply, LiveUpdate);
+}
+
+#if ENABLED(EEPROM_SETTINGS)
+  void WriteEeprom() {
+    const bool success = settings.save();
+    HMI_AudioFeedback(success);
+  }
+
+  void ReadEeprom() {
+    const bool success = settings.load();
+    DWIN_Redraw_screen();
+    HMI_AudioFeedback(success);
+  }
+
+  void ResetEeprom() {
+    settings.reset();
+    DWIN_Redraw_screen();
+    HMI_AudioFeedback();
+  }
+#endif
+
+// Reset Printer
+void RebootPrinter() {
+  dwin_abort_flag = true;
+  wait_for_heatup = wait_for_user = false;    // Stop waiting for heating/user
+  thermalManager.disable_all_heaters();
+  planner.finish_and_disable();
+  DWIN_RebootScreen();
+  HAL_reboot();
+}
+
+void Goto_InfoMenu(){
+  checkkey = Info;
+  Draw_Info_Menu();
+}
+
+void DisableMotors() {
+  queue.inject_P(PSTR("M84"));
+}
+
+void AutoHome() {
+  queue.inject_P(G28_STR);
+}
+
+void SetHome() {
+  // Apply workspace offset, making the current position 0,0,0
+  queue.inject_P(PSTR("G92 X0 Y0 Z0"));
+  HMI_AudioFeedback();
+}
+
+#if HAS_ZOFFSET_ITEM
+  bool printer_busy() { return planner.movesplanned() || printingIsActive(); }
+  void ApplyZOffset() { TERN_(EEPROM_SETTINGS, settings.save()); }
+  void LiveZOffset() {
+    last_zoffset = dwin_zoffset;
+    dwin_zoffset = HMI_value.Value / 100.0f;
+    #if EITHER(BABYSTEP_ZPROBE_OFFSET, JUST_BABYSTEP)
+      if (BABYSTEP_ALLOWED()) babystep.add_mm(Z_AXIS, dwin_zoffset - last_zoffset);
+    #endif
+  }
+    #if EITHER(HAS_BED_PROBE, BABYSTEPPING)
+    void SetZOffset() { SetPFloatOnClick(Z_PROBE_OFFSET_RANGE_MIN, Z_PROBE_OFFSET_RANGE_MAX, 2, ApplyZOffset, LiveZOffset); }
+    #endif
+#endif
+
+#if HAS_PREHEAT
+  void SetPreheat(const uint8_t i) {
+    TERN_(HAS_HOTEND,     thermalManager.setTargetHotend(ui.material_preset[i].hotend_temp, 0));
+    TERN_(HAS_HEATED_BED, thermalManager.setTargetBed(ui.material_preset[i].bed_temp));
+    TERN_(HAS_FAN,        thermalManager.set_fan_speed(0, ui.material_preset[i].fan_speed));
+  }
+  void SetPreheat0() { SetPreheat(0); }
+  void SetPreheat1() { SetPreheat(1); }
+  void SetPreheat2() { SetPreheat(2); }
+
+  void SetCoolDown() {
+    TERN_(HAS_FAN, thermalManager.zero_fan_speeds());
+    #if HAS_HOTEND || HAS_HEATED_BED
+      thermalManager.disable_all_heaters();
+    #endif
+  }
+#endif
+
+void SetLanguage() {
+  HMI_ToggleLanguage();
+  CurrentMenu = nullptr;  // Invalidate menu to full redraw
+  Draw_Prepare_Menu();
+}
+
+void LiveMove() {
+  *HMI_value.P_Float = HMI_value.Value / MINUNITMULT;
+  if (!planner.is_full()) {
+    planner.synchronize();
+    planner.buffer_line(current_position, homing_feedrate(HMI_value.axis));
+  }
+}
+void ApplyMoveE() {
+  last_E = HMI_value.Value / MINUNITMULT;
+  if (!planner.is_full()) {
+    planner.synchronize();
+    planner.buffer_line(current_position, MMM_TO_MMS(FEEDRATE_E));
+  }
+}
+void SetMoveX() { HMI_value.axis = X_AXIS; SetPFloatOnClick(X_MIN_POS, X_MAX_POS, UNITFDIGITS, planner.synchronize, LiveMove);}
+void SetMoveY() { HMI_value.axis = Y_AXIS; SetPFloatOnClick(Y_MIN_POS, Y_MAX_POS, UNITFDIGITS, planner.synchronize, LiveMove);}
+void SetMoveZ() { HMI_value.axis = Z_AXIS; SetPFloatOnClick(Z_MIN_POS, Z_MAX_POS, UNITFDIGITS, planner.synchronize, LiveMove);}
+
+#if HAS_HOTEND
+  void SetMoveE() {
+    #if ENABLED(PREVENT_COLD_EXTRUSION)
+      if (thermalManager.tooColdToExtrude(0)) {
+        Popup_Window_ETempTooLow();
+        return;
+      }
+    #endif
+    SetPFloatOnClick(last_E - (EXTRUDE_MAXLENGTH), last_E + (EXTRUDE_MAXLENGTH), UNITFDIGITS, ApplyMoveE);
+  }
+#endif
+
+void SetMoveZto0() {
+  char cmd[48] = "";
+  char str_1[5] = "", str_2[5] = "";
+  sprintf_P(cmd, PSTR("G28OXY\nG28Z\nG0X%sY%sF5000\nG0Z0F300"),
+    #if ENABLED(MESH_BED_LEVELING)
+      dtostrf(0, 1, 1, str_1),
+      dtostrf(0, 1, 1, str_2)
+    #else
+      dtostrf(X_CENTER, 1, 1, str_1),
+      dtostrf(Y_CENTER, 1, 1, str_2)
+    #endif
+  );
+  gcode.process_subcommands_now_P(cmd);
+  planner.synchronize();
+  ui.set_status_P(PSTR("Now adjust Z Offset"));
+  HMI_AudioFeedback(true);
+}
+
+void SetPID(celsius_t t, heater_id_t h) {
+  char cmd[48] = "";
+  char str_1[5] = "", str_2[5] = "";
+  sprintf_P(cmd, PSTR("G28OXY\nG0Z5F300\nG0X%sY%sF5000\nM84"),
+    dtostrf(X_CENTER, 1, 1, str_1),
+    dtostrf(Y_CENTER, 1, 1, str_2)
+  );
+  gcode.process_subcommands_now_P(cmd);
+  planner.synchronize();
+  thermalManager.PID_autotune(t, h, HMI_data.PidCycles, true);
+}
+#if HAS_HOTEND
+  void HotendPID() { SetPID(HMI_data.HotendPidT, H_E0); }
+#endif
+#if HAS_HEATED_BED
+  void BedPID() { SetPID(HMI_data.BedPidT, H_BED); }
+#endif
+
+#if ENABLED(POWER_LOSS_RECOVERY)
+  void SetPwrLossr() {
+    recovery.enable(!recovery.enabled);
+    Draw_Chkb_Line(CurrentMenu->line(), recovery.enabled);
+    DWIN_UpdateLCD();
+  }
+#endif
+
+#if HAS_LCD_BRIGHTNESS
+  void ApplyBrightness() { ui.set_brightness(HMI_value.Value); }
+  void LiveBrightness() { DWIN_LCD_Brightness(HMI_value.Value); }
+  void SetBrightness() { SetIntOnClick(MIN_LCD_BRIGHTNESS, MAX_LCD_BRIGHTNESS, ui.brightness, ApplyBrightness, LiveBrightness); }
+#endif
+
+#if ENABLED(SOUND_MENU_ITEM)
+  void SetEnableSound() {
+    ui.buzzer_enabled = !ui.buzzer_enabled;
+    Draw_Chkb_Line(CurrentMenu->line(), ui.buzzer_enabled);
+    DWIN_UpdateLCD();
+  }
+#endif
+
+void Goto_LockScreen() {
+  DWIN_LockScreen(true);
+}
+
+#if HAS_HOME_OFFSET
+  void ApplyHomeOffset() { set_home_offset(HMI_value.axis, HMI_value.Value / MINUNITMULT); }
+  void SetHomeOffsetX() { HMI_value.axis = X_AXIS; SetPFloatOnClick(-50, 50, UNITFDIGITS, ApplyHomeOffset); }
+  void SetHomeOffsetY() { HMI_value.axis = Y_AXIS; SetPFloatOnClick(-50, 50, UNITFDIGITS, ApplyHomeOffset); }
+  void SetHomeOffsetZ() { HMI_value.axis = Z_AXIS; SetPFloatOnClick( -2,  2, UNITFDIGITS, ApplyHomeOffset); }
+#endif
+
+#if HAS_BED_PROBE
+  void SetProbeOffsetX() { SetPFloatOnClick(-50, 50, UNITFDIGITS); }
+  void SetProbeOffsetY() { SetPFloatOnClick(-50, 50, UNITFDIGITS); }
+  void SetProbeOffsetZ() { SetPFloatOnClick(-10, 10, 2); }
+  void ProbeTest() {
+    ui.set_status_P(GET_TEXT(MSG_M48_TEST));
+    queue.inject_P(PSTR("G28O\nM48 P10"));
+  }
+#endif
+
+#if HAS_FILAMENT_SENSOR
+  void SetRunoutEnable() {
+    runout.reset();
+    runout.enabled = !runout.enabled;
+    Draw_Chkb_Line(CurrentMenu->line(), runout.enabled);
+    DWIN_UpdateLCD();
+  }
+  #if HAS_FILAMENT_RUNOUT_DISTANCE
+    void ApplyRunoutDistance() { runout.set_runout_distance(HMI_value.Value / MINUNITMULT); }
+    void SetRunoutDistance() { SetFloatOnClick(0, 999, UNITFDIGITS, runout.runout_distance(), ApplyRunoutDistance); }
+  #endif
+#endif
+
+#if ENABLED(ADVANCED_PAUSE_FEATURE)
+  void SetFilLoad()   { SetPFloatOnClick(0, MAX_LOAD_UNLOAD, UNITFDIGITS); }
+  void SetFilUnload() { SetPFloatOnClick(0, MAX_LOAD_UNLOAD, UNITFDIGITS); }
+#endif
+
+#if ENABLED(PREVENT_COLD_EXTRUSION)
+  void ApplyExtMinT() { thermalManager.extrude_min_temp = HMI_data.ExtMinT; thermalManager.allow_cold_extrude = (HMI_data.ExtMinT == 0); }
+  void SetExtMinT() { SetPIntOnClick(MIN_ETEMP, MAX_ETEMP, ApplyExtMinT); }
+#endif
+
+void RestoreDefaultsColors() {
+  DWIN_SetColorDefaults();
+  DWINUI::SetColors(HMI_data.Text_Color, HMI_data.Background_Color);
+  DWIN_Redraw_screen();
+}
+
+void SelColor() {
+  HMI_value.P_Int = (int16_t*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
+  HMI_value.Color[0] = GetRColor(*HMI_value.P_Int);  // Red
+  HMI_value.Color[1] = GetGColor(*HMI_value.P_Int);  // Green
+  HMI_value.Color[2] = GetBColor(*HMI_value.P_Int);  // Blue
+  Draw_GetColor_Menu();
+}
+
+void LiveRGBColor() {
+    HMI_value.Color[CurrentMenu->line() - 2] = HMI_value.Value;
+    uint16_t color = RGB(HMI_value.Color[0], HMI_value.Color[1], HMI_value.Color[2]);
+    DWIN_Draw_Rectangle(1, color, 20, 315, DWIN_WIDTH - 20, 335);
+}
+void SetRGBColor() {
+  const uint8_t color = CurrentMenu->SelectedItem()->icon;
+  SetIntOnClick(0, (color == 1) ? 63 : 31, HMI_value.Color[color], nullptr, LiveRGBColor);
+}
+
+void DWIN_ApplyColor() {
+  *HMI_value.P_Int = RGB(HMI_value.Color[0], HMI_value.Color[1], HMI_value.Color[2]);
+  DWINUI::SetColors(HMI_data.Text_Color, HMI_data.Background_Color);
+  Draw_Status_Area(false);
+  Draw_SelectColors_Menu();
+  ui.set_status_P(PSTR("Colors applied"));
+}
+
+void SetSpeed() { SetPIntOnClick(MIN_PRINT_SPEED, MAX_PRINT_SPEED); }
+
+#if HAS_HOTEND
+  void ApplyHotendTemp() { thermalManager.setTargetHotend(HMI_value.Value, 0); }
+  void SetHotendTemp() { SetIntOnClick(MIN_ETEMP, MAX_ETEMP, thermalManager.degTargetHotend(0), ApplyHotendTemp); }
+#endif
+
+#if HAS_HEATED_BED
+  void ApplyBedTemp() { thermalManager.setTargetBed(HMI_value.Value); }
+  void SetBedTemp() { SetIntOnClick(BED_MINTEMP, BED_MAX_TARGET, thermalManager.degTargetBed(), ApplyBedTemp); }
+#endif
+
+#if HAS_FAN
+  void ApplyFanSpeed() { thermalManager.set_fan_speed(0, HMI_value.Value); }
+  void SetFanSpeed() { SetIntOnClick(0, 255, thermalManager.fan_speed[0], ApplyFanSpeed); }
+#endif
+
+#if ENABLED(ADVANCED_PAUSE_FEATURE)
+  void ChangeFilament() {
+    HMI_SaveProcessID(NothingToDo);
+    queue.inject_P(PSTR("M600 B2"));
+  }
+
+  void ParkHead(){
+    ui.set_status_P(GET_TEXT(MSG_FILAMENT_PARK_ENABLED));
+    queue.inject_P(PSTR("G28O\nG27"));
+  }
+
+  #if ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
+    void UnloadFilament(){
+      ui.set_status_P(GET_TEXT(MSG_FILAMENTUNLOAD));
+      queue.inject_P(PSTR("M702 Z20"));
+    }
+
+    void LoadFilament(){
+      ui.set_status_P(GET_TEXT(MSG_FILAMENTLOAD));
+      queue.inject_P(PSTR("M701 Z20"));
+    }
+  #endif
+#endif
+
+void SetFlow() { SetPIntOnClick(MIN_PRINT_FLOW, MAX_PRINT_FLOW); }
+
+// Leveling Bed Corners
+void LevBed(uint8_t point) {
+  char cmd[100] = "";
+  #if HAS_ONESTEP_LEVELING
+    char str_1[6] = "", str_2[6] = "", str_3[6] = "";
+    #define fmt "X:%s, Y:%s, Z:%s"
+    float xpos = 0, ypos = 0, zval = 0;
+    float margin = PROBING_MARGIN;
+  #else
+    #define fmt "M420 S0\nG28O\nG90\nG0 Z5 F300\nG0 X%i Y%i F5000\nG0 Z0 F300"
+    int16_t xpos = 0, ypos = 0;
+    int16_t margin = 30;
+  #endif
+
+  switch (point) {
+    case 0:
+      ui.set_status_P(GET_TEXT(MSG_LEVBED_FL));
+      xpos = ypos = margin;
+      break;
+    case 1:
+      ui.set_status_P(GET_TEXT(MSG_LEVBED_FR));
+      xpos = X_BED_SIZE - margin; ypos = margin;
+      break;
+    case 2:
+      ui.set_status_P(GET_TEXT(MSG_LEVBED_BR));
+      xpos = X_BED_SIZE - margin; ypos = Y_BED_SIZE - margin;
+      break;
+    case 3:
+      ui.set_status_P(GET_TEXT(MSG_LEVBED_BL));
+      xpos = margin; ypos = Y_BED_SIZE - margin;
+      break;
+    case 4:
+      ui.set_status_P(GET_TEXT(MSG_LEVBED_C));
+      xpos = X_BED_SIZE / 2; ypos = Y_BED_SIZE / 2;
+      break;
+  }
+
+  #if HAS_ONESTEP_LEVELING
+    planner.synchronize();
+    gcode.process_subcommands_now_P(PSTR("M420S0\nG28O"));
+    planner.synchronize();
+    zval = probe.probe_at_point(xpos, ypos, PROBE_PT_STOW);
+    sprintf_P(cmd, PSTR(fmt),
+      dtostrf(xpos, 1, 1, str_1),
+      dtostrf(ypos, 1, 1, str_2),
+      dtostrf(zval, 1, 2, str_3)
+    );
+    ui.set_status_P(cmd);
+  #else
+    planner.synchronize();
+    sprintf_P(cmd, PSTR(fmt), xpos, ypos);
+    queue.inject(cmd);
+  #endif
+}
+
+void LevBedFL() { LevBed(0); }
+void LevBedFR() { LevBed(1); }
+void LevBedBR() { LevBed(2); }
+void LevBedBL() { LevBed(3); }
+void LevBedC () { LevBed(4); }
+
+#if ENABLED(MESH_BED_LEVELING)
+  void ManualMeshStart(){
+    ui.set_status_P(GET_TEXT(MSG_UBL_BUILD_MESH_MENU));
+    gcode.process_subcommands_now_P(PSTR("G28 XYO\nG28 Z\nM211 S0\nG29S1"));
+    planner.synchronize();
+    #ifdef MANUAL_PROBE_START_Z
+      const uint8_t line = CurrentMenu->line(MMeshMoveZItem->pos);
+      DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Background_Color, 3, 2, VALX - 2 * DWINUI::Get_font_width(DWIN_FONT_MENU), MBASE(line), MANUAL_PROBE_START_Z);
+    #endif
+  }
+
+  void LiveMeshMoveZ() {
+    *HMI_value.P_Float = HMI_value.Value / POW(10, 2);
+    if (!planner.is_full()) {
+      planner.synchronize();
+      planner.buffer_line(current_position, homing_feedrate(Z_AXIS));
+    }
+  }
+  void SetMMeshMoveZ() { SetPFloatOnClick(-1, 1, 2, planner.synchronize, LiveMeshMoveZ);}
+
+  void ManualMeshContinue(){
+    gcode.process_subcommands_now_P(PSTR("G29S2"));
+    planner.synchronize();
+    MMeshMoveZItem->Draw(CurrentMenu->line(MMeshMoveZItem->pos));
+  }
+
+  void ManualMeshSave(){
+    ui.set_status_P(GET_TEXT(MSG_UBL_STORAGE_MESH_MENU));
+    queue.inject_P(PSTR("M211 S1\nM500"));
+  }
+#endif
+
+#if HAS_PREHEAT
+  #if HAS_HOTEND
+    void SetPreheatEndTemp() { SetPIntOnClick(MIN_ETEMP, MAX_ETEMP); }
+  #endif
+  #if HAS_HEATED_BED
+    void SetPreheatBedTemp() { SetPIntOnClick(BED_MINTEMP, BED_MAX_TARGET); }
+  #endif
+  #if HAS_FAN
+    void SetPreheatFanSpeed() { SetPIntOnClick(0, 255); }
+  #endif
+#endif
+
+void ApplyMaxSpeed() { planner.set_max_feedrate(HMI_value.axis, HMI_value.Value / MINUNITMULT); }
+void SetMaxSpeedX() { HMI_value.axis = X_AXIS, SetFloatOnClick(MIN_MAXFEEDSPEED, default_max_feedrate[X_AXIS] * 2, UNITFDIGITS, planner.settings.max_feedrate_mm_s[X_AXIS], ApplyMaxSpeed); }
+void SetMaxSpeedY() { HMI_value.axis = Y_AXIS, SetFloatOnClick(MIN_MAXFEEDSPEED, default_max_feedrate[Y_AXIS] * 2, UNITFDIGITS, planner.settings.max_feedrate_mm_s[Y_AXIS], ApplyMaxSpeed); }
+void SetMaxSpeedZ() { HMI_value.axis = Z_AXIS, SetFloatOnClick(MIN_MAXFEEDSPEED, default_max_feedrate[Z_AXIS] * 2, UNITFDIGITS, planner.settings.max_feedrate_mm_s[Z_AXIS], ApplyMaxSpeed); }
+#if HAS_HOTEND
+  void SetMaxSpeedE() { HMI_value.axis = E_AXIS; SetFloatOnClick(MIN_MAXFEEDSPEED, default_max_feedrate[E_AXIS] * 2, UNITFDIGITS, planner.settings.max_feedrate_mm_s[E_AXIS], ApplyMaxSpeed); }
+#endif
+
+void ApplyMaxAccel() { planner.set_max_acceleration(HMI_value.axis, HMI_value.Value); }
+void SetMaxAccelX() { HMI_value.axis = X_AXIS, SetIntOnClick(MIN_MAXACCELERATION, default_max_acceleration[X_AXIS] * 2, planner.settings.max_acceleration_mm_per_s2[X_AXIS], ApplyMaxAccel); }
+void SetMaxAccelY() { HMI_value.axis = Y_AXIS, SetIntOnClick(MIN_MAXACCELERATION, default_max_acceleration[Y_AXIS] * 2, planner.settings.max_acceleration_mm_per_s2[Y_AXIS], ApplyMaxAccel); }
+void SetMaxAccelZ() { HMI_value.axis = Z_AXIS, SetIntOnClick(MIN_MAXACCELERATION, default_max_acceleration[Z_AXIS] * 2, planner.settings.max_acceleration_mm_per_s2[Z_AXIS], ApplyMaxAccel); }
+#if HAS_HOTEND
+  void SetMaxAccelE() { HMI_value.axis = E_AXIS; SetIntOnClick(MIN_MAXACCELERATION, default_max_acceleration[E_AXIS] * 2, planner.settings.max_acceleration_mm_per_s2[E_AXIS], ApplyMaxAccel); }
+#endif
+
+#if HAS_CLASSIC_JERK
+  void ApplyMaxJerk() { planner.set_max_jerk(HMI_value.axis, HMI_value.Value / MINUNITMULT); }
+  void SetMaxJerkX() { HMI_value.axis = X_AXIS, SetFloatOnClick(MIN_MAXJERK, default_max_jerk[X_AXIS] * 2, UNITFDIGITS, planner.max_jerk[X_AXIS], ApplyMaxJerk); }
+  void SetMaxJerkY() { HMI_value.axis = Y_AXIS, SetFloatOnClick(MIN_MAXJERK, default_max_jerk[Y_AXIS] * 2, UNITFDIGITS, planner.max_jerk[Y_AXIS], ApplyMaxJerk); }
+  void SetMaxJerkZ() { HMI_value.axis = Z_AXIS, SetFloatOnClick(MIN_MAXJERK, default_max_jerk[Z_AXIS] * 2, UNITFDIGITS, planner.max_jerk[Z_AXIS], ApplyMaxJerk); }
+  #if HAS_HOTEND
+    void SetMaxJerkE() { HMI_value.axis = E_AXIS; SetFloatOnClick(MIN_MAXJERK, default_max_jerk[E_AXIS] * 2, UNITFDIGITS, planner.max_jerk[E_AXIS], ApplyMaxJerk); }
+  #endif
+#endif
+
+void SetStepsX() { HMI_value.axis = X_AXIS, SetPFloatOnClick( MIN_STEP, MAX_STEP, UNITFDIGITS); }
+void SetStepsY() { HMI_value.axis = Y_AXIS, SetPFloatOnClick( MIN_STEP, MAX_STEP, UNITFDIGITS); }
+void SetStepsZ() { HMI_value.axis = Z_AXIS, SetPFloatOnClick( MIN_STEP, MAX_STEP, UNITFDIGITS); }
+#if HAS_HOTEND
+  void SetStepsE() { HMI_value.axis = E_AXIS; SetPFloatOnClick( MIN_STEP, MAX_STEP, UNITFDIGITS); }
+  void SetHotendPidT() { SetPIntOnClick(MIN_ETEMP, MAX_ETEMP); }
+#endif
+#if HAS_HEATED_BED
+  void SetBedPidT() { SetPIntOnClick(BED_MINTEMP, BED_MAX_TARGET); }
+#endif
+
+#if HAS_HOTEND || HAS_HEATED_BED
+  void SetPidCycles() { SetPIntOnClick(3, 50); }
+  void SetKp() { SetPFloatOnClick(0, 1000, 2); }
+  void ApplyPIDi() {
+    *HMI_value.P_Float = scalePID_i(HMI_value.Value / POW(10, 2));
+    thermalManager.updatePID();
+  }
+  void ApplyPIDd() {
+    *HMI_value.P_Float = scalePID_d(HMI_value.Value / POW(10, 2));
+    thermalManager.updatePID();
+  }
+  void SetKi() {
+    HMI_value.P_Float = (float*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
+    const float value = unscalePID_i(*HMI_value.P_Float);
+    SetFloatOnClick(0, 1000, 2, value, ApplyPIDi);
+  }
+  void SetKd() {
+    HMI_value.P_Float = (float*)static_cast<MenuItemPtrClass*>(CurrentMenu->SelectedItem())->value;
+    const float value = unscalePID_d(*HMI_value.P_Float);
+    SetFloatOnClick(0, 1000, 2, value, ApplyPIDd);
+  }
+#endif
+// Menuitem Drawing functions =================================================
+
+void onDrawMenuItem(MenuItemClass* menuitem, int8_t line) {
+  if (menuitem->icon) DWINUI::Draw_Icon(menuitem->icon, ICOX, MBASE(line) - 3);
+  if (menuitem->frameid)
+    DWIN_Frame_AreaCopy(menuitem->frameid, menuitem->frame.left, menuitem->frame.top, menuitem->frame.right, menuitem->frame.bottom, LBLX, MBASE(line));
+  else if (menuitem->caption)
+    DWINUI::Draw_String(LBLX, MBASE(line) - 1, menuitem->caption);
+  DWIN_Draw_HLine(HMI_data.SplitLine_Color, 16, MYPOS(line + 1), 240);
+}
+
+void onDrawSubMenu(MenuItemClass* menuitem, int8_t line) {
+  onDrawMenuItem(menuitem, line);
+  DWINUI::Draw_Icon(ICON_More, VALX + 16, MBASE(line) - 3);
+}
+
+void onDrawIntMenu(MenuItemClass* menuitem, int8_t line, uint16_t value) {
+  onDrawMenuItem(menuitem, line);
+  Draw_Menu_IntValue(HMI_data.Background_Color, line, 4, value);
+}
+
+void onDrawPIntMenu(MenuItemClass* menuitem, int8_t line) {
+  const uint16_t value = *(uint16_t*)static_cast<MenuItemPtrClass*>(menuitem)->value;
+  onDrawIntMenu(menuitem, line, value);
+}
+
+void onDrawPInt8Menu(MenuItemClass* menuitem, int8_t line) {
+  const uint8_t value = *(uint8_t*)static_cast<MenuItemPtrClass*>(menuitem)->value;
+  onDrawIntMenu(menuitem, line, value);
+}
+
+void onDrawPInt32Menu(MenuItemClass* menuitem, int8_t line) {
+  const uint32_t value = *(uint32_t*)static_cast<MenuItemPtrClass*>(menuitem)->value;
+  onDrawIntMenu(menuitem, line, value);
+}
+
+void onDrawFloatMenu(MenuItemClass* menuitem, int8_t line, uint8_t dp, const float value) {
+  onDrawMenuItem(menuitem, line);
+  DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Background_Color, 3, dp, VALX - dp * DWINUI::Get_font_width(DWIN_FONT_MENU), MBASE(line), value);
+}
+
+void onDrawPFloatMenu(MenuItemClass* menuitem, int8_t line) {
+  const float value = *(float*)static_cast<MenuItemPtrClass*>(menuitem)->value;
+  const int8_t dp = UNITFDIGITS;
+  onDrawFloatMenu(menuitem, line, dp, value);
+}
+
+void onDrawPFloat2Menu(MenuItemClass* menuitem, int8_t line) {
+  const float value = *(float*)static_cast<MenuItemPtrClass*>(menuitem)->value;
+  onDrawFloatMenu(menuitem, line, 2, value);
+}
+
+void onDrawChkbMenu(MenuItemClass* menuitem, int8_t line, bool checked) {
+  onDrawMenuItem(menuitem, line);
+  Draw_Chkb_Line(line, checked);
+}
+
+void onDrawBack(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 129, 72, 156, 84);
+  onDrawMenuItem(menuitem, line);
+}
+
+void onDrawTempSubMenu(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1,  57, 104,  84, 116);
+  onDrawSubMenu(menuitem, line);
+}
+
+void onDrawMotionSubMenu(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1,  87, 104, 114, 116);
+  onDrawSubMenu(menuitem, line);
+}
+
+#if ENABLED(EEPROM_SETTINGS)
+  void onDrawWriteEeprom(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 117, 104, 172, 116);
+    onDrawMenuItem(menuitem, line);
+  }
+
+  void onDrawReadEeprom(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 174, 103, 229, 116);
+    onDrawMenuItem(menuitem, line);
+  }
+
+  void onDrawResetEeprom(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1,   1, 118,  56, 131);
+    onDrawMenuItem(menuitem, line);
+  }
+#endif
+
+void onDrawInfoSubMenu(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 231, 104, 258, 116);
+  onDrawSubMenu(menuitem, line);
+}
+
+void onDrawMoveX(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 58, 118, 106, 132);
+  onDrawPFloatMenu(menuitem, line);
+}
+
+void onDrawMoveY(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 109, 118, 157, 132);
+  onDrawPFloatMenu(menuitem, line);
+}
+
+void onDrawMoveZ(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 160, 118, 209, 132);
+  onDrawPFloatMenu(menuitem, line);
+}
+
+#if HAS_HOTEND
+  void onDrawMoveE(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 212, 118, 253, 131);
+    onDrawPFloatMenu(menuitem, line);
+  }
+#endif
+
+void onDrawMoveSubMenu(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 159, 70, 200, 84);
+  onDrawSubMenu(menuitem, line);
+}
+
+void onDrawDisableMotors(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 204, 70, 259, 82);
+  onDrawMenuItem(menuitem, line);
+}
+
+void onDrawAutoHome(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 0, 89, 41, 101);
+  onDrawMenuItem(menuitem, line);
+}
+
+#if HAS_ZOFFSET_ITEM
+  #if EITHER(HAS_BED_PROBE, BABYSTEPPING)
+    void onDrawZOffset(MenuItemClass* menuitem, int8_t line) {
+      if (HMI_IsChinese()) menuitem->SetFrame(1, 174, 164, 223, 177);
+      onDrawPFloat2Menu(menuitem, line);
+    }
+  #else
+    void onDrawHomeOffset(MenuItemClass* menuitem, int8_t line) {
+      if (HMI_IsChinese()) menuitem->SetFrame(1, 43, 89, 98, 101);
+      onDrawMenuItem(menuitem, line);
+    }
+  #endif
+#endif
+
+#if HAS_HOTEND
+  void onDrawPreheat1(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 100, 89, 151, 101);
+    onDrawMenuItem(menuitem, line);
+  }
+  void onDrawPreheat2(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 180, 89, 233, 100);
+    onDrawMenuItem(menuitem, line);
+  }
+#endif
+
+#if HAS_PREHEAT
+  void onDrawCooldown(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 1, 104,  56, 117);
+    onDrawMenuItem(menuitem, line);
+  }
+#endif
+
+void onDrawLanguage(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 239, 134, 266, 146);
+  onDrawMenuItem(menuitem, line);
+  DWINUI::Draw_String(VALX, MBASE(line), HMI_IsChinese() ? F("CN") : F("EN"));
+}
+
+#if ENABLED(POWER_LOSS_RECOVERY)
+  void onDrawPwrLossR(MenuItemClass* menuitem, int8_t line) { onDrawChkbMenu(menuitem, line, recovery.enabled); }
+#endif
+
+void onDrawEnableSound(MenuItemClass* menuitem, int8_t line) { onDrawChkbMenu(menuitem, line, ui.buzzer_enabled); }
+
+void onDrawSelColorItem(MenuItemClass* menuitem, int8_t line) {
+  const uint16_t color = *(uint16_t*)static_cast<MenuItemPtrClass*>(menuitem)->value;
+  DWIN_Draw_Rectangle(0, HMI_data.Highlight_Color, ICOX + 1, MBASE(line) - 1 + 1, ICOX + 18, MBASE(line) - 1 + 18);
+  DWIN_Draw_Rectangle(1, color, ICOX + 2, MBASE(line) - 1 + 2, ICOX + 17, MBASE(line) - 1 + 17);
+  onDrawMenuItem(menuitem, line);
+}
+
+void onDrawGetColorItem(MenuItemClass* menuitem, int8_t line) {
+  const uint8_t i = menuitem->icon;
+  uint16_t color;
+  switch (i) {
+    case 0: color = RGB(31, 0, 0); break; // Red
+    case 1: color = RGB(0, 63, 0); break; // Green
+    case 2: color = RGB(0, 0, 31); break; // Blue
+    default: color = 0; break;
+  }
+  DWIN_Draw_Rectangle(0, HMI_data.Highlight_Color, ICOX + 1, MBASE(line) - 1 + 1, ICOX + 18, MBASE(line) - 1 + 18);
+  DWIN_Draw_Rectangle(1, color, ICOX + 2, MBASE(line) - 1 + 2, ICOX + 17, MBASE(line) - 1 + 17);
+  DWINUI::Draw_String(LBLX, MBASE(line) - 1, menuitem->caption);
+  Draw_Menu_IntValue(HMI_data.Background_Color, line, 4, HMI_value.Color[i]);
+  DWIN_Draw_HLine(HMI_data.SplitLine_Color, 16, MYPOS(line + 1), 240);
+}
+
+#if HAS_FILAMENT_SENSOR
+  void onDrawRunoutEnable(MenuItemClass* menuitem, int8_t line) { onDrawChkbMenu(menuitem, line, runout.enabled); }
+#endif
+
+void onDrawPIDi(MenuItemClass* menuitem, int8_t line) { onDrawFloatMenu(menuitem, line, 2, unscalePID_i(*(float*)static_cast<MenuItemPtrClass*>(menuitem)->value)); }
+void onDrawPIDd(MenuItemClass* menuitem, int8_t line) { onDrawFloatMenu(menuitem, line, 2, unscalePID_d(*(float*)static_cast<MenuItemPtrClass*>(menuitem)->value)); }
+
+
+void onDrawSpeedItem(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 116, 164, 171, 176);
+  onDrawPIntMenu(menuitem, line);
+}
+
+#if HAS_HOTEND
+  void onDrawHotendTemp(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 1, 134, 56, 146);
+    onDrawPIntMenu(menuitem, line);
+  }
+#endif
+
+#if HAS_HEATED_BED
+  void onDrawBedTemp(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 58, 134, 113, 146);
+    onDrawPIntMenu(menuitem, line);
+  }
+#endif
+
+#if HAS_FAN
+  void onDrawFanSpeed(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 115, 134, 170, 146);
+    onDrawPInt8Menu(menuitem, line);
+  }
+#endif
+
+void onDrawSpeed(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 173, 133, 228, 147);
+  onDrawSubMenu(menuitem, line);
+}
+
+void onDrawAcc(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame(1, 173, 133, 200, 147);
+    DWIN_Frame_AreaCopy(1, 28, 149, 69, 161, LBLX + 27, MBASE(line) + 1); // ...Acceleration
+  }
+  onDrawSubMenu(menuitem, line);
+}
+
+#if HAS_CLASSIC_JERK
+  void onDrawJerk(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) {
+      menuitem->SetFrame(1, 173, 133, 200, 147);
+      DWIN_Frame_AreaCopy(1, 1, 180, 28, 192, LBLX + 27, MBASE(line) + 1);  // ...
+      DWIN_Frame_AreaCopy(1, 202, 133, 228, 147, LBLX + 54, MBASE(line));   // ...Jerk
+    }
+    onDrawSubMenu(menuitem, line);
+  }
+#endif
+
+void onDrawSteps(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) menuitem->SetFrame(1, 153, 148, 194, 161);
+  onDrawSubMenu(menuitem, line);
+}
+
+#if ENABLED(MESH_BED_LEVELING)
+  void onDrawMMeshMoveZ(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 160, 118, 209, 132);
+    onDrawPFloatMenu(menuitem, line);
+  }
+#endif
+
+#if HAS_PREHEAT
+  #if HAS_HOTEND
+      void onDrawSetPreheatHotend(MenuItemClass* menuitem, int8_t line) {
+        if (HMI_IsChinese()) menuitem->SetFrame(1, 1, 134, 56, 146);
+        onDrawPIntMenu(menuitem, line);
+      }
+  #endif
+  #if HAS_HEATED_BED
+    void onDrawSetPreheatBed(MenuItemClass* menuitem, int8_t line) {
+      if (HMI_IsChinese()) menuitem->SetFrame(1, 58, 134, 113, 146);
+      onDrawPIntMenu(menuitem, line);
+    }
+  #endif
+  #if HAS_FAN
+    void onDrawSetPreheatFan(MenuItemClass* menuitem, int8_t line) {
+      if (HMI_IsChinese()) menuitem->SetFrame(1, 115, 134, 170, 146);
+      onDrawPIntMenu(menuitem, line);
+    }
+  #endif
+  void onDrawPLAPreheatSubMenu(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 100, 89, 178, 101);
+    onDrawSubMenu(menuitem,line);
+  }
+  void onDrawABSPreheatSubMenu(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) menuitem->SetFrame(1, 180, 89, 260, 100);
+    onDrawSubMenu(menuitem,line);
+  }
+#endif // HAS_HOTEND
+
+void onDrawMaxSpeedX(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame(1, 173, 133, 228, 147);
+    DWIN_Frame_AreaCopy(1, 229, 133, 236, 147, LBLX + 58, MBASE(line));      // X
+  }
+  onDrawPFloatMenu(menuitem, line);
+}
+
+void onDrawMaxSpeedY(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame(1, 173, 133, 228, 147);
+    DWIN_Frame_AreaCopy(1, 1, 150, 7, 160, LBLX + 58, MBASE(line));          // Y
+  }
+  onDrawPFloatMenu(menuitem, line);
+}
+
+void onDrawMaxSpeedZ(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame(1, 173, 133, 228, 147);
+    DWIN_Frame_AreaCopy(1, 9, 150, 16, 160, LBLX + 58, MBASE(line) + 3);     // Z
+  }
+  onDrawPFloatMenu(menuitem, line);
+}
+
+#if HAS_HOTEND
+  void onDrawMaxSpeedE(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) {
+      menuitem->SetFrame(1, 173, 133, 228, 147);
+      DWIN_Frame_AreaCopy(1, 18, 150, 25, 160, LBLX + 58, MBASE(line));      // E
+    }
+    onDrawPFloatMenu(menuitem, line);
+  }
+#endif
+
+void onDrawMaxAccelX(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame (1, 173, 133, 200, 147);
+    DWIN_Frame_AreaCopy(1, 28,  149,  69, 161, LBLX + 27, MBASE(line));
+    DWIN_Frame_AreaCopy(1, 229, 133, 236, 147, LBLX + 71, MBASE(line));      // X
+  }
+  onDrawPInt32Menu(menuitem, line);
+}
+
+void onDrawMaxAccelY(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame (1, 173, 133, 200, 147);
+    DWIN_Frame_AreaCopy(1,  28, 149,  69, 161, LBLX + 27, MBASE(line));
+    DWIN_Frame_AreaCopy(1,   1, 150,   7, 160, LBLX + 71, MBASE(line));          // Y
+  }
+  onDrawPInt32Menu(menuitem, line);
+}
+
+void onDrawMaxAccelZ(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame (1, 173, 133, 200, 147);
+    DWIN_Frame_AreaCopy(1,  28, 149,  69, 161, LBLX + 27, MBASE(line));
+    DWIN_Frame_AreaCopy(1,   9, 150,  16, 160, LBLX + 71, MBASE(line));         // Z
+  }
+  onDrawPInt32Menu(menuitem, line);
+}
+
+#if HAS_HOTEND
+  void onDrawMaxAccelE(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) {
+      menuitem->SetFrame (1, 173, 133, 200, 147);
+      DWIN_Frame_AreaCopy(1,  28, 149,  69, 161, LBLX + 27, MBASE(line));
+      DWIN_Frame_AreaCopy(1,  18, 150,  25, 160, LBLX + 71, MBASE(line));        // E
+    }
+    onDrawPInt32Menu(menuitem, line);
+  }
+#endif
+
+#if HAS_CLASSIC_JERK
+  void onDrawMaxJerkX(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) {
+      menuitem->SetFrame (1, 173, 133, 200, 147);
+      DWIN_Frame_AreaCopy(1,   1, 180,  28, 192, LBLX + 27, MBASE(line));
+      DWIN_Frame_AreaCopy(1, 202, 133, 228, 147, LBLX + 53, MBASE(line));
+      DWIN_Frame_AreaCopy(1, 229, 133, 236, 147, LBLX + 83, MBASE(line));
+    }
+    onDrawPFloatMenu(menuitem, line);
+  }
+
+  void onDrawMaxJerkY(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) {
+      menuitem->SetFrame (1, 173, 133, 200, 147);
+      DWIN_Frame_AreaCopy(1,   1, 180,  28, 192, LBLX + 27, MBASE(line));
+      DWIN_Frame_AreaCopy(1, 202, 133, 228, 147, LBLX + 53, MBASE(line));
+      DWIN_Frame_AreaCopy(1,   1, 150,   7, 160, LBLX + 83, MBASE(line));
+    }
+    onDrawPFloatMenu(menuitem, line);
+  }
+
+  void onDrawMaxJerkZ(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) {
+      menuitem->SetFrame (1, 173, 133, 200, 147);
+      DWIN_Frame_AreaCopy(1,   1, 180,  28, 192, LBLX + 27, MBASE(line));
+      DWIN_Frame_AreaCopy(1, 202, 133, 228, 147, LBLX + 53, MBASE(line));
+      DWIN_Frame_AreaCopy(1,   9, 150,  16, 160, LBLX + 83, MBASE(line));
+    }
+    onDrawPFloatMenu(menuitem, line);
+  }
+
+  #if HAS_HOTEND
+    void onDrawMaxJerkE(MenuItemClass* menuitem, int8_t line) {
+      if (HMI_IsChinese()) {
+        menuitem->SetFrame (1, 173, 133, 200, 147);
+        DWIN_Frame_AreaCopy(1,   1, 180,  28, 192, LBLX + 27, MBASE(line));
+        DWIN_Frame_AreaCopy(1, 202, 133, 228, 147, LBLX + 53, MBASE(line));
+        DWIN_Frame_AreaCopy(1,  18, 150,  25, 160, LBLX + 83, MBASE(line));
+      }
+      onDrawPFloatMenu(menuitem, line);
+    }
+  #endif
+#endif // HAS_CLASSIC_JERK
+
+void onDrawStepsX(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame (1, 153, 148, 194, 161);
+    DWIN_Frame_AreaCopy(1, 229, 133, 236, 147, LBLX + 44, MBASE(line));      // X
+  }
+  onDrawPFloatMenu(menuitem, line);
+}
+
+void onDrawStepsY(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame (1, 153, 148, 194, 161);
+    DWIN_Frame_AreaCopy(1,   1, 150,   7, 160, LBLX + 44, MBASE(line));      // Y
+  }
+  onDrawPFloatMenu(menuitem, line);
+}
+
+void onDrawStepsZ(MenuItemClass* menuitem, int8_t line) {
+  if (HMI_IsChinese()) {
+    menuitem->SetFrame (1, 153, 148, 194, 161);
+    DWIN_Frame_AreaCopy(1,   9, 150,  16, 160, LBLX + 44, MBASE(line));      // Z
+  }
+  onDrawPFloatMenu(menuitem, line);
+}
+
+#if HAS_HOTEND
+  void onDrawStepsE(MenuItemClass* menuitem, int8_t line) {
+    if (HMI_IsChinese()) {
+      menuitem->SetFrame (1, 153, 148, 194, 161);
+      DWIN_Frame_AreaCopy(1,  18, 150,  25, 160, LBLX + 44, MBASE(line));    // E
+    }
+    onDrawPFloatMenu(menuitem, line);
+  }
+#endif
+
+// HMI Control functions ======================================================
+
+// Generic menu control using the encoder
+void HMI_Menu() {
+  ENCODER_DiffState encoder_diffState = get_encoder_state();
+  if (encoder_diffState == ENCODER_DIFF_NO) return;
+  if (encoder_diffState == ENCODER_DIFF_ENTER) {
+    if (CurrentMenu != nullptr) CurrentMenu->onClick();
+  } else if (CurrentMenu != nullptr) CurrentMenu->onScroll(encoder_diffState == ENCODER_DIFF_CW);
+}
+
+// Get an integer value using the encoder without draw anything
+//  lo: low limit
+//  hi: high limit
+// Return value:
+//  0 : no change
+//  1 : live change
+//  2 : apply change
+int8_t HMI_GetIntNoDraw(const int32_t lo, const int32_t hi) {
+  ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+  if (encoder_diffState != ENCODER_DIFF_NO) {
+    if (Apply_Encoder(encoder_diffState, HMI_value.Value)) {
+      EncoderRate.enabled = false;
+      checkkey = last_checkkey;
+      return 2;
+    }
+    LIMIT(HMI_value.Value, lo, hi);
+    return 1;
+  }
+  return 0;
+}
+
+// Get an integer value using the encoder
+//  lo: low limit
+//  hi: high limit
+// Return value:
+//  0 : no change
+//  1 : live change
+//  2 : apply change
+int8_t HMI_GetInt(const int32_t lo, const int32_t hi) {
+  ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+  if (encoder_diffState != ENCODER_DIFF_NO) {
+    if (Apply_Encoder(encoder_diffState, HMI_value.Value)) {
+      EncoderRate.enabled = false;
+      DWINUI::Draw_Int(HMI_data.Text_Color, HMI_data.Background_Color, 4 , VALX, MBASE(CurrentMenu->line()) - 1, HMI_value.Value);
+      checkkey = last_checkkey;
+      return 2;
+    }
+    LIMIT(HMI_value.Value, lo, hi);
+    DWINUI::Draw_Int(HMI_data.Text_Color, HMI_data.Selected_Color, 4 , VALX, MBASE(CurrentMenu->line()) - 1, HMI_value.Value);
+    return 1;
+  }
+  return 0;
+}
+
+// Set an integer using the encoder
+void HMI_SetInt() {
+  int8_t val = HMI_GetInt(HMI_value.MinValue, HMI_value.MaxValue);
+  switch (val) {
+    case 0: return; break;
+    case 1: if (HMI_value.LiveUpdate != nullptr) HMI_value.LiveUpdate(); break;
+    case 2: if (HMI_value.Apply != nullptr) HMI_value.Apply(); break;
+  }
+}
+
+// Set an integer without drawing
+void HMI_SetIntNoDraw() {
+  int8_t val = HMI_GetIntNoDraw(HMI_value.MinValue, HMI_value.MaxValue);
+  switch (val) {
+    case 0: return; break;
+    case 1: if (HMI_value.LiveUpdate != nullptr) HMI_value.LiveUpdate(); break;
+    case 2: if (HMI_value.Apply != nullptr) HMI_value.Apply(); break;
+  }
+}
+
+// Set an integer pointer variable using the encoder
+void HMI_SetPInt() {
+  int8_t val = HMI_GetInt(HMI_value.MinValue, HMI_value.MaxValue);
+  if (!val) return;
+  else if (val == 2) {  // Apply
+    *HMI_value.P_Int = HMI_value.Value;
+    if (HMI_value.Apply != nullptr) HMI_value.Apply();
+  } else if (HMI_value.LiveUpdate != nullptr) HMI_value.LiveUpdate();
+}
+
+// Get an scaled float value using the encoder
+//  dp: decimal places
+//  lo: scaled low limit
+//  hi: scaled high limit
+// Return value:
+//  0 : no change
+//  1 : live change
+//  2 : apply change
+int8_t HMI_GetFloat(uint8_t dp, int32_t lo, int32_t hi) {
+  ENCODER_DiffState encoder_diffState = Encoder_ReceiveAnalyze();
+  if (encoder_diffState != ENCODER_DIFF_NO) {
+    if (Apply_Encoder(encoder_diffState, HMI_value.Value)) {
+      EncoderRate.enabled = false;
+      DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Background_Color, 3, dp, VALX - dp * DWINUI::Get_font_width(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), HMI_value.Value / POW(10, dp));
+      checkkey = last_checkkey;
+      return 2;
+    }
+    LIMIT(HMI_value.Value, lo, hi);
+    DWINUI::Draw_Signed_Float(HMI_data.Text_Color, HMI_data.Selected_Color, 3, dp, VALX - dp * DWINUI::Get_font_width(DWIN_FONT_MENU), MBASE(CurrentMenu->line()), HMI_value.Value / POW(10, dp));
+    return 1;
+  }
+  return 0;
+}
+
+// Set an scaled float using the encoder
+void HMI_SetFloat() {
+  int8_t val = HMI_GetFloat(HMI_value.dp, HMI_value.MinValue, HMI_value.MaxValue);
+  switch (val) {
+    case 0: return; break;
+    case 1: if (HMI_value.LiveUpdate != nullptr) HMI_value.LiveUpdate(); break;
+    case 2: if (HMI_value.Apply != nullptr) HMI_value.Apply(); break;
+  }
+}
+
+// Set an scaled float pointer variable using the encoder
+void HMI_SetPFloat() {
+  int8_t val = HMI_GetFloat(HMI_value.dp, HMI_value.MinValue, HMI_value.MaxValue);
+  if (!val) return;
+  else if (val == 2) {  // Apply
+    *HMI_value.P_Float = HMI_value.Value / POW(10, HMI_value.dp);
+    if (HMI_value.Apply != nullptr) HMI_value.Apply();
+  } else if (HMI_value.LiveUpdate != nullptr) HMI_value.LiveUpdate();
+}
+
+// Menu Creation and Drawing functions ======================================================
+
+void SetMenuTitle(frame_rect_t cn, frame_rect_t en, const __FlashStringHelper* text) {
+    if (HMI_IsChinese() && (cn.w != 0))
+      CurrentMenu->MenuTitle.SetFrame(cn.x, cn.y, cn.w, cn.h);
+    else {
+      #ifdef USE_STRING_HEADINGS
+        CurrentMenu->MenuTitle.SetCaption(text);
+      #else
+        if (en.w != 0) CurrentMenu->MenuTitle.SetFrame(en.x, en.y, en.w, en.h);
+      #endif
+    }
+}
+
+void Draw_Prepare_Menu() {
+  checkkey = Menu;
+  if (PrepareMenu == nullptr) PrepareMenu = new MenuClass();
+  if (CurrentMenu != PrepareMenu) {
+    CurrentMenu = PrepareMenu;
+    SetMenuTitle({133, 1, 28, 13}, {179, 0, 48, 14}, GET_TEXT_F(MSG_PREPARE));
+    DWINUI::MenuItemsPrepare(13);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Goto_Main_Menu);
+    #if ENABLED(ADVANCED_PAUSE_FEATURE)
+      ADDMENUITEM(ICON_FilMan, GET_TEXT(MSG_FILAMENT_MAN), onDrawSubMenu, Draw_FilamentMan_Menu);
+    #endif
+    ADDMENUITEM(ICON_Axis, GET_TEXT(MSG_MOVE_AXIS), onDrawMoveSubMenu, Draw_Move_Menu);
+    ADDMENUITEM(ICON_LevBed, GET_TEXT(MSG_BED_LEVELING), onDrawSubMenu, Draw_LevBedCorners_Menu);
+    ADDMENUITEM(ICON_CloseMotor, GET_TEXT(MSG_DISABLE_STEPPERS), onDrawDisableMotors, DisableMotors);
+    ADDMENUITEM(ICON_Homing, GET_TEXT(MSG_AUTO_HOME), onDrawAutoHome, AutoHome);
+    #if ENABLED(MESH_BED_LEVELING)
+      ADDMENUITEM(ICON_ManualMesh, GET_TEXT(MSG_MANUAL_MESH), onDrawSubMenu, Draw_ManualMesh_Menu);
+    #endif
+    #if HAS_ZOFFSET_ITEM
+      #if EITHER(HAS_BED_PROBE, BABYSTEPPING)
+        ADDMENUITEM(ICON_SetZOffset, GET_TEXT(MSG_PROBE_WIZARD), onDrawSubMenu, Draw_ZOffsetWiz_Menu);
+      #else
+        ADDMENUITEM(ICON_SetHome, GET_TEXT(MSG_SET_HOME_OFFSETS), onDrawHomeOffset, SetHome);
+      #endif
+    #endif
+    #if HAS_HOTEND
+      ADDMENUITEM(ICON_PLAPreheat, GET_TEXT(MSG_PREHEAT_1), onDrawPreheat1, SetPreheat0);
+      ADDMENUITEM(ICON_ABSPreheat, PSTR("Preheat " PREHEAT_2_LABEL), onDrawPreheat2, SetPreheat1);
+      ADDMENUITEM(ICON_CustomPreheat, GET_TEXT(MSG_PREHEAT_CUSTOM), onDrawMenuItem, SetPreheat2);
+    #endif
+    #if HAS_PREHEAT
+      ADDMENUITEM(ICON_Cool, GET_TEXT(MSG_COOLDOWN), onDrawCooldown, SetCoolDown);
+    #endif
+    ADDMENUITEM(ICON_Language, PSTR("UI Language"), onDrawLanguage, SetLanguage);
+  }
+  CurrentMenu->Draw();
+}
+
+void Draw_LevBedCorners_Menu() {
+  DWINUI::ClearMenuArea();
+  checkkey = Menu;
+  if (LevBedMenu == nullptr) LevBedMenu = new MenuClass();
+  if (CurrentMenu != LevBedMenu) {
+    CurrentMenu = LevBedMenu;
+    SetMenuTitle({0}, {0}, GET_TEXT_F(MSG_BED_TRAMMING)); // TODO: Chinese, English "Bed Tramming" JPG
+    DWINUI::MenuItemsPrepare(6);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Prepare_Menu);
+    ADDMENUITEM(ICON_Axis, GET_TEXT(MSG_LEVBED_FL), onDrawMenuItem, LevBedFL);
+    ADDMENUITEM(ICON_Axis, GET_TEXT(MSG_LEVBED_FR), onDrawMenuItem, LevBedFR);
+    ADDMENUITEM(ICON_Axis, GET_TEXT(MSG_LEVBED_BR), onDrawMenuItem, LevBedBR);
+    ADDMENUITEM(ICON_Axis, GET_TEXT(MSG_LEVBED_BL), onDrawMenuItem, LevBedBL);
+    ADDMENUITEM(ICON_Axis, GET_TEXT(MSG_LEVBED_C ), onDrawMenuItem, LevBedC );
+  }
+  CurrentMenu->Draw();
+}
+
+void Draw_Control_Menu() {
+  checkkey = Menu;
+  if (ControlMenu == nullptr) ControlMenu = new MenuClass();
+  if (CurrentMenu != ControlMenu) {
+    CurrentMenu = ControlMenu;
+    SetMenuTitle({103, 1, 28, 14}, {128, 2, 49, 11}, GET_TEXT_F(MSG_CONTROL));
+    DWINUI::MenuItemsPrepare(9);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Goto_Main_Menu);
+    ADDMENUITEM(ICON_Temperature, GET_TEXT(MSG_TEMPERATURE), onDrawTempSubMenu, Draw_Temperature_Menu);
+    ADDMENUITEM(ICON_Motion, GET_TEXT(MSG_MOTION), onDrawMotionSubMenu, Draw_Motion_Menu);
+    #if ENABLED(EEPROM_SETTINGS)
+      ADDMENUITEM(ICON_WriteEEPROM, GET_TEXT(MSG_STORE_EEPROM), onDrawWriteEeprom, WriteEeprom);
+      ADDMENUITEM(ICON_ReadEEPROM, GET_TEXT(MSG_LOAD_EEPROM), onDrawReadEeprom, ReadEeprom);
+      ADDMENUITEM(ICON_ResumeEEPROM, GET_TEXT(MSG_RESTORE_DEFAULTS), onDrawResetEeprom, ResetEeprom);
+    #endif
+    ADDMENUITEM(ICON_Reboot, GET_TEXT(MSG_RESET_PRINTER), onDrawMenuItem, RebootPrinter);
+    ADDMENUITEM(ICON_AdvSet, GET_TEXT(MSG_ADVANCED_SETTINGS), onDrawSubMenu, Draw_AdvancedSettings_Menu);
+    ADDMENUITEM(ICON_Info, GET_TEXT(MSG_INFO_SCREEN), onDrawInfoSubMenu, Goto_InfoMenu);
+  }
+  CurrentMenu->Draw();
+}
+
+void Draw_AdvancedSettings_Menu() {
+  checkkey = Menu;
+  if (AdvancedSettings == nullptr) AdvancedSettings = new MenuClass();
+  if (CurrentMenu != AdvancedSettings) {
+    CurrentMenu = AdvancedSettings;
+    SetMenuTitle({0}, {0}, GET_TEXT_F(MSG_ADVANCED_SETTINGS)); // TODO: Chinese, English "Advanced Settings" JPG
+    DWINUI::MenuItemsPrepare(11);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Control_Menu);
+    #if HAS_HOME_OFFSET
+      ADDMENUITEM(ICON_HomeOffset, GET_TEXT(MSG_SET_HOME_OFFSETS), onDrawSubMenu, Draw_HomeOffset_Menu);
+    #endif
+    #if HAS_BED_PROBE
+      ADDMENUITEM(ICON_ProbeSet, GET_TEXT(MSG_ZPROBE_SETTINGS), onDrawSubMenu, Draw_ProbeSet_Menu);
+    #endif
+    #if HAS_HOTEND
+      ADDMENUITEM(ICON_PIDNozzle, F("Hotend PID Settings"), onDrawSubMenu, Draw_HotendPID_Menu);
+    #endif
+    #if HAS_HEATED_BED
+      ADDMENUITEM(ICON_PIDbed, F("Bed PID Settings"), onDrawSubMenu, Draw_BedPID_Menu);
+    #endif
+    #if HAS_FILAMENT_SENSOR
+      ADDMENUITEM(ICON_FilSet, GET_TEXT(MSG_FILAMENT_SET), onDrawSubMenu, Draw_FilSet_Menu);
+    #endif
+    #if ENABLED(POWER_LOSS_RECOVERY)
+      ADDMENUITEM(ICON_Pwrlossr, F("Power-loss recovery"), onDrawPwrLossR, SetPwrLossr);
+    #endif
+    #if HAS_LCD_BRIGHTNESS
+      ADDMENUITEM_P(ICON_Brightness, F("LCD Brightness"), onDrawPInt8Menu, SetBrightness, &ui.brightness);
+    #endif
+    ADDMENUITEM(ICON_Scolor, F("Select Colors"), onDrawSubMenu, Draw_SelectColors_Menu);
+    #if ENABLED(SOUND_MENU_ITEM)
+      ADDMENUITEM(ICON_Sound, F("Enable Sound"), onDrawEnableSound, SetEnableSound);
+    #endif
+    ADDMENUITEM(ICON_Lock, F("Lock Screen"), onDrawMenuItem, Goto_LockScreen);
+  }
+  CurrentMenu->Draw();
+}
+
+void Draw_Move_Menu() {
+  checkkey = Menu;
+  if (MoveMenu == nullptr) MoveMenu = new MenuClass();
+  if (CurrentMenu != MoveMenu) {
+    CurrentMenu = MoveMenu;
+    SetMenuTitle({192, 1, 42, 14}, {231, 2, 35, 11}, GET_TEXT_F(MSG_MOVE_AXIS));
+    DWINUI::MenuItemsPrepare(5);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Prepare_Menu);
+    ADDMENUITEM_P(ICON_MoveX, GET_TEXT(MSG_MOVE_X), onDrawMoveX, SetMoveX, &current_position.x);
+    ADDMENUITEM_P(ICON_MoveY, GET_TEXT(MSG_MOVE_Y), onDrawMoveY, SetMoveY, &current_position.y);
+    ADDMENUITEM_P(ICON_MoveZ, GET_TEXT(MSG_MOVE_Z), onDrawMoveZ, SetMoveZ, &current_position.z);
+    #if HAS_HOTEND
+      ADDMENUITEM_P(ICON_Extruder, GET_TEXT(MSG_MOVE_E), onDrawMoveE, SetMoveE, &current_position.e);
+    #endif
+  }
+  CurrentMenu->Draw();
+  if (!all_axes_trusted()) ui.set_status_P(PSTR("WARNING: position is unknow"));
+}
+
+#if HAS_HOME_OFFSET
+  void Draw_HomeOffset_Menu() {
+    checkkey = Menu;
+    if (HomeOffMenu == nullptr) HomeOffMenu = new MenuClass();
+    if (CurrentMenu != HomeOffMenu) {
+      CurrentMenu = HomeOffMenu;
+      SetMenuTitle({0}, {0}, GET_TEXT_F(MSG_SET_HOME_OFFSETS)); // TODO: Chinese, English "Set Home Offsets" JPG
+      DWINUI::MenuItemsPrepare(4);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_AdvancedSettings_Menu);
+      ADDMENUITEM_P(ICON_HomeOffsetX, GET_TEXT(MSG_HOME_OFFSET_X), onDrawPFloatMenu, SetHomeOffsetX, &home_offset[X_AXIS]);
+      ADDMENUITEM_P(ICON_HomeOffsetY, GET_TEXT(MSG_HOME_OFFSET_Y), onDrawPFloatMenu, SetHomeOffsetY, &home_offset[Y_AXIS]);
+      ADDMENUITEM_P(ICON_HomeOffsetZ, GET_TEXT(MSG_HOME_OFFSET_Z), onDrawPFloatMenu, SetHomeOffsetZ, &home_offset[Z_AXIS]);
+    }
+    CurrentMenu->Draw();
+  }
+#endif
+
+#if HAS_BED_PROBE
+  void Draw_ProbeSet_Menu() {
+    checkkey = Menu;
+    if (ProbeSetMenu == nullptr) ProbeSetMenu = new MenuClass();
+    if (CurrentMenu != ProbeSetMenu) {
+      CurrentMenu = ProbeSetMenu;
+      SetMenuTitle({0}, {0}, GET_TEXT_F(MSG_ZPROBE_SETTINGS)); // TODO: Chinese, English "Probe Settings" JPG
+      DWINUI::MenuItemsPrepare(5);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_AdvancedSettings_Menu);
+      ADDMENUITEM_P(ICON_ProbeOffsetX, GET_TEXT(MSG_ZPROBE_XOFFSET), onDrawPFloatMenu, SetProbeOffsetX, &probe.offset.x);
+      ADDMENUITEM_P(ICON_ProbeOffsetY, GET_TEXT(MSG_ZPROBE_YOFFSET), onDrawPFloatMenu, SetProbeOffsetY, &probe.offset.y);
+      ADDMENUITEM_P(ICON_ProbeOffsetZ, GET_TEXT(MSG_ZPROBE_ZOFFSET), onDrawPFloat2Menu, SetProbeOffsetZ, &probe.offset.z);
+      ADDMENUITEM(ICON_ProbeTest, GET_TEXT(MSG_M48_TEST), onDrawMenuItem, ProbeTest);
+    }
+    CurrentMenu->Draw();
+  }
+#endif
+
+#if HAS_FILAMENT_SENSOR
+  void Draw_FilSet_Menu() {
+    checkkey = Menu;
+    if (FilSetMenu == nullptr) FilSetMenu = new MenuClass();
+    if (CurrentMenu != FilSetMenu) {
+      CurrentMenu = FilSetMenu;
+      CurrentMenu->MenuTitle.SetCaption(GET_TEXT_F(MSG_FILAMENT_SET));
+      DWINUI::MenuItemsPrepare(6);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawMenuItem, Draw_AdvancedSettings_Menu);
+      #if HAS_FILAMENT_SENSOR
+        ADDMENUITEM(ICON_Runout, GET_TEXT(MSG_RUNOUT_ENABLE), onDrawRunoutEnable, SetRunoutEnable);
+      #endif
+      #if HAS_FILAMENT_RUNOUT_DISTANCE
+        ADDMENUITEM_P(ICON_Runout, F("Runout Distance"), onDrawPFloatMenu, SetRunoutDistance, &runout.runout_distance());
+      #endif
+      #if ENABLED(PREVENT_COLD_EXTRUSION)
+        ADDMENUITEM_P(ICON_ExtrudeMinT, F("Extrude Min Temp."), onDrawPIntMenu, SetExtMinT, &HMI_data.ExtMinT);
+      #endif
+      #if ENABLED(ADVANCED_PAUSE_FEATURE)
+        ADDMENUITEM_P(ICON_FilLoad, GET_TEXT(MSG_FILAMENT_LOAD), onDrawPFloatMenu, SetFilLoad, &fc_settings[0].load_length);
+        ADDMENUITEM_P(ICON_FilUnload, GET_TEXT(MSG_FILAMENT_UNLOAD), onDrawPFloatMenu, SetFilUnload, &fc_settings[0].unload_length);
+      #endif
+    }
+    CurrentMenu->Draw();
+  }
+#endif
+void Draw_SelectColors_Menu() {
+  checkkey = Menu;
+  if (SelectColorMenu == nullptr) SelectColorMenu = new MenuClass();
+  if (CurrentMenu != SelectColorMenu) {
+    CurrentMenu = SelectColorMenu;
+    SetMenuTitle({0}, {0}, F("Select Colors")); // TODO: Chinese, English "Select Color" JPG
+    DWINUI::MenuItemsPrepare(20);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_AdvancedSettings_Menu);
+    ADDMENUITEM(ICON_StockConfiguration, GET_TEXT(MSG_RESTORE_DEFAULTS), onDrawMenuItem, RestoreDefaultsColors);
+    ADDMENUITEM_P(0, "Screen Background", onDrawSelColorItem, SelColor, &HMI_data.Background_Color);
+    ADDMENUITEM_P(0, "Cursor", onDrawSelColorItem, SelColor, &HMI_data.Cursor_color);
+    ADDMENUITEM_P(0, "Title Background", onDrawSelColorItem, SelColor, &HMI_data.TitleBg_color);
+    ADDMENUITEM_P(0, "Title Text", onDrawSelColorItem, SelColor, &HMI_data.TitleTxt_color);
+    ADDMENUITEM_P(0, "Text", onDrawSelColorItem, SelColor, &HMI_data.Text_Color);
+    ADDMENUITEM_P(0, "Selected", onDrawSelColorItem, SelColor, &HMI_data.Selected_Color);
+    ADDMENUITEM_P(0, "Split Line", onDrawSelColorItem, SelColor, &HMI_data.SplitLine_Color);
+    ADDMENUITEM_P(0, "Highlight", onDrawSelColorItem, SelColor, &HMI_data.Highlight_Color);
+    ADDMENUITEM_P(0, "Status Background", onDrawSelColorItem, SelColor, &HMI_data.StatusBg_Color);
+    ADDMENUITEM_P(0, "Status Text", onDrawSelColorItem, SelColor, &HMI_data.StatusTxt_Color);
+    ADDMENUITEM_P(0, "Popup Background", onDrawSelColorItem, SelColor, &HMI_data.PopupBg_color);
+    ADDMENUITEM_P(0, "Popup Text", onDrawSelColorItem, SelColor, &HMI_data.PopupTxt_Color);
+    ADDMENUITEM_P(0, "Alert Background", onDrawSelColorItem, SelColor, &HMI_data.AlertBg_Color);
+    ADDMENUITEM_P(0, "Alert Text", onDrawSelColorItem, SelColor, &HMI_data.AlertTxt_Color);
+    ADDMENUITEM_P(0, "Percent Text", onDrawSelColorItem, SelColor, &HMI_data.PercentTxt_Color);
+    ADDMENUITEM_P(0, "Bar Fill", onDrawSelColorItem, SelColor, &HMI_data.Barfill_Color);
+    ADDMENUITEM_P(0, "Indicator value", onDrawSelColorItem, SelColor, &HMI_data.Indicator_Color);
+    ADDMENUITEM_P(0, "Coordinate value", onDrawSelColorItem, SelColor, &HMI_data.Coordinate_Color);
+  }
+  CurrentMenu->Draw();
+}
+
+void Draw_GetColor_Menu() {
+  checkkey = Menu;
+  if (GetColorMenu == nullptr) GetColorMenu = new MenuClass();
+  if (CurrentMenu != GetColorMenu) {
+    CurrentMenu = GetColorMenu;
+    SetMenuTitle({0}, {0}, F("Get Color")); // TODO: Chinese, English "Get Color" JPG
+    DWINUI::MenuItemsPrepare(5);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, DWIN_ApplyColor);
+    ADDMENUITEM(ICON_Cancel, GET_TEXT(MSG_BUTTON_CANCEL), onDrawMenuItem, Draw_SelectColors_Menu);
+    ADDMENUITEM(0, "Red", onDrawGetColorItem, SetRGBColor);
+    ADDMENUITEM(1, "Green", onDrawGetColorItem, SetRGBColor);
+    ADDMENUITEM(2, "Blue", onDrawGetColorItem, SetRGBColor);
+  }
+  CurrentMenu->Draw();
+  DWIN_Draw_Rectangle(1, *HMI_value.P_Int, 20, 315, DWIN_WIDTH - 20, 335);
+}
+
+void Draw_Tune_Menu() {
+  checkkey = Menu;
+  if (TuneMenu == nullptr) TuneMenu = new MenuClass();
+  if (CurrentMenu != TuneMenu) {
+    CurrentMenu = TuneMenu;
+    SetMenuTitle({73, 2, 28, 12}, {94, 2, 33, 11}, GET_TEXT_F(MSG_TUNE)); // TODO: Chinese, English "Tune" JPG
+    DWINUI::MenuItemsPrepare(10);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Goto_PrintProcess);
+    ADDMENUITEM_P(ICON_Speed, GET_TEXT(MSG_SPEED), onDrawSpeedItem, SetSpeed, &feedrate_percentage);
+    #if HAS_HOTEND
+      HotendTargetItem = ADDMENUITEM_P(ICON_HotendTemp, GET_TEXT(MSG_UBL_SET_TEMP_HOTEND), onDrawHotendTemp, SetHotendTemp, &thermalManager.temp_hotend[0].target);
+    #endif
+    #if HAS_HEATED_BED
+      BedTargetItem = ADDMENUITEM_P(ICON_BedTemp, GET_TEXT(MSG_UBL_SET_TEMP_BED), onDrawBedTemp, SetBedTemp, &thermalManager.temp_bed.target);
+    #endif
+    #if HAS_FAN
+      FanSpeedItem = ADDMENUITEM_P(ICON_FanSpeed, GET_TEXT(MSG_FAN_SPEED), onDrawFanSpeed, SetFanSpeed, &thermalManager.fan_speed[0]);
+    #endif
+    #if HAS_ZOFFSET_ITEM && EITHER(HAS_BED_PROBE, BABYSTEPPING)
+      ADDMENUITEM_P(ICON_Zoffset, GET_TEXT(MSG_ZPROBE_ZOFFSET), onDrawZOffset, SetZOffset, &BABY_Z_VAR);
+    #endif
+    ADDMENUITEM_P(ICON_Flow, GET_TEXT(MSG_FLOW), onDrawPIntMenu, SetFlow, &planner.flow_percentage[0]);
+    #if ENABLED(ADVANCED_PAUSE_FEATURE)
+      ADDMENUITEM(ICON_FilMan, GET_TEXT(MSG_FILAMENTCHANGE), onDrawMenuItem, ChangeFilament);
+    #endif
+    ADDMENUITEM(ICON_Lock, F("Lock Screen"), onDrawMenuItem, Goto_LockScreen);
+    #if HAS_LCD_BRIGHTNESS
+      ADDMENUITEM_P(ICON_Brightness, F("LCD Brightness"), onDrawPInt8Menu, SetBrightness, &ui.brightness);
+    #endif
+  }
+  CurrentMenu->Draw();
+}
+
+void Draw_Motion_Menu() {
+  checkkey = Menu;
+  if (MotionMenu == nullptr) MotionMenu = new MenuClass();
+  if (CurrentMenu != MotionMenu) {
+    CurrentMenu = MotionMenu;
+    SetMenuTitle({1, 16, 28, 13}, {144, 16, 46, 11}, GET_TEXT_F(MSG_MOTION)); // TODO: Chinese, English "Motion" JPG
+    DWINUI::MenuItemsPrepare(6);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Control_Menu);
+    ADDMENUITEM(ICON_MaxSpeed, GET_TEXT(MSG_SPEED), onDrawSpeed, Draw_MaxSpeed_Menu);
+    ADDMENUITEM(ICON_MaxAccelerated, GET_TEXT(MSG_ACCELERATION), onDrawAcc, Draw_MaxAccel_Menu);
+    #if HAS_CLASSIC_JERK
+      ADDMENUITEM(ICON_MaxJerk, GET_TEXT(MSG_JERK), onDrawJerk, Draw_MaxJerk_Menu);
+    #endif
+    ADDMENUITEM(ICON_Step, GET_TEXT(MSG_STEPS_PER_MM), onDrawSteps, Draw_Steps_Menu);
+    ADDMENUITEM_P(ICON_Flow, GET_TEXT(MSG_FLOW), onDrawPIntMenu, SetFlow, &planner.flow_percentage[0]);
+  }
+  CurrentMenu->Draw();
+}
+
+#if ENABLED(ADVANCED_PAUSE_FEATURE)
+  void Draw_FilamentMan_Menu() {
+    checkkey = Menu;
+    if (FilamentMenu == nullptr) FilamentMenu = new MenuClass();
+    if (CurrentMenu != FilamentMenu) {
+      CurrentMenu = FilamentMenu;
+      SetMenuTitle({0}, {0}, GET_TEXT_F(MSG_FILAMENT_MAN)); // TODO: Chinese, English "Filament Management" JPG
+      DWINUI::MenuItemsPrepare(5);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Prepare_Menu);
+      ADDMENUITEM(ICON_Park, GET_TEXT(MSG_FILAMENT_PARK_ENABLED), onDrawMenuItem, ParkHead);
+      ADDMENUITEM(ICON_FilMan, GET_TEXT(MSG_FILAMENTCHANGE), onDrawMenuItem, ChangeFilament);
+      #if ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
+        ADDMENUITEM(ICON_FilUnload, GET_TEXT(MSG_FILAMENTUNLOAD), onDrawMenuItem, UnloadFilament);
+        ADDMENUITEM(ICON_FilLoad, GET_TEXT(MSG_FILAMENTLOAD), onDrawMenuItem, LoadFilament);
+      #endif
+    }
+    CurrentMenu->Draw();
+  }
+#endif
+
+#if ENABLED(MESH_BED_LEVELING)
+  void Draw_ManualMesh_Menu() {
+    checkkey = Menu;
+    if (ManualMesh == nullptr) ManualMesh = new MenuClass();
+    if (CurrentMenu != ManualMesh) {
+      CurrentMenu = ManualMesh;
+      SetMenuTitle({0}, {0}, GET_TEXT_F(MSG_MANUAL_MESH)); // TODO: Chinese, English "Manual Mesh Leveling" JPG
+      DWINUI::MenuItemsPrepare(5);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Prepare_Menu);
+      ADDMENUITEM(ICON_ManualMesh, GET_TEXT(MSG_LEVEL_BED), onDrawMenuItem, ManualMeshStart);
+      MMeshMoveZItem = ADDMENUITEM_P(ICON_Zoffset, GET_TEXT(MSG_MOVE_Z), onDrawMMeshMoveZ, SetMMeshMoveZ, &current_position.z);
+      ADDMENUITEM(ICON_Axis, GET_TEXT(MSG_UBL_CONTINUE_MESH), onDrawMenuItem, ManualMeshContinue);
+      ADDMENUITEM(ICON_MeshSave, GET_TEXT(MSG_UBL_SAVE_MESH), onDrawMenuItem, ManualMeshSave);
+    }
+    CurrentMenu->Draw();
+  }
+#endif
+
+#if HAS_PREHEAT
+  void Draw_Preheat_Menu(frame_rect_t cn, frame_rect_t en, const __FlashStringHelper* text) {
+    checkkey = Menu;
+    if (CurrentMenu != PreheatMenu) {
+      CurrentMenu = PreheatMenu;
+      SetMenuTitle(cn, en, text);
+      DWINUI::MenuItemsPrepare(5);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Temperature_Menu);
+      #if HAS_HOTEND
+        ADDMENUITEM_P(ICON_SetEndTemp, GET_TEXT(MSG_UBL_SET_TEMP_HOTEND), onDrawSetPreheatHotend, SetPreheatEndTemp, &ui.material_preset[HMI_value.Preheat].hotend_temp);
+      #endif
+      #if HAS_HEATED_BED
+        ADDMENUITEM_P(ICON_SetBedTemp, GET_TEXT(MSG_UBL_SET_TEMP_BED), onDrawSetPreheatBed, SetPreheatBedTemp, &ui.material_preset[HMI_value.Preheat].bed_temp);
+      #endif
+      #if HAS_FAN
+        ADDMENUITEM_P(ICON_FanSpeed, GET_TEXT(MSG_FAN_SPEED), onDrawSetPreheatFan, SetPreheatFanSpeed, &ui.material_preset[HMI_value.Preheat].fan_speed);
+      #endif
+      #if ENABLED(EEPROM_SETTINGS)
+        ADDMENUITEM(ICON_WriteEEPROM, GET_TEXT(MSG_STORE_EEPROM), onDrawWriteEeprom, WriteEeprom);
+      #endif
+    }
+    CurrentMenu->Draw();
+  }
+
+  void Draw_Preheat1_Menu() {
+    HMI_value.Preheat = 0;
+    if (PreheatMenu == nullptr) PreheatMenu = new MenuClass();
+    Draw_Preheat_Menu({59, 16, 81, 14}, {56, 15, 85, 14}, F(PREHEAT_1_LABEL " Preheat Settings")); // TODO: English "PLA Settings" JPG
+  }
+
+  void Draw_Preheat2_Menu() {
+    HMI_value.Preheat = 1;
+    if (PreheatMenu == nullptr) PreheatMenu = new MenuClass();
+    Draw_Preheat_Menu({142, 16, 82, 14}, {56, 15, 85, 14}, F(PREHEAT_2_LABEL " Preheat Settings"));  // TODO: English "ABS Settings" JPG
+  }
+
+  #ifdef PREHEAT_3_LABEL
+    void Draw_Preheat3_Menu() {
+      HMI_value.Preheat = 2;
+      if (PreheatMenu == nullptr) PreheatMenu = new MenuClass();
+      #define PREHEAT_3_TITLE PREHEAT_3_LABEL " Preheat Set."
+      Draw_Preheat_Menu({0}, {0}, F(PREHEAT_3_TITLE));  // TODO: Chinese, English "Custom Preheat Settings" JPG
+    }
+  #endif
+
+#endif
+
+void Draw_Temperature_Menu() {
+  checkkey = Menu;
+  if (TemperatureMenu == nullptr) TemperatureMenu = new MenuClass();
+  if (CurrentMenu != TemperatureMenu) {
+    CurrentMenu = TemperatureMenu;
+    SetMenuTitle({236, 2, 28, 12}, {56, 15, 85, 14}, GET_TEXT_F(MSG_TEMPERATURE));
+    DWINUI::MenuItemsPrepare(7);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Control_Menu);
+    #if HAS_HOTEND
+      HotendTargetItem = ADDMENUITEM_P(ICON_SetEndTemp, GET_TEXT(MSG_UBL_SET_TEMP_HOTEND), onDrawHotendTemp, SetHotendTemp, &thermalManager.temp_hotend[0].target);
+    #endif
+    #if HAS_HEATED_BED
+      BedTargetItem = ADDMENUITEM_P(ICON_SetBedTemp, GET_TEXT(MSG_UBL_SET_TEMP_BED), onDrawBedTemp, SetBedTemp, &thermalManager.temp_bed.target);
+    #endif
+    #if HAS_FAN
+      FanSpeedItem = ADDMENUITEM_P(ICON_FanSpeed, GET_TEXT(MSG_FAN_SPEED), onDrawFanSpeed, SetFanSpeed, &thermalManager.fan_speed[0]);
+    #endif
+    #if HAS_HOTEND
+      ADDMENUITEM(ICON_SetPLAPreheat, F(PREHEAT_1_LABEL " Preheat Settings"), onDrawPLAPreheatSubMenu, Draw_Preheat1_Menu);
+      ADDMENUITEM(ICON_SetABSPreheat, F(PREHEAT_2_LABEL " Preheat Settings"), onDrawABSPreheatSubMenu, Draw_Preheat2_Menu);
+      #ifdef PREHEAT_3_LABEL
+        ADDMENUITEM(ICON_SetCustomPreheat, PREHEAT_3_TITLE, onDrawSubMenu, Draw_Preheat3_Menu);
+      #endif
+    #endif
+  }
+  CurrentMenu->Draw();
+}
+
+void Draw_MaxSpeed_Menu() {
+  checkkey = Menu;
+  if (MaxSpeedMenu == nullptr) MaxSpeedMenu = new MenuClass();
+  if (CurrentMenu != MaxSpeedMenu) {
+    CurrentMenu = MaxSpeedMenu;
+    SetMenuTitle({1, 16, 28, 13}, {144, 16, 46, 11}, GET_TEXT_F(MSG_MAXSPEED));
+    DWINUI::MenuItemsPrepare(5);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Motion_Menu);
+    ADDMENUITEM_P(ICON_MaxSpeedX, GET_TEXT(MSG_MAXSPEED_X), onDrawMaxSpeedX, SetMaxSpeedX, &planner.settings.max_feedrate_mm_s[X_AXIS]);
+    ADDMENUITEM_P(ICON_MaxSpeedY, GET_TEXT(MSG_MAXSPEED_Y), onDrawMaxSpeedY, SetMaxSpeedY, &planner.settings.max_feedrate_mm_s[Y_AXIS]);
+    ADDMENUITEM_P(ICON_MaxSpeedZ, GET_TEXT(MSG_MAXSPEED_Z), onDrawMaxSpeedZ, SetMaxSpeedZ, &planner.settings.max_feedrate_mm_s[Z_AXIS]);
+    #if HAS_HOTEND
+      ADDMENUITEM_P(ICON_MaxSpeedE, GET_TEXT(MSG_MAXSPEED_E), onDrawMaxSpeedE, SetMaxSpeedE, &planner.settings.max_feedrate_mm_s[Z_AXIS]);
+    #endif
+  }
+  CurrentMenu->Draw();
+}
+
+void Draw_MaxAccel_Menu() {
+  checkkey = Menu;
+  if (MaxAccelMenu == nullptr) MaxAccelMenu = new MenuClass();
+  if (CurrentMenu != MaxAccelMenu) {
+    CurrentMenu = MaxAccelMenu;
+    SetMenuTitle({1, 16, 28, 13}, {144, 16, 46, 11}, GET_TEXT_F(MSG_ACCELERATION));
+    DWINUI::MenuItemsPrepare(5);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Motion_Menu);
+    ADDMENUITEM_P(ICON_MaxAccX, GET_TEXT(MSG_AMAX_A), onDrawMaxAccelX, SetMaxAccelX, &planner.settings.max_acceleration_mm_per_s2[X_AXIS]);
+    ADDMENUITEM_P(ICON_MaxAccY, GET_TEXT(MSG_AMAX_B), onDrawMaxAccelY, SetMaxAccelY, &planner.settings.max_acceleration_mm_per_s2[Y_AXIS]);
+    ADDMENUITEM_P(ICON_MaxAccZ, GET_TEXT(MSG_AMAX_C), onDrawMaxAccelZ, SetMaxAccelZ, &planner.settings.max_acceleration_mm_per_s2[Z_AXIS]);
+    #if HAS_HOTEND
+      ADDMENUITEM_P(ICON_MaxAccE, GET_TEXT(MSG_AMAX_E), onDrawMaxAccelE, SetMaxAccelE, &planner.settings.max_acceleration_mm_per_s2[E_AXIS]);
+    #endif
+  }
+  CurrentMenu->Draw();
+}
+
+#if HAS_CLASSIC_JERK
+  void Draw_MaxJerk_Menu() {
+    checkkey = Menu;
+    if (MaxJerkMenu == nullptr) MaxJerkMenu = new MenuClass();
+    if (CurrentMenu != MaxJerkMenu) {
+      CurrentMenu = MaxJerkMenu;
+      SetMenuTitle({1, 16, 28, 13}, {144, 16, 46, 11}, GET_TEXT_F(MSG_JERK));
+      DWINUI::MenuItemsPrepare(5);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Motion_Menu);
+      ADDMENUITEM_P(ICON_MaxSpeedJerkX, GET_TEXT(MSG_VA_JERK), onDrawMaxJerkX, SetMaxJerkX, &planner.max_jerk[X_AXIS]);
+      ADDMENUITEM_P(ICON_MaxSpeedJerkY, GET_TEXT(MSG_VB_JERK), onDrawMaxJerkY, SetMaxJerkY, &planner.max_jerk[Y_AXIS]);
+      ADDMENUITEM_P(ICON_MaxSpeedJerkZ, GET_TEXT(MSG_VC_JERK), onDrawMaxJerkZ, SetMaxJerkZ, &planner.max_jerk[Z_AXIS]);
+      #if HAS_HOTEND
+        ADDMENUITEM_P(ICON_MaxSpeedJerkE, GET_TEXT(MSG_VE_JERK), onDrawMaxJerkE, SetMaxJerkE, &planner.max_jerk[E_AXIS]);
+      #endif
+    }
+    CurrentMenu->Draw();
+  }
+#endif
+
+void Draw_Steps_Menu() {
+  checkkey = Menu;
+  if (StepsMenu == nullptr) StepsMenu = new MenuClass();
+  if (CurrentMenu != StepsMenu) {
+    CurrentMenu = StepsMenu;
+    SetMenuTitle({1, 16, 28, 13}, {144, 16, 46, 11}, GET_TEXT_F(MSG_STEPS_PER_MM));
+    DWINUI::MenuItemsPrepare(5);
+    ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawBack, Draw_Motion_Menu);
+    ADDMENUITEM_P(ICON_StepX, GET_TEXT(MSG_A_STEPS), onDrawStepsX, SetStepsX, &planner.settings.axis_steps_per_mm[X_AXIS]);
+    ADDMENUITEM_P(ICON_StepY, GET_TEXT(MSG_B_STEPS), onDrawStepsY, SetStepsY, &planner.settings.axis_steps_per_mm[Y_AXIS]);
+    ADDMENUITEM_P(ICON_StepZ, GET_TEXT(MSG_C_STEPS), onDrawStepsZ, SetStepsZ, &planner.settings.axis_steps_per_mm[Z_AXIS]);
+    #if HAS_HOTEND
+      ADDMENUITEM_P(ICON_StepE, GET_TEXT(MSG_E_STEPS), onDrawStepsE, SetStepsE, &planner.settings.axis_steps_per_mm[E_AXIS]);
+    #endif
+  }
+  CurrentMenu->Draw();
+}
+
+#if HAS_HOTEND
+  void Draw_HotendPID_Menu() {
+    checkkey = Menu;
+    if (HotendPIDMenu == nullptr) HotendPIDMenu = new MenuClass();
+    if (CurrentMenu != HotendPIDMenu) {
+      CurrentMenu = HotendPIDMenu;
+      CurrentMenu->MenuTitle.SetCaption(F("Hotend PID Settings"));
+      DWINUI::MenuItemsPrepare(8);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawMenuItem, Draw_AdvancedSettings_Menu);
+      ADDMENUITEM(ICON_PIDNozzle, F("Hotend PID"), onDrawMenuItem, HotendPID);
+      ADDMENUITEM_P(ICON_PIDValue, F("Set" STR_KP), onDrawPFloat2Menu, SetKp, &thermalManager.temp_hotend[0].pid.Kp);
+      ADDMENUITEM_P(ICON_PIDValue, F("Set" STR_KI), onDrawPIDi, SetKi, &thermalManager.temp_hotend[0].pid.Ki);
+      ADDMENUITEM_P(ICON_PIDValue, F("Set" STR_KD), onDrawPIDd, SetKd, &thermalManager.temp_hotend[0].pid.Kd);
+      ADDMENUITEM_P(ICON_Temperature, GET_TEXT(MSG_TEMPERATURE), onDrawPIntMenu, SetHotendPidT, &HMI_data.HotendPidT);
+      ADDMENUITEM_P(ICON_PIDcycles, GET_TEXT(MSG_PID_CYCLE), onDrawPIntMenu, SetPidCycles, &HMI_data.PidCycles);
+      #if ENABLED(EEPROM_SETTINGS)
+        ADDMENUITEM(ICON_WriteEEPROM, GET_TEXT(MSG_STORE_EEPROM), onDrawMenuItem, WriteEeprom);
+      #endif
+    }
+    CurrentMenu->Draw();
+  }
+#endif
+
+#if HAS_HEATED_BED
+  void Draw_BedPID_Menu() {
+    checkkey = Menu;
+    if (BedPIDMenu == nullptr) BedPIDMenu = new MenuClass();
+    if (CurrentMenu != BedPIDMenu) {
+      CurrentMenu = BedPIDMenu;
+      CurrentMenu->MenuTitle.SetCaption(F("Bed PID Settings"));
+      DWINUI::MenuItemsPrepare(8);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawMenuItem, Draw_AdvancedSettings_Menu);
+      ADDMENUITEM(ICON_PIDNozzle, F("Bed PID"), onDrawMenuItem,BedPID);
+      ADDMENUITEM_P(ICON_PIDValue, F("Set" STR_KP), onDrawPFloat2Menu, SetKp, &thermalManager.temp_bed.pid.Kp);
+      ADDMENUITEM_P(ICON_PIDValue, F("Set" STR_KI), onDrawPIDi, SetKi, &thermalManager.temp_bed.pid.Ki);
+      ADDMENUITEM_P(ICON_PIDValue, F("Set" STR_KD), onDrawPIDd, SetKd, &thermalManager.temp_bed.pid.Kd);
+      ADDMENUITEM_P(ICON_Temperature, GET_TEXT(MSG_TEMPERATURE), onDrawPIntMenu, SetBedPidT, &HMI_data.BedPidT);
+      ADDMENUITEM_P(ICON_PIDcycles, GET_TEXT(MSG_PID_CYCLE), onDrawPIntMenu, SetPidCycles, &HMI_data.PidCycles);
+      #if ENABLED(EEPROM_SETTINGS)
+        ADDMENUITEM(ICON_WriteEEPROM, GET_TEXT(MSG_STORE_EEPROM), onDrawMenuItem, WriteEeprom);
+      #endif
+    }
+    CurrentMenu->Draw();
+  }
+#endif
+
+#if EITHER(HAS_BED_PROBE, BABYSTEPPING)
+  void Draw_ZOffsetWiz_Menu() {
+    checkkey = Menu;
+    if (ZOffsetWizMenu == nullptr) ZOffsetWizMenu = new MenuClass();
+    if (CurrentMenu != ZOffsetWizMenu) {
+      CurrentMenu = ZOffsetWizMenu;
+      CurrentMenu->MenuTitle.SetCaption(GET_TEXT_F(MSG_PROBE_WIZARD));
+      DWINUI::MenuItemsPrepare(4);
+      ADDMENUITEM(ICON_Back, GET_TEXT(MSG_BUTTON_BACK), onDrawMenuItem, Draw_Prepare_Menu);
+      ADDMENUITEM(ICON_Homing, GET_TEXT(MSG_AUTO_HOME), onDrawMenuItem, AutoHome);
+      ADDMENUITEM(ICON_MoveZ0, F("Move Z to Home"), onDrawMenuItem, SetMoveZto0);
+      ADDMENUITEM_P(ICON_Zoffset, GET_TEXT(MSG_ZPROBE_ZOFFSET), onDrawPFloat2Menu, SetZOffset, &BABY_Z_VAR);
+    }
+    CurrentMenu->Draw();
+    if (!axis_is_trusted(Z_AXIS)) ui.set_status_P(PSTR("WARNING: Z position is unknow, move Z to home"));
+  }
+#endif
+
+
+#endif // DWIN_CREALITY_LCD_ENHANCED
diff --git a/Marlin/src/lcd/e3v2/enhanced/dwin.h b/Marlin/src/lcd/e3v2/enhanced/dwin.h
new file mode 100644
index 0000000000..db4cc2121f
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/dwin.h
@@ -0,0 +1,265 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.1
+ * Date: 2021/08/29
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "../../../inc/MarlinConfigPre.h"
+#include "dwinui.h"
+#include "rotary_encoder.h"
+#include "../../../libs/BL24CXX.h"
+
+#if ANY(HAS_HOTEND, HAS_HEATED_BED, HAS_FAN) && PREHEAT_COUNT
+  #define HAS_PREHEAT 1
+  #if PREHEAT_COUNT < 2
+    #error "Creality DWIN requires two material preheat presets."
+  #endif
+#endif
+
+#if ANY(AUTO_BED_LEVELING_BILINEAR, AUTO_BED_LEVELING_LINEAR, AUTO_BED_LEVELING_3POINT) && DISABLED(PROBE_MANUALLY)
+  #define HAS_ONESTEP_LEVELING 1
+#endif
+
+#if !HAS_BED_PROBE && ENABLED(BABYSTEPPING)
+  #define JUST_BABYSTEP 1
+#endif
+
+#if ANY(BABYSTEPPING, HAS_BED_PROBE, HAS_WORKSPACE_OFFSET)
+  #define HAS_ZOFFSET_ITEM 1
+#endif
+
+static constexpr size_t eeprom_data_size = 64;
+
+enum processID : uint8_t {
+  // Process ID
+  MainMenu,
+  Menu,
+  SetInt,
+  SetPInt,
+  SetIntNoDraw,
+  SetFloat,
+  SetPFloat,
+  SelectFile,
+  PrintProcess,
+  PrintDone,
+  Info,
+
+  // Popup Windows
+  Homing,
+  Leveling,
+  PauseOrStop,
+  FilamentPurge,
+  WaitResponse,
+  Locked,
+  NothingToDo,
+};
+
+enum pidresult_t : uint8_t {
+  PID_BAD_EXTRUDER_NUM,
+  PID_TEMP_TOO_HIGH,
+  PID_TUNING_TIMEOUT,
+  PID_EXTR_START,
+  PID_BED_START,
+  PID_DONE
+};
+
+// Picture ID
+#define Start_Process       0
+#define Language_English    1
+#define Language_Chinese    2
+
+#define DWIN_CHINESE 123
+#define DWIN_ENGLISH 0
+
+typedef struct {
+  int8_t Color[3];                    // Color components
+  int8_t Preheat          = 0;        // Material Select 0: PLA, 1: ABS, 2: Custom
+  AxisEnum axis           = X_AXIS;   // Axis Select
+  int32_t MaxValue        = 0;        // Auxiliar max integer/scaled float value
+  int32_t MinValue        = 0;        // Auxiliar min integer/scaled float value
+  int8_t dp               = 0;        // Auxiliar decimal places
+  int32_t Value           = 0;        // Auxiliar integer / scaled float value
+  int16_t *P_Int          = nullptr;  // Auxiliar pointer to 16 bit integer variable
+  float *P_Float          = nullptr;  // Auxiliar pointer to float variable
+  void (*Apply)()         = nullptr;  // Auxiliar apply function
+  void (*LiveUpdate)()    = nullptr;  // Auxiliar live update function
+} HMI_value_t;
+
+typedef struct {
+  uint16_t Background_Color = Def_Background_Color;
+  uint16_t Cursor_color     = Def_Cursor_color;
+  uint16_t TitleBg_color    = Def_TitleBg_color;
+  uint16_t TitleTxt_color   = Def_TitleTxt_color;
+  uint16_t Text_Color       = Def_Text_Color;
+  uint16_t Selected_Color   = Def_Selected_Color;
+  uint16_t SplitLine_Color  = Def_SplitLine_Color;
+  uint16_t Highlight_Color  = Def_Highlight_Color;
+  uint16_t StatusBg_Color   = Def_StatusBg_Color;
+  uint16_t StatusTxt_Color  = Def_StatusTxt_Color;
+  uint16_t PopupBg_color    = Def_PopupBg_color;
+  uint16_t PopupTxt_Color   = Def_PopupTxt_Color;
+  uint16_t AlertBg_Color    = Def_AlertBg_Color;
+  uint16_t AlertTxt_Color   = Def_AlertTxt_Color;
+  uint16_t PercentTxt_Color = Def_PercentTxt_Color;
+  uint16_t Barfill_Color    = Def_Barfill_Color;
+  uint16_t Indicator_Color  = Def_Indicator_Color;
+  uint16_t Coordinate_Color = Def_Coordinate_Color;
+  TERN_(HAS_HOTEND, int16_t HotendPidT = PREHEAT_1_TEMP_HOTEND);
+  TERN_(HAS_HOTEND, int16_t PidCycles = 10);
+  #ifdef PREHEAT_1_TEMP_BED
+    int16_t BedPidT = PREHEAT_1_TEMP_BED;
+  #endif
+  TERN_(PREVENT_COLD_EXTRUSION, int16_t ExtMinT = EXTRUDE_MINTEMP);
+} HMI_data_t;
+
+typedef struct {
+  uint8_t language;
+  bool pause_flag:1;    // printing is paused
+  bool pause_action:1;  // flag a pause action
+  bool print_finish:1;  // print was finished
+  bool select_flag:1;   // Popup button selected
+  bool home_flag:1;     // homing in course
+  bool heat_flag:1;     // 0: heating done  1: during heating
+  bool lock_flag:1;     // 0: lock called from AdvSet  1: lock called from Tune
+} HMI_flag_t;
+
+extern HMI_value_t HMI_value;
+extern HMI_flag_t HMI_flag;
+extern HMI_data_t HMI_data;
+extern uint8_t checkkey;
+extern millis_t dwin_heat_time;
+
+// Popup windows
+void DWIN_Popup_Confirm(uint8_t icon, const char * const msg1, const char * const msg2);
+#if HAS_HOTEND || HAS_HEATED_BED
+  void DWIN_Popup_Temperature(const bool toohigh);
+#endif
+TERN_(HAS_HOTEND, void Popup_Window_ETempTooLow());
+void Popup_Window_Resume();
+
+// SD Card
+void HMI_SDCardInit();
+void HMI_SDCardUpdate();
+
+// Main Process
+//void Icon_print();
+//void Icon_control();
+//void Icon_leveling(bool value);
+
+// Other
+void Goto_PrintProcess();
+void Goto_Main_Menu();
+void update_variable();
+void Draw_Select_Highlight(const bool sel);
+void Draw_Status_Area(const bool with_update); // Status Area
+void Draw_Main_Area();      // Redraw main area;
+void DWIN_Redraw_screen();  // Redraw all screen elements
+void HMI_StartFrame(const bool with_update);   // Prepare the menu view
+void HMI_MainMenu();        // Main process screen
+void HMI_SelectFile();      // File page
+void HMI_Printing();        // Print page
+void HMI_ReturnScreen();    // Return to previous screen before popups
+void ApplyExtMinT();
+void HMI_SetLanguageCache(); // Set the languaje image cache
+
+//void HMI_Leveling();    // Level the page
+//void HMI_LevBedCorners();   // Tramming menu
+//void HMI_Info();          // Information menu
+
+
+void HMI_Init();
+void HMI_Popup();
+void HMI_SaveProcessID(const uint8_t id);
+void HMI_AudioFeedback(const bool success=true);
+void DWIN_Startup();
+void DWIN_Update();
+void EachMomentUpdate();
+void DWIN_HandleScreen();
+void DWIN_DrawStatusLine(const uint16_t color, const uint16_t bgcolor, const char *text);
+void DWIN_StatusChanged(const char * const text);
+void DWIN_StatusChanged_P(PGM_P const text);
+void DWIN_StartHoming();
+void DWIN_CompletedHoming();
+#if HAS_MESH
+  void DWIN_MeshUpdate(const int8_t xpos, const int8_t ypos, const float zval);
+#endif
+void DWIN_MeshLevelingStart();
+void DWIN_CompletedLeveling();
+void DWIN_PidTuning(pidresult_t result);
+void DWIN_Print_Started(const bool sd = false);
+void DWIN_Print_Finished();
+#if HAS_FILAMENT_SENSOR
+  void DWIN_FilamentRunout(const uint8_t extruder);
+#endif
+void DWIN_Progress_Update();
+void DWIN_Print_Header(const char *text);
+void DWIN_SetColorDefaults();
+void DWIN_StoreSettings(char *buff);
+void DWIN_LoadSettings(const char *buff);
+void DWIN_SetDataDefaults();
+void DWIN_RebootScreen();
+
+#if ENABLED(ADVANCED_PAUSE_FEATURE)
+  void Draw_Popup_FilamentPurge();
+  void DWIN_Popup_FilamentPurge();
+  void HMI_FilamentPurge();
+#endif
+
+// Utility and extensions
+void HMI_LockScreen();
+void DWIN_LockScreen(const bool flag = true);
+
+// HMI user control functions
+void HMI_Menu();
+void HMI_SetInt();
+void HMI_SetPInt();
+void HMI_SetIntNoDraw();
+void HMI_SetFloat();
+void HMI_SetPFloat();
+
+// Menu drawing functions
+void Draw_Control_Menu();
+void Draw_AdvancedSettings_Menu();
+void Draw_Prepare_Menu();
+void Draw_Move_Menu();
+void Draw_LevBedCorners_Menu();
+TERN_(HAS_HOME_OFFSET, void Draw_HomeOffset_Menu());
+TERN_(HAS_BED_PROBE, void Draw_ProbeSet_Menu());
+TERN_(HAS_FILAMENT_SENSOR, void Draw_FilSet_Menu());
+void Draw_SelectColors_Menu();
+void Draw_GetColor_Menu();
+void Draw_Tune_Menu();
+void Draw_Motion_Menu();
+TERN_(ADVANCED_PAUSE_FEATURE, void Draw_FilamentMan_Menu());
+TERN_(MESH_BED_LEVELING, void Draw_ManualMesh_Menu());
+#if HAS_HOTEND
+  void Draw_Preheat1_Menu();
+  void Draw_Preheat2_Menu();
+  void Draw_Preheat3_Menu();
+#endif
+void Draw_Temperature_Menu();
+void Draw_MaxSpeed_Menu();
+void Draw_MaxAccel_Menu();
+TERN_(HAS_CLASSIC_JERK, void Draw_MaxJerk_Menu());
+void Draw_Steps_Menu();
+TERN_(HAS_HOTEND, void Draw_HotendPID_Menu());
+TERN_(HAS_HEATED_BED, void Draw_BedPID_Menu());
+#if EITHER(HAS_BED_PROBE, BABYSTEPPING)
+  void Draw_ZOffsetWiz_Menu();
+#endif
diff --git a/Marlin/src/lcd/e3v2/enhanced/dwin_lcd.cpp b/Marlin/src/lcd/e3v2/enhanced/dwin_lcd.cpp
new file mode 100644
index 0000000000..b9246523ce
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/dwin_lcd.cpp
@@ -0,0 +1,570 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.1
+ * Date: 2021/08/29
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/********************************************************************************
+ * @file     lcd/e3v2/enhanced/dwin_lcd.cpp
+ * @author   LEO / Creality3D - Enhanced by Miguel A. Risco-Castillo
+ * @date     2021/09/08
+ * @version  2.2.1
+ * @brief    DWIN screen control functions
+ ********************************************************************************/
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+
+#include "../../../inc/MarlinConfig.h"
+
+#include "dwin_lcd.h"
+#include <string.h> // for memset
+
+//#define DEBUG_OUT 1
+#include "../../../core/debug_out.h"
+
+// Make sure DWIN_SendBuf is large enough to hold the largest string plus draw command and tail.
+// Assume the narrowest (6 pixel) font and 2-byte gb2312-encoded characters.
+uint8_t DWIN_SendBuf[11 + DWIN_DataLength] = { 0xAA };
+uint8_t DWIN_BufTail[4] = { 0xCC, 0x33, 0xC3, 0x3C };
+uint8_t databuf[26] = { 0 };
+uint8_t receivedType;
+
+int recnum = 0;
+
+inline void DWIN_Byte(size_t &i, const uint16_t bval) {
+  DWIN_SendBuf[++i] = bval;
+}
+
+inline void DWIN_Word(size_t &i, const uint16_t wval) {
+  DWIN_SendBuf[++i] = wval >> 8;
+  DWIN_SendBuf[++i] = wval & 0xFF;
+}
+
+inline void DWIN_Long(size_t &i, const uint32_t lval) {
+  DWIN_SendBuf[++i] = (lval >> 24) & 0xFF;
+  DWIN_SendBuf[++i] = (lval >> 16) & 0xFF;
+  DWIN_SendBuf[++i] = (lval >>  8) & 0xFF;
+  DWIN_SendBuf[++i] = lval & 0xFF;
+}
+
+inline void DWIN_String(size_t &i, const char * const string, uint16_t rlimit = 0xFFFF) {
+  if (!string) return;
+  const size_t len = _MIN(sizeof(DWIN_SendBuf) - i, _MIN(strlen(string), rlimit));
+  if (len == 0) return;
+  memcpy(&DWIN_SendBuf[i+1], string, len);
+  i += len;
+}
+
+inline void DWIN_String(size_t &i, const __FlashStringHelper * string, uint16_t rlimit = 0xFFFF) {
+  if (!string) return;
+  const size_t len = _MIN(sizeof(DWIN_SendBuf) - i, _MIN(rlimit, strlen_P((PGM_P)string))); // cast it to PGM_P, which is basically const char *, and measure it using the _P version of strlen.
+  if (len == 0) return;
+  memcpy(&DWIN_SendBuf[i+1], string, len);
+  i += len;
+}
+
+// Send the data in the buffer and the packet end
+inline void DWIN_Send(size_t &i) {
+  ++i;
+  LOOP_L_N(n, i) { LCD_SERIAL.write(DWIN_SendBuf[n]); delayMicroseconds(1); }
+  LOOP_L_N(n, 4) { LCD_SERIAL.write(DWIN_BufTail[n]); delayMicroseconds(1); }
+}
+
+/*-------------------------------------- System variable function --------------------------------------*/
+
+// Handshake (1: Success, 0: Fail)
+bool DWIN_Handshake(void) {
+  #ifndef LCD_BAUDRATE
+    #define LCD_BAUDRATE 115200
+  #endif
+  LCD_SERIAL.begin(LCD_BAUDRATE);
+  const millis_t serial_connect_timeout = millis() + 1000UL;
+  while (!LCD_SERIAL.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ }
+
+  size_t i = 0;
+  DWIN_Byte(i, 0x00);
+  DWIN_Send(i);
+
+  while (LCD_SERIAL.available() > 0 && recnum < (signed)sizeof(databuf)) {
+    databuf[recnum] = LCD_SERIAL.read();
+    // ignore the invalid data
+    if (databuf[0] != FHONE) { // prevent the program from running.
+      if (recnum > 0) {
+        recnum = 0;
+        ZERO(databuf);
+      }
+      continue;
+    }
+    delay(10);
+    recnum++;
+  }
+
+  return ( recnum >= 3
+        && databuf[0] == FHONE
+        && databuf[1] == '\0'
+        && databuf[2] == 'O'
+        && databuf[3] == 'K' );
+}
+
+// Set screen display direction
+//  dir: 0=0°, 1=90°, 2=180°, 3=270°
+void DWIN_Frame_SetDir(uint8_t dir) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x34);
+  DWIN_Byte(i, 0x5A);
+  DWIN_Byte(i, 0xA5);
+  DWIN_Byte(i, dir);
+  DWIN_Send(i);
+}
+
+// Update display
+void DWIN_UpdateLCD(void) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x3D);
+  DWIN_Send(i);
+}
+
+/*---------------------------------------- Drawing functions ----------------------------------------*/
+
+// Clear screen
+//  color: Clear screen color
+void DWIN_Frame_Clear(const uint16_t color) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x01);
+  DWIN_Word(i, color);
+  DWIN_Send(i);
+}
+
+// Draw a point
+//  color: point color
+//  width: point width   0x01-0x0F
+//  height: point height 0x01-0x0F
+//  x,y: upper left point
+void DWIN_Draw_Point(uint16_t color, uint8_t width, uint8_t height, uint16_t x, uint16_t y) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x02);
+  DWIN_Word(i, color);
+  DWIN_Byte(i, width);
+  DWIN_Byte(i, height);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  DWIN_Send(i);
+}
+
+// Draw a line
+//  color: Line segment color
+//  xStart/yStart: Start point
+//  xEnd/yEnd: End point
+void DWIN_Draw_Line(uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x03);
+  DWIN_Word(i, color);
+  DWIN_Word(i, xStart);
+  DWIN_Word(i, yStart);
+  DWIN_Word(i, xEnd);
+  DWIN_Word(i, yEnd);
+  DWIN_Send(i);
+}
+
+// Draw a rectangle
+//  mode: 0=frame, 1=fill, 2=XOR fill
+//  color: Rectangle color
+//  xStart/yStart: upper left point
+//  xEnd/yEnd: lower right point
+void DWIN_Draw_Rectangle(uint8_t mode, uint16_t color,
+                         uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x05);
+  DWIN_Byte(i, mode);
+  DWIN_Word(i, color);
+  DWIN_Word(i, xStart);
+  DWIN_Word(i, yStart);
+  DWIN_Word(i, xEnd);
+  DWIN_Word(i, yEnd);
+  DWIN_Send(i);
+}
+
+// Move a screen area
+//  mode: 0, circle shift; 1, translation
+//  dir: 0=left, 1=right, 2=up, 3=down
+//  dis: Distance
+//  color: Fill color
+//  xStart/yStart: upper left point
+//  xEnd/yEnd: bottom right point
+void DWIN_Frame_AreaMove(uint8_t mode, uint8_t dir, uint16_t dis,
+                         uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x09);
+  DWIN_Byte(i, (mode << 7) | dir);
+  DWIN_Word(i, dis);
+  DWIN_Word(i, color);
+  DWIN_Word(i, xStart);
+  DWIN_Word(i, yStart);
+  DWIN_Word(i, xEnd);
+  DWIN_Word(i, yEnd);
+  DWIN_Send(i);
+}
+
+/*---------------------------------------- Text related functions ----------------------------------------*/
+
+// Draw a string
+//  widthAdjust: true=self-adjust character width; false=no adjustment
+//  bShow: true=display background color; false=don't display background color
+//  size: Font size
+//  color: Character color
+//  bColor: Background color
+//  x/y: Upper-left coordinate of the string
+//  *string: The string
+//  rlimit: For draw less chars than string length use rlimit
+void DWIN_Draw_String(bool widthAdjust, bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t x, uint16_t y, const char * const string, uint16_t rlimit) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x11);
+  // Bit 7: widthAdjust
+  // Bit 6: bShow
+  // Bit 5-4: Unused (0)
+  // Bit 3-0: size
+  DWIN_Byte(i, (widthAdjust * 0x80) | (bShow * 0x40) | size);
+  DWIN_Word(i, color);
+  DWIN_Word(i, bColor);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  DWIN_String(i, string, rlimit);
+  DWIN_Send(i);
+}
+
+// Draw a positive integer
+//  bShow: true=display background color; false=don't display background color
+//  zeroFill: true=zero fill; false=no zero fill
+//  zeroMode: 1=leading 0 displayed as 0; 0=leading 0 displayed as a space
+//  size: Font size
+//  color: Character color
+//  bColor: Background color
+//  iNum: Number of digits
+//  x/y: Upper-left coordinate
+//  value: Integer value
+void DWIN_Draw_IntValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+                          uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x14);
+  // Bit 7: bshow
+  // Bit 6: 1 = signed; 0 = unsigned number;
+  // Bit 5: zeroFill
+  // Bit 4: zeroMode
+  // Bit 3-0: size
+  DWIN_Byte(i, (bShow * 0x80) | (zeroFill * 0x20) | (zeroMode * 0x10) | size);
+  DWIN_Word(i, color);
+  DWIN_Word(i, bColor);
+  DWIN_Byte(i, iNum);
+  DWIN_Byte(i, 0); // fNum
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  #if 0
+    for (char count = 0; count < 8; count++) {
+      DWIN_Byte(i, value);
+      value >>= 8;
+      if (!(value & 0xFF)) break;
+    }
+  #else
+    // Write a big-endian 64 bit integer
+    const size_t p = i + 1;
+    for (char count = 8; count--;) { // 7..0
+      ++i;
+      DWIN_SendBuf[p + count] = value;
+      value >>= 8;
+    }
+  #endif
+
+  DWIN_Send(i);
+}
+
+// Draw a positive floating point number
+//  bShow: true=display background color; false=don't display background color
+//  zeroFill: true=zero fill; false=no zero fill
+//  zeroMode: 1=leading 0 displayed as 0; 0=leading 0 displayed as a space
+//  size: Font size
+//  color: Character color
+//  bColor: Background color
+//  iNum: Number of whole digits
+//  fNum: Number of decimal digits
+//  x/y: Upper-left point
+//  value: Scaled positive float value
+void DWIN_Draw_FloatValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+                            uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, long value) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x14);
+  DWIN_Byte(i, (bShow * 0x80) | (zeroFill * 0x20) | (zeroMode * 0x10) | size);
+  DWIN_Word(i, color);
+  DWIN_Word(i, bColor);
+  DWIN_Byte(i, iNum);
+  DWIN_Byte(i, fNum);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  DWIN_Long(i, value);
+  DWIN_Send(i);
+}
+//  value: positive float value
+void DWIN_Draw_FloatValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+                            uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+  const long val = round(value * POW(10, fNum));
+  DWIN_Draw_FloatValue(bShow, zeroFill, zeroMode, size, color, bColor, iNum, fNum, x, y, val);
+}
+
+/*---------------------------------------- Picture related functions ----------------------------------------*/
+
+// Display QR code
+//  The size of the QR code is (46*QR_Pixel)*(46*QR_Pixel) dot matrix
+//  QR_Pixel: The pixel size occupied by each point of the QR code: 0x01-0x0F (1-16)
+//  (Nx, Ny): The coordinates of the upper left corner displayed by the QR code
+//  str: multi-bit data
+void DWIN_Draw_QR(uint8_t QR_Pixel, uint16_t x, uint16_t y, char *string) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x21);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  DWIN_Byte(i, QR_Pixel);
+  DWIN_String(i, string);
+  DWIN_Send(i);
+}
+
+// Draw JPG and cached in #0 virtual display area
+// id: Picture ID
+void DWIN_JPG_ShowAndCache(const uint8_t id) {
+  size_t i = 0;
+  DWIN_Word(i, 0x2200);
+  DWIN_Byte(i, id);
+  DWIN_Send(i);
+}
+
+// Draw an Icon
+//  IBD: The icon background display: 0=Background filtering is not displayed, 1=Background display \\When setting the background filtering not to display, the background must be pure black
+//  BIR: Background image restoration: 0=Background image is not restored, 1=Automatically use virtual display area image for background restoration
+//  BFI: Background filtering strength: 0=normal, 1=enhanced, (only valid when the icon background display=0)
+//  libID: Icon library ID
+//  picID: Icon ID
+//  x/y: Upper-left point
+void DWIN_ICON_Show(uint8_t IBD, uint8_t BIR, uint8_t BFI, uint8_t libID, uint8_t picID, uint16_t x, uint16_t y) {
+  NOMORE(x, DWIN_WIDTH - 1);
+  NOMORE(y, DWIN_HEIGHT - 1); // -- ozy -- srl
+  size_t i = 0;
+  DWIN_Byte(i, 0x23);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  DWIN_Byte(i, IBD%2<<7 | BIR%2<<6 | BFI%2<<5 | libID);
+  DWIN_Byte(i, picID);
+  DWIN_Send(i);
+}
+
+// Draw an Icon from SRAM
+//  IBD: The icon background display: 0=Background filtering is not displayed, 1=Background display \\When setting the background filtering not to display, the background must be pure black
+//  BIR: Background image restoration: 0=Background image is not restored, 1=Automatically use virtual display area image for background restoration
+//  BFI: Background filtering strength: 0=normal, 1=enhanced, (only valid when the icon background display=0)
+//  x/y: Upper-left point
+//  addr: SRAM address
+void DWIN_ICON_Show(uint8_t IBD, uint8_t BIR, uint8_t BFI, uint16_t x, uint16_t y, uint16_t addr) {
+  NOMORE(x, DWIN_WIDTH - 1);
+  NOMORE(y, DWIN_HEIGHT - 1); // -- ozy -- srl
+  size_t i = 0;
+  DWIN_Byte(i, 0x24);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  DWIN_Byte(i, IBD%2<<7 | BIR%2<<6 | BFI%2<<5 | 0x00);
+  DWIN_Word(i, addr);
+  DWIN_Send(i);
+}
+
+// Unzip the JPG picture to a virtual display area
+//  n: Cache index
+//  id: Picture ID
+void DWIN_JPG_CacheToN(uint8_t n, uint8_t id) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x25);
+  DWIN_Byte(i, n);
+  DWIN_Byte(i, id);
+  DWIN_Send(i);
+}
+
+// Copy area from current virtual display area to current screen
+//  xStart/yStart: Upper-left of virtual area
+//  xEnd/yEnd: Lower-right of virtual area
+//  x/y: Screen paste point
+void DWIN_Frame_AreaCopy(uint16_t xStart, uint16_t yStart,
+                         uint16_t xEnd, uint16_t yEnd, uint16_t x, uint16_t y) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x26);
+  DWIN_Word(i, xStart);
+  DWIN_Word(i, yStart);
+  DWIN_Word(i, xEnd);
+  DWIN_Word(i, yEnd);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  DWIN_Send(i);
+}
+
+// Copy area from virtual display area to current screen
+//  IBD: background display: 0=Background filtering is not displayed, 1=Background display \\When setting the background filtering not to display, the background must be pure black
+//  BIR: Background image restoration: 0=Background image is not restored, 1=Automatically use virtual display area image for background restoration
+//  BFI: Background filtering strength: 0=normal, 1=enhanced, (only valid when the icon background display=0)
+//  cacheID: virtual area number
+//  xStart/yStart: Upper-left of virtual area
+//  xEnd/yEnd: Lower-right of virtual area
+//  x/y: Screen paste point
+void DWIN_Frame_AreaCopy(uint8_t IBD, uint8_t BIR, uint8_t BFI, uint8_t cacheID, uint16_t xStart, uint16_t yStart,
+                         uint16_t xEnd, uint16_t yEnd, uint16_t x, uint16_t y) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x27);
+  DWIN_Byte(i, IBD%2<<7 | BIR%2<<6 | BFI%2<<5 | cacheID);
+  DWIN_Word(i, xStart);
+  DWIN_Word(i, yStart);
+  DWIN_Word(i, xEnd);
+  DWIN_Word(i, yEnd);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  DWIN_Send(i);
+}
+
+// Animate a series of icons
+//  animID: Animation ID; 0x00-0x0F
+//  animate: true on; false off;
+//  libID: Icon library ID
+//  picIDs: Icon starting ID
+//  picIDe: Icon ending ID
+//  x/y: Upper-left point
+//  interval: Display time interval, unit 10mS
+void DWIN_ICON_Animation(uint8_t animID, bool animate, uint8_t libID, uint8_t picIDs, uint8_t picIDe, uint16_t x, uint16_t y, uint16_t interval) {
+  NOMORE(x, DWIN_WIDTH - 1);
+  NOMORE(y, DWIN_HEIGHT - 1); // -- ozy -- srl
+  size_t i = 0;
+  DWIN_Byte(i, 0x28);
+  DWIN_Word(i, x);
+  DWIN_Word(i, y);
+  // Bit 7: animation on or off
+  // Bit 6: start from begin or end
+  // Bit 5-4: unused (0)
+  // Bit 3-0: animID
+  DWIN_Byte(i, (animate * 0x80) | 0x40 | animID);
+  DWIN_Byte(i, libID);
+  DWIN_Byte(i, picIDs);
+  DWIN_Byte(i, picIDe);
+  DWIN_Byte(i, interval);
+  DWIN_Send(i);
+}
+
+// Animation Control
+//  state: 16 bits, each bit is the state of an animation id
+void DWIN_ICON_AnimationControl(uint16_t state) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x29);
+  DWIN_Word(i, state);
+  DWIN_Send(i);
+}
+
+// Set LCD Brightness 0x00-0xFF
+void DWIN_LCD_Brightness(const uint8_t brightness) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x30);
+  DWIN_Byte(i, brightness);
+  DWIN_Send(i);
+}
+
+// Write buffer data to the SRAM or Flash
+//  mem: 0x5A=32KB SRAM, 0xA5=16KB Flash
+//  addr: start address
+//  length: Bytes to write
+//  data: address of the buffer with data
+void DWIN_WriteToMem(uint8_t mem, uint16_t addr, uint16_t length, uint8_t *data) {
+  const uint8_t max_size = 128;
+  uint16_t pending = length;
+  uint16_t to_send;
+  uint16_t indx;
+  uint8_t block = 0;
+
+  while (pending > 0) {
+    indx = block * max_size;
+    to_send = _MIN(pending, max_size);
+    size_t i = 0;
+    DWIN_Byte(i, 0x31);
+    DWIN_Byte(i, mem);
+    DWIN_Word(i, addr + indx); // start address of the data block
+    ++i;
+    LOOP_L_N(j, i) { LCD_SERIAL.write(DWIN_SendBuf[j]); delayMicroseconds(1); }  // Buf header
+    for (uint16_t j = indx; j <= indx + to_send - 1; j++) LCD_SERIAL.write(*(data + j)); delayMicroseconds(1);  // write block of data
+    LOOP_L_N(j, 4) { LCD_SERIAL.write(DWIN_BufTail[j]); delayMicroseconds(1); }
+    block++;
+    pending -= to_send;
+  }
+}
+
+// Write the contents of the 32KB SRAM data memory into the designated image memory space.
+//  picID: Picture memory space location, 0x00-0x0F, each space is 32Kbytes
+void DWIN_SRAMToPic(uint8_t picID) {
+  size_t i = 0;
+  DWIN_Byte(i, 0x33);
+  DWIN_Byte(i, 0x5A);
+  DWIN_Byte(i, 0xA5);
+  DWIN_Byte(i, picID);
+  DWIN_Send(i);
+}
+
+//--------------------------Test area -------------------------
+
+// void DWIN_ReadSRAM(uint16_t addr, uint8_t length, const char * const data) {
+//   size_t i = 0;
+//   DWIN_Byte(i, 0x32);
+//   DWIN_Byte(i, 0x5A);  // 0x5A Read from SRAM - 0xA5 Read from Flash
+//   DWIN_Word(i, addr);  // 0x0000 to 0x7FFF
+//   const size_t len = _MIN(0xF0, length);
+//   DWIN_Byte(i, len);
+//   DWIN_Send(i);
+// }
+
+/*---------------------------------------- Memory functions ----------------------------------------*/
+// The LCD has an additional 32KB SRAM and 16KB Flash
+
+// Data can be written to the sram and save to one of the jpeg page files
+
+// Write Data Memory
+//  command 0x31
+//  Type: Write memory selection; 0x5A=SRAM; 0xA5=Flash
+//  Address: Write data memory address; 0x000-0x7FFF for SRAM; 0x000-0x3FFF for Flash
+//  Data: data
+//
+//  Flash writing returns 0xA5 0x4F 0x4B
+
+// Read Data Memory
+//  command 0x32
+//  Type: Read memory selection; 0x5A=SRAM; 0xA5=Flash
+//  Address: Read data memory address; 0x000-0x7FFF for SRAM; 0x000-0x3FFF for Flash
+//  Length: leangth of data to read; 0x01-0xF0
+//
+//  Response:
+//    Type, Address, Length, Data
+
+// Write Picture Memory
+//  Write the contents of the 32KB SRAM data memory into the designated image memory space
+//  Issued: 0x5A, 0xA5, PIC_ID
+//  Response: 0xA5 0x4F 0x4B
+//
+//  command 0x33
+//  0x5A, 0xA5
+//  PicId: Picture Memory location, 0x00-0x0F
+//
+//  Flash writing returns 0xA5 0x4F 0x4B
+
+#endif // DWIN_CREALITY_LCD_ENHANCED
diff --git a/Marlin/src/lcd/e3v2/enhanced/dwin_lcd.h b/Marlin/src/lcd/e3v2/enhanced/dwin_lcd.h
new file mode 100644
index 0000000000..c66416a7ed
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/dwin_lcd.h
@@ -0,0 +1,285 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.1
+ * Date: 2021/08/29
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/********************************************************************************
+ * @file     lcd/e3v2/enhanced/dwin_lcd.h
+ * @author   LEO / Creality3D - Enhanced by Miguel A. Risco-Castillo
+ * @date     2021/08/09
+ * @version  2.2.1
+ * @brief    DWIN screen control functions
+ ********************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+
+#define RECEIVED_NO_DATA         0x00
+#define RECEIVED_SHAKE_HAND_ACK  0x01
+
+#define FHONE                    0xAA
+
+#define DWIN_SCROLL_UP   2
+#define DWIN_SCROLL_DOWN 3
+
+#define DWIN_WIDTH  272
+#define DWIN_HEIGHT 480
+
+#define DWIN_DataLength (DWIN_WIDTH / 6 * 2)
+
+/*-------------------------------------- System variable function --------------------------------------*/
+
+// Handshake (1: Success, 0: Fail)
+bool DWIN_Handshake(void);
+
+// Set the backlight luminance
+//  luminance: (0x00-0xFF)
+void DWIN_Backlight_SetLuminance(const uint8_t luminance);
+
+// Set screen display direction
+//  dir: 0=0°, 1=90°, 2=180°, 3=270°
+void DWIN_Frame_SetDir(uint8_t dir);
+
+// Update display
+void DWIN_UpdateLCD(void);
+
+/*---------------------------------------- Drawing functions ----------------------------------------*/
+
+// Clear screen
+//  color: Clear screen color
+void DWIN_Frame_Clear(const uint16_t color);
+
+// Draw a point
+//  color: point color
+//  width: point width   0x01-0x0F
+//  height: point height 0x01-0x0F
+//  x,y: upper left point
+void DWIN_Draw_Point(uint16_t color, uint8_t width, uint8_t height, uint16_t x, uint16_t y);
+
+// Draw a line
+//  color: Line segment color
+//  xStart/yStart: Start point
+//  xEnd/yEnd: End point
+void DWIN_Draw_Line(uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd);
+
+// Draw a Horizontal line
+//  color: Line segment color
+//  xStart/yStart: Start point
+//  xLength: Line Length
+inline void DWIN_Draw_HLine(uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xLength) {
+  DWIN_Draw_Line(color, xStart, yStart, xStart + xLength - 1, yStart);
+}
+
+// Draw a Vertical line
+//  color: Line segment color
+//  xStart/yStart: Start point
+//  yLength: Line Length
+inline void DWIN_Draw_VLine(uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t yLength) {
+  DWIN_Draw_Line(color, xStart, yStart, xStart, yStart + yLength - 1);
+}
+
+// Draw a rectangle
+//  mode: 0=frame, 1=fill, 2=XOR fill
+//  color: Rectangle color
+//  xStart/yStart: upper left point
+//  xEnd/yEnd: lower right point
+void DWIN_Draw_Rectangle(uint8_t mode, uint16_t color,  uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd);
+
+// Draw a box
+//  mode: 0=frame, 1=fill, 2=XOR fill
+//  color: Rectangle color
+//  xStart/yStart: upper left point
+//  xSize/ySize: box size
+inline void DWIN_Draw_Box(uint8_t mode, uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xSize, uint16_t ySize) {
+  DWIN_Draw_Rectangle(mode, color, xStart, yStart, xStart + xSize - 1, yStart + ySize - 1);
+}
+
+// Move a screen area
+//  mode: 0, circle shift; 1, translation
+//  dir: 0=left, 1=right, 2=up, 3=down
+//  dis: Distance
+//  color: Fill color
+//  xStart/yStart: upper left point
+//  xEnd/yEnd: bottom right point
+void DWIN_Frame_AreaMove(uint8_t mode, uint8_t dir, uint16_t dis,
+                         uint16_t color, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd);
+
+/*---------------------------------------- Text related functions ----------------------------------------*/
+
+// Draw a string
+//  widthAdjust: true=self-adjust character width; false=no adjustment
+//  bShow: true=display background color; false=don't display background color
+//  size: Font size
+//  color: Character color
+//  bColor: Background color
+//  x/y: Upper-left coordinate of the string
+//  *string: The string
+//  rlimit: For draw less chars than string length use rlimit
+void DWIN_Draw_String(bool widthAdjust, bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t x, uint16_t y, const char * const string, uint16_t rlimit = 0xFFFF);
+inline void DWIN_Draw_String(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t x, uint16_t y, const char * const string, uint16_t rlimit = 0xFFFF) {
+  DWIN_Draw_String(0, bShow, size, color, bColor, x, y, string, rlimit);
+}
+
+class __FlashStringHelper;
+
+inline void DWIN_Draw_String(bool widthAdjust, bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t x, uint16_t y, const __FlashStringHelper *title) {
+  DWIN_Draw_String(widthAdjust, bShow, size, color, bColor, x, y, (char *)title);
+}
+inline void DWIN_Draw_String(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t x, uint16_t y, const __FlashStringHelper *title) {
+  DWIN_Draw_String(0, bShow, size, color, bColor, x, y, (char *)title);
+}
+
+// Draw a positive integer
+//  bShow: true=display background color; false=don't display background color
+//  zeroFill: true=zero fill; false=no zero fill
+//  zeroMode: 1=leading 0 displayed as 0; 0=leading 0 displayed as a space
+//  size: Font size
+//  color: Character color
+//  bColor: Background color
+//  iNum: Number of digits
+//  x/y: Upper-left coordinate
+//  value: Integer value
+void DWIN_Draw_IntValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+                          uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value);
+
+// Draw a positive floating point number
+//  bShow: true=display background color; false=don't display background color
+//  zeroFill: true=zero fill; false=no zero fill
+//  zeroMode: 1=leading 0 displayed as 0; 0=leading 0 displayed as a space
+//  size: Font size
+//  color: Character color
+//  bColor: Background color
+//  iNum: Number of whole digits
+//  fNum: Number of decimal digits
+//  x/y: Upper-left point
+//  value: Scaled positive float value
+void DWIN_Draw_FloatValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+                            uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, long value);
+//  value: positive float value
+void DWIN_Draw_FloatValue(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color,
+                            uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value);
+
+/*---------------------------------------- Picture related functions ----------------------------------------*/
+
+// Display QR code
+//  The size of the QR code is (46*QR_Pixel)*(46*QR_Pixel) dot matrix
+//  QR_Pixel: The pixel size occupied by each point of the QR code: 0x01-0x0F (1-16)
+//  (Nx, Ny): The coordinates of the upper left corner displayed by the QR code
+//  str: multi-bit data
+void DWIN_Draw_QR(uint8_t QR_Pixel, uint16_t x, uint16_t y, char *string);
+
+inline void DWIN_Draw_QR(uint8_t QR_Pixel, uint16_t x, uint16_t y, const __FlashStringHelper *title) {
+  DWIN_Draw_QR(QR_Pixel, x, y, (char *)title);
+}
+
+// Draw JPG and cached in #0 virtual display area
+// id: Picture ID
+void DWIN_JPG_ShowAndCache(const uint8_t id);
+
+// Draw an Icon
+//  IBD: The icon background display: 0=Background filtering is not displayed, 1=Background display \\When setting the background filtering not to display, the background must be pure black
+//  BIR: Background image restoration: 0=Background image is not restored, 1=Automatically use virtual display area image for background restoration
+//  BFI: Background filtering strength: 0=normal, 1=enhanced, (only valid when the icon background display=0)
+//  libID: Icon library ID
+//  picID: Icon ID
+//  x/y: Upper-left point
+void DWIN_ICON_Show(uint8_t IBD, uint8_t BIR, uint8_t BFI, uint8_t libID, uint8_t picID, uint16_t x, uint16_t y);
+
+// Draw an Icon with transparent background
+//  libID: Icon library ID
+//  picID: Icon ID
+//  x/y: Upper-left point
+inline void DWIN_ICON_Show(uint8_t libID, uint8_t picID, uint16_t x, uint16_t y) {
+  DWIN_ICON_Show(0, 0, 1, libID, picID, x, y);
+}
+
+// Draw an Icon from SRAM
+//  IBD: The icon background display: 0=Background filtering is not displayed, 1=Background display \\When setting the background filtering not to display, the background must be pure black
+//  BIR: Background image restoration: 0=Background image is not restored, 1=Automatically use virtual display area image for background restoration
+//  BFI: Background filtering strength: 0=normal, 1=enhanced, (only valid when the icon background display=0)
+//  x/y: Upper-left point
+//  addr: SRAM address
+void DWIN_ICON_Show(uint8_t IBD, uint8_t BIR, uint8_t BFI, uint16_t x, uint16_t y, uint16_t addr);
+
+// Unzip the JPG picture to a virtual display area
+//  n: Cache index
+//  id: Picture ID
+void DWIN_JPG_CacheToN(uint8_t n, uint8_t id);
+
+// Unzip the JPG picture to virtual display area #1
+//  id: Picture ID
+inline void DWIN_JPG_CacheTo1(uint8_t id) { DWIN_JPG_CacheToN(1, id); }
+
+// Copy area from current virtual display area to current screen
+//  xStart/yStart: Upper-left of virtual area
+//  xEnd/yEnd: Lower-right of virtual area
+//  x/y: Screen paste point
+void DWIN_Frame_AreaCopy(uint16_t xStart, uint16_t yStart,
+                         uint16_t xEnd, uint16_t yEnd, uint16_t x, uint16_t y);
+
+// Copy area from virtual display area to current screen
+//  IBD: background display: 0=Background filtering is not displayed, 1=Background display \\When setting the background filtering not to display, the background must be pure black
+//  BIR: Background image restoration: 0=Background image is not restored, 1=Automatically use virtual display area image for background restoration
+//  BFI: Background filtering strength: 0=normal, 1=enhanced, (only valid when the icon background display=0)
+//  cacheID: virtual area number
+//  xStart/yStart: Upper-left of virtual area
+//  xEnd/yEnd: Lower-right of virtual area
+//  x/y: Screen paste point
+void DWIN_Frame_AreaCopy(uint8_t IBD, uint8_t BIR, uint8_t BFI, uint8_t cacheID, uint16_t xStart, uint16_t yStart,
+                         uint16_t xEnd, uint16_t yEnd, uint16_t x, uint16_t y);
+
+// Copy area from virtual display area to current screen with transparent background
+//  cacheID: virtual area number
+//  xStart/yStart: Upper-left of virtual area
+//  xEnd/yEnd: Lower-right of virtual area
+//  x/y: Screen paste point
+inline void DWIN_Frame_AreaCopy(uint8_t cacheID, uint16_t xStart, uint16_t yStart,
+                         uint16_t xEnd, uint16_t yEnd, uint16_t x, uint16_t y) {
+  DWIN_Frame_AreaCopy(0, 0, 1, cacheID, xStart, yStart, xEnd, yEnd, x, y);
+}
+
+// Animate a series of icons
+//  animID: Animation ID  up to 16
+//  animate: animation on or off
+//  libID: Icon library ID
+//  picIDs: Icon starting ID
+//  picIDe: Icon ending ID
+//  x/y: Upper-left point
+//  interval: Display time interval, unit 10mS
+void DWIN_ICON_Animation(uint8_t animID, bool animate, uint8_t libID, uint8_t picIDs,
+                         uint8_t picIDe, uint16_t x, uint16_t y, uint16_t interval);
+
+// Animation Control
+//  state: 16 bits, each bit is the state of an animation id
+void DWIN_ICON_AnimationControl(uint16_t state);
+
+// Set LCD Brightness 0x00-0x0F
+void DWIN_LCD_Brightness(const uint8_t brightness);
+
+// Write buffer data to the SRAM or Flash
+//  mem: 0x5A=32KB SRAM, 0xA5=16KB Flash
+//  addr: start address
+//  length: Bytes to write
+//  data: address of the buffer with data
+void DWIN_WriteToMem(uint8_t mem, uint16_t addr, uint16_t length, uint8_t *data);
+
+// Write the contents of the 32KB SRAM data memory into the designated image memory space.
+//  picID: Picture memory space location, 0x00-0x0F, each space is 32Kbytes
+void DWIN_SRAMToPic(uint8_t picID);
diff --git a/Marlin/src/lcd/e3v2/enhanced/dwinui.cpp b/Marlin/src/lcd/e3v2/enhanced/dwinui.cpp
new file mode 100644
index 0000000000..85353bed28
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/dwinui.cpp
@@ -0,0 +1,452 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.3
+ * Date: 2021/08/09
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+
+#include "../../../inc/MarlinConfig.h"
+#include "../../../core/macros.h"
+#include "dwin_lcd.h"
+#include "dwinui.h"
+
+//#define DEBUG_OUT 1
+#include "../../../core/debug_out.h"
+
+uint8_t MenuItemTotal = 0;
+uint8_t MenuItemCount = 0;
+MenuItemClass** MenuItems = nullptr;
+MenuClass *CurrentMenu = nullptr;
+MenuClass *PreviousMenu = nullptr;
+
+xy_int_t DWINUI::cursor = { 0 };
+uint16_t DWINUI::pencolor = Color_White;
+uint16_t DWINUI::textcolor = Def_Text_Color;
+uint16_t DWINUI::backcolor = Def_Background_Color;
+uint8_t  DWINUI::font = font8x16;
+
+void (*DWINUI::onCursorErase)(uint8_t line)=nullptr;
+void (*DWINUI::onCursorDraw)(uint8_t line)=nullptr;
+void (*DWINUI::onTitleDraw)(TitleClass* title)=nullptr;
+void (*DWINUI::onMenuDraw)(MenuClass* menu)=nullptr;
+
+void DWINUI::Init(void) {
+  DEBUG_ECHOPGM("\r\nDWIN handshake ");
+  delay(750);   // Delay here or init later in the boot process
+  const bool success = DWIN_Handshake();
+  if (success) DEBUG_ECHOLNPGM("ok."); else DEBUG_ECHOLNPGM("error.");
+  DWIN_Frame_SetDir(1);
+  TERN(SHOW_BOOTSCREEN,,DWIN_Frame_Clear(Color_Bg_Black));
+  DWIN_UpdateLCD();
+  cursor.x = 0;
+  cursor.y = 0;
+  pencolor = Color_White;
+  textcolor = Def_Text_Color;
+  backcolor = Def_Background_Color;
+  font = font8x16;
+}
+
+// Set text/number font
+void DWINUI::SetFont(uint8_t cfont) {
+  font = cfont;
+}
+
+// Get font character width
+uint8_t DWINUI::Get_font_width(uint8_t cfont) {
+  switch (cfont) {
+    case font6x12 : return 6;
+    case font8x16 : return 8;
+    case font10x20: return 10;
+    case font12x24: return 12;
+    case font14x28: return 14;
+    case font16x32: return 16;
+    case font20x40: return 20;
+    case font24x48: return 24;
+    case font28x56: return 28;
+    case font32x64: return 32;
+    default: return 0;
+  }
+}
+
+// Get font character heigh
+uint8_t DWINUI::Get_font_height(uint8_t cfont) {
+  switch (cfont) {
+    case font6x12 : return 12;
+    case font8x16 : return 16;
+    case font10x20: return 20;
+    case font12x24: return 24;
+    case font14x28: return 28;
+    case font16x32: return 32;
+    case font20x40: return 40;
+    case font24x48: return 48;
+    case font28x56: return 56;
+    case font32x64: return 64;
+    default: return 0;
+  }
+}
+
+// Get screen x coodinates from text column
+uint16_t DWINUI::ColToX(uint8_t col) {
+  return col * Get_font_width(font);
+}
+
+// Get screen y coodinates from text row
+uint16_t DWINUI::RowToY(uint8_t row) {
+  return row * Get_font_height(font);
+}
+
+// Set text/number color
+void DWINUI::SetColors(uint16_t fgcolor, uint16_t bgcolor) {
+  textcolor = fgcolor;
+  backcolor = bgcolor;
+}
+void DWINUI::SetTextColor(uint16_t fgcolor) {
+  textcolor = fgcolor;
+}
+void DWINUI::SetBackgroundColor(uint16_t bgcolor) {
+  backcolor = bgcolor;
+}
+
+// Moves cursor to point
+//  x: abscissa of the display
+//  y: ordinate of the display
+//  point: xy coordinate
+void DWINUI::MoveTo(int16_t x, int16_t y) {
+  cursor.x = x;
+  cursor.y = y;
+}
+void DWINUI::MoveTo(xy_int_t point) {
+  cursor = point;
+}
+
+// Moves cursor relative to the actual position
+//  x: abscissa of the display
+//  y: ordinate of the display
+//  point: xy coordinate
+void DWINUI::MoveBy(int16_t x, int16_t y) {
+  cursor.x += x;
+  cursor.y += y;
+}
+void DWINUI::MoveBy(xy_int_t point) {
+  cursor += point;
+}
+
+// Draw a Centered string using DWIN_WIDTH
+void DWINUI::Draw_CenteredString(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t y, const char * const string) {
+  const int8_t x = _MAX(0U, DWIN_WIDTH - strlen_P(string) * Get_font_width(size)) / 2 - 1;
+  DWIN_Draw_String(bShow, size, color, bColor, x, y, string);
+}
+
+// Draw a char at cursor position
+void DWINUI::Draw_Char(const char c) {
+  const char string[2] = { c, 0};
+  DWIN_Draw_String(false, font, textcolor, backcolor, cursor.x, cursor.y, string, 1);
+  MoveBy(Get_font_width(font), 0);
+}
+
+// Draw a string at cursor position
+//  color: Character color
+//  *string: The string
+//  rlimit: For draw less chars than string length use rlimit
+void DWINUI::Draw_String(const char * const string, uint16_t rlimit) {
+  DWIN_Draw_String(false, font, textcolor, backcolor, cursor.x, cursor.y, string, rlimit);
+  MoveBy(strlen(string) * Get_font_width(font), 0);
+}
+void DWINUI::Draw_String(uint16_t color, const char * const string, uint16_t rlimit) {
+  DWIN_Draw_String(false, font, color, backcolor, cursor.x, cursor.y, string, rlimit);
+  MoveBy(strlen(string) * Get_font_width(font), 0);
+}
+
+// Draw a signed floating point number
+//  bShow: true=display background color; false=don't display background color
+//  zeroFill: true=zero fill; false=no zero fill
+//  zeroMode: 1=leading 0 displayed as 0; 0=leading 0 displayed as a space
+//  size: Font size
+//  bColor: Background color
+//  iNum: Number of whole digits
+//  fNum: Number of decimal digits
+//  x/y: Upper-left point
+//  value: Float value
+void DWINUI::Draw_Signed_Float(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+  if (value < 0) {
+    DWIN_Draw_FloatValue(bShow, zeroFill, zeroMode, size, color, bColor, iNum, fNum, x, y, -value);
+    DWIN_Draw_String(bShow, size, color, bColor, x - 6, y, F("-"));
+  }
+  else {
+    DWIN_Draw_String(bShow, size, color, bColor, x - 6, y, F(" "));
+    DWIN_Draw_FloatValue(bShow, zeroFill, zeroMode, size, color, bColor, iNum, fNum, x, y, value);
+  }
+}
+
+// Draw a circle
+//  color: circle color
+//  x: the abscissa of the center of the circle
+//  y: ordinate of the center of the circle
+//  r: circle radius
+void DWINUI::Draw_Circle(uint16_t color, uint16_t x, uint16_t y, uint8_t r) {
+  int a = 0, b = 0;
+  while (a <= b) {
+    b = SQRT(sq(r) - sq(a));
+    if (a == 0) b--;
+    DWIN_Draw_Point(color, 1, 1, x + a, y + b);   // Draw some sector 1
+    DWIN_Draw_Point(color, 1, 1, x + b, y + a);   // Draw some sector 2
+    DWIN_Draw_Point(color, 1, 1, x + b, y - a);   // Draw some sector 3
+    DWIN_Draw_Point(color, 1, 1, x + a, y - b);   // Draw some sector 4
+    DWIN_Draw_Point(color, 1, 1, x - a, y - b);   // Draw some sector 5
+    DWIN_Draw_Point(color, 1, 1, x - b, y - a);   // Draw some sector 6
+    DWIN_Draw_Point(color, 1, 1, x - b, y + a);   // Draw some sector 7
+    DWIN_Draw_Point(color, 1, 1, x - a, y + b);   // Draw some sector 8
+    a++;
+  }
+}
+
+// Draw a circle filled with color
+//  bcolor: fill color
+//  x: the abscissa of the center of the circle
+//  y: ordinate of the center of the circle
+//  r: circle radius
+void DWINUI::Draw_FillCircle(uint16_t bcolor, uint16_t x,uint16_t y,uint8_t r) {
+  int a = 0, b = 0;
+  while (a <= b) {
+    b = SQRT(sq(r) - sq(a)); // b=sqrt(r*r-a*a);
+    if (a == 0) b--;
+    DWIN_Draw_Line(bcolor, x-b,y-a,x+b,y-a);
+    DWIN_Draw_Line(bcolor, x-a,y-b,x+a,y-b);
+    DWIN_Draw_Line(bcolor, x-b,y+a,x+b,y+a);
+    DWIN_Draw_Line(bcolor, x-a,y+b,x+a,y+b);
+    a++;
+  }
+}
+
+// Color Interpolator
+//  val : Interpolator minv..maxv
+//  minv : Minimum value
+//  maxv : Maximun value
+//  color1 : Start color
+//  color2 : End color
+uint16_t DWINUI::ColorInt(int16_t val, int16_t minv, int16_t maxv, uint16_t color1, uint16_t color2) {
+  uint8_t B,G,R;
+  float n;
+  n = (float)(val-minv)/(maxv-minv);
+  R = (1-n)*GetRColor(color1) + n*GetRColor(color2);
+  G = (1-n)*GetGColor(color1) + n*GetGColor(color2);
+  B = (1-n)*GetBColor(color1) + n*GetBColor(color2);
+  return RGB(R,G,B);
+}
+
+// Color Interpolator through Red->Yellow->Green->Blue
+//  val : Interpolator minv..maxv
+//  minv : Minimum value
+//  maxv : Maximun value
+uint16_t DWINUI::RainbowInt(int16_t val, int16_t minv, int16_t maxv) {
+  uint8_t B,G,R;
+  const uint8_t maxB = 28;
+  const uint8_t maxR = 28;
+  const uint8_t maxG = 38;
+  const int16_t limv = _MAX(abs(minv), abs(maxv));
+  float n;
+  if (minv>=0) {
+    n = (float)(val-minv)/(maxv-minv);
+  } else {
+    n = (float)val/limv;
+  }
+  n = _MIN(1, n);
+  n = _MAX(-1, n);
+  if (n < 0) {
+    R = 0;
+    G = (1+n)*maxG;
+    B = (-n)*maxB;
+  } else if (n < 0.5) {
+    R = maxR*n*2;
+    G = maxG;
+    B = 0;
+  } else {
+    R = maxR;
+    G = maxG*(1-n);
+    B = 0;
+  }
+  return RGB(R,G,B);
+}
+
+// Draw a checkbox
+//  Color: frame color
+//  bColor: Background color
+//  x/y: Upper-left point
+//  mode : 0 : unchecked, 1 : checked
+void DWINUI::Draw_Checkbox(uint16_t color, uint16_t bcolor, uint16_t x, uint16_t y, bool checked=false) {
+  DWIN_Draw_String(false, true, font8x16, color, bcolor, x + 4, y, checked ? F("x") : F(" "));
+  DWIN_Draw_Rectangle(0, color, x + 2, y + 2, x + 17, y + 17);
+}
+
+// Clear Menu by filling the menu area with background color
+void DWINUI::ClearMenuArea() {
+  DWIN_Draw_Rectangle(1, backcolor, 0, TITLE_HEIGHT, DWIN_WIDTH - 1, STATUS_Y - 1);
+}
+
+void DWINUI::MenuItemsClear() {
+  if (MenuItems == nullptr) return;
+  for (uint8_t i = 0; i < MenuItemCount; i++) delete MenuItems[i];
+  delete[] MenuItems;
+  MenuItems = nullptr;
+  MenuItemCount = 0;
+  MenuItemTotal = 0;
+}
+
+void DWINUI::MenuItemsPrepare(uint8_t totalitems) {
+  MenuItemsClear();
+  MenuItemTotal = totalitems;
+  MenuItems = new MenuItemClass*[totalitems];
+}
+
+MenuItemClass* DWINUI::MenuItemsAdd(MenuItemClass* menuitem) {
+  if (MenuItemCount < MenuItemTotal) {
+    MenuItems[MenuItemCount] = menuitem;
+    menuitem->pos = MenuItemCount++;
+    return menuitem;
+  }
+  else {
+    delete menuitem;
+    return nullptr;
+  }
+}
+
+/* Title Class ==============================================================*/
+
+TitleClass Title;
+
+void TitleClass::Draw() {
+  if (DWINUI::onTitleDraw != nullptr) (*DWINUI::onTitleDraw)(this);
+}
+
+void TitleClass::SetCaption(const char * const title) {
+  frameid = 0;
+  if ( caption == title ) return;
+  const uint8_t len = _MIN(sizeof(caption) - 1, strlen(title));
+  memcpy(&caption[0], title, len);
+  caption[len] = '\0';
+}
+
+void TitleClass::ShowCaption(const char * const title) {
+  SetCaption(title);
+  Draw();
+}
+
+void TitleClass::SetFrame(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
+  caption[0] = '\0';
+  frameid = id;
+  frame = { x1, y1, x2, y2 };
+}
+
+void TitleClass::SetFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
+  SetFrame(1, x, y, x + w - 1, y + h - 1);
+}
+
+void TitleClass::FrameCopy(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
+  SetFrame(id, x1, y1, x2, y2);
+  Draw();
+}
+
+void TitleClass::FrameCopy(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
+  FrameCopy(1, x, y, x + w - 1, y + h - 1);
+}
+
+/* Menu Class ===============================================================*/
+
+MenuClass::MenuClass() {
+  selected = 0;
+  topline = 0;
+}
+
+void MenuClass::Draw() {
+  MenuTitle.Draw();
+  if (DWINUI::onMenuDraw != nullptr) (*DWINUI::onMenuDraw)(this);
+  for (uint8_t i = 0; i < MenuItemCount; i++)
+    MenuItems[i]->Draw(i - topline);
+  if (DWINUI::onCursorDraw != nullptr) DWINUI::onCursorDraw(line());
+  DWIN_UpdateLCD();
+}
+
+void MenuClass::onScroll(bool dir) {
+  int8_t sel = selected;
+  if (dir) sel++; else sel--;
+  LIMIT(sel, 0, MenuItemCount - 1);
+  if (sel != selected) {
+    if (DWINUI::onCursorErase != nullptr) DWINUI::onCursorErase(line());
+    if ((sel - topline) == TROWS) {
+      DWIN_Frame_AreaMove(1, DWIN_SCROLL_UP, MLINE, DWINUI::backcolor, 0, TITLE_HEIGHT + 1, DWIN_WIDTH, STATUS_Y - 1);
+      topline++;
+      MenuItems[sel]->Draw(TROWS - 1);
+    }
+    if ((sel < topline)) {
+      DWIN_Frame_AreaMove(1, DWIN_SCROLL_DOWN, MLINE, DWINUI::backcolor, 0, TITLE_HEIGHT + 1, DWIN_WIDTH, STATUS_Y - 1);
+      topline--;
+      MenuItems[sel]->Draw(0);
+    }
+    selected = sel;
+    if (DWINUI::onCursorDraw != nullptr) DWINUI::onCursorDraw(line());
+    DWIN_UpdateLCD();
+  }
+}
+
+void MenuClass::onClick() {
+  if (MenuItems[selected]->onClick != nullptr) (*MenuItems[selected]->onClick)();
+}
+
+MenuItemClass *MenuClass::SelectedItem() {
+  return MenuItems[selected];
+}
+
+/* MenuItem Class ===========================================================*/
+
+MenuItemClass::MenuItemClass(uint8_t cicon, const char * const text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)()) {
+  icon = cicon;
+  onClick = onclick;
+  onDraw = ondraw;
+  const uint8_t len = _MIN(sizeof(caption) - 1, strlen(text));
+  memcpy(&caption[0], text, len);
+  caption[len] = '\0';
+}
+
+MenuItemClass::MenuItemClass(uint8_t cicon, uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)()) {
+  icon = cicon;
+  onClick = onclick;
+  onDraw = ondraw;
+  caption[0] = '\0';
+  frameid = id;
+  frame = { x1, y1, x2, y2 };
+}
+
+void MenuItemClass::SetFrame(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
+  caption[0] = '\0';
+  frameid = id;
+  frame = { x1, y1, x2, y2 };
+}
+
+void MenuItemClass::Draw(int8_t line) {
+  if (line < 0 || line >= TROWS) return;
+  if (onDraw != nullptr) (*onDraw)(this, line);
+};
+
+MenuItemPtrClass::MenuItemPtrClass(uint8_t cicon, const char * const text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)(), void* val) : MenuItemClass(cicon, text, ondraw, onclick) {
+  value = val;
+};
+
+#endif // DWIN_CREALITY_LCD_ENHANCED
diff --git a/Marlin/src/lcd/e3v2/enhanced/dwinui.h b/Marlin/src/lcd/e3v2/enhanced/dwinui.h
new file mode 100644
index 0000000000..1ec51bec22
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/dwinui.h
@@ -0,0 +1,624 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.3
+ * Date: 2021/08/09
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "../../../core/types.h"
+#include "dwin_lcd.h"
+
+// ICON ID
+#define ICON                7 // Icon set file 7.ICO
+
+#define ICON_LOGO                  0
+#define ICON_Print_0               1
+#define ICON_Print_1               2
+#define ICON_Prepare_0             3
+#define ICON_Prepare_1             4
+#define ICON_Control_0             5
+#define ICON_Control_1             6
+#define ICON_Leveling_0            7
+#define ICON_Leveling_1            8
+#define ICON_HotendTemp            9
+#define ICON_BedTemp              10
+#define ICON_Speed                11
+#define ICON_Zoffset              12
+#define ICON_Back                 13
+#define ICON_File                 14
+#define ICON_PrintTime            15
+#define ICON_RemainTime           16
+#define ICON_Setup_0              17
+#define ICON_Setup_1              18
+#define ICON_Pause_0              19
+#define ICON_Pause_1              20
+#define ICON_Continue_0           21
+#define ICON_Continue_1           22
+#define ICON_Stop_0               23
+#define ICON_Stop_1               24
+#define ICON_Bar                  25
+#define ICON_More                 26
+
+#define ICON_Axis                 27
+#define ICON_CloseMotor           28
+#define ICON_Homing               29
+#define ICON_SetHome              30
+#define ICON_PLAPreheat           31
+#define ICON_ABSPreheat           32
+#define ICON_Cool                 33
+#define ICON_Language             34
+
+#define ICON_MoveX                35
+#define ICON_MoveY                36
+#define ICON_MoveZ                37
+#define ICON_Extruder             38
+
+#define ICON_Temperature          40
+#define ICON_Motion               41
+#define ICON_WriteEEPROM          42
+#define ICON_ReadEEPROM           43
+#define ICON_ResumeEEPROM         44
+#define ICON_Info                 45
+
+#define ICON_SetEndTemp           46
+#define ICON_SetBedTemp           47
+#define ICON_FanSpeed             48
+#define ICON_SetPLAPreheat        49
+#define ICON_SetABSPreheat        50
+
+#define ICON_MaxSpeed             51
+#define ICON_MaxAccelerated       52
+#define ICON_MaxJerk              53
+#define ICON_Step                 54
+#define ICON_PrintSize            55
+#define ICON_Version              56
+#define ICON_Contact              57
+#define ICON_StockConfiguration   58
+#define ICON_MaxSpeedX            59
+#define ICON_MaxSpeedY            60
+#define ICON_MaxSpeedZ            61
+#define ICON_MaxSpeedE            62
+#define ICON_MaxAccX              63
+#define ICON_MaxAccY              64
+#define ICON_MaxAccZ              65
+#define ICON_MaxAccE              66
+#define ICON_MaxSpeedJerkX        67
+#define ICON_MaxSpeedJerkY        68
+#define ICON_MaxSpeedJerkZ        69
+#define ICON_MaxSpeedJerkE        70
+#define ICON_StepX                71
+#define ICON_StepY                72
+#define ICON_StepZ                73
+#define ICON_StepE                74
+#define ICON_Setspeed             75
+#define ICON_SetZOffset           76
+#define ICON_Rectangle            77
+#define ICON_BLTouch              78
+#define ICON_TempTooLow           79
+#define ICON_AutoLeveling         80
+#define ICON_TempTooHigh          81
+#define ICON_NoTips_C             82
+#define ICON_NoTips_E             83
+#define ICON_Continue_C           84
+#define ICON_Continue_E           85
+#define ICON_Cancel_C             86
+#define ICON_Cancel_E             87
+#define ICON_Confirm_C            88
+#define ICON_Confirm_E            89
+#define ICON_Info_0               90
+#define ICON_Info_1               91
+
+// Extra Icons
+#define ICON_AdvSet               ICON_Language
+#define ICON_Brightness           ICON_Motion
+#define ICON_Cancel               ICON_StockConfiguration
+#define ICON_CustomPreheat        ICON_SetEndTemp
+#define ICON_Error                ICON_TempTooHigh
+#define ICON_ExtrudeMinT          ICON_HotendTemp
+#define ICON_FilLoad              ICON_WriteEEPROM
+#define ICON_FilMan               ICON_ResumeEEPROM
+#define ICON_FilSet               ICON_ResumeEEPROM
+#define ICON_FilUnload            ICON_ReadEEPROM
+#define ICON_Flow                 ICON_StepE
+#define ICON_HomeOffset           ICON_AdvSet
+#define ICON_HomeOffsetX          ICON_StepX
+#define ICON_HomeOffsetY          ICON_StepY
+#define ICON_HomeOffsetZ          ICON_StepZ
+#define ICON_LevBed               ICON_SetEndTemp
+#define ICON_Lock                 ICON_Cool
+#define ICON_ManualMesh           ICON_HotendTemp
+#define ICON_MeshNext             ICON_Axis
+#define ICON_MeshSave             ICON_WriteEEPROM
+#define ICON_MoveZ0               ICON_HotendTemp
+#define ICON_Park                 ICON_Motion
+#define ICON_PIDbed               ICON_SetBedTemp
+#define ICON_PIDcycles            ICON_ResumeEEPROM
+#define ICON_PIDNozzle            ICON_SetEndTemp
+#define ICON_PIDValue             ICON_Contact
+#define ICON_ProbeOffsetX         ICON_StepX
+#define ICON_ProbeOffsetY         ICON_StepY
+#define ICON_ProbeOffsetZ         ICON_StepZ
+#define ICON_ProbeSet             ICON_SetEndTemp
+#define ICON_ProbeTest            ICON_SetEndTemp
+#define ICON_Pwrlossr             ICON_Motion
+#define ICON_Reboot               ICON_ResumeEEPROM
+#define ICON_Runout               ICON_MaxAccE
+#define ICON_Scolor               ICON_MaxSpeed
+#define ICON_SetCustomPreheat     ICON_SetEndTemp
+#define ICON_Sound                ICON_Cool
+
+/**
+ * 3-.0:The font size, 0x00-0x09, corresponds to the font size below:
+ * 0x00=6*12   0x01=8*16   0x02=10*20  0x03=12*24  0x04=14*28
+ * 0x05=16*32  0x06=20*40  0x07=24*48  0x08=28*56  0x09=32*64
+ */
+#define font6x12  0x00
+#define font8x16  0x01
+#define font10x20 0x02
+#define font12x24 0x03
+#define font14x28 0x04
+#define font16x32 0x05
+#define font20x40 0x06
+#define font24x48 0x07
+#define font28x56 0x08
+#define font32x64 0x09
+
+// Extended and default UI Colors
+#define RGB(R,G,B)  (R << 11) | (G << 5) | (B) // R,B: 0..31; G: 0..63
+#define GetRColor(color) ((color >> 11) & 0x1F)
+#define GetGColor(color) ((color >>  5) & 0x3F)
+#define GetBColor(color) ((color >>  0) & 0x1F)
+
+#define Color_White       0xFFFF
+#define Color_Bg_Window   0x31E8  // Popup background color
+#define Color_Bg_Blue     0x1125  // Dark blue background color
+#define Color_Bg_Black    0x0841  // Black background color
+#define Color_Bg_Red      0xF00F  // Red background color
+#define Popup_Text_Color  0xD6BA  // Popup font background color
+#define Line_Color        0x3A6A  // Split line color
+#define Rectangle_Color   0xEE2F  // Blue square cursor color
+#define Percent_Color     0xFE29  // Percentage color
+#define BarFill_Color     0x10E4  // Fill color of progress bar
+#define Select_Color      0x33BB  // Selected color
+
+#define Color_Black           0
+#define Color_Red             RGB(31,0,0)
+#define Color_Yellow          RGB(31,63,0)
+#define Color_Green           RGB(0,63,0)
+#define Color_Aqua            RGB(0,63,31)
+#define Color_Blue            RGB(0,0,31)
+
+// Default UI Colors
+#define Def_Background_Color  Color_Bg_Black
+#define Def_Cursor_color      Rectangle_Color
+#define Def_TitleBg_color     Color_Bg_Blue
+#define Def_TitleTxt_color    Color_White
+#define Def_Text_Color        Color_White
+#define Def_Selected_Color    Select_Color
+#define Def_SplitLine_Color   Line_Color
+#define Def_Highlight_Color   Color_White
+#define Def_StatusBg_Color    RGB(0,20,20)
+#define Def_StatusTxt_Color   Color_Yellow
+#define Def_PopupBg_color     Color_Bg_Window
+#define Def_PopupTxt_Color    Popup_Text_Color
+#define Def_AlertBg_Color     Color_Bg_Red
+#define Def_AlertTxt_Color    Color_Yellow
+#define Def_PercentTxt_Color  Percent_Color
+#define Def_Barfill_Color     BarFill_Color
+#define Def_Indicator_Color   Color_White
+#define Def_Coordinate_Color   Color_White
+
+//UI elements defines and constants
+#define DWIN_FONT_MENU font8x16
+#define DWIN_FONT_STAT font10x20
+#define DWIN_FONT_HEAD font10x20
+#define DWIN_FONT_ALERT font10x20
+#define STATUS_Y 354
+#define LCD_WIDTH (DWIN_WIDTH / 8)
+
+constexpr uint16_t TITLE_HEIGHT = 30,                          // Title bar height
+                   MLINE = 53,                                 // Menu line height
+                   TROWS = (STATUS_Y - TITLE_HEIGHT) / MLINE,  // Total rows
+                   MROWS = TROWS - 1,                          // Other-than-Back
+                   ICOX = 26,                                  // Menu item icon X position
+                   LBLX = 60,                                  // Menu item label X position
+                   VALX = 210,                                 // Menu item value X position
+                   MENU_CHR_W = 8, MENU_CHR_H = 16,            // Menu font 8x16
+                   STAT_CHR_W = 10;
+
+// Menuitem Y position
+#define MYPOS(L) (TITLE_HEIGHT + MLINE * (L))
+
+// Menuitem caption Offset
+#define CAPOFF ((MLINE - MENU_CHR_H) / 2)
+
+// Menuitem caption Y position
+#define MBASE(L) (MYPOS(L) + CAPOFF)
+
+// Create and add a MenuItem object to the menu array
+#define ADDMENUITEM(V...) DWINUI::MenuItemsAdd(new MenuItemClass(V))
+#define ADDMENUITEM_P(V...) DWINUI::MenuItemsAdd(new MenuItemPtrClass(V))
+
+typedef struct { uint16_t left, top, right, bottom; } rect_t;
+typedef struct { uint16_t x, y, w, h; } frame_rect_t;
+
+class TitleClass {
+public:
+  char caption[32] = "";
+  uint8_t frameid = 0;
+  rect_t frame = {0};
+  void Draw();
+  void SetCaption(const char * const title);
+  inline void SetCaption(const __FlashStringHelper * title) { SetCaption((char *)title); }
+  void ShowCaption(const char * const title);
+  inline void ShowCaption(const __FlashStringHelper * title) { ShowCaption((char *)title); }
+  void SetFrame(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
+  void SetFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
+  void FrameCopy(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
+  void FrameCopy(uint16_t x, uint16_t y, uint16_t h, uint16_t v);
+};
+extern TitleClass Title;
+
+class MenuItemClass {
+protected:
+public:
+  uint8_t pos = 0;
+  uint8_t icon = 0;
+  char caption[32] = "";
+  uint8_t frameid = 0;
+  rect_t frame = {0};
+  void (*onDraw)(MenuItemClass* menuitem, int8_t line) = nullptr;
+  void (*onClick)() = nullptr;
+  MenuItemClass() {};
+  MenuItemClass(uint8_t cicon, const char * const text=nullptr, void (*ondraw)(MenuItemClass* menuitem, int8_t line)=nullptr, void (*onclick)()=nullptr);
+  MenuItemClass(uint8_t cicon, const __FlashStringHelper * text = nullptr, void (*ondraw)(MenuItemClass* menuitem, int8_t line)=nullptr, void (*onclick)()=nullptr) : MenuItemClass(cicon, (char*)text, ondraw, onclick){}
+  MenuItemClass(uint8_t cicon, uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, void (*ondraw)(MenuItemClass* menuitem, int8_t line)=nullptr, void (*onclick)()=nullptr);
+  void SetFrame(uint8_t id, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
+  virtual ~MenuItemClass(){};
+  virtual void Draw(int8_t line);
+};
+
+class MenuItemPtrClass: public MenuItemClass {
+public:
+  void *value = nullptr;
+  using MenuItemClass::MenuItemClass;
+  MenuItemPtrClass(uint8_t cicon, const char * const text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)(), void* val);
+  MenuItemPtrClass(uint8_t cicon, const __FlashStringHelper * text, void (*ondraw)(MenuItemClass* menuitem, int8_t line), void (*onclick)(), void* val) : MenuItemPtrClass(cicon, (char*)text, ondraw, onclick, val){}
+};
+
+class MenuClass {
+public:
+  int8_t topline = 0;
+  int8_t selected = 0;
+  TitleClass MenuTitle;
+  MenuClass();
+  virtual ~MenuClass(){};
+  inline int8_t line() { return selected - topline; };
+  inline int8_t line(uint8_t pos) {return pos - topline; };
+  void Draw();
+  void onScroll(bool dir);
+  void onClick();
+  MenuItemClass* SelectedItem();
+};
+extern MenuClass *CurrentMenu;
+
+namespace DWINUI {
+  extern xy_int_t cursor;
+  extern uint16_t pencolor;
+  extern uint16_t textcolor;
+  extern uint16_t backcolor;
+  extern uint8_t  font;
+
+  extern void (*onCursorErase)(uint8_t line);
+  extern void (*onCursorDraw)(uint8_t line);
+  extern void (*onTitleDraw)(TitleClass* title);
+  extern void (*onMenuDraw)(MenuClass* menu);
+
+  // DWIN LCD Initialization
+  void Init(void);
+
+  // Set text/number font
+  void SetFont(uint8_t cfont);
+
+  // Get font character width
+  uint8_t Get_font_width(uint8_t cfont);
+
+  // Get font character heigh
+  uint8_t Get_font_height(uint8_t cfont);
+
+  // Get screen x coodinates from text column
+  uint16_t ColToX(uint8_t col);
+
+  // Get screen y coodinates from text row
+  uint16_t RowToY(uint8_t row);
+
+  // Set text/number color
+  void SetColors(uint16_t fgcolor, uint16_t bgcolor);
+  void SetTextColor(uint16_t fgcolor);
+  void SetBackgroundColor(uint16_t bgcolor);
+
+  // Moves cursor to point
+  //  x: abscissa of the display
+  //  y: ordinate of the display
+  //  point: xy coordinate
+  void MoveTo(int16_t x, int16_t y);
+  void MoveTo(xy_int_t point);
+
+  // Moves cursor relative to the actual position
+  //  x: abscissa of the display
+  //  y: ordinate of the display
+  //  point: xy coordinate
+  void MoveBy(int16_t x, int16_t y);
+  void MoveBy(xy_int_t point);
+
+  // Draw a line from the cursor to xy position
+  //  color: Line segment color
+  //  x/y: End point
+  inline void LineTo(uint16_t color, uint16_t x, uint16_t y) {
+    DWIN_Draw_Line(color, cursor.x, cursor.y, x, y);
+  }
+  inline void LineTo(uint16_t x, uint16_t y) {
+    DWIN_Draw_Line(pencolor, cursor.x, cursor.y, x, y);
+  }
+
+  // Draw an Icon with transparent background from the library ICON
+  //  icon: Icon ID
+  //  x/y: Upper-left point
+  inline void Draw_Icon(uint8_t icon, uint16_t x, uint16_t y) {
+    DWIN_ICON_Show(ICON, icon, x, y);
+  }
+
+  // Draw a positive integer
+  //  bShow: true=display background color; false=don't display background color
+  //  zeroFill: true=zero fill; false=no zero fill
+  //  zeroMode: 1=leading 0 displayed as 0; 0=leading 0 displayed as a space
+  //  size: Font size
+  //  color: Character color
+  //  bColor: Background color
+  //  iNum: Number of digits
+  //  x/y: Upper-left coordinate
+  //  value: Integer value
+  inline void Draw_Int(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
+    DWIN_Draw_IntValue(bShow, zeroFill, zeroMode, size, color, bColor, iNum, x, y, value);
+  }
+  inline void Draw_Int(uint8_t iNum, long value) {
+    DWIN_Draw_IntValue(false, true, 0, font, textcolor, backcolor, iNum, cursor.x, cursor.y, value);
+    MoveBy(iNum * Get_font_width(font), 0);
+  }
+  inline void Draw_Int(uint8_t iNum, uint16_t x, uint16_t y, long value) {
+    DWIN_Draw_IntValue(false, true, 0, font, textcolor, backcolor, iNum, x, y, value);
+  }
+  inline void Draw_Int(uint16_t color, uint8_t iNum, uint16_t x, uint16_t y, long value) {
+    DWIN_Draw_IntValue(false, true, 0, font, color, backcolor, iNum, x, y, value);
+  }
+  inline void Draw_Int(uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
+    DWIN_Draw_IntValue(true, true, 0, font, color, bColor, iNum, x, y, value);
+  }
+  inline void Draw_Int(uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint16_t x, uint16_t y, long value) {
+    DWIN_Draw_IntValue(true, true, 0, size, color, bColor, iNum, x, y, value);
+  }
+
+  // Draw a floating point number
+  //  bShow: true=display background color; false=don't display background color
+  //  zeroFill: true=zero fill; false=no zero fill
+  //  zeroMode: 1=leading 0 displayed as 0; 0=leading 0 displayed as a space
+  //  size: Font size
+  //  color: Character color
+  //  bColor: Background color
+  //  iNum: Number of whole digits
+  //  fNum: Number of decimal digits
+  //  x/y: Upper-left point
+  //  value: Float value
+  inline void Draw_Float(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    DWIN_Draw_FloatValue(bShow, zeroFill, zeroMode, size, color, bColor, iNum, fNum, x, y, value);
+  }
+  inline void Draw_Float(uint8_t iNum, uint8_t fNum, float value) {
+    DWIN_Draw_FloatValue(false, true, 0, font, textcolor, backcolor, iNum, fNum,  cursor.x, cursor.y, value);
+    MoveBy((iNum + fNum + 1) * Get_font_width(font), 0);
+  }
+  inline void Draw_Float(uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    DWIN_Draw_FloatValue(false, true, 0, font, textcolor, backcolor, iNum, fNum, x, y, value);
+  }
+  inline void Draw_Float(uint16_t color, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    DWIN_Draw_FloatValue(false, true, 0, font, color, backcolor, iNum, fNum, x, y, value);
+  }
+  inline void Draw_Float(uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    DWIN_Draw_FloatValue(true, true, 0, font, color, bColor, iNum, fNum, x, y, value);
+  }
+  inline void Draw_Float(uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    DWIN_Draw_FloatValue(true, true, 0, size, color, bColor, iNum, fNum, x, y, value);
+  }
+
+  // Draw a signed floating point number
+  //  bShow: true=display background color; false=don't display background color
+  //  zeroFill: true=zero fill; false=no zero fill
+  //  zeroMode: 1=leading 0 displayed as 0; 0=leading 0 displayed as a space
+  //  size: Font size
+  //  bColor: Background color
+  //  iNum: Number of whole digits
+  //  fNum: Number of decimal digits
+  //  x/y: Upper-left point
+  //  value: Float value
+  void Draw_Signed_Float(uint8_t bShow, bool zeroFill, uint8_t zeroMode, uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value);
+  inline void Draw_Signed_Float(uint8_t iNum, uint8_t fNum, float value) {
+    Draw_Signed_Float(false, true, 0, font, textcolor, backcolor, iNum, fNum, cursor.x, cursor.y, value);
+    MoveBy((iNum + fNum + 1) * Get_font_width(font), 0);
+  }
+  inline void Draw_Signed_Float(uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    Draw_Signed_Float(false, true, 0, font, textcolor, backcolor, iNum, fNum, x, y, value);
+  }
+  inline void Draw_Signed_Float(uint8_t size, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    Draw_Signed_Float(false, true, 0, size, textcolor, backcolor, iNum, fNum, x, y, value);
+  }
+  inline void Draw_Signed_Float(uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    Draw_Signed_Float(true, true, 0, font, color, bColor, iNum, fNum, x, y, value);
+  }
+  inline void Draw_Signed_Float(uint8_t size, uint16_t color, uint16_t bColor, uint8_t iNum, uint8_t fNum, uint16_t x, uint16_t y, float value) {
+    Draw_Signed_Float(true, true, 0, size, color, bColor, iNum, fNum, x, y, value);
+  }
+
+  // Draw a char at cursor position
+  void Draw_Char(const char c);
+
+  // Draw a string at cursor position
+  //  color: Character color
+  //  *string: The string
+  //  rlimit: For draw less chars than string length use rlimit
+  void Draw_String(const char * const string, uint16_t rlimit = 0xFFFF);
+  void Draw_String(uint16_t color, const char * const string, uint16_t rlimit = 0xFFFF);
+
+  // Draw a string
+  //  size: Font size
+  //  color: Character color
+  //  bColor: Background color
+  //  x/y: Upper-left coordinate of the string
+  //  *string: The string
+  inline void Draw_String(uint16_t x, uint16_t y, const char * const string) {
+    DWIN_Draw_String(false, font, textcolor, backcolor, x, y, string);
+  }
+  inline void Draw_String(uint16_t x, uint16_t y, const __FlashStringHelper *title) {
+    DWIN_Draw_String(false, font, textcolor, backcolor, x, y, (char *)title);
+  }
+  inline void Draw_String(uint16_t color, uint16_t x, uint16_t y, const char * const string) {
+    DWIN_Draw_String(false, font, color, backcolor, x, y, string);
+  }
+  inline void Draw_String(uint16_t color, uint16_t x, uint16_t y, const __FlashStringHelper *title) {
+    DWIN_Draw_String(false, font, color, backcolor, x, y, (char *)title);
+  }
+  inline void Draw_String(uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, const char * const string) {
+    DWIN_Draw_String(true, font, color, bgcolor, x, y, string);
+  }
+  inline void Draw_String(uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, const __FlashStringHelper *title) {
+    DWIN_Draw_String(true, font, color, bgcolor, x, y, (char *)title);
+  }
+  inline void Draw_String(uint8_t size, uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, const char * const string) {
+    DWIN_Draw_String(true, size, color, bgcolor, x, y, string);
+  }
+  inline void Draw_String(uint8_t size, uint16_t color, uint16_t bgcolor, uint16_t x, uint16_t y, const __FlashStringHelper *title) {
+    DWIN_Draw_String(true, size, color, bgcolor, x, y, (char *)title);
+  }
+
+  // Draw a centered string using DWIN_WIDTH
+  //  bShow: true=display background color; false=don't display background color
+  //  size: Font size
+  //  color: Character color
+  //  bColor: Background color
+  //  y: Upper coordinate of the string
+  //  *string: The string
+  void Draw_CenteredString(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t y, const char * const string);
+  inline void Draw_CenteredString(bool bShow, uint8_t size, uint16_t color, uint16_t bColor, uint16_t y, const __FlashStringHelper *title) {
+    Draw_CenteredString(bShow, size, color, bColor, y, (char *)title);
+  }
+  inline void Draw_CenteredString(uint16_t color, uint16_t bcolor, uint16_t y, const char * const string) {
+    Draw_CenteredString(true, font, color, bcolor, y, string);
+  }
+  inline void Draw_CenteredString(uint8_t size, uint16_t color, uint16_t y, const char * const string) {
+    Draw_CenteredString(false, size, color, backcolor, y, string);
+  }
+  inline void Draw_CenteredString(uint8_t size, uint16_t color, uint16_t y, const __FlashStringHelper *title) {
+    Draw_CenteredString(false, size, color, backcolor, y, (char *)title);
+  }
+  inline void Draw_CenteredString(uint16_t color, uint16_t y, const char * const string) {
+    Draw_CenteredString(false, font, color, backcolor, y, string);
+  }
+  inline void Draw_CenteredString(uint16_t color, uint16_t y, const __FlashStringHelper *title) {
+    Draw_CenteredString(false, font, color, backcolor, y, (char *)title);
+  }
+  inline void Draw_CenteredString(uint16_t y, const char * const string) {
+    Draw_CenteredString(false, font, textcolor, backcolor, y, string);
+  }
+  inline void Draw_CenteredString(uint16_t y, const __FlashStringHelper *title) {
+    Draw_CenteredString(false, font, textcolor, backcolor, y, (char *)title);
+  }
+
+  // Draw a circle
+  //  Color: circle color
+  //  x: the abscissa of the center of the circle
+  //  y: ordinate of the center of the circle
+  //  r: circle radius
+  void Draw_Circle(uint16_t color, uint16_t x,uint16_t y,uint8_t r);
+  inline void Draw_Circle(uint16_t color, uint8_t r) {
+    Draw_Circle(color, cursor.x, cursor.y, r);
+  }
+
+  // Draw a checkbox
+  //  Color: frame color
+  //  bColor: Background color
+  //  x/y: Upper-left point
+  //  checked : 0 : unchecked, 1 : checked
+  void Draw_Checkbox(uint16_t color, uint16_t bcolor, uint16_t x, uint16_t y, bool checked);
+  inline void Draw_Checkbox(uint16_t x, uint16_t y, bool checked=false) {
+    Draw_Checkbox(textcolor, backcolor, x, y, checked);
+  }
+
+  // Color Interpolator
+  //  val : Interpolator minv..maxv
+  //  minv : Minimum value
+  //  maxv : Maximun value
+  //  color1 : Start color
+  //  color2 : End color
+  uint16_t ColorInt(int16_t val, int16_t minv, int16_t maxv, uint16_t color1, uint16_t color2);
+
+  // -------------------------- Extra -------------------------------//
+
+  // Draw a circle filled with color
+  //  bcolor: fill color
+  //  x: the abscissa of the center of the circle
+  //  y: ordinate of the center of the circle
+  //  r: circle radius
+  void Draw_FillCircle(uint16_t bcolor, uint16_t x,uint16_t y,uint8_t r);
+  inline void Draw_FillCircle(uint16_t bcolor, uint8_t r) {
+    Draw_FillCircle(bcolor, cursor.x, cursor.y, r);
+  }
+
+  // Color Interpolator through Red->Yellow->Green->Blue
+  //  val : Interpolator minv..maxv
+  //  minv : Minimum value
+  //  maxv : Maximun value
+  uint16_t RainbowInt(int16_t val, int16_t minv, int16_t maxv);
+
+  // Write buffer data to the SRAM
+  //  addr: SRAM start address 0x0000-0x7FFF
+  //  length: Bytes to write
+  //  data: address of the buffer with data
+  inline void WriteToSRAM(uint16_t addr, uint16_t length, uint8_t *data) {
+    DWIN_WriteToMem(0x5A, addr, length, data);
+  }
+
+  // Write buffer data to the Flash
+  //  addr: Flash start address 0x0000-0x3FFF
+  //  length: Bytes to write
+  //  data: address of the buffer with data
+  inline void WriteToFlash(uint16_t addr, uint16_t length, uint8_t *data) {
+    DWIN_WriteToMem(0xA5, addr, length, data);
+  }
+
+  // Clear Menu by filling the area with background color
+  // Area (0, TITLE_HEIGHT, DWIN_WIDTH, STATUS_Y - 1)
+  void ClearMenuArea();
+
+  // Clear MenuItems array and free MenuItems elements
+  void MenuItemsClear();
+
+  // Prepare MenuItems array
+  void MenuItemsPrepare(uint8_t totalitems);
+
+  // Add elements to the MenuItems array
+  MenuItemClass* MenuItemsAdd(MenuItemClass* menuitem);
+
+};
diff --git a/Marlin/src/lcd/e3v2/enhanced/lockscreen.cpp b/Marlin/src/lcd/e3v2/enhanced/lockscreen.cpp
new file mode 100644
index 0000000000..2615a05881
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/lockscreen.cpp
@@ -0,0 +1,69 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.1
+ * Date: 2021/08/29
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+
+#include "../../../core/types.h"
+#include "dwin_lcd.h"
+#include "dwinui.h"
+#include "dwin.h"
+#include "lockscreen.h"
+
+LockScreenClass LockScreen;
+
+void LockScreenClass::Init() {
+  Lock_Pos = 0;
+  unlocked = false;
+  Draw();
+}
+
+void LockScreenClass::Draw() {
+  Title.SetCaption(PSTR("Lock Screen"));
+  DWINUI::ClearMenuArea();
+  DWINUI::Draw_Icon(ICON_LOGO, 71, 120);  // CREALITY logo
+  DWINUI::Draw_CenteredString(Color_White, 180, F("Printer is Locked,"));
+  DWINUI::Draw_CenteredString(Color_White, 200, F("Scroll to unlock."));
+  DWINUI::Draw_CenteredString(Color_White, 240, F("-> | <-"));
+  DWIN_Draw_Box(1, HMI_data.Barfill_Color, 0, 260, DWIN_WIDTH, 20);
+  DWIN_Draw_VLine(Color_Yellow, Lock_Pos * DWIN_WIDTH / 255, 260, 20);
+  DWIN_UpdateLCD();
+}
+
+void LockScreenClass::onEncoderState(ENCODER_DiffState encoder_diffState) {
+  if (encoder_diffState == ENCODER_DIFF_CW) {
+    Lock_Pos += 8;
+  }
+  else if (encoder_diffState == ENCODER_DIFF_CCW) {
+    Lock_Pos -= 8;
+  }
+  else if (encoder_diffState == ENCODER_DIFF_ENTER) {
+    unlocked = (Lock_Pos == 128);
+  }
+  DWIN_Draw_Box(1, HMI_data.Barfill_Color, 0, 260, DWIN_WIDTH, 20);
+  DWIN_Draw_VLine(Color_Yellow, Lock_Pos * DWIN_WIDTH / 255, 260, 20);
+  DWIN_UpdateLCD();
+}
+
+bool LockScreenClass::isUnlocked() { return unlocked; }
+
+#endif // DWIN_CREALITY_LCD_ENHANCED
diff --git a/Marlin/src/lcd/e3v2/enhanced/lockscreen.h b/Marlin/src/lcd/e3v2/enhanced/lockscreen.h
new file mode 100644
index 0000000000..32a0cc3e5a
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/lockscreen.h
@@ -0,0 +1,35 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.1
+ * Date: 2021/08/29
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "../../../core/types.h"
+
+class LockScreenClass {
+private:
+  uint8_t Lock_Pos = 0;
+  bool unlocked = false;
+public:
+  void Init();
+  void onEncoderState(ENCODER_DiffState encoder_diffState);
+  void Draw();
+  bool isUnlocked();
+};
+extern LockScreenClass LockScreen;
diff --git a/Marlin/src/lcd/e3v2/enhanced/rotary_encoder.cpp b/Marlin/src/lcd/e3v2/enhanced/rotary_encoder.cpp
new file mode 100644
index 0000000000..4f815fdee0
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/rotary_encoder.cpp
@@ -0,0 +1,261 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.1
+ * Date: 2021/08/29
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/*****************************************************************************
+ * @file     lcd/e3v2/enhanced/rotary_encoder.cpp
+ * @author   LEO / Creality3D
+ * @date     2019/07/06
+ * @version  2.0.1
+ * @brief    Rotary encoder functions
+ *****************************************************************************/
+
+#include "../../../inc/MarlinConfigPre.h"
+
+#if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+
+#include "rotary_encoder.h"
+#include "../../buttons.h"
+
+#include "../../../MarlinCore.h"
+#include "../../../HAL/shared/Delay.h"
+
+#if HAS_BUZZER
+  #include "../../../libs/buzzer.h"
+#endif
+
+#include <stdlib.h>
+
+#ifndef ENCODER_PULSES_PER_STEP
+  #define ENCODER_PULSES_PER_STEP 4
+#endif
+
+#if ENABLED(SOUND_MENU_ITEM)
+  #include "../../marlinui.h"
+#endif
+
+ENCODER_Rate EncoderRate;
+
+// Buzzer
+void Encoder_tick() {
+  #if PIN_EXISTS(BEEPER)
+    if (TERN1(SOUND_MENU_ITEM, ui.buzzer_enabled)) {
+      WRITE(BEEPER_PIN, HIGH);
+      delay(10);
+      WRITE(BEEPER_PIN, LOW);
+    }
+  #endif
+}
+
+// Encoder initialization
+void Encoder_Configuration() {
+  #if BUTTON_EXISTS(EN1)
+    SET_INPUT_PULLUP(BTN_EN1);
+  #endif
+  #if BUTTON_EXISTS(EN2)
+    SET_INPUT_PULLUP(BTN_EN2);
+  #endif
+  #if BUTTON_EXISTS(ENC)
+    SET_INPUT_PULLUP(BTN_ENC);
+  #endif
+  #if PIN_EXISTS(BEEPER)
+    SET_OUTPUT(BEEPER_PIN);
+  #endif
+}
+
+// Analyze encoder value and return state
+ENCODER_DiffState Encoder_ReceiveAnalyze() {
+  const millis_t now = millis();
+  static uint8_t lastEncoderBits;
+  uint8_t newbutton = 0;
+  static signed char temp_diff = 0;
+
+  ENCODER_DiffState temp_diffState = ENCODER_DIFF_NO;
+  if (BUTTON_PRESSED(EN1)) newbutton |= EN_A;
+  if (BUTTON_PRESSED(EN2)) newbutton |= EN_B;
+  if (BUTTON_PRESSED(ENC)) {
+    static millis_t next_click_update_ms;
+    if (ELAPSED(now, next_click_update_ms)) {
+      next_click_update_ms = millis() + 300;
+      Encoder_tick();
+      #if PIN_EXISTS(LCD_LED)
+        //LED_Action();
+      #endif
+      const bool was_waiting = wait_for_user;
+      wait_for_user = false;
+      return was_waiting ? ENCODER_DIFF_NO : ENCODER_DIFF_ENTER;
+    }
+    else return ENCODER_DIFF_NO;
+  }
+  if (newbutton != lastEncoderBits) {
+    switch (newbutton) {
+      case ENCODER_PHASE_0:
+             if (lastEncoderBits == ENCODER_PHASE_3) temp_diff++;
+        else if (lastEncoderBits == ENCODER_PHASE_1) temp_diff--;
+        break;
+      case ENCODER_PHASE_1:
+             if (lastEncoderBits == ENCODER_PHASE_0) temp_diff++;
+        else if (lastEncoderBits == ENCODER_PHASE_2) temp_diff--;
+        break;
+      case ENCODER_PHASE_2:
+             if (lastEncoderBits == ENCODER_PHASE_1) temp_diff++;
+        else if (lastEncoderBits == ENCODER_PHASE_3) temp_diff--;
+        break;
+      case ENCODER_PHASE_3:
+             if (lastEncoderBits == ENCODER_PHASE_2) temp_diff++;
+        else if (lastEncoderBits == ENCODER_PHASE_0) temp_diff--;
+        break;
+    }
+    lastEncoderBits = newbutton;
+  }
+
+  if (ABS(temp_diff) >= ENCODER_PULSES_PER_STEP) {
+    if (temp_diff > 0) temp_diffState = ENCODER_DIFF_CW;
+    else temp_diffState = ENCODER_DIFF_CCW;
+
+    #if ENABLED(ENCODER_RATE_MULTIPLIER)
+
+      millis_t ms = millis();
+      int32_t encoderMultiplier = 1;
+
+      // if must encoder rati multiplier
+      if (EncoderRate.enabled) {
+        const float abs_diff = ABS(temp_diff),
+                    encoderMovementSteps = abs_diff / (ENCODER_PULSES_PER_STEP);
+        if (EncoderRate.lastEncoderTime) {
+          // Note that the rate is always calculated between two passes through the
+          // loop and that the abs of the temp_diff value is tracked.
+          const float encoderStepRate = encoderMovementSteps / float(ms - EncoderRate.lastEncoderTime) * 1000;
+               if (encoderStepRate >= ENCODER_100X_STEPS_PER_SEC) encoderMultiplier = 100;
+          else if (encoderStepRate >= ENCODER_10X_STEPS_PER_SEC)  encoderMultiplier = 10;
+          else if (encoderStepRate >= ENCODER_5X_STEPS_PER_SEC)   encoderMultiplier = 5;
+        }
+        EncoderRate.lastEncoderTime = ms;
+      }
+
+    #else
+
+      constexpr int32_t encoderMultiplier = 1;
+
+    #endif
+
+    // EncoderRate.encoderMoveValue += (temp_diff * encoderMultiplier) / (ENCODER_PULSES_PER_STEP);
+    EncoderRate.encoderMoveValue = (temp_diff * encoderMultiplier) / (ENCODER_PULSES_PER_STEP);
+    if (EncoderRate.encoderMoveValue < 0) EncoderRate.encoderMoveValue = -EncoderRate.encoderMoveValue;
+
+    temp_diff = 0;
+  }
+  return temp_diffState;
+}
+
+#if PIN_EXISTS(LCD_LED)
+
+  // Take the low 24 valid bits  24Bit: G7 G6 G5 G4 G3 G2 G1 G0 R7 R6 R5 R4 R3 R2 R1 R0 B7 B6 B5 B4 B3 B2 B1 B0
+  uint16_t LED_DataArray[LED_NUM];
+
+  // LED light operation
+  void LED_Action() {
+    LED_Control(RGB_SCALE_WARM_WHITE,0x0F);
+    delay(30);
+    LED_Control(RGB_SCALE_WARM_WHITE,0x00);
+  }
+
+  // LED initialization
+  void LED_Configuration() {
+    SET_OUTPUT(LCD_LED_PIN);
+  }
+
+  // LED write data
+  void LED_WriteData() {
+    uint8_t tempCounter_LED, tempCounter_Bit;
+    for (tempCounter_LED = 0; tempCounter_LED < LED_NUM; tempCounter_LED++) {
+      for (tempCounter_Bit = 0; tempCounter_Bit < 24; tempCounter_Bit++) {
+        if (LED_DataArray[tempCounter_LED] & (0x800000 >> tempCounter_Bit)) {
+          LED_DATA_HIGH;
+          DELAY_NS(300);
+          LED_DATA_LOW;
+          DELAY_NS(200);
+        }
+        else {
+          LED_DATA_HIGH;
+          LED_DATA_LOW;
+          DELAY_NS(200);
+        }
+      }
+    }
+  }
+
+  // LED control
+  //  RGB_Scale: RGB color ratio
+  //  luminance: brightness (0~0xFF)
+  void LED_Control(const uint8_t RGB_Scale, const uint8_t luminance) {
+    for (uint8_t i = 0; i < LED_NUM; i++) {
+      LED_DataArray[i] = 0;
+      switch (RGB_Scale) {
+        case RGB_SCALE_R10_G7_B5: LED_DataArray[i] = (luminance * 10/10) << 8 | (luminance * 7/10) << 16 | luminance * 5/10; break;
+        case RGB_SCALE_R10_G7_B4: LED_DataArray[i] = (luminance * 10/10) << 8 | (luminance * 7/10) << 16 | luminance * 4/10; break;
+        case RGB_SCALE_R10_G8_B7: LED_DataArray[i] = (luminance * 10/10) << 8 | (luminance * 8/10) << 16 | luminance * 7/10; break;
+      }
+    }
+    LED_WriteData();
+  }
+
+  // LED gradient control
+  //  RGB_Scale: RGB color ratio
+  //  luminance: brightness (0~0xFF)
+  //  change_Time: gradient time (ms)
+  void LED_GraduallyControl(const uint8_t RGB_Scale, const uint8_t luminance, const uint16_t change_Interval) {
+    struct { uint8_t g, r, b; } led_data[LED_NUM];
+    for (uint8_t i = 0; i < LED_NUM; i++) {
+      switch (RGB_Scale) {
+        case RGB_SCALE_R10_G7_B5:
+          led_data[i] = { luminance * 7/10, luminance * 10/10, luminance * 5/10 };
+          break;
+        case RGB_SCALE_R10_G7_B4:
+          led_data[i] = { luminance * 7/10, luminance * 10/10, luminance * 4/10 };
+          break;
+        case RGB_SCALE_R10_G8_B7:
+          led_data[i] = { luminance * 8/10, luminance * 10/10, luminance * 7/10 };
+          break;
+      }
+    }
+
+    struct { bool g, r, b; } led_flag = { false, false, false };
+    for (uint8_t i = 0; i < LED_NUM; i++) {
+      while (1) {
+        const uint8_t g = uint8_t(LED_DataArray[i] >> 16),
+                      r = uint8_t(LED_DataArray[i] >> 8),
+                      b = uint8_t(LED_DataArray[i]);
+        if (g == led_data[i].g) led_flag.g = true;
+        else LED_DataArray[i] += (g > led_data[i].g) ? -0x010000 : 0x010000;
+        if (r == led_data[i].r) led_flag.r = true;
+        else LED_DataArray[i] += (r > led_data[i].r) ? -0x000100 : 0x000100;
+        if (b == led_data[i].b) led_flag.b = true;
+        else LED_DataArray[i] += (b > led_data[i].b) ? -0x000001 : 0x000001;
+        LED_WriteData();
+        if (led_flag.r && led_flag.g && led_flag.b) break;
+        delay(change_Interval);
+      }
+    }
+  }
+
+#endif // LCD_LED
+
+#endif // DWIN_CREALITY_LCD_ENHANCED
diff --git a/Marlin/src/lcd/e3v2/enhanced/rotary_encoder.h b/Marlin/src/lcd/e3v2/enhanced/rotary_encoder.h
new file mode 100644
index 0000000000..c500cfe5bb
--- /dev/null
+++ b/Marlin/src/lcd/e3v2/enhanced/rotary_encoder.h
@@ -0,0 +1,93 @@
+/**
+ * DWIN UI Enhanced implementation
+ * Author: Miguel A. Risco-Castillo
+ * Version: 3.6.1
+ * Date: 2021/08/29
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser 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 Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+/*****************************************************************************
+ * @file     lcd/e3v2/enhanced/rotary_encoder.h
+ * @author   LEO / Creality3D
+ * @date     2019/07/06
+ * @version  2.0.1
+ * @brief    Rotary encoder functions
+ ****************************************************************************/
+
+#include "../../../inc/MarlinConfig.h"
+
+/*********************** Encoder Set ***********************/
+
+typedef struct {
+  bool enabled = false;
+  int encoderMoveValue = 0;
+  millis_t lastEncoderTime = 0;
+} ENCODER_Rate;
+
+extern ENCODER_Rate EncoderRate;
+
+typedef enum {
+  ENCODER_DIFF_NO    = 0,  // no state
+  ENCODER_DIFF_CW    = 1,  // clockwise rotation
+  ENCODER_DIFF_CCW   = 2,  // counterclockwise rotation
+  ENCODER_DIFF_ENTER = 3   // click
+} ENCODER_DiffState;
+
+// Encoder initialization
+void Encoder_Configuration();
+
+// Analyze encoder value and return state
+ENCODER_DiffState Encoder_ReceiveAnalyze();
+
+/*********************** Encoder LED ***********************/
+
+#if PIN_EXISTS(LCD_LED)
+
+  #define LED_NUM  4
+  #define LED_DATA_HIGH  WRITE(LCD_LED_PIN, 1)
+  #define LED_DATA_LOW   WRITE(LCD_LED_PIN, 0)
+
+  #define RGB_SCALE_R10_G7_B5  1
+  #define RGB_SCALE_R10_G7_B4  2
+  #define RGB_SCALE_R10_G8_B7  3
+  #define RGB_SCALE_NEUTRAL_WHITE RGB_SCALE_R10_G7_B5
+  #define RGB_SCALE_WARM_WHITE    RGB_SCALE_R10_G7_B4
+  #define RGB_SCALE_COOL_WHITE    RGB_SCALE_R10_G8_B7
+
+  extern unsigned int LED_DataArray[LED_NUM];
+
+  // LED light operation
+  void LED_Action();
+
+  // LED initialization
+  void LED_Configuration();
+
+  // LED write data
+  void LED_WriteData();
+
+  // LED control
+  //  RGB_Scale: RGB color ratio
+  //  luminance: brightness (0~0xFF)
+  void LED_Control(const uint8_t RGB_Scale, const uint8_t luminance);
+
+  // LED gradient control
+  //  RGB_Scale: RGB color ratio
+  //  luminance: brightness (0~0xFF)
+  //  change_Time: gradient time (ms)
+  void LED_GraduallyControl(const uint8_t RGB_Scale, const uint8_t luminance, const uint16_t change_Interval);
+
+#endif // LCD_LED
diff --git a/Marlin/src/lcd/e3v2/jyersui/dwin_lcd.h b/Marlin/src/lcd/e3v2/jyersui/dwin_lcd.h
index 18b7c34744..9f8bd25295 100644
--- a/Marlin/src/lcd/e3v2/jyersui/dwin_lcd.h
+++ b/Marlin/src/lcd/e3v2/jyersui/dwin_lcd.h
@@ -44,9 +44,6 @@
 // Handshake (1: Success, 0: Fail)
 bool DWIN_Handshake(void);
 
-// Common DWIN startup
-void DWIN_Startup(void);
-
 // Set the backlight luminance
 //  luminance: (0x00-0xFF)
 void DWIN_Backlight_SetLuminance(const uint8_t luminance);
diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h
index bddddb75bf..11c976ee41 100644
--- a/Marlin/src/lcd/language/language_en.h
+++ b/Marlin/src/lcd/language/language_en.h
@@ -78,6 +78,14 @@ namespace Language_en {
   PROGMEM Language_Str MSG_AUTO_HOME_I                     = _UxGT("Home ") LCD_STR_I;
   PROGMEM Language_Str MSG_AUTO_HOME_J                     = _UxGT("Home ") LCD_STR_J;
   PROGMEM Language_Str MSG_AUTO_HOME_K                     = _UxGT("Home ") LCD_STR_K;
+  PROGMEM Language_Str MSG_FILAMENT_SET                    = _UxGT("Filament Settings");
+  PROGMEM Language_Str MSG_FILAMENT_MAN                    = _UxGT("Filament Management");
+  PROGMEM Language_Str MSG_LEVBED_FL                       = _UxGT("Front Left");
+  PROGMEM Language_Str MSG_LEVBED_FR                       = _UxGT("Front Right");
+  PROGMEM Language_Str MSG_LEVBED_C                        = _UxGT("Center");
+  PROGMEM Language_Str MSG_LEVBED_BL                       = _UxGT("Back Left");
+  PROGMEM Language_Str MSG_LEVBED_BR                       = _UxGT("Back Right");
+  PROGMEM Language_Str MSG_MANUAL_MESH                     = _UxGT("Manual Mesh");
   PROGMEM Language_Str MSG_AUTO_Z_ALIGN                    = _UxGT("Auto Z-Align");
   PROGMEM Language_Str MSG_ITERATION                       = _UxGT("G34 Iteration: %i");
   PROGMEM Language_Str MSG_DECREASING_ACCURACY             = _UxGT("Accuracy Decreasing!");
@@ -289,6 +297,11 @@ namespace Language_en {
   PROGMEM Language_Str MSG_MOVE_01IN                       = _UxGT("Move 0.1in");
   PROGMEM Language_Str MSG_MOVE_1IN                        = _UxGT("Move 1.0in");
   PROGMEM Language_Str MSG_SPEED                           = _UxGT("Speed");
+  PROGMEM Language_Str MSG_MAXSPEED                        = _UxGT("Max Speed (mm/s)");
+  PROGMEM Language_Str MSG_MAXSPEED_X                      = _UxGT("Max ") LCD_STR_A _UxGT(" Speed");
+  PROGMEM Language_Str MSG_MAXSPEED_Y                      = _UxGT("Max ") LCD_STR_B _UxGT(" Speed");
+  PROGMEM Language_Str MSG_MAXSPEED_Z                      = _UxGT("Max ") LCD_STR_C _UxGT(" Speed");
+  PROGMEM Language_Str MSG_MAXSPEED_E                      = _UxGT("Max ") LCD_STR_E _UxGT(" Speed");
   PROGMEM Language_Str MSG_BED_Z                           = _UxGT("Bed Z");
   PROGMEM Language_Str MSG_NOZZLE                          = _UxGT("Nozzle");
   PROGMEM Language_Str MSG_NOZZLE_N                        = _UxGT("Nozzle ~");
@@ -321,7 +334,7 @@ namespace Language_en {
   PROGMEM Language_Str MSG_LCD_OFF                         = _UxGT("Off");
   PROGMEM Language_Str MSG_PID_AUTOTUNE                    = _UxGT("PID Autotune");
   PROGMEM Language_Str MSG_PID_AUTOTUNE_E                  = _UxGT("PID Autotune *");
-  PROGMEM Language_Str MSG_PID_CYCLE                       = _UxGT("PID Cycle");
+  PROGMEM Language_Str MSG_PID_CYCLE                       = _UxGT("PID Cycles");
   PROGMEM Language_Str MSG_PID_AUTOTUNE_DONE               = _UxGT("PID tuning done");
   PROGMEM Language_Str MSG_PID_BAD_EXTRUDER_NUM            = _UxGT("Autotune failed. Bad extruder.");
   PROGMEM Language_Str MSG_PID_TEMP_TOO_HIGH               = _UxGT("Autotune failed. Temperature too high.");
@@ -504,6 +517,7 @@ namespace Language_en {
   PROGMEM Language_Str MSG_MANUAL_DEPLOY                   = _UxGT("Deploy Z-Probe");
   PROGMEM Language_Str MSG_MANUAL_STOW                     = _UxGT("Stow Z-Probe");
   PROGMEM Language_Str MSG_HOME_FIRST                      = _UxGT("Home %s%s%s First");
+  PROGMEM Language_Str MSG_ZPROBE_SETTINGS                 = _UxGT("Probe Settings");
   PROGMEM Language_Str MSG_ZPROBE_OFFSETS                  = _UxGT("Probe Offsets");
   PROGMEM Language_Str MSG_ZPROBE_XOFFSET                  = _UxGT("Probe X Offset");
   PROGMEM Language_Str MSG_ZPROBE_YOFFSET                  = _UxGT("Probe Y Offset");
@@ -611,6 +625,7 @@ namespace Language_en {
   PROGMEM Language_Str MSG_FILAMENT_CHANGE_NOZZLE          = _UxGT("  Nozzle: ");
   PROGMEM Language_Str MSG_RUNOUT_SENSOR                   = _UxGT("Runout Sensor");
   PROGMEM Language_Str MSG_RUNOUT_DISTANCE_MM              = _UxGT("Runout Dist mm");
+  PROGMEM Language_Str MSG_RUNOUT_ENABLE                   = _UxGT("Enable Runout");
   PROGMEM Language_Str MSG_KILL_HOMING_FAILED              = _UxGT("Homing Failed");
   PROGMEM Language_Str MSG_LCD_PROBING_FAILED              = _UxGT("Probing Failed");
 
diff --git a/Marlin/src/lcd/marlinui.cpp b/Marlin/src/lcd/marlinui.cpp
index 51cbf4534f..8c59500574 100644
--- a/Marlin/src/lcd/marlinui.cpp
+++ b/Marlin/src/lcd/marlinui.cpp
@@ -48,6 +48,8 @@ MarlinUI ui;
 
 #if ENABLED(DWIN_CREALITY_LCD)
   #include "e3v2/creality/dwin.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "e3v2/enhanced/dwin.h"
 #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
   #include "e3v2/jyersui/dwin.h"
 #endif
@@ -101,6 +103,7 @@ constexpr uint8_t epps = ENCODER_PULSES_PER_STEP;
     backlight = !!value;
     if (backlight) brightness = constrain(value, MIN_LCD_BRIGHTNESS, MAX_LCD_BRIGHTNESS);
     // Set brightness on enabled LCD here
+    TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_LCD_Brightness(brightness));
     TERN_(DWIN_CREALITY_LCD_JYERSUI, DWIN_Backlight_SetLuminance(backlight ? brightness : 0));
   }
 #endif
@@ -1474,7 +1477,7 @@ constexpr uint8_t epps = ENCODER_PULSES_PER_STEP;
     #endif
 
     TERN_(EXTENSIBLE_UI, ExtUI::onStatusChanged(status_message));
-    TERN_(DWIN_CREALITY_LCD, DWIN_StatusChanged(status_message));
+    TERN_(HAS_DWIN_E3V2_BASIC, DWIN_StatusChanged(status_message));
     TERN_(DWIN_CREALITY_LCD_JYERSUI, CrealityDWIN.Update_Status(status_message));
   }
 
diff --git a/Marlin/src/lcd/marlinui.h b/Marlin/src/lcd/marlinui.h
index 268d018508..d3a3c9d521 100644
--- a/Marlin/src/lcd/marlinui.h
+++ b/Marlin/src/lcd/marlinui.h
@@ -55,11 +55,17 @@
   #include "../module/printcounter.h"
 #endif
 
-#if ENABLED(ADVANCED_PAUSE_FEATURE) && ANY(HAS_LCD_MENU, EXTENSIBLE_UI, DWIN_CREALITY_LCD_JYERSUI)
+#if ENABLED(ADVANCED_PAUSE_FEATURE) && ANY(HAS_LCD_MENU, EXTENSIBLE_UI, HAS_DWIN_E3V2)
   #include "../feature/pause.h"
   #include "../module/motion.h" // for active_extruder
 #endif
 
+#if ENABLED(DWIN_CREALITY_LCD)
+  #include "e3v2/creality/dwin.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "e3v2/enhanced/dwin.h"
+#endif
+
 #define START_OF_UTF8_CHAR(C) (((C) & 0xC0u) != 0x80U)
 
 #if HAS_WIRED_LCD
@@ -257,7 +263,7 @@ public:
     FORCE_INLINE static void refresh_brightness() { set_brightness(brightness); }
   #endif
 
-  #if ENABLED(DWIN_CREALITY_LCD)
+  #if HAS_DWIN_E3V2_BASIC
     static void refresh();
   #else
     FORCE_INLINE static void refresh() {
@@ -315,7 +321,7 @@ public:
 
   #if HAS_STATUS_MESSAGE
 
-    #if HAS_WIRED_LCD
+    #if EITHER(HAS_WIRED_LCD, DWIN_CREALITY_LCD_ENHANCED)
       #if ENABLED(STATUS_MESSAGE_SCROLLING)
         #define MAX_MESSAGE_LENGTH _MAX(LONG_FILENAME_LENGTH, MAX_LANG_CHARSIZE * 2 * (LCD_WIDTH))
       #else
@@ -351,6 +357,12 @@ public:
     static inline void reset_alert_level() {}
   #endif
 
+  #if EITHER(HAS_DISPLAY, DWIN_CREALITY_LCD_ENHANCED)
+    static void kill_screen(PGM_P const lcd_error, PGM_P const lcd_component);
+  #else
+    static inline void kill_screen(PGM_P const, PGM_P const) {}
+  #endif
+
   #if HAS_DISPLAY
 
     static void init();
@@ -457,7 +469,6 @@ public:
       static bool did_first_redraw;
     #endif
 
-    static void kill_screen(PGM_P const lcd_error, PGM_P const lcd_component);
     static void draw_kill_screen();
 
   #else // No LCD
@@ -585,7 +596,7 @@ public:
     static inline bool use_click() { return false; }
   #endif
 
-  #if ENABLED(ADVANCED_PAUSE_FEATURE) && ANY(HAS_LCD_MENU, EXTENSIBLE_UI, DWIN_CREALITY_LCD_JYERSUI)
+  #if ENABLED(ADVANCED_PAUSE_FEATURE) && ANY(HAS_LCD_MENU, EXTENSIBLE_UI, HAS_DWIN_E3V2)
     static void pause_show_message(const PauseMessage message, const PauseMode mode=PAUSE_MODE_SAME, const uint8_t extruder=active_extruder);
   #else
     static inline void _pause_show_message() {}
diff --git a/Marlin/src/lcd/tft/ui_common.h b/Marlin/src/lcd/tft/ui_common.h
index 617447a181..759712b64c 100644
--- a/Marlin/src/lcd/tft/ui_common.h
+++ b/Marlin/src/lcd/tft/ui_common.h
@@ -23,7 +23,7 @@
 
 #include "../../inc/MarlinConfigPre.h"
 
-#if !HAS_LCD_MENU
+#if ENABLED(NO_LCD_MENUS)
   #error "Seriously? High resolution TFT screen without menu?"
 #endif
 
diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp
index cbadda2eef..a4469bb209 100644
--- a/Marlin/src/module/probe.cpp
+++ b/Marlin/src/module/probe.cpp
@@ -79,6 +79,8 @@
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../lcd/extui/ui_api.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
@@ -295,7 +297,7 @@ FORCE_INLINE void probe_specific_action(const bool deploy) {
 
       TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Stow Probe"), CONTINUE_STR));
       TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("Stow Probe")));
-
+      TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_Popup_Confirm(ICON_BLTouch, PSTR("Stow Probe"), CONTINUE_STR));
       wait_for_user_response();
       ui.reset_status();
 
diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp
index f2d2aeee92..c24a63ee51 100644
--- a/Marlin/src/module/settings.cpp
+++ b/Marlin/src/module/settings.cpp
@@ -71,9 +71,9 @@
 
 #if ENABLED(EXTENSIBLE_UI)
   #include "../lcd/extui/ui_api.h"
-#endif
-
-#if ENABLED(DWIN_CREALITY_LCD_JYERSUI)
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../lcd/e3v2/enhanced/dwin.h"
+#elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
   #include "../lcd/e3v2/jyersui/dwin.h"
 #endif
 
@@ -441,14 +441,15 @@ typedef struct SettingsDataStruct {
   // EXTENSIBLE_UI
   //
   #if ENABLED(EXTENSIBLE_UI)
-    // This is a significant hardware change; don't reserve space when not present
     uint8_t extui_data[ExtUI::eeprom_data_size];
   #endif
 
   //
-  // DWIN_CREALITY_LCD_JYERSUI
+  // Ender-3 V2 DWIN
   //
-  #if ENABLED(DWIN_CREALITY_LCD_JYERSUI)
+  #if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+    uint8_t dwin_data[eeprom_data_size];
+  #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
     uint8_t dwin_settings[CrealityDWIN.eeprom_data_size];
   #endif
 
@@ -1358,9 +1359,16 @@ void MarlinSettings::postprocess() {
     #endif
 
     //
-    // Creality UI Settings
+    // Creality DWIN User Data
     //
-    #if ENABLED(DWIN_CREALITY_LCD_JYERSUI)
+    #if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+    {
+      char dwin_data[eeprom_data_size] = { 0 };
+      DWIN_StoreSettings(dwin_data);
+      _FIELD_TEST(dwin_data);
+      EEPROM_WRITE(dwin_data);
+    }
+    #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
     {
       char dwin_settings[CrealityDWIN.eeprom_data_size] = { 0 };
       CrealityDWIN.Save_Settings(dwin_settings);
@@ -1488,6 +1496,8 @@ void MarlinSettings::postprocess() {
         stored_ver[1] = '\0';
       }
       DEBUG_ECHO_MSG("EEPROM version mismatch (EEPROM=", stored_ver, " Marlin=" EEPROM_VERSION ")");
+      TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status(GET_TEXT(MSG_ERR_EEPROM_VERSION)));
+
       IF_DISABLED(EEPROM_AUTO_INIT, ui.eeprom_alert_version());
       eeprom_error = true;
     }
@@ -2249,9 +2259,16 @@ void MarlinSettings::postprocess() {
       #endif
 
       //
-      // Creality UI Settings
+      // Creality DWIN User Data
       //
-      #if ENABLED(DWIN_CREALITY_LCD_JYERSUI)
+      #if ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+      {
+        const char dwin_data[eeprom_data_size] = { 0 };
+        _FIELD_TEST(dwin_data);
+        EEPROM_READ(dwin_data);
+        if (!validating) DWIN_LoadSettings(dwin_data);
+      }
+      #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI)
       {
         const char dwin_settings[CrealityDWIN.eeprom_data_size] = { 0 };
         _FIELD_TEST(dwin_settings);
@@ -2340,6 +2357,7 @@ void MarlinSettings::postprocess() {
       else if (working_crc != stored_crc) {
         eeprom_error = true;
         DEBUG_ERROR_MSG("EEPROM CRC mismatch - (stored) ", stored_crc, " != ", working_crc, " (calculated)!");
+        TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status(GET_TEXT(MSG_ERR_EEPROM_CRC)));
         IF_DISABLED(EEPROM_AUTO_INIT, ui.eeprom_alert_crc());
       }
       else if (!validating) {
@@ -2656,7 +2674,7 @@ void MarlinSettings::reset() {
   #endif
 
   TERN_(EXTENSIBLE_UI, ExtUI::onFactoryReset());
-
+  TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_SetDataDefaults());
   TERN_(DWIN_CREALITY_LCD_JYERSUI, CrealityDWIN.Reset_Settings());
 
   //
diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp
index 935de772f4..1bb86ed4c7 100644
--- a/Marlin/src/module/temperature.cpp
+++ b/Marlin/src/module/temperature.cpp
@@ -47,6 +47,8 @@
 
 #if ENABLED(DWIN_CREALITY_LCD)
   #include "../lcd/e3v2/creality/dwin.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #if ENABLED(EXTENSIBLE_UI)
@@ -603,10 +605,12 @@ volatile bool Temperature::raw_temps_ready = false;
     TERN_(HAS_AUTO_FAN, next_auto_fan_check_ms = next_temp_ms + 2500UL);
 
     TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_STARTED));
+    TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_PidTuning(isbed ? PID_BED_START : PID_EXTR_START));
 
     if (target > GHV(CHAMBER_MAX_TARGET, BED_MAX_TARGET, temp_range[heater_id].maxtemp - (HOTEND_OVERSHOOT))) {
       SERIAL_ECHOLNPGM(STR_PID_TEMP_TOO_HIGH);
       TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_TEMP_TOO_HIGH));
+      TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_PidTuning(PID_TEMP_TOO_HIGH));
       return;
     }
 
@@ -627,6 +631,7 @@ volatile bool Temperature::raw_temps_ready = false;
 
     // PID Tuning loop
     wait_for_heatup = true; // Can be interrupted with M108
+    TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT), "Wait for heat up..."));
     while (wait_for_heatup) {
 
       const millis_t ms = millis();
@@ -687,6 +692,7 @@ volatile bool Temperature::raw_temps_ready = false;
             }
           }
           SHV((bias + d) >> 1);
+          TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_PID_CYCLE), cycles, ncycles));
           cycles++;
           minT = target;
         }
@@ -699,6 +705,7 @@ volatile bool Temperature::raw_temps_ready = false;
       if (current_temp > target + MAX_OVERSHOOT_PID_AUTOTUNE) {
         SERIAL_ECHOLNPGM(STR_PID_TEMP_TOO_HIGH);
         TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_TEMP_TOO_HIGH));
+        TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_PidTuning(PID_TEMP_TOO_HIGH));
         break;
       }
 
@@ -734,6 +741,7 @@ volatile bool Temperature::raw_temps_ready = false;
       #endif
       if ((ms - _MIN(t1, t2)) > (MAX_CYCLE_TIME_PID_AUTOTUNE * 60L * 1000L)) {
         TERN_(DWIN_CREALITY_LCD, DWIN_Popup_Temperature(0));
+        TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_PidTuning(PID_TUNING_TIMEOUT));
         TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_TUNING_TIMEOUT));
         SERIAL_ECHOLNPGM(STR_PID_TIMEOUT);
         break;
@@ -787,6 +795,7 @@ volatile bool Temperature::raw_temps_ready = false;
         TERN_(PRINTER_EVENT_LEDS, printerEventLEDs.onPidTuningDone(color));
 
         TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_DONE));
+        TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_PidTuning(PID_DONE));
 
         goto EXIT_M303;
       }
@@ -795,7 +804,7 @@ volatile bool Temperature::raw_temps_ready = false;
       TERN_(HAL_IDLETASK, HAL_idletask());
 
       // Run UI update
-      TERN(DWIN_CREALITY_LCD, DWIN_Update(), ui.update());
+      TERN(HAS_DWIN_E3V2_BASIC, DWIN_Update(), ui.update());
     }
     wait_for_heatup = false;
 
@@ -804,6 +813,7 @@ volatile bool Temperature::raw_temps_ready = false;
     TERN_(PRINTER_EVENT_LEDS, printerEventLEDs.onPidTuningDone(color));
 
     TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_DONE));
+    TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_PidTuning(PID_DONE));
 
     EXIT_M303:
       TERN_(NO_FAN_SLOWING_IN_PID_TUNING, adaptive_fan_slowing = true);
@@ -1014,14 +1024,14 @@ void Temperature::_temp_error(const heater_id_t heater_id, PGM_P const serial_ms
 }
 
 void Temperature::max_temp_error(const heater_id_t heater_id) {
-  #if ENABLED(DWIN_CREALITY_LCD) && (HAS_HOTEND || HAS_HEATED_BED)
+  #if HAS_DWIN_E3V2_BASIC && (HAS_HOTEND || HAS_HEATED_BED)
     DWIN_Popup_Temperature(1);
   #endif
   _temp_error(heater_id, PSTR(STR_T_MAXTEMP), GET_TEXT(MSG_ERR_MAXTEMP));
 }
 
 void Temperature::min_temp_error(const heater_id_t heater_id) {
-  #if ENABLED(DWIN_CREALITY_LCD) && (HAS_HOTEND || HAS_HEATED_BED)
+  #if HAS_DWIN_E3V2_BASIC && (HAS_HOTEND || HAS_HEATED_BED)
     DWIN_Popup_Temperature(0);
   #endif
   _temp_error(heater_id, PSTR(STR_T_MINTEMP), GET_TEXT(MSG_ERR_MINTEMP));
@@ -1329,7 +1339,7 @@ void Temperature::manage_heater() {
           if (watch_hotend[e].check(degHotend(e)))  // Increased enough?
             start_watching_hotend(e);               // If temp reached, turn off elapsed check
           else {
-            TERN_(DWIN_CREALITY_LCD, DWIN_Popup_Temperature(0));
+            TERN_(HAS_DWIN_E3V2_BASIC, DWIN_Popup_Temperature(0));
             _temp_error((heater_id_t)e, str_t_heating_failed, GET_TEXT(MSG_HEATING_FAILED_LCD));
           }
         }
@@ -1372,7 +1382,7 @@ void Temperature::manage_heater() {
         if (watch_bed.check(degBed()))          // Increased enough?
           start_watching_bed();                 // If temp reached, turn off elapsed check
         else {
-          TERN_(DWIN_CREALITY_LCD, DWIN_Popup_Temperature(0));
+          TERN_(HAS_DWIN_E3V2_BASIC, DWIN_Popup_Temperature(0));
           _temp_error(H_BED, str_t_heating_failed, GET_TEXT(MSG_HEATING_FAILED_LCD));
         }
       }
@@ -2586,7 +2596,7 @@ void Temperature::init() {
         state = TRRunaway;
 
       case TRRunaway:
-        TERN_(DWIN_CREALITY_LCD, DWIN_Popup_Temperature(0));
+        TERN_(HAS_DWIN_E3V2_BASIC, DWIN_Popup_Temperature(0));
         _temp_error(heater_id, str_t_thermal_runaway, GET_TEXT(MSG_THERMAL_RUNAWAY));
     }
   }
@@ -3600,7 +3610,7 @@ void Temperature::isr() {
         #if HAS_MULTI_HOTEND
           PSTR("E%c " S_FMT), '1' + e
         #else
-          PSTR("E " S_FMT)
+          PSTR("E1 " S_FMT)
         #endif
         , heating ? GET_TEXT(MSG_HEATING) : GET_TEXT(MSG_COOLING)
       );
@@ -3720,13 +3730,12 @@ void Temperature::isr() {
 
       if (wait_for_heatup) {
         wait_for_heatup = false;
-        #if ENABLED(DWIN_CREALITY_LCD)
+        #if HAS_DWIN_E3V2_BASIC
           HMI_flag.heat_flag = 0;
           duration_t elapsed = print_job_timer.duration();  // print timer
           dwin_heat_time = elapsed.value;
-        #else
-          ui.reset_status();
         #endif
+        ui.reset_status();
         TERN_(PRINTER_EVENT_LEDS, printerEventLEDs.onHeatingDone());
         return true;
       }
diff --git a/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_E3_common.h b/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_E3_common.h
index 9f923b4ea9..48d38e2213 100644
--- a/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_E3_common.h
+++ b/Marlin/src/pins/stm32f1/pins_BTT_SKR_MINI_E3_common.h
@@ -148,7 +148,7 @@
    * All pins are labeled as printed on DWIN PCB. Connect TX-TX, A-A and so on.
    */
 
-  #error "DWIN_CREALITY_LCD requires a custom cable, see diagram above this line. Comment out this line to continue."
+  #error "Ender-3 V2 display requires a custom cable, see diagram above this line. Comment out this line to continue."
 
   #define BEEPER_PIN                      EXP1_9
   #define BTN_EN1                         EXP1_3
diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp
index c0bc81a3ef..832cfa405f 100644
--- a/Marlin/src/sd/cardreader.cpp
+++ b/Marlin/src/sd/cardreader.cpp
@@ -33,6 +33,8 @@
 
 #if ENABLED(DWIN_CREALITY_LCD)
   #include "../lcd/e3v2/creality/dwin.h"
+#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED)
+  #include "../lcd/e3v2/enhanced/dwin.h"
 #endif
 
 #include "../module/planner.h"        // for synchronize
@@ -564,7 +566,7 @@ void CardReader::startOrResumeFilePrinting() {
 //
 void CardReader::endFilePrintNow(TERN_(SD_RESORT, const bool re_sort/*=false*/)) {
   TERN_(ADVANCED_PAUSE_FEATURE, did_pause_print = 0);
-  TERN_(DWIN_CREALITY_LCD, HMI_flag.print_finish = flag.sdprinting);
+  TERN_(HAS_DWIN_E3V2_BASIC, HMI_flag.print_finish = flag.sdprinting);
   flag.abort_sd_printing = false;
   if (isFileOpen()) file.close();
   TERN_(SD_RESORT, if (re_sort) presort());
diff --git a/buildroot/tests/STM32F103RET6_creality b/buildroot/tests/STM32F103RET6_creality
index 0ad66bfdb9..277a68411d 100755
--- a/buildroot/tests/STM32F103RET6_creality
+++ b/buildroot/tests/STM32F103RET6_creality
@@ -13,6 +13,11 @@ use_example_configs "Creality/Ender-3 V2/CrealityUI"
 opt_enable MARLIN_DEV_MODE BUFFER_MONITORING
 exec_test $1 $2 "Ender 3 v2 with CrealityUI" "$3"
 
+use_example_configs "Creality/Ender-3 V2/CrealityUI"
+opt_disable DWIN_CREALITY_LCD
+opt_enable DWIN_CREALITY_LCD_ENHANCED
+exec_test $1 $2 "Ender 3 v2 with Enhanced UI" "$3"
+
 use_example_configs "Creality/Ender-3 V2/CrealityUI"
 opt_disable DWIN_CREALITY_LCD
 opt_enable DWIN_CREALITY_LCD_JYERSUI
diff --git a/ini/features.ini b/ini/features.ini
index acadd7763a..6ad375e594 100644
--- a/ini/features.ini
+++ b/ini/features.ini
@@ -45,6 +45,7 @@ I2C_EEPROM                             = src_filter=+<src/HAL/shared/eeprom_if_i
 SOFT_I2C_EEPROM                        = SlowSoftI2CMaster, SlowSoftWire=https://github.com/felias-fogg/SlowSoftWire/archive/master.zip
 SPI_EEPROM                             = src_filter=+<src/HAL/shared/eeprom_if_spi.cpp>
 DWIN_CREALITY_LCD                      = src_filter=+<src/lcd/e3v2/creality>
+DWIN_CREALITY_LCD_ENHANCED             = src_filter=+<src/lcd/e3v2/enhanced>
 DWIN_CREALITY_LCD_JYERSUI              = src_filter=+<src/lcd/e3v2/jyersui>
 DWIN_MARLINUI_.+                       = src_filter=+<src/lcd/e3v2/marlinui>
 HAS_GRAPHICAL_TFT                      = src_filter=+<src/lcd/tft>
diff --git a/platformio.ini b/platformio.ini
index 23ee15d98b..5502069800 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -50,7 +50,7 @@ lib_deps           =
 default_src_filter = +<src/*> -<src/config> -<src/HAL> +<src/HAL/shared>
   -<src/lcd/HD44780> -<src/lcd/TFTGLCD> -<src/lcd/dogm> -<src/lcd/tft> -<src/lcd/tft_io>
   -<src/HAL/STM32/tft> -<src/HAL/STM32F1/tft>
-  -<src/lcd/e3v2/creality> -<src/lcd/e3v2/jyersui> -<src/lcd/e3v2/marlinui>
+  -<src/lcd/e3v2/creality> -<src/lcd/e3v2/enhanced> -<src/lcd/e3v2/jyersui> -<src/lcd/e3v2/marlinui>
   -<src/lcd/menu>
   -<src/lcd/menu/game/game.cpp> -<src/lcd/menu/game/brickout.cpp> -<src/lcd/menu/game/invaders.cpp>
   -<src/lcd/menu/game/maze.cpp> -<src/lcd/menu/game/snake.cpp>