From 6093df11dc91273764d0bf5607fa4314391793a2 Mon Sep 17 00:00:00 2001
From: Scott Lahteine <thinkyhead@users.noreply.github.com>
Date: Sat, 10 Nov 2018 18:07:38 -0600
Subject: [PATCH] Allow G26 to use the active extruder (#12387)

* Make lcd_quick_feedback argument optional
* Add click_to_cancel option to wait_for_hotend/bed
* Have G26 use the active nozzle and wait_for_hotend/bed
* Use wait_for_release in UBL G29
* Add 'T' parameter to G26 for an initial tool-change
---
 Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp | 36 ++++----
 Marlin/src/gcode/bedlevel/G26.cpp           | 96 ++++++++++-----------
 Marlin/src/lcd/menu/menu.h                  |  1 -
 Marlin/src/lcd/ultralcd.cpp                 | 10 +--
 Marlin/src/lcd/ultralcd.h                   |  4 +-
 Marlin/src/module/temperature.cpp           | 28 +++++-
 Marlin/src/module/temperature.h             | 14 ++-
 7 files changed, 107 insertions(+), 82 deletions(-)

diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
index a261581e5d6..71c0097f786 100644
--- a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
+++ b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp
@@ -630,7 +630,7 @@
 
     #if HAS_LCD_MENU
       lcd_reset_alert_level();
-      lcd_quick_feedback(true);
+      lcd_quick_feedback();
       lcd_reset_status();
       lcd_external_control = false;
     #endif
@@ -689,16 +689,14 @@
 
     bool click_and_hold(const clickFunc_t func=NULL) {
       if (is_lcd_clicked()) {
-        lcd_quick_feedback(false); // Do NOT clear button status!  If cleared, the code
-                                   // code can not look for a 'click and hold'
+        lcd_quick_feedback(false);                // Preserve button state for click-and-hold
         const millis_t nxt = millis() + 1500UL;
         while (is_lcd_clicked()) {                // Loop while the encoder is pressed. Uses hardware flag!
           idle();                                 // idle, of course
           if (ELAPSED(millis(), nxt)) {           // After 1.5 seconds
-            lcd_quick_feedback(true);
+            lcd_quick_feedback();
             if (func) (*func)();
             wait_for_release();
-            safe_delay(50);                       // Debounce the Encoder wheel
             return true;
           }
         }
@@ -721,7 +719,7 @@
         lcd_external_control = true;
       #endif
 
-      save_ubl_active_state_and_disable();   // we don't do bed level correction because we want the raw data when we probe
+      save_ubl_active_state_and_disable();   // No bed level correction so only raw data is obtained
       DEPLOY_PROBE();
 
       uint16_t count = GRID_MAX_POINTS;
@@ -731,14 +729,13 @@
 
         #if HAS_LCD_MENU
           if (is_lcd_clicked()) {
+            lcd_quick_feedback(false); // Preserve button state for click-and-hold
             SERIAL_PROTOCOLLNPGM("\nMesh only partially populated.\n");
-            lcd_quick_feedback(false);
             STOW_PROBE();
-            while (is_lcd_clicked()) idle();
+            wait_for_release();
+            lcd_quick_feedback();
             lcd_external_control = false;
             restore_ubl_active_state_and_leave();
-            lcd_quick_feedback(true);
-            safe_delay(50);  // Debounce the Encoder wheel
             return;
           }
         #endif
@@ -843,7 +840,7 @@
       do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE);
       lcd_external_control = false;
       KEEPALIVE_STATE(IN_HANDLER);
-      lcd_quick_feedback(true);
+      lcd_quick_feedback();
       ubl.restore_ubl_active_state_and_leave();
     }
 
@@ -910,12 +907,16 @@
     }
   #endif // HAS_LCD_MENU
 
