From 66db6c3acc83afeaea87cd39dfccd0af87cacf90 Mon Sep 17 00:00:00 2001
From: Tannoo <tannoo@msn.com>
Date: Sun, 7 May 2017 21:38:03 -0600
Subject: [PATCH] UBL Menu System 1.1

    /**
     * UBL System submenu
     *
     *  Prepare
     * - Unified Bed Leveling
     *   - Activate UBL
     *   - Deactivate UBL
     *   - Mesh Storage
     *       Memory Slot:
     *       Load Bed Mesh
     *       Save Bed Mesh
     *   - Output Map
     *       Map Type:
     *       Output Bed Mesh Host / Output Bed Mesh CSV
     *   - UBL Tools
     *     - Build Mesh
     *         Build PLA Mesh
     *         Build ABS Mesh
     *       - Build Custom Mesh
     *           Hotend Temp:
     *           Bed Temp:
     *           Build Custom Mesh
     *         Info Screen
     *       - Build Cold Mesh
     *       - Fill-in Mesh
     *           Fill-in Mesh
     *           Smart Fill-in
     *           Manual Fill-in
     *           Info Screen
     *         Continue Bed Mesh
     *         Invalidate All
     *         Invalidate Closest
     *     - Validate Mesh
     *         PLA Mesh Validation
     *         ABS Mesh Validation
     *       - Custom Mesh Validation
     *           Hotend Temp:
     *           Bed Temp:
     *           Validate Mesh
     *         Info Screen
     *     - Edit Mesh
     *         Fine Tune All
     *         Fine Tune Closest
     *       - Adjust Mesh Height
     *           Height Amount:
     *           Adjust Mesh Height
     *         Info Screen
     *     - Mesh Leveling
     *         3-Point Mesh Leveling
     *       - Grid Mesh Leveling
     *           Side points:
     *           Level Mesh
     *         Info Screen
     *   - Output UBL Info
     */
---
 Marlin/language_en.h | 143 +++++++++++++++++-
 Marlin/ubl_G29.cpp   |  32 ++--
 Marlin/ultralcd.cpp  | 350 ++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 512 insertions(+), 13 deletions(-)

diff --git a/Marlin/language_en.h b/Marlin/language_en.h
index 78d674c715..7dd2edf0af 100644
--- a/Marlin/language_en.h
+++ b/Marlin/language_en.h
@@ -153,6 +153,145 @@
 #ifndef MSG_LEVEL_BED
   #define MSG_LEVEL_BED                       _UxGT("Level bed")
 #endif
