Browse Source

Group UBL parameters, add comments

Scott Lahteine 4 years ago
parent
commit
da4b6896f7

+ 19
- 16
Marlin/src/feature/bedlevel/ubl/ubl.h View File

@@ -45,23 +45,26 @@ struct mesh_index_pair;
45 45
   typedef int16_t mesh_store_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
46 46
 #endif
47 47
 
48
+typedef struct {
49
+  bool      C_seen;
50
+  int8_t    V_verbosity,
51
+            P_phase,
52
+            R_repetition,
53
+            KLS_storage_slot,
54
+            T_map_type;
55
+  float     B_shim_thickness,
56
+            C_constant;
57
+  xy_pos_t  XY_pos;
58
+  xy_bool_t XY_seen;
59
+  #if HAS_BED_PROBE
60
+    int     grid_size;
61
+  #endif
62
+} G29_parameters_t;
63
+
48 64
 class unified_bed_leveling {
49 65
 private:
50 66
 
51
-  static int    g29_verbose_level,
52
-                g29_phase_value,
53
-                g29_repetition_cnt,
54
-                g29_storage_slot,
55
-                g29_map_type;
56
-  static bool   g29_c_flag;
57
-  static float  g29_card_thickness,
58
-                g29_constant;
59
-  static xy_pos_t g29_pos;
60
-  static xy_bool_t xy_seen;
61
-
62
-  #if HAS_BED_PROBE
63
-    static int  g29_grid_size;
64
-  #endif
67
+  static G29_parameters_t param;
65 68
 
66 69
   #if IS_NEWPANEL
67 70
     static void move_z_with_encoder(const float &multiplier);
@@ -71,7 +74,7 @@ private:
71 74
     static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) _O0;
72 75
   #endif
73 76
 
74
-  static bool g29_parameter_parsing() _O0;
77
+  static bool G29_parse_parameters() _O0;
75 78
   static void shift_mesh_height();
76 79
   static void probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) _O0;
77 80
   static void tilt_mesh_based_on_3pts(const float &z1, const float &z2, const float &z3);
@@ -129,7 +132,7 @@ public:
129 132
     static inline void steppers_were_disabled() {}
130 133
   #endif
131 134
 
132
-  static volatile int16_t encoder_diff; // Volatile because buttons may changed it at interrupt time
135
+  static volatile int16_t encoder_diff; // Volatile because buttons may change it at interrupt time
133 136
 
134 137
   unified_bed_leveling();
135 138
 

+ 172
- 163
Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp View File

@@ -71,21 +71,6 @@
71 71
 #define SIZE_OF_LITTLE_RAISE 1
72 72
 #define BIG_RAISE_NOT_NEEDED 0
73 73
 
74
-int    unified_bed_leveling::g29_verbose_level,
75
-       unified_bed_leveling::g29_phase_value,
76
-       unified_bed_leveling::g29_repetition_cnt,
77
-       unified_bed_leveling::g29_storage_slot = 0,
78
-       unified_bed_leveling::g29_map_type;
79
-bool   unified_bed_leveling::g29_c_flag;
80
-float  unified_bed_leveling::g29_card_thickness = 0,
81
-       unified_bed_leveling::g29_constant = 0;
82
-xy_bool_t unified_bed_leveling::xy_seen;
83
-xy_pos_t unified_bed_leveling::g29_pos;
84
-
85
-#if HAS_BED_PROBE
86
-  int  unified_bed_leveling::g29_grid_size;
87
-#endif
88
-
89 74
 /**
90 75
  *   G29: Unified Bed Leveling by Roxy
91 76
  *
@@ -309,10 +294,12 @@ xy_pos_t unified_bed_leveling::g29_pos;
309 294
  *   features of all three systems combined.
310 295
  */
311 296
 