+  inline void set_message_with_feedback(PGM_P const msg_P) {
+    lcd_setstatusPGM(msg_P);
+    lcd_quick_feedback();
+  }
+
   bool unified_bed_leveling::g29_parameter_parsing() {
     bool err_flag = false;
 
     #if HAS_LCD_MENU
-      LCD_MESSAGEPGM(MSG_UBL_DOING_G29);
-      lcd_quick_feedback(true);
+      set_message_with_feedback(PSTR(MSG_UBL_DOING_G29));
     #endif
 
     g29_constant = 0;
@@ -1037,8 +1038,7 @@
       if (ubl_state_recursion_chk != 1) {
         SERIAL_ECHOLNPGM("save_ubl_active_state_and_disabled() called multiple times in a row.");
         #if HAS_LCD_MENU
-          LCD_MESSAGEPGM(MSG_UBL_SAVE_ERROR);
-          lcd_quick_feedback(true);
+          set_message_with_feedback(PSTR(MSG_UBL_SAVE_ERROR));
         #endif
         return;
       }
@@ -1052,8 +1052,7 @@
       if (--ubl_state_recursion_chk) {
         SERIAL_ECHOLNPGM("restore_ubl_active_state_and_leave() called too many times.");
         #if HAS_LCD_MENU
-          LCD_MESSAGEPGM(MSG_UBL_RESTORE_ERROR);
-          lcd_quick_feedback(true);
+          set_message_with_feedback(PSTR(MSG_UBL_RESTORE_ERROR));
         #endif
         return;
       }
@@ -1344,8 +1343,7 @@
     void abort_fine_tune() {
       lcd_return_to_status();
       do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
-      LCD_MESSAGEPGM(MSG_EDITING_STOPPED);
-      lcd_quick_feedback(true);
+      set_message_with_feedback(PSTR(MSG_EDITING_STOPPED));
     }
 
     void unified_bed_leveling::fine_tune_mesh(const float &rx, const float &ry, const bool do_ubl_mesh_map) {
diff --git a/Marlin/src/gcode/bedlevel/G26.cpp b/Marlin/src/gcode/bedlevel/G26.cpp
index 5bb75565542..6deeadb3261 100644
--- a/Marlin/src/gcode/bedlevel/G26.cpp
+++ b/Marlin/src/gcode/bedlevel/G26.cpp
@@ -38,6 +38,7 @@
 #include "../../module/planner.h"
 #include "../../module/stepper.h"
 #include "../../module/motion.h"
+#include "../../module/tool_change.h"
 #include "../../module/temperature.h"
 #include "../../lcd/ultralcd.h"
 
@@ -165,18 +166,12 @@ int8_t g26_prime_flag;
     if (!is_lcd_clicked()) return false; // Return if the button isn't pressed
     lcd_setstatusPGM(PSTR("Mesh Validation Stopped."), 99);
     #if HAS_LCD_MENU
-      lcd_quick_feedback(true);
+      lcd_quick_feedback();
     #endif
     wait_for_release();
     return true;
   }
 
-  bool exit_from_g26() {
-    lcd_setstatusPGM(PSTR("Leaving G26"), -1);
-    wait_for_release();
-    return G26_ERR;
-  }
-
 #endif
 
 mesh_index_pair find_closest_circle_to_print(const float &X, const float &Y) {
@@ -412,58 +407,50 @@ inline bool look_for_lines_to_connect() {
  * wait for them to get up to temperature.
  */
 inline bool turn_on_heaters() {
-  millis_t next = millis() + 5000UL;
+
+  SERIAL_ECHOLNPGM("Waiting for heatup.");
+
   #if HAS_HEATED_BED
-    #if ENABLED(ULTRA_LCD)
-      if (g26_bed_temp > 25) {
+
+    if (g26_bed_temp > 25) {
+      #if ENABLED(ULTRA_LCD)
         lcd_setstatusPGM(PSTR("G26 Heating Bed."), 99);
-        lcd_quick_feedback(true);
+        lcd_quick_feedback();
         #if HAS_LCD_MENU
           lcd_external_control = true;
         #endif
-    #endif
-        thermalManager.setTargetBed(g26_bed_temp);
-        while (ABS(thermalManager.degBed() - g26_bed_temp) > 3) {
+      #endif
+      thermalManager.setTargetBed(g26_bed_temp);
 
-          #if HAS_LCD_MENU
-            if (is_lcd_clicked()) return exit_from_g26();
+      // Wait for the temperature to stabilize
+      if (!thermalManager.wait_for_bed(true
+          #if G26_CLICK_CAN_CANCEL
+            , true
           #endif
-
-          if (ELAPSED(millis(), next)) {
-            next = millis() + 5000UL;
-            thermalManager.print_heaterstates();
-            SERIAL_EOL();
-          }
-          idle();
-          SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
-        }
-    #if ENABLED(ULTRA_LCD)
-      }
-      lcd_setstatusPGM(PSTR("G26 Heating Nozzle."), 99);
-      lcd_quick_feedback(true);
-    #endif
-  #endif
-
-  // Start heating the nozzle and wait for it to reach temperature.
-  thermalManager.setTargetHotend(g26_hotend_temp, 0);
-  while (ABS(thermalManager.degHotend(0) - g26_hotend_temp) > 3) {
-
-    #if HAS_LCD_MENU
-      if (is_lcd_clicked()) return exit_from_g26();
-    #endif
-
-    if (ELAPSED(millis(), next)) {
-      next = millis() + 5000UL;
-      thermalManager.print_heaterstates();
-      SERIAL_EOL();
+        )
+      ) return G26_ERR;
     }
-    idle();
-    SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
-  }
+
+  #endif // HAS_HEATED_BED
+
+  // Start heating the active nozzle
+  #if ENABLED(ULTRA_LCD)
+    lcd_setstatusPGM(PSTR("G26 Heating Nozzle."), 99);
+    lcd_quick_feedback();
+  #endif
+  thermalManager.setTargetHotend(g26_hotend_temp, active_extruder);
+
+  // Wait for the temperature to stabilize
+  if (!thermalManager.wait_for_hotend(active_extruder, true
+      #if G26_CLICK_CAN_CANCEL
+        , true
+      #endif
+    )
+  ) return G26_ERR;
 
   #if ENABLED(ULTRA_LCD)
     lcd_reset_status();
-    lcd_quick_feedback(true);
+    lcd_quick_feedback();
   #endif
 
   return G26_OK;
@@ -507,7 +494,7 @@ inline bool prime_nozzle() {
       wait_for_release();
 
       lcd_setstatusPGM(PSTR("Done Priming"), 99);
-      lcd_quick_feedback(true);
+      lcd_quick_feedback();
       lcd_external_control = false;
     }
     else
@@ -515,7 +502,7 @@ inline bool prime_nozzle() {
   {
     #if ENABLED(ULTRA_LCD)
       lcd_setstatusPGM(PSTR("Fixed Length Prime."), 99);
-      lcd_quick_feedback(true);
+      lcd_quick_feedback();
     #endif
     set_destination_from_current();
     destination[E_AXIS] += g26_prime_length;
@@ -553,17 +540,21 @@ float valid_trig_angle(float d) {
  *  Q  Retraction multiplier
  *  R  Repetitions (number of grid points)
  *  S  Nozzle Size (diameter) in mm
+ *  T  Tool index to change to, if included
  *  U  Random deviation (50 if no value given)
  *  X  X position
  *  Y  Y position
  */
 void GcodeSuite::G26() {
-  SERIAL_ECHOLNPGM("G26 command started. Waiting for heater(s).");
+  SERIAL_ECHOLNPGM("G26 starting...");
 
   // Don't allow Mesh Validation without homing first,
   // or if the parameter parsing did not go OK, abort
   if (axis_unhomed_error()) return;
 
+  // Change the tool first, if specified
+  if (parser.seenval('T')) tool_change(parser.value_int());
+
   g26_extrusion_multiplier    = EXTRUSION_MULTIPLIER;
   g26_retraction_multiplier   = RETRACTION_MULTIPLIER;
   g26_layer_height            = MESH_TEST_LAYER_HEIGHT;
@@ -891,6 +882,7 @@ void GcodeSuite::G26() {
 
   LEAVE:
   lcd_setstatusPGM(PSTR("Leaving G26"), -1);
+  wait_for_release();
 
   retract_filament(destination);
   destination[Z_AXIS] = Z_CLEARANCE_BETWEEN_PROBES;
@@ -914,7 +906,7 @@ void GcodeSuite::G26() {
     #if HAS_HEATED_BED
       thermalManager.setTargetBed(0);
     #endif
-    thermalManager.setTargetHotend(0, 0);
+    thermalManager.setTargetHotend(active_extruder, 0);
   }
 }
 
diff --git a/Marlin/src/lcd/menu/menu.h b/Marlin/src/lcd/menu/menu.h
index 8ff59b8b9c4..2dffc7e1c17 100644
--- a/Marlin/src/lcd/menu/menu.h
+++ b/Marlin/src/lcd/menu/menu.h
@@ -30,7 +30,6 @@ extern bool screen_changed;
 constexpr int16_t heater_maxtemp[HOTENDS] = ARRAY_BY_HOTENDS(HEATER_0_MAXTEMP, HEATER_1_MAXTEMP, HEATER_2_MAXTEMP, HEATER_3_MAXTEMP, HEATER_4_MAXTEMP);
 
 void scroll_screen(const uint8_t limit, const bool is_menu);
-bool use_click();
 bool printer_busy();
 void lcd_completion_feedback(const bool good=true);
 void lcd_save_previous_screen();
diff --git a/Marlin/src/lcd/ultralcd.cpp b/Marlin/src/lcd/ultralcd.cpp
index 7cb7eaec9a8..14b8e0d1a42 100644
--- a/Marlin/src/lcd/ultralcd.cpp
+++ b/Marlin/src/lcd/ultralcd.cpp
@@ -244,8 +244,8 @@ bool lcd_blink() {
           #if HAS_LCD_MENU
             if      (RRK(EN_REPRAPWORLD_KEYPAD_DOWN))   encoderPosition -= ENCODER_STEPS_PER_MENU_ITEM;
             else if (RRK(EN_REPRAPWORLD_KEYPAD_UP))     encoderPosition += ENCODER_STEPS_PER_MENU_ITEM;
-            else if (RRK(EN_REPRAPWORLD_KEYPAD_LEFT))   { menu_item_back::action(); lcd_quick_feedback(true); }
-            else if (RRK(EN_REPRAPWORLD_KEYPAD_RIGHT))  { lcd_return_to_status(); lcd_quick_feedback(true); }
+            else if (RRK(EN_REPRAPWORLD_KEYPAD_LEFT))   { menu_item_back::action(); lcd_quick_feedback(); }
+            else if (RRK(EN_REPRAPWORLD_KEYPAD_RIGHT))  { lcd_return_to_status(); lcd_quick_feedback(); }
           #endif
         }
         else if (RRK(EN_REPRAPWORLD_KEYPAD_DOWN))     encoderPosition += ENCODER_PULSES_PER_STEP;
@@ -484,7 +484,7 @@ void kill_screen(PGM_P lcd_msg) {
   }
 #endif
 
-void lcd_quick_feedback(const bool clear_buttons) {
+void lcd_quick_feedback(const bool clear_buttons/*=true*/) {
 
   #if HAS_LCD_MENU
     lcd_refresh();
@@ -661,14 +661,14 @@ void lcd_update() {
         wait_for_unclick = true;         //  Set debounce flag to ignore continous clicks
         lcd_clicked = !wait_for_user && !no_reentry; //  Keep the click if not waiting for a user-click
         wait_for_user = false;           //  Any click clears wait for user
-        lcd_quick_feedback(true);        //  Always make a click sound
+        lcd_quick_feedback();        //  Always make a click sound
       }
     }
     else wait_for_unclick = false;
 
     #if BUTTON_EXISTS(BACK)
       if (LCD_BACK_CLICKED) {
-        lcd_quick_feedback(true);
+        lcd_quick_feedback();
         lcd_goto_previous_menu();
       }
     #endif
diff --git a/Marlin/src/lcd/ultralcd.h b/Marlin/src/lcd/ultralcd.h
index a8b38269074..42f8e82bf7a 100644
--- a/Marlin/src/lcd/ultralcd.h
+++ b/Marlin/src/lcd/ultralcd.h
@@ -242,7 +242,7 @@
     inline void lcd_buzz(const long duration, const uint16_t freq) { UNUSED(duration); UNUSED(freq); }
   #endif
 
-  void lcd_quick_feedback(const bool clear_buttons); // Audible feedback for a button click - could also be visual
+  void lcd_quick_feedback(const bool clear_buttons=true); // Audible feedback for a button click - could also be visual
 
   #if ENABLED(LCD_PROGRESS_BAR)
     extern millis_t progress_bar_ms;  // Start time for the current progress bar cycle
@@ -351,6 +351,8 @@
 
   bool lcd_blink();
 
+  bool use_click();
+
   #if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(G26_MESH_VALIDATION)
     bool is_lcd_clicked();
     void wait_for_release();
diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp
index 892379c5364..4750b7dcd63 100644
--- a/Marlin/src/module/temperature.cpp
+++ b/Marlin/src/module/temperature.cpp
@@ -2441,7 +2441,11 @@ void Temperature::isr() {
       #define MIN_COOLING_SLOPE_TIME 60
     #endif
 
-    bool Temperature::wait_for_hotend(const uint8_t target_extruder, const bool no_wait_for_cooling/*=true*/) {
+    bool Temperature::wait_for_hotend(const uint8_t target_extruder, const bool no_wait_for_cooling/*=true*/
+      #if G26_CLICK_CAN_CANCEL
+        , const bool click_to_cancel/*=false*/
+      #endif
+    ) {
       #if TEMP_RESIDENCY_TIME > 0
         millis_t residency_start_ms = 0;
         // Loop until the temperature has stabilized
@@ -2525,6 +2529,13 @@ void Temperature::isr() {
           }
         }
 
+        #if G26_CLICK_CAN_CANCEL
+          if (click_to_cancel && use_click()) {
+            wait_for_heatup = false;
+            lcd_quick_feedback();
+          }
+        #endif
+
       } while (wait_for_heatup && TEMP_CONDITIONS);
 
       if (wait_for_heatup) {
@@ -2552,7 +2563,11 @@ void Temperature::isr() {
       #define MIN_COOLING_SLOPE_TIME_BED 60
     #endif
 
-    void Temperature::wait_for_bed(const bool no_wait_for_cooling) {
+    bool Temperature::wait_for_bed(const bool no_wait_for_cooling
+      #if G26_CLICK_CAN_CANCEL
+        , const bool click_to_cancel/*=false*/
+      #endif
+    ) {
       #if TEMP_BED_RESIDENCY_TIME > 0
         millis_t residency_start_ms = 0;
         // Loop until the temperature has stabilized
@@ -2639,6 +2654,13 @@ void Temperature::isr() {
           }
         }
 
+        #if G26_CLICK_CAN_CANCEL
+          if (click_to_cancel && use_click()) {
+            wait_for_heatup = false;
+            lcd_quick_feedback();
+          }
+        #endif
+
       } while (wait_for_heatup && TEMP_BED_CONDITIONS);
 
       if (wait_for_heatup) lcd_reset_status();
@@ -2646,6 +2668,8 @@ void Temperature::isr() {
       #if DISABLED(BUSY_WHILE_HEATING) && ENABLED(HOST_KEEPALIVE_FEATURE)
         gcode.busy_state = old_busy_state;
       #endif
+
+      return wait_for_heatup;
     }
 
   #endif // HAS_HEATED_BED
diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h
index 7c56105552e..c7031fd4657 100644
--- a/Marlin/src/module/temperature.h
+++ b/Marlin/src/module/temperature.h
@@ -138,6 +138,8 @@ enum ADCSensorState : char {
   #define unscalePID_d(d) ( float(d) * PID_dT )
 #endif
 
+#define G26_CLICK_CAN_CANCEL (HAS_LCD_MENU && ENABLED(G26_MESH_VALIDATION))
+
 class Temperature {
 
   public:
@@ -426,7 +428,11 @@ class Temperature {
     }
 
     #if HAS_TEMP_HOTEND
-      static bool wait_for_hotend(const uint8_t target_extruder, const bool no_wait_for_cooling=true);
+      static bool wait_for_hotend(const uint8_t target_extruder, const bool no_wait_for_cooling=true
+        #if G26_CLICK_CAN_CANCEL
+          , const bool click_to_cancel=false
+        #endif
+      );
     #endif
 
     #if HAS_HEATED_BED
@@ -459,7 +465,11 @@ class Temperature {
         static void start_watching_bed();
       #endif
 
-      static void wait_for_bed(const bool no_wait_for_cooling);
+      static bool wait_for_bed(const bool no_wait_for_cooling
+        #if G26_CLICK_CAN_CANCEL
+          , const bool click_to_cancel=false
+        #endif
+      );
 
     #endif // HAS_HEATED_BED