+
+#if ENABLED(AUTO_BED_LEVELING_UBL)
+  #ifndef MSG_UBL_UNHOMED
+    #define MSG_UBL_UNHOMED                   _UxGT("Home XYZ first")
+  #endif
+  #ifndef MSG_UBL_TOOLS
+    #define MSG_UBL_TOOLS                     _UxGT("UBL Tools")
+  #endif
+  #ifndef MSG_UBL_LEVEL_BED
+    #define MSG_UBL_LEVEL_BED                 _UxGT("Unified Bed Leveling")
+  #endif
+  #ifndef MSG_UBL_ACTIVATE_MESH
+    #define MSG_UBL_ACTIVATE_MESH             _UxGT("Activate UBL")
+  #endif
+  #ifndef MSG_UBL_DEACTIVATE_MESH
+    #define MSG_UBL_DEACTIVATE_MESH           _UxGT("Deactivate UBL")
+  #endif
+  #ifndef MSG_UBL_CUSTOM_BED_TEMP
+    #define MSG_UBL_CUSTOM_BED_TEMP           _UxGT("Bed Temp")
+  #endif
+  #ifndef MSG_UBL_SET_BED_TEMP
+    #define MSG_UBL_SET_BED_TEMP              _UxGT("Bed Temp")
+  #endif
+  #ifndef MSG_UBL_CUSTOM_HOTEND_TEMP
+    #define MSG_UBL_CUSTOM_HOTEND_TEMP        _UxGT("Hotend Temp")
+  #endif
+  #ifndef MSG_UBL_SET_HOTEND_TEMP
+    #define MSG_UBL_SET_HOTEND_TEMP           _UxGT("Hotend Temp")
+  #endif
+  #ifndef MSG_UBL_EDIT_CUSTOM_MESH
+    #define MSG_UBL_EDIT_CUSTOM_MESH          _UxGT("Edit Custom Mesh")
+  #endif
+  #ifndef MSG_UBL_BUILD_CUSTOM_MESH
+    #define MSG_UBL_BUILD_CUSTOM_MESH         _UxGT("Build Custom Mesh")
+  #endif
+  #ifndef MSG_UBL_BUILD_MESH_MENU
+    #define MSG_UBL_BUILD_MESH_MENU           _UxGT("Build Mesh")
+  #endif
+  #ifndef MSG_UBL_BUILD_PLA_MESH
+    #define MSG_UBL_BUILD_PLA_MESH            _UxGT("Build PLA Mesh")
+  #endif
+  #ifndef MSG_UBL_BUILD_ABS_MESH
+    #define MSG_UBL_BUILD_ABS_MESH            _UxGT("Build ABS Mesh")
+  #endif
+  #ifndef MSG_UBL_BUILD_COLD_MESH
+    #define MSG_UBL_BUILD_COLD_MESH           _UxGT("Build Cold Mesh")
+  #endif
+  #ifndef MSG_UBL_MESH_HEIGHT_ADJUST
+    #define MSG_UBL_MESH_HEIGHT_ADJUST        _UxGT("Adjust Mesh Height")
+  #endif
+  #ifndef MSG_UBL_MESH_HEIGHT_AMOUNT
+    #define MSG_UBL_MESH_HEIGHT_AMOUNT        _UxGT("Height Amount")
+  #endif
+  #ifndef MSG_UBL_VALIDATE_MESH_MENU
+    #define MSG_UBL_VALIDATE_MESH_MENU        _UxGT("Validate Mesh")
+  #endif
+  #ifndef MSG_UBL_VALIDATE_PLA_MESH
+    #define MSG_UBL_VALIDATE_PLA_MESH         _UxGT("Validate PLA Mesh")
+  #endif
+  #ifndef MSG_UBL_VALIDATE_ABS_MESH
+    #define MSG_UBL_VALIDATE_ABS_MESH         _UxGT("Validate ABS Mesh")
+  #endif
+  #ifndef MSG_UBL_VALIDATE_CUSTOM_MESH
+    #define MSG_UBL_VALIDATE_CUSTOM_MESH      _UxGT("Validate Custom Mesh")
+  #endif
+  #ifndef MSG_UBL_CONTINUE_MESH
+    #define MSG_UBL_CONTINUE_MESH             _UxGT("Continue Bed Mesh")
+  #endif
+  #ifndef MSG_UBL_MESH_LEVELING
+    #define MSG_UBL_MESH_LEVELING             _UxGT("Mesh Leveling")
+  #endif
+  #ifndef MSG_UBL_3POINT_MESH_LEVELING
+    #define MSG_UBL_3POINT_MESH_LEVELING      _UxGT("3-Point Leveling")
+  #endif
+  #ifndef MSG_UBL_GRID_MESH_LEVELING
+    #define MSG_UBL_GRID_MESH_LEVELING        _UxGT("Grid Mesh Leveling")
+  #endif
+  #ifndef MSG_UBL_MESH_LEVEL
+    #define MSG_UBL_MESH_LEVEL                _UxGT("Level Mesh")
+  #endif
+  #ifndef MSG_UBL_SIDE_POINTS
+    #define MSG_UBL_SIDE_POINTS               _UxGT("Side Points")
+  #endif
+  #ifndef MSG_UBL_MAP_TYPE
+    #define MSG_UBL_MAP_TYPE                  _UxGT("Map Type")
+  #endif
+  #ifndef MSG_UBL_OUTPUT_MAP
+    #define MSG_UBL_OUTPUT_MAP                _UxGT("Output Mesh Map")
+  #endif
+  #ifndef MSG_UBL_OUTPUT_MAP_HOST
+    #define MSG_UBL_OUTPUT_MAP_HOST           _UxGT("Output for Host")
+  #endif
+  #ifndef MSG_UBL_OUTPUT_MAP_CSV
+    #define MSG_UBL_OUTPUT_MAP_CSV            _UxGT("Output for CSV")
+  #endif
+  #ifndef MSG_UBL_INFO_UBL
+    #define MSG_UBL_INFO_UBL                  _UxGT("Output UBL Info")
+  #endif
+  #ifndef MSG_UBL_EDIT_MESH_MENU
+    #define MSG_UBL_EDIT_MESH_MENU            _UxGT("Edit Mesh")
+  #endif
+  #ifndef MSG_UBL_FILLIN_AMOUNT
+    #define MSG_UBL_FILLIN_AMOUNT             _UxGT("Fill-in Amount")
+  #endif
+  #ifndef MSG_UBL_MANUAL_FILLIN
+    #define MSG_UBL_MANUAL_FILLIN             _UxGT("Manual Fill-in")
+  #endif
+  #ifndef MSG_UBL_SMART_FILLIN
+    #define MSG_UBL_SMART_FILLIN              _UxGT("Smart Fill-in")
+  #endif
+  #ifndef MSG_UBL_FILLIN_MESH
+    #define MSG_UBL_FILLIN_MESH               _UxGT("Fill-in Mesh")
+  #endif
+  #ifndef MSG_UBL_INVALIDATE_ALL
+    #define MSG_UBL_INVALIDATE_ALL            _UxGT("Invalidate All")
+  #endif
+  #ifndef MSG_UBL_INVALIDATE_CLOSEST
+    #define MSG_UBL_INVALIDATE_CLOSEST        _UxGT("Invalidate Closest")
+  #endif
+  #ifndef MSG_UBL_FINE_TUNE_ALL
+    #define MSG_UBL_FINE_TUNE_ALL             _UxGT("Fine Tune All")
+  #endif
+  #ifndef MSG_UBL_FINE_TUNE_CLOSEST
+    #define MSG_UBL_FINE_TUNE_CLOSEST         _UxGT("Fine Tune Closest")
+  #endif
+  #ifndef MSG_UBL_STORAGE_MESH_MENU
+    #define MSG_UBL_STORAGE_MESH_MENU         _UxGT("Mesh Storage")
+  #endif
+  #ifndef MSG_UBL_STORAGE_SLOT
+    #define MSG_UBL_STORAGE_SLOT              _UxGT("Memory Slot")
+  #endif
+  #ifndef MSG_UBL_LOAD_MESH
+    #define MSG_UBL_LOAD_MESH                 _UxGT("Load Bed Mesh")
+  #endif
+  #ifndef MSG_UBL_SAVE_MESH
+    #define MSG_UBL_SAVE_MESH                 _UxGT("Save Bed Mesh")
+  #endif
+#endif  // AUTO_BED_LEVELING_UBL
+
 #ifndef MSG_MOVING
   #define MSG_MOVING                          _UxGT("Moving...")
 #endif