297
+G29_parameters_t unified_bed_leveling::param;
298
+
312 299
 void unified_bed_leveling::G29() {
313 300
 
314 301
   bool probe_deployed = false;
315
-  if (g29_parameter_parsing()) return; // Abort on parameter error
302
+  if (G29_parse_parameters()) return; // Abort on parameter error
316 303
 
317 304
   const int8_t p_val = parser.intval('P', -1);
318 305
   const bool may_move = p_val == 1 || p_val == 2 || p_val == 4 || parser.seen('J');
@@ -326,33 +313,29 @@ void unified_bed_leveling::G29() {
326 313
     TERN_(HAS_MULTI_HOTEND, if (active_extruder) tool_change(0));
327 314
   }
328 315
 
329
-  // Invalidate Mesh Points. This command is a little bit asymmetrical because
330
-  // it directly specifies the repetition count and does not use the 'R' parameter.
316
+  // Invalidate one or more nearby mesh points, possibly all.
331 317
   if (parser.seen('I')) {
332
-    uint8_t cnt = 0;
333
-    g29_repetition_cnt = parser.has_value() ? parser.value_int() : 1;
334
-    if (g29_repetition_cnt >= GRID_MAX_POINTS) {
335
-      set_all_mesh_points_to_value(NAN);
336
-    }
337
-    else {
338
-      while (g29_repetition_cnt--) {
339
-        if (cnt > 20) { cnt = 0; idle(); }
340
-        const mesh_index_pair closest = find_closest_mesh_point_of_type(REAL, g29_pos);
341
-        const xy_int8_t &cpos = closest.pos;
342
-        if (cpos.x < 0) {
343
-          // No more REAL mesh points to invalidate, so we ASSUME the user
344
-          // meant to invalidate the ENTIRE mesh, which cannot be done with
345
-          // find_closest_mesh_point loop which only returns REAL points.
346
-          set_all_mesh_points_to_value(NAN);
347
-          SERIAL_ECHOLNPGM("Entire Mesh invalidated.\n");
348
-          break;            // No more invalid Mesh Points to populate
349
-        }
350
-        z_values[cpos.x][cpos.y] = NAN;
351
-        TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(cpos, 0.0f));
352
-        cnt++;
318
+    int16_t count = parser.has_value() ? parser.value_int() : 1;
319
+    bool invalidate_all = count >= GRID_MAX_POINTS;
320
+    if (!invalidate_all) {
321
+      while (count--) {
322
+        if ((count & 0x0F) == 0x0F) idle();
323
+        const mesh_index_pair closest = find_closest_mesh_point_of_type(REAL, param.XY_pos);
324
+        // No more REAL mesh points to invalidate? Assume the user meant
325
+        // to invalidate the ENTIRE mesh, which can't be done with
326
+        // find_closest_mesh_point (which only returns REAL points).
327
+        if (closest.pos.x < 0) { invalidate_all = true; break; }
328
+        z_values[closest.pos.x][closest.pos.y] = NAN;
329
+        TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(closest.pos, 0.0f));
353 330
       }
354 331
     }
355
-    SERIAL_ECHOLNPGM("Locations invalidated.\n");
332
+    if (invalidate_all) {
333
+      invalidate();
334
+      SERIAL_ECHOPGM("Entire Mesh");
335
+    }
336
+    else
337
+      SERIAL_ECHOPGM("Locations");
338
+    SERIAL_ECHOLNPGM(" invalidated.\n");
356 339
   }
357 340
 
358 341
   if (parser.seen('Q')) {
@@ -364,11 +347,7 @@ void unified_bed_leveling::G29() {
364 347
     SERIAL_ECHOLNPGM("Loading test_pattern values.\n");
365 348
     switch (test_pattern) {
366 349
 
367
-      #if ENABLED(UBL_DEVEL_DEBUGGING)
368
-        case -1:
369
-          g29_eeprom_dump();
370
-          break;
371
-      #endif
350
+      case -1: TERN_(UBL_DEVEL_DEBUGGING, g29_eeprom_dump()); break;
372 351
 
373 352
       case 0:
374 353
         GRID_LOOP(x, y) {                                     // Create a bowl shape similar to a poorly-calibrated Delta
@@ -395,7 +374,7 @@ void unified_bed_leveling::G29() {
395 374
         // Allow the user to specify the height because 10mm is a little extreme in some cases.
396 375
         for (uint8_t x = (GRID_MAX_POINTS_X) / 3; x < 2 * (GRID_MAX_POINTS_X) / 3; x++)     // Create a rectangular raised area in
397 376
           for (uint8_t y = (GRID_MAX_POINTS_Y) / 3; y < 2 * (GRID_MAX_POINTS_Y) / 3; y++) { // the center of the bed
398
-            z_values[x][y] += parser.seen('C') ? g29_constant : 9.99f;
377
+            z_values[x][y] += parser.seen('C') ? param.C_constant : 9.99f;
399 378
             TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y]));
400 379
           }
401 380
         break;
@@ -406,7 +385,7 @@ void unified_bed_leveling::G29() {
406 385
 
407 386
     if (parser.seen('J')) {
408 387
       save_ubl_active_state_and_disable();
409
-      tilt_mesh_based_on_probed_grid(g29_grid_size == 0); // Zero size does 3-Point
388
+      tilt_mesh_based_on_probed_grid(param.grid_size == 0); // Zero size does 3-Point
410 389
       restore_ubl_active_state_and_leave();
411 390
       #if ENABLED(UBL_G29_J_RECENTER)
412 391
         do_blocking_move_to_xy(0.5f * ((MESH_MIN_X) + (MESH_MAX_X)), 0.5f * ((MESH_MIN_Y) + (MESH_MAX_Y)));
@@ -418,12 +397,12 @@ void unified_bed_leveling::G29() {
418 397
   #endif // HAS_BED_PROBE
419 398
 
420 399
   if (parser.seen('P')) {
421
-    if (WITHIN(g29_phase_value, 0, 1) && storage_slot == -1) {
400
+    if (WITHIN(param.P_phase, 0, 1) && storage_slot == -1) {
422 401
       storage_slot = 0;
423 402
       SERIAL_ECHOLNPGM("Default storage slot 0 selected.");
424 403
     }
425 404
 
426
-    switch (g29_phase_value) {
405
+    switch (param.P_phase) {
427 406
       case 0:
428 407
         //
429 408
         // Zero Mesh Data
@@ -442,13 +421,13 @@ void unified_bed_leveling::G29() {
442 421
             invalidate();
443 422
             SERIAL_ECHOLNPGM("Mesh invalidated. Probing mesh.");
444 423
           }
445
-          if (g29_verbose_level > 1) {
446
-            SERIAL_ECHOPAIR("Probing around (", g29_pos.x);
424
+          if (param.V_verbosity > 1) {
425
+            SERIAL_ECHOPAIR("Probing around (", param.XY_pos.x);
447 426
             SERIAL_CHAR(',');
448
-            SERIAL_DECIMAL(g29_pos.y);
427
+            SERIAL_DECIMAL(param.XY_pos.y);
449 428
             SERIAL_ECHOLNPGM(").\n");
450 429
           }
451
-          const xy_pos_t near_probe_xy = g29_pos + probe.offset_xy;
430
+          const xy_pos_t near_probe_xy = param.XY_pos + probe.offset_xy;
452 431
           probe_entire_mesh(near_probe_xy, parser.seen('T'), parser.seen('E'), parser.seen('U'));
453 432
 
454 433
           report_current_position();
@@ -465,7 +444,7 @@ void unified_bed_leveling::G29() {
465 444
           SERIAL_ECHOLNPGM("Manually probing unreachable points.");
466 445
           do_z_clearance(Z_CLEARANCE_BETWEEN_PROBES);
467 446
 
468
-          if (parser.seen('C') && !xy_seen) {
447
+          if (parser.seen('C') && !param.XY_seen) {
469 448
 
470 449
             /**
471 450
              * Use a good default location for the path.
@@ -474,7 +453,7 @@ void unified_bed_leveling::G29() {
474 453
              * It may make sense to have Delta printers default to the center of the bed.
475 454
              * Until that is decided, this can be forced with the X and Y parameters.
476 455
              */
477
-            g29_pos.set(
456
+            param.XY_pos.set(
478 457
               #if IS_KINEMATIC
479 458
                 X_HOME_POS, Y_HOME_POS
480 459
               #else
@@ -485,21 +464,21 @@ void unified_bed_leveling::G29() {
485 464
           }
486 465
 
487 466
           if (parser.seen('B')) {
488
-            g29_card_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness();
489
-            if (ABS(g29_card_thickness) > 1.5f) {
467
+            param.B_shim_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness();
468
+            if (ABS(param.B_shim_thickness) > 1.5f) {
490 469
               SERIAL_ECHOLNPGM("?Error in Business Card measurement.");
491 470
               return;
492 471
             }
493 472
             probe_deployed = true;
494 473
           }
495 474
 
496
-          if (!position_is_reachable(g29_pos)) {
475
+          if (!position_is_reachable(param.XY_pos)) {
497 476
             SERIAL_ECHOLNPGM("XY outside printable radius.");
498 477
             return;
499 478
           }
500 479
 
501 480
           const float height = parser.floatval('H', Z_CLEARANCE_BETWEEN_PROBES);
502
-          manually_probe_remaining_mesh(g29_pos, height, g29_card_thickness, parser.seen('T'));
481
+          manually_probe_remaining_mesh(param.XY_pos, height, param.B_shim_thickness, parser.seen('T'));
503 482
 
504 483
           SERIAL_ECHOLNPGM("G29 P2 finished.");
505 484
 
@@ -521,23 +500,23 @@ void unified_bed_leveling::G29() {
521 500
          *   - Allow 'G29 P3' to choose a 'reasonable' constant.
522 501
          */
523 502
 
524
-        if (g29_c_flag) {
525
-          if (g29_repetition_cnt >= GRID_MAX_POINTS) {
526
-            set_all_mesh_points_to_value(g29_constant);
503
+        if (param.C_seen) {
504
+          if (param.R_repetition >= GRID_MAX_POINTS) {
505
+            set_all_mesh_points_to_value(param.C_constant);
527 506
           }
528 507
           else {
529
-            while (g29_repetition_cnt--) {  // this only populates reachable mesh points near
530
-              const mesh_index_pair closest = find_closest_mesh_point_of_type(INVALID, g29_pos);
508
+            while (param.R_repetition--) {  // this only populates reachable mesh points near
509
+              const mesh_index_pair closest = find_closest_mesh_point_of_type(INVALID, param.XY_pos);
531 510
               const xy_int8_t &cpos = closest.pos;
532 511
               if (cpos.x < 0) {
533 512
                 // No more REAL INVALID mesh points to populate, so we ASSUME
534 513
                 // user meant to populate ALL INVALID mesh points to value
535
-                GRID_LOOP(x, y) if (isnan(z_values[x][y])) z_values[x][y] = g29_constant;
514
+                GRID_LOOP(x, y) if (isnan(z_values[x][y])) z_values[x][y] = param.C_constant;
536 515
                 break; // No more invalid Mesh Points to populate
537 516
               }
538 517
               else {
539
-                z_values[cpos.x][cpos.y] = g29_constant;
540
-                TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(cpos, g29_constant));
518
+                z_values[cpos.x][cpos.y] = param.C_constant;
519
+                TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(cpos, param.C_constant));
541 520
               }
542 521
             }
543 522
           }
@@ -571,14 +550,14 @@ void unified_bed_leveling::G29() {
571 550
 
572 551
       case 4: // Fine Tune (i.e., Edit) the Mesh
573 552
         #if HAS_LCD_MENU
574
-          fine_tune_mesh(g29_pos, parser.seen('T'));
553
+          fine_tune_mesh(param.XY_pos, parser.seen('T'));
575 554
         #else
576 555
           SERIAL_ECHOLNPGM("?P4 is only available when an LCD is present.");
577 556
           return;
578 557
         #endif
579 558
         break;
580 559
 
581
-      case 5: adjust_mesh_to_mean(g29_c_flag, g29_constant); break;
560
+      case 5: adjust_mesh_to_mean(param.C_seen, param.C_constant); break;
582 561
 
583 562
       case 6: shift_mesh_height(); break;
584 563
     }
@@ -608,7 +587,7 @@ void unified_bed_leveling::G29() {
608 587
   //
609 588
 
610 589
   if (parser.seen('L')) {     // Load Current Mesh Data
611
-    g29_storage_slot = parser.has_value() ? parser.value_int() : storage_slot;
590
+    param.KLS_storage_slot = parser.has_value() ? parser.value_int() : storage_slot;
612 591
 
613 592
     int16_t a = settings.calc_num_meshes();
614 593
 
@@ -617,13 +596,13 @@ void unified_bed_leveling::G29() {
617 596
       return;
618 597
     }
619 598
 
620
-    if (!WITHIN(g29_storage_slot, 0, a - 1)) {
599
+    if (!WITHIN(param.KLS_storage_slot, 0, a - 1)) {
621 600
       SERIAL_ECHOLNPAIR("?Invalid storage slot.\n?Use 0 to ", a - 1);
622 601
       return;
623 602
     }
624 603
 
625
-    settings.load_mesh(g29_storage_slot);
626
-    storage_slot = g29_storage_slot;
604
+    settings.load_mesh(param.KLS_storage_slot);
605
+    storage_slot = param.KLS_storage_slot;
627 606
 
628 607
     SERIAL_ECHOLNPGM("Done.");
629 608
   }
@@ -633,9 +612,9 @@ void unified_bed_leveling::G29() {
633 612
   //
634 613
 
635 614
   if (parser.seen('S')) {     // Store (or Save) Current Mesh Data
636
-    g29_storage_slot = parser.has_value() ? parser.value_int() : storage_slot;
615
+    param.KLS_storage_slot = parser.has_value() ? parser.value_int() : storage_slot;
637 616
 
638
-    if (g29_storage_slot == -1)                     // Special case, the user wants to 'Export' the mesh to the
617
+    if (param.KLS_storage_slot == -1)                     // Special case, the user wants to 'Export' the mesh to the
639 618
       return report_current_mesh();                 // host program to be saved on the user's computer
640 619
 
641 620
     int16_t a = settings.calc_num_meshes();
@@ -645,19 +624,19 @@ void unified_bed_leveling::G29() {
645 624
       goto LEAVE;
646 625
     }
647 626
 
648
-    if (!WITHIN(g29_storage_slot, 0, a - 1)) {
627
+    if (!WITHIN(param.KLS_storage_slot, 0, a - 1)) {
649 628
       SERIAL_ECHOLNPAIR("?Invalid storage slot.\n?Use 0 to ", a - 1);
650 629
       goto LEAVE;
651 630
     }
652 631
 
653
-    settings.store_mesh(g29_storage_slot);
654
-    storage_slot = g29_storage_slot;
632
+    settings.store_mesh(param.KLS_storage_slot);
633
+    storage_slot = param.KLS_storage_slot;
655 634
 
656 635
     SERIAL_ECHOLNPGM("Done.");
657 636
   }
658 637
 
659 638
   if (parser.seen('T'))
660
-    display_map(g29_map_type);
639
+    display_map(param.T_map_type);
661 640
 
662 641
   LEAVE:
663 642
 
@@ -682,7 +661,12 @@ void unified_bed_leveling::G29() {
682 661
   return;
683 662
 }
684 663
 
685
-void unified_bed_leveling::adjust_mesh_to_mean(const bool cflag, const float value) {
664
+/**
665
+ * M420 C<value>
666
+ * G29 P5 C<value> : Adjust Mesh To Mean (and subtract the given offset).
667
+ *                   Find the mean average and shift the mesh to center on that value.
668
+ */
669
+void unified_bed_leveling::adjust_mesh_to_mean(const bool cflag, const float offset) {
686 670
   float sum = 0;
687 671
   int n = 0;
688 672
   GRID_LOOP(x, y)
@@ -710,23 +694,27 @@ void unified_bed_leveling::adjust_mesh_to_mean(const bool cflag, const float val
710 694
   if (cflag)
711 695
     GRID_LOOP(x, y)
712 696
       if (!isnan(z_values[x][y])) {
713
-        z_values[x][y] -= mean + value;
697
+        z_values[x][y] -= mean + offset;
714 698
         TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y]));
715 699
       }
716 700
 }
717 701
 
702
+/**
703
+ * G29 P6 C<offset> : Shift Mesh Height by a uniform constant.
704
+ */
718 705
 void unified_bed_leveling::shift_mesh_height() {
719 706
   GRID_LOOP(x, y)
720 707
     if (!isnan(z_values[x][y])) {
721
-      z_values[x][y] += g29_constant;
708
+      z_values[x][y] += param.C_constant;
722 709
       TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y]));
723 710
     }
724 711
 }
725 712
 
726 713
 #if HAS_BED_PROBE
727 714
   /**
728
-   * Probe all invalidated locations of the mesh that can be reached by the probe.
729
-   * This attempts to fill in locations closest to the nozzle's start location first.
715
+   * G29 P1 T<maptype> V<verbosity> : Probe Entire Mesh
716
+   *   Probe all invalidated locations of the mesh that can be reached by the probe.
717
+   *   This attempts to fill in locations closest to the nozzle's start location first.
730 718
    */
731 719
   void unified_bed_leveling::probe_entire_mesh(const xy_pos_t &nearby, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) {
732 720
     probe.deploy(); // Deploy before ui.capture() to allow for PAUSE_BEFORE_DEPLOY_STOW
@@ -739,7 +727,7 @@ void unified_bed_leveling::shift_mesh_height() {
739 727
     mesh_index_pair best;
740 728
     TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::MESH_START));
741 729
     do {
742
-      if (do_ubl_mesh_map) display_map(g29_map_type);
730
+      if (do_ubl_mesh_map) display_map(param.T_map_type);
743 731
 
744 732
       const int point_num = (GRID_MAX_POINTS) - count + 1;
745 733
       SERIAL_ECHOLNPAIR("Probing mesh point ", point_num, "/", GRID_MAX_POINTS, ".");
@@ -767,7 +755,7 @@ void unified_bed_leveling::shift_mesh_height() {
767 755
         TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::PROBE_START));
768 756
         const float measured_z = probe.probe_at_point(
769 757
                       best.meshpos(),
770
-                      stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level
758
+                      stow_probe ? PROBE_PT_STOW : PROBE_PT_RAISE, param.V_verbosity
771 759
                     );
772 760
         z_values[best.pos.x][best.pos.y] = measured_z;
773 761
         #if ENABLED(EXTENSIBLE_UI)
@@ -798,11 +786,20 @@ void unified_bed_leveling::shift_mesh_height() {
798 786
 
799 787
 #endif // HAS_BED_PROBE
800 788
 
789
+void set_message_with_feedback(PGM_P const msg_P) {
790
+  #if HAS_LCD_MENU
791
+    ui.set_status_P(msg_P);
792
+    ui.quick_feedback();
793
+  #else
794
+    UNUSED(msg_P);
795
+  #endif
796
+}
797
+
801 798
 #if HAS_LCD_MENU
802 799
 
803 800
   typedef void (*clickFunc_t)();
804 801
 
805
-  bool click_and_hold(const clickFunc_t func=nullptr) {
802
+  bool _click_and_hold(const clickFunc_t func=nullptr) {
806 803
     if (ui.button_pressed()) {
807 804
       ui.quick_feedback(false);         // Preserve button state for click-and-hold
808 805
       const millis_t nxt = millis() + 1500UL;
@@ -834,7 +831,8 @@ void unified_bed_leveling::shift_mesh_height() {
834 831
 
835 832
   float unified_bed_leveling::measure_point_with_encoder() {
836 833
     KEEPALIVE_STATE(PAUSED_FOR_USER);
837
-    move_z_with_encoder(0.01f);
834
+    const float z_step = 0.01f;
835
+    move_z_with_encoder(z_step);
838 836
     return current_position.z;
839 837
   }
840 838
 
@@ -866,7 +864,7 @@ void unified_bed_leveling::shift_mesh_height() {
866 864
 
867 865
     const float thickness = ABS(z1 - z2);
868 866
 
869
-    if (g29_verbose_level > 1) {
867
+    if (param.V_verbosity > 1) {
870 868
       SERIAL_ECHOPAIR_F("Business Card is ", thickness, 4);
871 869
       SERIAL_ECHOLNPGM("mm thick.");
872 870
     }
@@ -876,6 +874,11 @@ void unified_bed_leveling::shift_mesh_height() {
876 874
     return thickness;
877 875
   }
878 876
 
877
+  /**
878
+   * G29 P2 : Manually Probe Remaining Mesh Points.
879
+   *          Move to INVALID points and
880
+   *          NOTE: Blocks the G-code queue and captures Marlin UI during use.
881
+   */
879 882
   void unified_bed_leveling::manually_probe_remaining_mesh(const xy_pos_t &pos, const float &z_clearance, const float &thick, const bool do_ubl_mesh_map) {
880 883
     ui.capture();
881 884
 
@@ -907,7 +910,7 @@ void unified_bed_leveling::shift_mesh_height() {
907 910
       KEEPALIVE_STATE(PAUSED_FOR_USER);
908 911
       ui.capture();
909 912
 
910
-      if (do_ubl_mesh_map) display_map(g29_map_type);  // show user where we're probing
913
+      if (do_ubl_mesh_map) display_map(param.T_map_type);   // Show user where we're probing
911 914
 
912 915
       if (parser.seen('B')) {
913 916
         SERIAL_ECHOPGM_P(GET_TEXT(MSG_UBL_BC_INSERT));
@@ -918,45 +921,38 @@ void unified_bed_leveling::shift_mesh_height() {
918 921
         LCD_MESSAGEPGM(MSG_UBL_BC_INSERT2);
919 922
       }
920 923
 
921
-      const float z_step = 0.01f;                         // existing behavior: 0.01mm per click, occasionally step
922
-      //const float z_step = planner.steps_to_mm[Z_AXIS]; // approx one step each click
923
-
924
+      const float z_step = 0.01f;                         // 0.01mm per encoder tick, occasionally step
924 925
       move_z_with_encoder(z_step);
925 926
 
926
-      if (click_and_hold()) {
927
+      if (_click_and_hold([]{
927 928
         SERIAL_ECHOLNPGM("\nMesh only partially populated.");
928 929
         do_z_clearance(Z_CLEARANCE_DEPLOY_PROBE);
929
-        return restore_ubl_active_state_and_leave();
930
-      }
930
+      })) return restore_ubl_active_state_and_leave();
931 931
 
932
+      // Store the Z position minus the shim height
932 933
       z_values[lpos.x][lpos.y] = current_position.z - thick;
934
+
935
+      // Tell the external UI to update
933 936
       TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location, z_values[lpos.x][lpos.y]));
934 937
 
935
-      if (g29_verbose_level > 2)
938
+      if (param.V_verbosity > 2)
936 939
         SERIAL_ECHOLNPAIR_F("Mesh Point Measured at: ", z_values[lpos.x][lpos.y], 6);
937 940
       SERIAL_FLUSH(); // Prevent host M105 buffer overrun.
938 941
     } while (location.valid());
939 942
 
940
-    if (do_ubl_mesh_map) display_map(g29_map_type);  // show user where we're probing
943
+    if (do_ubl_mesh_map) display_map(param.T_map_type);  // show user where we're probing
941 944
 
942 945
     restore_ubl_active_state_and_leave();
943 946
     do_blocking_move_to_xy_z(pos, Z_CLEARANCE_DEPLOY_PROBE);
944 947
   }
945 948
 
946
-  inline void set_message_with_feedback(PGM_P const msg_P) {
947
-    ui.set_status_P(msg_P);
948
-    ui.quick_feedback();
949
-  }
950
-
951
-  void abort_fine_tune() {
952
-    ui.return_to_status();
953
-    do_z_clearance(Z_CLEARANCE_BETWEEN_PROBES);
954
-    set_message_with_feedback(GET_TEXT(MSG_EDITING_STOPPED));
955
-  }
956
-
949
+  /**
950
+   * G29 P4 : Mesh Fine-Tuning. Go to point(s) and adjust values with the LCD.
951
+   *          NOTE: Blocks the G-code queue and captures Marlin UI during use.
952
+   */
957 953
   void unified_bed_leveling::fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) {
958
-    if (!parser.seen('R'))      // fine_tune_mesh() is special. If no repetition count flag is specified
959
-      g29_repetition_cnt = 1;   // do exactly one mesh location. Otherwise use what the parser decided.
954
+    if (!parser.seen('R'))        // fine_tune_mesh() is special. If no repetition count flag is specified
955
+      param.R_repetition = 1;   // do exactly one mesh location. Otherwise use what the parser decided.
960 956
 
961 957
     #if ENABLED(UBL_MESH_EDIT_MOVES_Z)
962 958
       const float h_offset = parser.seenval('H') ? parser.value_linear_units() : MANUAL_PROBE_START_Z;
@@ -984,7 +980,7 @@ void unified_bed_leveling::shift_mesh_height() {
984 980
     const xy_int8_t &lpos = location.pos;
985 981
 
986 982
     #if IS_TFTGLCD_PANEL
987
-      lcd_mesh_edit_setup(0);                             // Change current screen before calling ui.ubl_plot
983
+      ui.ubl_mesh_edit_start(0);                          // Change current screen before calling ui.ubl_plot
988 984
       safe_delay(50);
989 985
     #endif
990 986
 
@@ -1009,7 +1005,7 @@ void unified_bed_leveling::shift_mesh_height() {
1009 1005
 
1010 1006
       KEEPALIVE_STATE(PAUSED_FOR_USER);
1011 1007
 
1012
-      if (do_ubl_mesh_map) display_map(g29_map_type);     // Display the current point
1008
+      if (do_ubl_mesh_map) display_map(param.T_map_type);     // Display the current point
1013 1009
 
1014 1010
       #if IS_TFTGLCD_PANEL
1015 1011
         ui.ubl_plot(lpos.x, lpos.y);   // update plot screen
@@ -1021,13 +1017,13 @@ void unified_bed_leveling::shift_mesh_height() {
1021 1017
       if (isnan(new_z)) new_z = 0;                        // Invalid points begin at 0
1022 1018
       new_z = FLOOR(new_z * 1000) * 0.001f;               // Chop off digits after the 1000ths place
1023 1019
 
1024
-      lcd_mesh_edit_setup(new_z);
1020
+      ui.ubl_mesh_edit_start(new_z);
1025 1021
 
1026 1022
       SET_SOFT_ENDSTOP_LOOSE(true);
1027 1023
 
1028 1024
       do {
1029 1025
         idle();
1030
-        new_z = lcd_mesh_edit();
1026
+        new_z = ui.ubl_mesh_value();
1031 1027
         TERN_(UBL_MESH_EDIT_MOVES_Z, do_blocking_move_to_z(h_offset + new_z)); // Move the nozzle as the point is edited
1032 1028
         SERIAL_FLUSH();                                   // Prevent host M105 buffer overrun.
1033 1029
       } while (!ui.button_pressed());
@@ -1036,17 +1032,27 @@ void unified_bed_leveling::shift_mesh_height() {
1036 1032
 
1037 1033
       if (!lcd_map_control) ui.return_to_status();        // Just editing a single point? Return to status
1038 1034
 
1039
-      if (click_and_hold(abort_fine_tune)) break;         // Button held down? Abort editing
1035
+      // Button held down? Abort editing
1036
+      if (_click_and_hold([]{
1037
+        ui.return_to_status();
1038
+        do_z_clearance(Z_CLEARANCE_BETWEEN_PROBES);
1039
+        set_message_with_feedback(GET_TEXT(MSG_EDITING_STOPPED));
1040
+      })) break;
1041
+
1042
+      // TODO: Disable leveling here so the Z value becomes the 'native' Z value.
1040 1043
 
1041 1044
       z_values[lpos.x][lpos.y] = new_z;                   // Save the updated Z value
1045
+
1046
+      // TODO: Re-enable leveling here so Z is correctly based on the updated mesh.
1047
+
1042 1048
       TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location, new_z));
1043 1049
 
1044 1050
       serial_delay(20);                                   // No switch noise
1045 1051
       ui.refresh();
1046 1052
 
1047
-    } while (lpos.x >= 0 && --g29_repetition_cnt > 0);
1053
+    } while (lpos.x >= 0 && --param.R_repetition > 0);
1048 1054
 
1049
-    if (do_ubl_mesh_map) display_map(g29_map_type);
1055
+    if (do_ubl_mesh_map) display_map(param.T_map_type);
1050 1056
     restore_ubl_active_state_and_leave();
1051 1057
 
1052 1058
     do_blocking_move_to_xy_z(pos, Z_CLEARANCE_BETWEEN_PROBES);
@@ -1062,25 +1068,28 @@ void unified_bed_leveling::shift_mesh_height() {
1062 1068
 
1063 1069
 #endif // HAS_LCD_MENU
1064 1070
 
1065
-bool unified_bed_leveling::g29_parameter_parsing() {
1071
+/**
1072
+ * Parse and validate most G29 parameters, store for use by G29 functions.
1073
+ */
1074
+bool unified_bed_leveling::G29_parse_parameters() {
1066 1075
   bool err_flag = false;
1067 1076
 
1068
-  TERN_(HAS_LCD_MENU, set_message_with_feedback(GET_TEXT(MSG_UBL_DOING_G29)));
1077
+  set_message_with_feedback(GET_TEXT(MSG_UBL_DOING_G29));
1069 1078
 
1070
-  g29_constant = 0;
1071
-  g29_repetition_cnt = 0;
1079
+  param.C_constant = 0;
1080
+  param.R_repetition = 0;
1072 1081
 
1073 1082
   if (parser.seen('R')) {
1074
-    g29_repetition_cnt = parser.has_value() ? parser.value_int() : GRID_MAX_POINTS;
1075
-    NOMORE(g29_repetition_cnt, GRID_MAX_POINTS);
1076
-    if (g29_repetition_cnt < 1) {
1083
+    param.R_repetition = parser.has_value() ? parser.value_int() : GRID_MAX_POINTS;
1084
+    NOMORE(param.R_repetition, GRID_MAX_POINTS);
1085
+    if (param.R_repetition < 1) {
1077 1086
       SERIAL_ECHOLNPGM("?(R)epetition count invalid (1+).\n");
1078 1087
       return UBL_ERR;
1079 1088
     }
1080 1089
   }
1081 1090
 
1082
-  g29_verbose_level = parser.seen('V') ? parser.value_int() : 0;
1083
-  if (!WITHIN(g29_verbose_level, 0, 4)) {
1091
+  param.V_verbosity = parser.seen('V') ? parser.value_int() : 0;
1092
+  if (!WITHIN(param.V_verbosity, 0, 4)) {
1084 1093
     SERIAL_ECHOLNPGM("?(V)erbose level implausible (0-4).\n");
1085 1094
     err_flag = true;
1086 1095
   }
@@ -1095,8 +1104,8 @@ bool unified_bed_leveling::g29_parameter_parsing() {
1095 1104
       else
1096 1105
     #endif
1097 1106
       {
1098
-        g29_phase_value = pv;
1099
-        if (!WITHIN(g29_phase_value, 0, 6)) {
1107
+        param.P_phase = pv;
1108
+        if (!WITHIN(param.P_phase, 0, 6)) {
1100 1109
           SERIAL_ECHOLNPGM("?(P)hase value invalid (0-6).\n");
1101 1110
           err_flag = true;
1102 1111
         }
@@ -1105,8 +1114,8 @@ bool unified_bed_leveling::g29_parameter_parsing() {
1105 1114
 
1106 1115
   if (parser.seen('J')) {
1107 1116
     #if HAS_BED_PROBE
1108
-      g29_grid_size = parser.has_value() ? parser.value_int() : 0;
1109
-      if (g29_grid_size && !WITHIN(g29_grid_size, 2, 9)) {
1117
+      param.grid_size = parser.has_value() ? parser.value_int() : 0;
1118
+      if (param.grid_size && !WITHIN(param.grid_size, 2, 9)) {
1110 1119
         SERIAL_ECHOLNPGM("?Invalid grid size (J) specified (2-9).\n");
1111 1120
         err_flag = true;
1112 1121
       }
@@ -1116,12 +1125,12 @@ bool unified_bed_leveling::g29_parameter_parsing() {
1116 1125
     #endif
1117 1126
   }
1118 1127
 
1119
-  xy_seen.x = parser.seenval('X');
1120
-  float sx = xy_seen.x ? parser.value_float() : current_position.x;
1121
-  xy_seen.y = parser.seenval('Y');
1122
-  float sy = xy_seen.y ? parser.value_float() : current_position.y;
1128
+  param.XY_seen.x = parser.seenval('X');
1129
+  float sx = param.XY_seen.x ? parser.value_float() : current_position.x;
1130
+  param.XY_seen.y = parser.seenval('Y');
1131
+  float sy = param.XY_seen.y ? parser.value_float() : current_position.y;
1123 1132
 
1124
-  if (xy_seen.x != xy_seen.y) {
1133
+  if (param.XY_seen.x != param.XY_seen.y) {
1125 1134
     SERIAL_ECHOLNPGM("Both X & Y locations must be specified.\n");
1126 1135
     err_flag = true;
1127 1136
   }
@@ -1132,7 +1141,7 @@ bool unified_bed_leveling::g29_parameter_parsing() {
1132 1141
 
1133 1142
   if (err_flag) return UBL_ERR;
1134 1143
 
1135
-  g29_pos.set(sx, sy);
1144
+  param.XY_pos.set(sx, sy);
1136 1145
 
1137 1146
   /**
1138 1147
    * Activate or deactivate UBL
@@ -1154,8 +1163,8 @@ bool unified_bed_leveling::g29_parameter_parsing() {
1154 1163
   }
1155 1164
 
1156 1165
   // Set global 'C' flag and its value
1157
-  if ((g29_c_flag = parser.seen('C')))
1158
-    g29_constant = parser.value_float();
1166
+  if ((param.C_seen = parser.seen('C')))
1167
+    param.C_constant = parser.value_float();
1159 1168
 
1160 1169
   #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
1161 1170
     if (parser.seenval('F')) {
@@ -1168,8 +1177,8 @@ bool unified_bed_leveling::g29_parameter_parsing() {
1168 1177
     }
1169 1178
   #endif
1170 1179
 
1171
-  g29_map_type = parser.intval('T');
1172
-  if (!WITHIN(g29_map_type, 0, 2)) {
1180
+  param.T_map_type = parser.intval('T');
1181
+  if (!WITHIN(param.T_map_type, 0, 2)) {
1173 1182
     SERIAL_ECHOLNPGM("Invalid map type.\n");
1174 1183
     return UBL_ERR;
1175 1184
   }
@@ -1187,7 +1196,7 @@ void unified_bed_leveling::save_ubl_active_state_and_disable() {
1187 1196
     ubl_state_recursion_chk++;
1188 1197
     if (ubl_state_recursion_chk != 1) {
1189 1198
       SERIAL_ECHOLNPGM("save_ubl_active_state_and_disabled() called multiple times in a row.");
1190
-      TERN_(HAS_LCD_MENU, set_message_with_feedback(GET_TEXT(MSG_UBL_SAVE_ERROR)));
1199
+      set_message_with_feedback(GET_TEXT(MSG_UBL_SAVE_ERROR));
1191 1200
       return;
1192 1201
     }
1193 1202
   #endif
@@ -1200,7 +1209,7 @@ void unified_bed_leveling::restore_ubl_active_state_and_leave() {
1200 1209
   #if ENABLED(UBL_DEVEL_DEBUGGING)
1201 1210
     if (--ubl_state_recursion_chk) {
1202 1211
       SERIAL_ECHOLNPGM("restore_ubl_active_state_and_leave() called too many times.");
1203
-      TERN_(HAS_LCD_MENU, set_message_with_feedback(GET_TEXT(MSG_UBL_RESTORE_ERROR)));
1212
+      set_message_with_feedback(GET_TEXT(MSG_UBL_RESTORE_ERROR));
1204 1213
       return;
1205 1214
     }
1206 1215
   #endif
@@ -1411,8 +1420,8 @@ void unified_bed_leveling::smart_fill_mesh() {
1411 1420
   void unified_bed_leveling::tilt_mesh_based_on_probed_grid(const bool do_3_pt_leveling) {
1412 1421
     const float x_min = probe.min_x(), x_max = probe.max_x(),
1413 1422
                 y_min = probe.min_y(), y_max = probe.max_y(),
1414
-                dx = (x_max - x_min) / (g29_grid_size - 1),
1415
-                dy = (y_max - y_min) / (g29_grid_size - 1);
1423
+                dx = (x_max - x_min) / (param.grid_size - 1),
1424
+                dy = (y_max - y_min) / (param.grid_size - 1);
1416 1425
 
1417 1426
     xy_float_t points[3];
1418 1427
     probe.get_three_points(points);
@@ -1431,7 +1440,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1431 1440
       SERIAL_ECHOLNPGM("Tilting mesh (1/3)");
1432 1441
       TERN_(HAS_DISPLAY, ui.status_printf_P(0, PSTR(S_FMT " 1/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
1433 1442
 
1434
-      measured_z = probe.probe_at_point(points[0], PROBE_PT_RAISE, g29_verbose_level);
1443
+      measured_z = probe.probe_at_point(points[0], PROBE_PT_RAISE, param.V_verbosity);
1435 1444
       if (isnan(measured_z))
1436 1445
         abort_flag = true;
1437 1446
       else {
@@ -1439,7 +1448,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1439 1448
         #ifdef VALIDATE_MESH_TILT
1440 1449
           z1 = measured_z;
1441 1450
         #endif
1442
-        if (g29_verbose_level > 3) {
1451
+        if (param.V_verbosity > 3) {
1443 1452
           serial_spaces(16);
1444 1453
           SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z);
1445 1454
         }
@@ -1450,7 +1459,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1450 1459
         SERIAL_ECHOLNPGM("Tilting mesh (2/3)");
1451 1460
         TERN_(HAS_DISPLAY, ui.status_printf_P(0, PSTR(S_FMT " 2/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
1452 1461
 
1453
-        measured_z = probe.probe_at_point(points[1], PROBE_PT_RAISE, g29_verbose_level);
1462
+        measured_z = probe.probe_at_point(points[1], PROBE_PT_RAISE, param.V_verbosity);
1454 1463
         #ifdef VALIDATE_MESH_TILT
1455 1464
           z2 = measured_z;
1456 1465
         #endif
@@ -1458,7 +1467,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1458 1467
           abort_flag = true;
1459 1468
         else {
1460 1469
           measured_z -= get_z_correction(points[1]);
1461
-          if (g29_verbose_level > 3) {
1470
+          if (param.V_verbosity > 3) {
1462 1471
             serial_spaces(16);
1463 1472
             SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z);
1464 1473
           }
@@ -1470,7 +1479,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1470 1479
         SERIAL_ECHOLNPGM("Tilting mesh (3/3)");
1471 1480
         TERN_(HAS_DISPLAY, ui.status_printf_P(0, PSTR(S_FMT " 3/3"), GET_TEXT(MSG_LCD_TILTING_MESH)));
1472 1481
 
1473
-        measured_z = probe.probe_at_point(points[2], PROBE_PT_STOW, g29_verbose_level);
1482
+        measured_z = probe.probe_at_point(points[2], PROBE_PT_STOW, param.V_verbosity);
1474 1483
         #ifdef VALIDATE_MESH_TILT
1475 1484
           z3 = measured_z;
1476 1485
         #endif
@@ -1478,7 +1487,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1478 1487
           abort_flag = true;
1479 1488
         else {
1480 1489
           measured_z -= get_z_correction(points[2]);
1481
-          if (g29_verbose_level > 3) {
1490
+          if (param.V_verbosity > 3) {
1482 1491
             serial_spaces(16);
1483 1492
             SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z);
1484 1493
           }
@@ -1498,20 +1507,20 @@ void unified_bed_leveling::smart_fill_mesh() {
1498 1507
 
1499 1508
       bool zig_zag = false;
1500 1509
 
1501
-      const uint16_t total_points = sq(g29_grid_size);
1510
+      const uint16_t total_points = sq(param.grid_size);
1502 1511
       uint16_t point_num = 1;
1503 1512
 
1504 1513
       xy_pos_t rpos;
1505
-      LOOP_L_N(ix, g29_grid_size) {
1514
+      LOOP_L_N(ix, param.grid_size) {
1506 1515
         rpos.x = x_min + ix * dx;
1507
-        LOOP_L_N(iy, g29_grid_size) {
1508
-          rpos.y = y_min + dy * (zig_zag ? g29_grid_size - 1 - iy : iy);
1516
+        LOOP_L_N(iy, param.grid_size) {
1517
+          rpos.y = y_min + dy * (zig_zag ? param.grid_size - 1 - iy : iy);
1509 1518
 
1510 1519
           if (!abort_flag) {
1511 1520
             SERIAL_ECHOLNPAIR("Tilting mesh point ", point_num, "/", total_points, "\n");
1512 1521
             TERN_(HAS_DISPLAY, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_LCD_TILTING_MESH), point_num, total_points));
1513 1522
 
1514
-            measured_z = probe.probe_at_point(rpos, parser.seen('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, g29_verbose_level); // TODO: Needs error handling
1523
+            measured_z = probe.probe_at_point(rpos, parser.seen('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, param.V_verbosity); // TODO: Needs error handling
1515 1524
 
1516 1525
             abort_flag = isnan(measured_z);
1517 1526
 
@@ -1534,7 +1543,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1534 1543
 
1535 1544
             if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPAIR_F("   final >>>---> ", measured_z, 7);
1536 1545
 
1537
-            if (g29_verbose_level > 3) {
1546
+            if (param.V_verbosity > 3) {
1538 1547
               serial_spaces(16);
1539 1548
               SERIAL_ECHOLNPAIR("Corrected_Z=", measured_z);
1540 1549
             }
@@ -1557,7 +1566,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1557 1566
 
1558 1567
     vector_3 normal = vector_3(lsf_results.A, lsf_results.B, 1).get_normal();
1559 1568
 
1560
-    if (g29_verbose_level > 2) {
1569
+    if (param.V_verbosity > 2) {
1561 1570
       SERIAL_ECHOPAIR_F("bed plane normal = [", normal.x, 7);
1562 1571
       SERIAL_CHAR(',');
1563 1572
       SERIAL_ECHO_F(normal.y, 7);
@@ -1721,7 +1730,7 @@ void unified_bed_leveling::smart_fill_mesh() {
1721 1730
       SERIAL_ECHOLNPAIR_F("Fade Height M420 Z", planner.z_fade_height, 4);
1722 1731
     #endif
1723 1732
 
1724
-    adjust_mesh_to_mean(g29_c_flag, g29_constant);
1733
+    adjust_mesh_to_mean(param.C_seen, param.C_constant);
1725 1734
 
1726 1735
     #if HAS_BED_PROBE
1727 1736
       SERIAL_ECHOLNPAIR_F("Probe Offset M851 Z", probe.offset.z, 7);
@@ -1819,17 +1828,17 @@ void unified_bed_leveling::smart_fill_mesh() {
1819 1828
       return;
1820 1829
     }
1821 1830
 
1822
-    if (!parser.has_value() || !WITHIN(g29_storage_slot, 0, a - 1)) {
1831
+    if (!parser.has_value() || !WITHIN(parser.value_int(), 0, a - 1)) {
1823 1832
       SERIAL_ECHOLNPAIR("?Invalid storage slot.\n?Use 0 to ", a - 1);
1824 1833
       return;
1825 1834
     }
1826 1835
 
1827
-    g29_storage_slot = parser.value_int();
1836
+    param.KLS_storage_slot = parser.value_int();
1828 1837
 
1829 1838
     float tmp_z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
1830
-    settings.load_mesh(g29_storage_slot, &tmp_z_values);
1839
+    settings.load_mesh(param.KLS_storage_slot, &tmp_z_values);
1831 1840
 
1832
-    SERIAL_ECHOLNPAIR("Subtracting mesh in slot ", g29_storage_slot, " from current mesh.");
1841
+    SERIAL_ECHOLNPAIR("Subtracting mesh in slot ", param.KLS_storage_slot, " from current mesh.");
1833 1842
 
1834 1843
     GRID_LOOP(x, y) {
1835 1844
       z_values[x][y] -= tmp_z_values[x][y];

+ 3
- 3
Marlin/src/gcode/bedlevel/M420.cpp View File

@@ -156,16 +156,16 @@ void GcodeSuite::M420() {
156 156
             GRID_LOOP(x, y) mesh_sum += Z_VALUES(x, y);
157 157
             const float zmean = mesh_sum / float(GRID_MAX_POINTS);
158 158
 
159
-          #else
159
+          #else // midrange
160 160
 
161
-            // Find the low and high mesh values
161
+            // Find the low and high mesh values.
162 162
             float lo_val = 100, hi_val = -100;
163 163
             GRID_LOOP(x, y) {
164 164
               const float z = Z_VALUES(x, y);
165 165
               NOMORE(lo_val, z);
166 166
               NOLESS(hi_val, z);
167 167
             }
168
-            // Take the mean of the lowest and highest
168
+            // Get the midrange plus C value. (The median may be better.)
169 169
             const float zmean = (lo_val + hi_val) / 2.0 + cval;
170 170
 
171 171
           #endif

+ 5
- 5
Marlin/src/lcd/marlinui.h View File

@@ -85,11 +85,6 @@
85 85
     typedef void (*screenFunc_t)();
86 86
     typedef void (*menuAction_t)();
87 87
 
88
-    #if ENABLED(AUTO_BED_LEVELING_UBL)
89
-      void lcd_mesh_edit_setup(const float &initial);
90
-      float lcd_mesh_edit();
91
-    #endif
92
-
93 88
   #endif // HAS_LCD_MENU
94 89
 
95 90
 #endif // HAS_WIRED_LCD
@@ -488,6 +483,11 @@ public:
488 483
       static void ubl_plot(const uint8_t x_plot, const uint8_t y_plot);
489 484
     #endif
490 485
 
486
+    #if ENABLED(AUTO_BED_LEVELING_UBL)
487
+      static void ubl_mesh_edit_start(const float &initial);
488
+      static float ubl_mesh_value();
489
+    #endif
490
+
491 491
     static void draw_select_screen_prompt(PGM_P const pref, const char * const string=nullptr, PGM_P const suff=nullptr);
492 492
 
493 493
   #elif HAS_WIRED_LCD

+ 24
- 22
Marlin/src/lcd/menu/menu_ubl.cpp View File

@@ -56,12 +56,24 @@ inline float rounded_mesh_value() {
56 56
   return float(rounded - (rounded % 5L)) / 1000;
57 57
 }
58 58
 
59
-static void _lcd_mesh_fine_tune(PGM_P const msg) {
59
+/**
60
+ * This screen displays the temporary mesh value and updates it based on encoder
61
+ * movement. While this screen is active ubl.fine_tune_mesh sits in a loop getting
62
+ * the current value via ubl_mesh_value, moves the Z axis, and updates the mesh
63
+ * value until the encoder button is pressed.
64
+ *
65
+ * - Update the 'mesh_edit_accumulator' from encoder rotation
66
+ * - Draw the mesh value (with draw_edit_screen)
67
+ * - Draw the graphical overlay, if enabled.
68
+ * - Update the 'refresh' state according to the display type
69
+ */
70
+void _lcd_mesh_fine_tune(PGM_P const msg) {
71
+  constexpr float mesh_edit_step = 1.0f / 200.0f;
60 72
   ui.defer_status_screen();
61 73
   if (ubl.encoder_diff) {
62 74
     mesh_edit_accumulator += TERN(IS_TFTGLCD_PANEL,
63
-      ubl.encoder_diff * 0.005f / ENCODER_PULSES_PER_STEP,
64
-      ubl.encoder_diff > 0 ? 0.005f : -0.005f
75
+      ubl.encoder_diff * mesh_edit_step / ENCODER_PULSES_PER_STEP,
76
+      ubl.encoder_diff > 0 ? mesh_edit_step : -mesh_edit_step
65 77
     );
66 78
     ubl.encoder_diff = 0;
67 79
     IF_DISABLED(IS_TFTGLCD_PANEL, ui.refresh(LCDVIEW_CALL_REDRAW_NEXT));
@@ -77,29 +89,19 @@ static void _lcd_mesh_fine_tune(PGM_P const msg) {
77 89
 }
78 90
 
79 91
 //
80
-// Called external to the menu system to acquire the result of an edit.
92
+// Init mesh editing and go to the fine tuning screen (ubl.fine_tune_mesh)
93
+// To capture encoder events UBL will also call ui.capture and ui.release.
81 94
 //
82
-float lcd_mesh_edit() { return rounded_mesh_value(); }
83
-
84
-void lcd_mesh_edit_setup(const float &initial) {
85
-  TERN_(HAS_GRAPHICAL_TFT, ui.clear_lcd());
95
+void MarlinUI::ubl_mesh_edit_start(const float &initial) {
96
+  TERN_(HAS_GRAPHICAL_TFT, clear_lcd());
86 97
   mesh_edit_accumulator = initial;
87
-  ui.goto_screen([]{ _lcd_mesh_fine_tune(GET_TEXT(MSG_MESH_EDIT_Z)); });
88
-}
89
-
90
-void _lcd_z_offset_edit() {
91
-  _lcd_mesh_fine_tune(GET_TEXT(MSG_UBL_Z_OFFSET));
98
+  goto_screen([]{ _lcd_mesh_fine_tune(GET_TEXT(MSG_MESH_EDIT_Z)); });
92 99
 }
93 100
 
94
-float lcd_z_offset_edit() {
95
-  ui.goto_screen(_lcd_z_offset_edit);
96
-  return rounded_mesh_value();
97
-}
98
-
99
-void lcd_z_offset_edit_setup(const float &initial) {
100
-  mesh_edit_accumulator = initial;
101
-  ui.goto_screen(_lcd_z_offset_edit);
102
-}
101
+//
102
+// Get the mesh value within a Z adjustment loop (ubl.fine_tune_mesh)
103
+//
104
+float MarlinUI::ubl_mesh_value() { return rounded_mesh_value(); }
103 105
 
104 106
 /**
105 107
  * UBL Build Custom Mesh Command

Loading…
Cancel
Save