@@ -324,6 +463,9 @@
 #ifndef MSG_RESTORE_FAILSAFE
   #define MSG_RESTORE_FAILSAFE                _UxGT("Restore failsafe")
 #endif
+#ifndef MSG_INIT_EEPROM
+  #define MSG_INIT_EEPROM                     _UxGT("Initalize Memory")
+#endif
 #ifndef MSG_REFRESH
   #define MSG_REFRESH                         _UxGT("Refresh")
 #endif
@@ -590,7 +732,6 @@
 #ifndef MSG_INFO_PSU
   #define MSG_INFO_PSU                        _UxGT("Power Supply")
 #endif
-
 #ifndef MSG_DRIVE_STRENGTH
   #define MSG_DRIVE_STRENGTH                  _UxGT("Drive Strength")
 #endif
diff --git a/Marlin/ubl_G29.cpp b/Marlin/ubl_G29.cpp
index 7a174f702d..6e794a3b05 100644
--- a/Marlin/ubl_G29.cpp
+++ b/Marlin/ubl_G29.cpp
@@ -55,12 +55,16 @@
   extern float probe_pt(float x, float y, bool, int);
   extern bool set_probe_deployed(bool);
   void smart_fill_mesh();
+  float measure_business_card_thickness(float &in_height);
+  void manually_probe_remaining_mesh(const float &lx, const float &ly, float &z_clearance, const float &card_thickness, const bool do_ubl_mesh_map);
 
   bool ProbeStay = true;
 
-  #define SIZE_OF_LITTLE_RAISE 0
+  #define SIZE_OF_LITTLE_RAISE 1
   #define BIG_RAISE_NOT_NEEDED 0
-  extern void lcd_quick_feedback();
+  extern void lcd_status_screen();
+  typedef void (*screenFunc_t)();
+  extern void lcd_goto_screen(screenFunc_t screen, const uint32_t encoder = 0);
 
   /**
    *   G29: Unified Bed Leveling by Roxy
@@ -444,7 +448,7 @@
             y_pos = current_position[Y_AXIS];
           }
 
-          const float height = code_seen('H') && code_has_value() ? code_value_float() : Z_CLEARANCE_BETWEEN_PROBES;
+          float height = code_seen('H') && code_has_value() ? code_value_float() : Z_CLEARANCE_BETWEEN_PROBES;
 
           if (code_seen('B')) {
             card_thickness = code_has_value() ? code_value_float() : measure_business_card_thickness(height);
@@ -876,7 +880,7 @@
     SERIAL_PROTOCOLLNPGM(" and take a measurement.");
   }
 
-  float measure_business_card_thickness(const float &in_height) {
+  float measure_business_card_thickness(float &in_height) {
     ubl.has_control_of_lcd_panel = true;
     ubl.save_ubl_active_state_and_disable();   // Disable bed level correction for probing
 
@@ -886,6 +890,8 @@
 
     stepper.synchronize();
     SERIAL_PROTOCOLPGM("Place shim under nozzle.");
+    LCD_MESSAGEPGM("Place shim & measure");
+    lcd_goto_screen(lcd_status_screen);
     say_and_take_a_measurement();
 
     const float z1 = use_encoder_wheel_to_measure_point();
@@ -893,29 +899,33 @@
     stepper.synchronize();
 
     SERIAL_PROTOCOLPGM("Remove shim.");
+    LCD_MESSAGEPGM("Remove & measure");
+ 
     say_and_take_a_measurement();
 
     const float z2 = use_encoder_wheel_to_measure_point();
-    do_blocking_move_to_z(current_position[Z_AXIS] + SIZE_OF_LITTLE_RAISE);
+    do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES);
 
     if (g29_verbose_level > 1) {
       SERIAL_PROTOCOLPGM("Business Card is: ");
       SERIAL_PROTOCOL_F(abs(z1 - z2), 6);
       SERIAL_PROTOCOLLNPGM("mm thick.");
     }
+    in_height = current_position[Z_AXIS]; // do manual probing at lower height
     ubl.has_control_of_lcd_panel = false;
 
     ubl.restore_ubl_active_state_and_leave();
     return abs(z1 - z2);
   }
 
-  void manually_probe_remaining_mesh(const float &lx, const float &ly, const float &z_clearance, const float &card_thickness, const bool do_ubl_mesh_map) {
+  void manually_probe_remaining_mesh(const float &lx, const float &ly, float &z_clearance, const float &card_thickness, const bool do_ubl_mesh_map) {
 
     ubl.has_control_of_lcd_panel = true;
     ubl.save_ubl_active_state_and_disable();   // we don't do bed level correction because we want the raw data when we probe
     do_blocking_move_to_z(z_clearance);
     do_blocking_move_to_xy(lx, ly);
 
+    lcd_goto_screen(lcd_status_screen);
     float last_x = -9999.99, last_y = -9999.99;
     mesh_index_pair location;
     do {
@@ -943,6 +953,7 @@
         do_blocking_move_to_z(current_position[Z_AXIS] + SIZE_OF_LITTLE_RAISE);
       else
         do_blocking_move_to_z(z_clearance);
+      LCD_MESSAGEPGM("Moving to next");
 
       do_blocking_move_to_xy(xProbe, yProbe);
 
@@ -953,6 +964,8 @@
       ubl.has_control_of_lcd_panel = true;
 
       if (do_ubl_mesh_map) ubl.display_map(map_type);  // show user where we're probing
+      if (code_seen('B')) {LCD_MESSAGEPGM("Place shim & measure");}
+      else {LCD_MESSAGEPGM("Measure");}
 
       while (ubl_lcd_clicked()) delay(50);             // wait for user to release encoder wheel
       delay(50);                                       // debounce
@@ -1024,6 +1037,7 @@
     repeat_flag = code_seen('R');
     if (repeat_flag) {
       repetition_cnt = code_has_value() ? code_value_int() : (GRID_MAX_POINTS_X) * (GRID_MAX_POINTS_Y);
+      repetition_cnt = min(repetition_cnt, (GRID_MAX_POINTS_X) * (GRID_MAX_POINTS_Y));
       if (repetition_cnt < 1) {
         SERIAL_PROTOCOLLNPGM("?(R)epetition count invalid (1+).\n");
         return UBL_ERR;
@@ -1056,7 +1070,6 @@
       SERIAL_PROTOCOLLNPGM("Both X & Y locations must be specified.\n");
       err_flag = true;
     }
-
     if (!WITHIN(RAW_X_POSITION(x_pos), X_MIN_POS, X_MAX_POS)) {
       SERIAL_PROTOCOLLNPGM("Invalid X location specified.\n");
       err_flag = true;
@@ -1422,8 +1435,7 @@
     do_blocking_move_to_xy(lx, ly);
     do {
       location = find_closest_mesh_point_of_type(SET_IN_BITMAP, lx, ly, USE_NOZZLE_AS_REFERENCE, not_done, false);
-                                                                  // It doesn't matter if the probe can't reach this
-                                                                  // location. This is a manual edit of the Mesh Point.
+
       if (location.x_index < 0 && location.y_index < 0) continue; // abort if we can't find any more points.
 
       bit_clear(not_done, location.x_index, location.y_index);  // Mark this location as 'adjusted' so we will find a
@@ -1612,7 +1624,7 @@
       SERIAL_ECHOPGM("Could not complete LSF!");
       return;
     }
-
+    
     if (g29_verbose_level > 3) {
       SERIAL_ECHOPGM("LSF Results A=");
       SERIAL_PROTOCOL_F(lsf_results.A, 7);
diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp
index 60771a5f51..29eb269bdc 100644
--- a/Marlin/ultralcd.cpp
+++ b/Marlin/ultralcd.cpp
@@ -1674,6 +1674,348 @@ void kill_screen(const char* lcd_msg) {
       END_MENU();
     }
 
+  #if ENABLED(AUTO_BED_LEVELING_UBL)
+
+    void _lcd_ubl_level_bed();
+
+    int UBL_STORAGE_SLOT = 0;
+    int CUSTOM_BED_TEMP = 50;
+    int CUSTOM_HOTEND_TEMP = 190;
+    int SIDE_POINTS = 3;
+    int UBL_FILLIN_AMOUNT = 5;
+    int UBL_HEIGHT_AMOUNT;
+    int map_type;
+
+    char UBL_LCD_GCODE [30];
+
+    /**
+     * UBL Build Custom Mesh Command
+     */
+    void _lcd_ubl_build_custom_mesh() {
+      enqueue_and_echo_command("G28");
+      #if (WATCH_THE_BED)
+      sprintf_P(UBL_LCD_GCODE, PSTR("M190 S%i"), CUSTOM_BED_TEMP);
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+      #endif
+      sprintf_P(UBL_LCD_GCODE, PSTR("M109 S%i"), CUSTOM_HOTEND_TEMP);
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+      enqueue_and_echo_command("G29 P1");
+    }
+
+    /**
+     * UBL Custom Mesh submenu
+     */
+    void _lcd_ubl_custom_mesh() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_BUILD_MESH_MENU);
+      MENU_ITEM_EDIT(int3, MSG_UBL_CUSTOM_HOTEND_TEMP, &CUSTOM_HOTEND_TEMP, EXTRUDE_MINTEMP, (HEATER_0_MAXTEMP - 10));
+      #if (WATCH_THE_BED)
+      MENU_ITEM_EDIT(int3, MSG_UBL_CUSTOM_BED_TEMP, &CUSTOM_BED_TEMP, BED_MINTEMP, (BED_MAXTEMP - 5));
+      #endif
+      MENU_ITEM(function, MSG_UBL_BUILD_CUSTOM_MESH, _lcd_ubl_build_custom_mesh);
+      END_MENU();
+    }
+
+    /**
+     * UBL Adjust Mesh Height Command
+     */
+    void _lcd_ubl_adjust_height_cmd() {
+      if (UBL_HEIGHT_AMOUNT < 0) {
+        // Convert to positive for the `sprintf_P` string.
+        UBL_HEIGHT_AMOUNT = (UBL_HEIGHT_AMOUNT - (UBL_HEIGHT_AMOUNT * 2)); // Convert to positive
+        sprintf_P(UBL_LCD_GCODE, PSTR("G29 N Z-.%i"), UBL_HEIGHT_AMOUNT);
+        // Convert back to negative to preserve the user setting.
+        UBL_HEIGHT_AMOUNT = (UBL_HEIGHT_AMOUNT - (UBL_HEIGHT_AMOUNT * 2)); // Convert back to negative
+      }
+      else {
+        sprintf_P(UBL_LCD_GCODE, PSTR("G29 N Z.%i"), UBL_HEIGHT_AMOUNT);
+      }
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+    }
+
+    /**
+     * UBL Adjust Mesh Height submenu
+     */
+    void _lcd_ubl_height_adjust_menu() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_EDIT_MESH_MENU);
+      MENU_ITEM_EDIT(int3, MSG_UBL_MESH_HEIGHT_AMOUNT, &UBL_HEIGHT_AMOUNT, -9, 9);
+      MENU_ITEM(function, MSG_UBL_MESH_HEIGHT_ADJUST, _lcd_ubl_adjust_height_cmd);
+      MENU_ITEM(submenu, MSG_WATCH, lcd_status_screen);
+      END_MENU();
+    }
+
+    /**
+     * UBL Edit Mesh submenu
+     */
+    void _lcd_ubl_edit_mesh() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_TOOLS);
+      MENU_BACK(MSG_UBL_LEVEL_BED);
+      MENU_ITEM(gcode, MSG_UBL_FINE_TUNE_ALL, PSTR("G29 P4 R O"));
+      MENU_ITEM(gcode, MSG_UBL_FINE_TUNE_CLOSEST, PSTR("G29 P4 O"));
+      MENU_ITEM(submenu, MSG_UBL_MESH_HEIGHT_ADJUST, _lcd_ubl_height_adjust_menu);
+      MENU_ITEM(submenu, MSG_WATCH, lcd_status_screen);
+      END_MENU();
+    }
+
+    /**
+     * UBL Validate Custom Mesh Command
+     */
+    void _lcd_ubl_validate_custom_mesh() {
+      enqueue_and_echo_command("G28");
+      #if (WATCH_THE_BED)
+        sprintf_P(UBL_LCD_GCODE, PSTR("G26 C B%i H%i P"), CUSTOM_BED_TEMP, CUSTOM_HOTEND_TEMP);
+      #else
+        sprintf_P(UBL_LCD_GCODE, PSTR("G26 C B0 H%i P"), CUSTOM_HOTEND_TEMP);
+      #endif
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+    }
+
+    /**
+     * UBL Validate Mesh submenu
+     */
+    void _lcd_ubl_validate_mesh() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_TOOLS);
+      #if (WATCH_THE_BED)
+        MENU_ITEM(gcode, MSG_UBL_VALIDATE_PLA_MESH, PSTR("G28\nG26 C B" STRINGIFY(PREHEAT_1_TEMP_BED)
+          " H" STRINGIFY(PREHEAT_1_TEMP_HOTEND) " P"));
+        MENU_ITEM(gcode, MSG_UBL_VALIDATE_ABS_MESH, PSTR("G28\nG26 C B" STRINGIFY(PREHEAT_2_TEMP_BED)
+          " H" STRINGIFY(PREHEAT_2_TEMP_HOTEND) " P"));
+      #else
+        MENU_ITEM(gcode, MSG_UBL_VALIDATE_PLA_MESH, PSTR("G28\nG26 C B0 H" STRINGIFY(PREHEAT_1_TEMP_HOTEND) " P"));
+        MENU_ITEM(gcode, MSG_UBL_VALIDATE_ABS_MESH, PSTR("G28\nG26 C B0 H" STRINGIFY(PREHEAT_2_TEMP_HOTEND) " P"));
+      #endif
+      MENU_ITEM(function, MSG_UBL_VALIDATE_CUSTOM_MESH, _lcd_ubl_validate_custom_mesh);
+      MENU_ITEM(submenu, MSG_WATCH, lcd_status_screen);
+      END_MENU();
+    }
+
+    /**
+     * UBL Grid Leveling Command
+     */
+    void _lcd_ubl_grid_level_cmd() {
+      sprintf_P(UBL_LCD_GCODE, PSTR("G29 J%i"), SIDE_POINTS);
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+    }
+
+    /**
+     * UBL Grid Leveling submenu
+     */
+    void _lcd_ubl_grid_level() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_TOOLS);
+      MENU_ITEM_EDIT(int3, MSG_UBL_SIDE_POINTS, &SIDE_POINTS, 2, 6);
+      MENU_ITEM(function, MSG_UBL_MESH_LEVEL, _lcd_ubl_grid_level_cmd);
+      END_MENU();
+    }
+
+    /**
+     * UBL Mesh Leveling submenu
+     */
+    void _lcd_ubl_mesh_leveling() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_TOOLS);
+      MENU_ITEM(gcode, MSG_UBL_3POINT_MESH_LEVELING, PSTR("G29 T"));
+      MENU_ITEM(submenu, MSG_UBL_GRID_MESH_LEVELING, _lcd_ubl_grid_level);
+      MENU_ITEM(submenu, MSG_WATCH, lcd_status_screen);
+      END_MENU();
+    }
+
+    /**
+     * UBL Fill-in Amount Mesh Command
+     */
+    void _lcd_ubl_fillin_amount_cmd() {
+      sprintf_P(UBL_LCD_GCODE, PSTR("G29 P3 R C.%i N"), UBL_FILLIN_AMOUNT);
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+    }
+
+    /**
+     * UBL Smart Fill-in Command
+     */
+    void _lcd_ubl_smart_fillin_cmd() {
+      sprintf_P(UBL_LCD_GCODE, PSTR("G29 P3 N O%i"), map_type);
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+    }
+
+    /**
+     * UBL Fill-in Mesh submenu
+     */
+    void _lcd_ubl_fillin_menu() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_BUILD_MESH_MENU);
+      MENU_ITEM_EDIT(int3, MSG_UBL_FILLIN_AMOUNT, &UBL_FILLIN_AMOUNT, 0, 9);
+      MENU_ITEM(function, MSG_UBL_FILLIN_MESH, _lcd_ubl_fillin_amount_cmd);
+      MENU_ITEM(function, MSG_UBL_SMART_FILLIN, _lcd_ubl_smart_fillin_cmd);
+      MENU_ITEM(gcode, MSG_UBL_MANUAL_FILLIN, PSTR("G29 P2 B O"));
+      MENU_ITEM(submenu, MSG_WATCH, lcd_status_screen);
+      END_MENU();
+    }
+
+    void _lcd_ubl_invalidate() {
+      ubl.invalidate();
+      SERIAL_PROTOCOLLNPGM("Mesh invalidated.");
+    }
+
+    /**
+     * UBL Build Mesh submenu
+     */
+    void _lcd_ubl_build_mesh() {
+      int GRID_NUM_POINTS = GRID_MAX_POINTS_X * GRID_MAX_POINTS_Y ;
+      START_MENU();
+      MENU_BACK(MSG_UBL_TOOLS);
+      #if (WATCH_THE_BED)
+        MENU_ITEM(gcode, MSG_UBL_BUILD_PLA_MESH, PSTR("G28\nM190 S" STRINGIFY(PREHEAT_1_TEMP_BED)
+          "\nM109 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND) "\nG29 P1\nM104 S0\nM140 S0"));
+        MENU_ITEM(gcode, MSG_UBL_BUILD_ABS_MESH, PSTR("G28\nM190 S" STRINGIFY(PREHEAT_1_TEMP_BED)
+          "\nM109 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND) "\nG29 P1\nM104 S0\nM140 S0"));
+      #else
+        MENU_ITEM(gcode, MSG_UBL_BUILD_PLA_MESH, PSTR("G28\nM109 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND)
+          "\nG29 P1\nM104 S0"));
+        MENU_ITEM(gcode, MSG_UBL_BUILD_ABS_MESH, PSTR("G28\nM109 S" STRINGIFY(PREHEAT_1_TEMP_HOTEND)
+          "\nG29 P1\nM104 S0"));
+      #endif
+      MENU_ITEM(submenu, MSG_UBL_BUILD_CUSTOM_MESH, _lcd_ubl_custom_mesh);
+      MENU_ITEM(gcode, MSG_UBL_BUILD_COLD_MESH, PSTR("G28\nG29 P1"));
+      MENU_ITEM(submenu, MSG_UBL_FILLIN_MESH, _lcd_ubl_fillin_menu);
+      MENU_ITEM(gcode, MSG_UBL_CONTINUE_MESH, PSTR("G29 P1 C"));
+      MENU_ITEM(function, MSG_UBL_INVALIDATE_ALL, _lcd_ubl_invalidate);
+      MENU_ITEM(gcode, MSG_UBL_INVALIDATE_CLOSEST, PSTR("G29 I"));
+      MENU_ITEM(submenu, MSG_WATCH, lcd_status_screen);
+      END_MENU();
+    }
+
+    /**
+     * UBL Load Mesh Command
+     */
+    void _lcd_ubl_load_mesh_cmd() {
+      sprintf_P(UBL_LCD_GCODE, PSTR("G29 N L%i"), UBL_STORAGE_SLOT);
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+    }
+
+    /**
+     * UBL Save Mesh Command
+     */
+    void _lcd_ubl_save_mesh_cmd() {
+      sprintf_P(UBL_LCD_GCODE, PSTR("G29 N S%i"), UBL_STORAGE_SLOT);
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+    }
+
+    /**
+     * UBL Mesh Storage submenu
+     */
+    void _lcd_ubl_storage_mesh() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_LEVEL_BED);
+      MENU_ITEM_EDIT(int3, MSG_UBL_STORAGE_SLOT, &UBL_STORAGE_SLOT, 0, 9);
+      MENU_ITEM(function, MSG_UBL_LOAD_MESH, _lcd_ubl_load_mesh_cmd);
+      MENU_ITEM(function, MSG_UBL_SAVE_MESH, _lcd_ubl_save_mesh_cmd);
+      END_MENU();
+    }
+
+    /**
+     * UBL Output map Command
+     */
+    void _lcd_ubl_output_map_cmd() {
+      sprintf_P(UBL_LCD_GCODE, PSTR("G29 N O%i"), map_type);
+      enqueue_and_echo_command(UBL_LCD_GCODE);
+    }
+
+    /**
+     * UBL Output map submenu
+     */
+    void _lcd_ubl_output_map() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_LEVEL_BED);
+      MENU_ITEM_EDIT(int3, MSG_UBL_MAP_TYPE, &map_type, 0, 1);
+      if (map_type == 0) MENU_ITEM(function, MSG_UBL_OUTPUT_MAP_HOST, _lcd_ubl_output_map_cmd);
+      if (map_type == 1) MENU_ITEM(function, MSG_UBL_OUTPUT_MAP_CSV, _lcd_ubl_output_map_cmd);
+      END_MENU();
+    }
+
+    /**
+     * UBL Tools submenu
+     */
+    void _lcd_ubl_tools_menu() {
+      START_MENU();
+      MENU_BACK(MSG_UBL_LEVEL_BED);
+      MENU_ITEM(submenu, MSG_UBL_BUILD_MESH_MENU, _lcd_ubl_build_mesh);
+      MENU_ITEM(submenu, MSG_UBL_VALIDATE_MESH_MENU, _lcd_ubl_validate_mesh);
+      MENU_ITEM(submenu, MSG_UBL_EDIT_MESH_MENU, _lcd_ubl_edit_mesh);
+      MENU_ITEM(submenu, MSG_UBL_MESH_LEVELING, _lcd_ubl_mesh_leveling);
+      END_MENU();
+    }
+
+    /**
+     * UBL System submenu
+     * 
+     *  Prepare
+     * - Unified Bed Leveling
+     *   - Activate UBL
+     *   - Deactivate UBL
+     *   - Mesh Storage
+     *       Memory Slot:
+     *       Load Bed Mesh
+     *       Save Bed Mesh
+     *   - Output Map
+     *       Map Type:
+     *       Output Bed Mesh Host / Output Bed Mesh CSV
+     *   - UBL Tools
+     *     - Build Mesh
+     *         Build PLA Mesh
+     *         Build ABS Mesh
+     *       - Build Custom Mesh
+     *           Hotend Temp:
+     *           Bed Temp:
+     *           Build Custom Mesh
+     *         Info Screen
+     *       - Build Cold Mesh
+     *       - Fill-in Mesh
+     *           Fill-in Mesh
+     *           Smart Fill-in
+     *           Manual Fill-in
+     *           Info Screen
+     *         Continue Bed Mesh
+     *         Invalidate All
+     *         Invalidate Closest
+     *     - Validate Mesh
+     *         PLA Mesh Validation
+     *         ABS Mesh Validation
+     *       - Custom Mesh Validation
+     *           Hotend Temp:
+     *           Bed Temp:
+     *           Validate Mesh
+     *         Info Screen
+     *     - Edit Mesh
+     *         Fine Tune All
+     *         Fine Tune Closest
+     *       - Adjust Mesh Height
+     *           Height Amount:
+     *           Adjust Mesh Height
+     *         Info Screen
+     *     - Mesh Leveling
+     *         3-Point Mesh Leveling
+     *       - Grid Mesh Leveling
+     *           Side points:
+     *           Level Mesh
+     *         Info Screen
+     *   - Output UBL Info
+     */
+
+    void _lcd_ubl_level_bed() {
+      START_MENU();
+      MENU_BACK(MSG_PREPARE);
+      MENU_ITEM(gcode, MSG_UBL_ACTIVATE_MESH, PSTR("G29 A N"));
+      MENU_ITEM(gcode, MSG_UBL_DEACTIVATE_MESH, PSTR("G29 D N"));
+      MENU_ITEM(submenu, MSG_UBL_STORAGE_MESH_MENU, _lcd_ubl_storage_mesh);
+      MENU_ITEM(submenu, MSG_UBL_OUTPUT_MAP, _lcd_ubl_output_map);
+      MENU_ITEM(submenu, MSG_UBL_TOOLS, _lcd_ubl_tools_menu);
+      MENU_ITEM(gcode, MSG_UBL_INFO_UBL, PSTR("G29 W N"));
+      END_MENU();
+    }
+  #endif
+
   #endif // LCD_BED_LEVELING || HAS_ABL
 
   /**
@@ -1716,7 +2058,11 @@ void kill_screen(const char* lcd_msg) {
       #if ENABLED(PROBE_MANUALLY)
         if (!g29_in_progress)
       #endif
-          MENU_ITEM(submenu, MSG_LEVEL_BED, lcd_level_bed);
+      #if ENABLED(AUTO_BED_LEVELING_UBL)
+        MENU_ITEM(submenu, MSG_UBL_LEVEL_BED, _lcd_ubl_level_bed);
+      #else
+        MENU_ITEM(submenu, MSG_LEVEL_BED, lcd_level_bed);
+      #endif
 
     #endif
 
@@ -2158,7 +2504,7 @@ void kill_screen(const char* lcd_msg) {
     #endif
 
     MENU_ITEM(function, MSG_RESTORE_FAILSAFE, lcd_factory_settings);
-    END_MENU();
+     END_MENU();
   }
 
   /**
-- 
GitLab