|
@@ -40,11 +40,14 @@
|
40
|
40
|
|
41
|
41
|
extern float destination[XYZE], current_position[XYZE];
|
42
|
42
|
|
43
|
|
- void lcd_return_to_status();
|
44
|
|
- void lcd_mesh_edit_setup(float initial);
|
45
|
|
- float lcd_mesh_edit();
|
46
|
|
- void lcd_z_offset_edit_setup(float);
|
47
|
|
- float lcd_z_offset_edit();
|
|
43
|
+ #if ENABLED(NEWPANEL)
|
|
44
|
+ void lcd_return_to_status();
|
|
45
|
+ void lcd_mesh_edit_setup(float initial);
|
|
46
|
+ float lcd_mesh_edit();
|
|
47
|
+ void lcd_z_offset_edit_setup(float);
|
|
48
|
+ float lcd_z_offset_edit();
|
|
49
|
+ #endif
|
|
50
|
+
|
48
|
51
|
extern float meshedit_done;
|
49
|
52
|
extern long babysteps_done;
|
50
|
53
|
extern float probe_pt(const float &x, const float &y, bool, int);
|
|
@@ -149,9 +152,10 @@
|
149
|
152
|
* parameter can be given to prioritize where the command should be trying to measure points.
|
150
|
153
|
* If the X and Y parameters are not specified the current probe position is used.
|
151
|
154
|
* P1 accepts a 'T' (Topology) parameter so you can observe mesh generation.
|
152
|
|
- * P1 also watches for the LCD Panel Encoder Switch to be held down, and will suspend
|
153
|
|
- * generation of the Mesh in that case. (Note: This check is only done between probe points,
|
154
|
|
- * so you must press and hold the switch until the Phase 1 command detects it.)
|
|
155
|
+ * P1 also watches for the LCD Panel Encoder Switch to be held down (assuming you have one),
|
|
156
|
+ * and will suspend generation of the Mesh in that case. (Note: This check is only done
|
|
157
|
+ * between probe points, so you must press and hold the switch until the Phase 1 command
|
|
158
|
+ * detects it.)
|
155
|
159
|
*
|
156
|
160
|
* P2 Phase 2 Probe areas of the Mesh that can't be automatically handled. Phase 2 respects an H
|
157
|
161
|
* parameter to control the height between Mesh points. The default height for movement
|
|
@@ -187,6 +191,8 @@
|
187
|
191
|
* Phase 2 allows the T (Map) parameter to be specified. This helps the user see the progression
|
188
|
192
|
* of the Mesh being built.
|
189
|
193
|
*
|
|
194
|
+ * NOTE: P2 is not available unless you have LCD support enabled!
|
|
195
|
+ *
|
190
|
196
|
* P3 Phase 3 Fill the unpopulated regions of the Mesh with a fixed value. There are two different paths the
|
191
|
197
|
* user can go down. If the user specifies the value using the C parameter, the closest invalid
|
192
|
198
|
* mesh points to the nozzle will be filled. The user can specify a repeat count using the R
|
|
@@ -204,8 +210,9 @@
|
204
|
210
|
* numbers. You should use some scrutiny and caution.
|
205
|
211
|
*
|
206
|
212
|
* P4 Phase 4 Fine tune the Mesh. The Delta Mesh Compensation System assume the existence of
|
207
|
|
- * an LCD Panel. It is possible to fine tune the mesh without the use of an LCD Panel.
|
208
|
|
- * (More work and details on doing this later!)
|
|
213
|
+ * an LCD Panel. It is possible to fine tune the mesh without the use of an LCD Panel using
|
|
214
|
+ * G42 and M421; see the UBL documentation for further details.
|
|
215
|
+ *
|
209
|
216
|
* The System will search for the closest Mesh Point to the nozzle. It will move the
|
210
|
217
|
* nozzle to this location. The user can use the LCD Panel to carefully adjust the nozzle
|
211
|
218
|
* so it is just barely touching the bed. When the user clicks the control, the System
|
|
@@ -228,6 +235,7 @@
|
228
|
235
|
* LOWER the Mesh Point at the location. If you did not get good adheasion, you want to
|
229
|
236
|
* RAISE the Mesh Point at that location.
|
230
|
237
|
*
|
|
238
|
+ * NOTE: P4 is not available unless you have LCD support enabled!
|
231
|
239
|
*
|
232
|
240
|
* P5 Phase 5 Find Mean Mesh Height and Standard Deviation. Typically, it is easier to use and
|
233
|
241
|
* work with the Mesh if it is Mean Adjusted. You can specify a C parameter to
|
|
@@ -452,52 +460,57 @@
|
452
|
460
|
break;
|
453
|
461
|
|
454
|
462
|
case 2: {
|
455
|
|
- //
|
456
|
|
- // Manually Probe Mesh in areas that can't be reached by the probe
|
457
|
|
- //
|
458
|
|
- SERIAL_PROTOCOLLNPGM("Manually probing unreachable mesh locations.");
|
459
|
|
- do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
460
|
|
- if (!g29_x_flag && !g29_y_flag) {
|
461
|
|
- /**
|
462
|
|
- * Use a good default location for the path.
|
463
|
|
- * The flipped > and < operators in these comparisons is intentional.
|
464
|
|
- * It should cause the probed points to follow a nice path on Cartesian printers.
|
465
|
|
- * It may make sense to have Delta printers default to the center of the bed.
|
466
|
|
- * Until that is decided, this can be forced with the X and Y parameters.
|
467
|
|
- */
|
468
|
|
- #if IS_KINEMATIC
|
469
|
|
- g29_x_pos = X_HOME_POS;
|
470
|
|
- g29_y_pos = Y_HOME_POS;
|
471
|
|
- #else // cartesian
|
472
|
|
- g29_x_pos = X_PROBE_OFFSET_FROM_EXTRUDER > 0 ? X_MAX_POS : X_MIN_POS;
|
473
|
|
- g29_y_pos = Y_PROBE_OFFSET_FROM_EXTRUDER < 0 ? Y_MAX_POS : Y_MIN_POS;
|
474
|
|
- #endif
|
475
|
|
- }
|
|
463
|
+ #if ENABLED(NEWPANEL)
|
|
464
|
+ //
|
|
465
|
+ // Manually Probe Mesh in areas that can't be reached by the probe
|
|
466
|
+ //
|
|
467
|
+ SERIAL_PROTOCOLLNPGM("Manually probing unreachable mesh locations.");
|
|
468
|
+ do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
|
469
|
+ if (!g29_x_flag && !g29_y_flag) {
|
|
470
|
+ /**
|
|
471
|
+ * Use a good default location for the path.
|
|
472
|
+ * The flipped > and < operators in these comparisons is intentional.
|
|
473
|
+ * It should cause the probed points to follow a nice path on Cartesian printers.
|
|
474
|
+ * It may make sense to have Delta printers default to the center of the bed.
|
|
475
|
+ * Until that is decided, this can be forced with the X and Y parameters.
|
|
476
|
+ */
|
|
477
|
+ #if IS_KINEMATIC
|
|
478
|
+ g29_x_pos = X_HOME_POS;
|
|
479
|
+ g29_y_pos = Y_HOME_POS;
|
|
480
|
+ #else // cartesian
|
|
481
|
+ g29_x_pos = X_PROBE_OFFSET_FROM_EXTRUDER > 0 ? X_MAX_POS : X_MIN_POS;
|
|
482
|
+ g29_y_pos = Y_PROBE_OFFSET_FROM_EXTRUDER < 0 ? Y_MAX_POS : Y_MIN_POS;
|
|
483
|
+ #endif
|
|
484
|
+ }
|
476
|
485
|
|
477
|
|
- if (parser.seen('C')) {
|
478
|
|
- g29_x_pos = current_position[X_AXIS];
|
479
|
|
- g29_y_pos = current_position[Y_AXIS];
|
480
|
|
- }
|
|
486
|
+ if (parser.seen('C')) {
|
|
487
|
+ g29_x_pos = current_position[X_AXIS];
|
|
488
|
+ g29_y_pos = current_position[Y_AXIS];
|
|
489
|
+ }
|
481
|
490
|
|
482
|
|
- float height = Z_CLEARANCE_BETWEEN_PROBES;
|
|
491
|
+ float height = Z_CLEARANCE_BETWEEN_PROBES;
|
483
|
492
|
|
484
|
|
- if (parser.seen('B')) {
|
485
|
|
- g29_card_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness(height);
|
486
|
|
- if (fabs(g29_card_thickness) > 1.5) {
|
487
|
|
- SERIAL_PROTOCOLLNPGM("?Error in Business Card measurement.");
|
488
|
|
- return;
|
|
493
|
+ if (parser.seen('B')) {
|
|
494
|
+ g29_card_thickness = parser.has_value() ? parser.value_float() : measure_business_card_thickness(height);
|
|
495
|
+ if (fabs(g29_card_thickness) > 1.5) {
|
|
496
|
+ SERIAL_PROTOCOLLNPGM("?Error in Business Card measurement.");
|
|
497
|
+ return;
|
|
498
|
+ }
|
489
|
499
|
}
|
490
|
|
- }
|
491
|
500
|
|
492
|
|
- if (parser.seen('H') && parser.has_value()) height = parser.value_float();
|
|
501
|
+ if (parser.seen('H') && parser.has_value()) height = parser.value_float();
|
493
|
502
|
|
494
|
|
- if (!position_is_reachable_xy(g29_x_pos, g29_y_pos)) {
|
495
|
|
- SERIAL_PROTOCOLLNPGM("XY outside printable radius.");
|
496
|
|
- return;
|
497
|
|
- }
|
|
503
|
+ if (!position_is_reachable_xy(g29_x_pos, g29_y_pos)) {
|
|
504
|
+ SERIAL_PROTOCOLLNPGM("XY outside printable radius.");
|
|
505
|
+ return;
|
|
506
|
+ }
|
498
|
507
|
|
499
|
|
- manually_probe_remaining_mesh(g29_x_pos, g29_y_pos, height, g29_card_thickness, parser.seen('T'));
|
500
|
|
- SERIAL_PROTOCOLLNPGM("G29 P2 finished.");
|
|
508
|
+ manually_probe_remaining_mesh(g29_x_pos, g29_y_pos, height, g29_card_thickness, parser.seen('T'));
|
|
509
|
+ SERIAL_PROTOCOLLNPGM("G29 P2 finished.");
|
|
510
|
+ #else
|
|
511
|
+ SERIAL_PROTOCOLLNPGM("?P2 is only available when an LCD is present.");
|
|
512
|
+ return;
|
|
513
|
+ #endif
|
501
|
514
|
} break;
|
502
|
515
|
|
503
|
516
|
case 3: {
|
|
@@ -557,11 +570,13 @@
|
557
|
570
|
break;
|
558
|
571
|
}
|
559
|
572
|
|
560
|
|
- case 4:
|
561
|
|
- //
|
562
|
|
- // Fine Tune (i.e., Edit) the Mesh
|
563
|
|
- //
|
564
|
|
- fine_tune_mesh(g29_x_pos, g29_y_pos, parser.seen('T'));
|
|
573
|
+ case 4: // Fine Tune (i.e., Edit) the Mesh
|
|
574
|
+ #if ENABLED(NEWPANEL)
|
|
575
|
+ fine_tune_mesh(g29_x_pos, g29_y_pos, parser.seen('T'));
|
|
576
|
+ #else
|
|
577
|
+ SERIAL_PROTOCOLLNPGM("?P4 is only available when an LCD is present.");
|
|
578
|
+ return;
|
|
579
|
+ #endif
|
565
|
580
|
break;
|
566
|
581
|
|
567
|
582
|
case 5: find_mean_mesh_height(); break;
|
|
@@ -716,11 +731,15 @@
|
716
|
731
|
|
717
|
732
|
LEAVE:
|
718
|
733
|
|
719
|
|
- lcd_reset_alert_level();
|
720
|
|
- LCD_MESSAGEPGM("");
|
721
|
|
- lcd_quick_feedback();
|
|
734
|
+ #if ENABLED(NEWPANEL)
|
|
735
|
+ lcd_reset_alert_level();
|
|
736
|
+ LCD_MESSAGEPGM("");
|
|
737
|
+ lcd_quick_feedback();
|
722
|
738
|
|
723
|
|
- has_control_of_lcd_panel = false;
|
|
739
|
+ has_control_of_lcd_panel = false;
|
|
740
|
+ #endif
|
|
741
|
+
|
|
742
|
+ return;
|
724
|
743
|
}
|
725
|
744
|
|
726
|
745
|
void unified_bed_leveling::find_mean_mesh_height() {
|
|
@@ -782,16 +801,18 @@
|
782
|
801
|
uint16_t max_iterations = GRID_MAX_POINTS;
|
783
|
802
|
|
784
|
803
|
do {
|
785
|
|
- if (ubl_lcd_clicked()) {
|
786
|
|
- SERIAL_PROTOCOLLNPGM("\nMesh only partially populated.\n");
|
787
|
|
- lcd_quick_feedback();
|
788
|
|
- STOW_PROBE();
|
789
|
|
- while (ubl_lcd_clicked()) idle();
|
790
|
|
- has_control_of_lcd_panel = false;
|
791
|
|
- restore_ubl_active_state_and_leave();
|
792
|
|
- safe_delay(50); // Debounce the Encoder wheel
|
793
|
|
- return;
|
794
|
|
- }
|
|
804
|
+ #if ENABLED(NEWPANEL)
|
|
805
|
+ if (ubl_lcd_clicked()) {
|
|
806
|
+ SERIAL_PROTOCOLLNPGM("\nMesh only partially populated.\n");
|
|
807
|
+ lcd_quick_feedback();
|
|
808
|
+ STOW_PROBE();
|
|
809
|
+ while (ubl_lcd_clicked()) idle();
|
|
810
|
+ has_control_of_lcd_panel = false;
|
|
811
|
+ restore_ubl_active_state_and_leave();
|
|
812
|
+ safe_delay(50); // Debounce the Encoder wheel
|
|
813
|
+ return;
|
|
814
|
+ }
|
|
815
|
+ #endif
|
795
|
816
|
|
796
|
817
|
location = find_closest_mesh_point_of_type(INVALID, lx, ly, USE_PROBE_AS_REFERENCE, NULL, close_or_far);
|
797
|
818
|
|
|
@@ -920,155 +941,165 @@
|
920
|
941
|
}
|
921
|
942
|
}
|
922
|
943
|
|
923
|
|
- float unified_bed_leveling::measure_point_with_encoder() {
|
|
944
|
+ #if ENABLED(NEWPANEL)
|
|
945
|
+ float unified_bed_leveling::measure_point_with_encoder() {
|
924
|
946
|
|
925
|
|
- while (ubl_lcd_clicked()) delay(50); // wait for user to release encoder wheel
|
926
|
|
- delay(50); // debounce
|
|
947
|
+ while (ubl_lcd_clicked()) delay(50); // wait for user to release encoder wheel
|
|
948
|
+ delay(50); // debounce
|
927
|
949
|
|
928
|
|
- KEEPALIVE_STATE(PAUSED_FOR_USER);
|
929
|
|
- while (!ubl_lcd_clicked()) { // we need the loop to move the nozzle based on the encoder wheel here!
|
930
|
|
- idle();
|
931
|
|
- if (encoder_diff) {
|
932
|
|
- do_blocking_move_to_z(current_position[Z_AXIS] + 0.01 * float(encoder_diff));
|
933
|
|
- encoder_diff = 0;
|
|
950
|
+ KEEPALIVE_STATE(PAUSED_FOR_USER);
|
|
951
|
+ while (!ubl_lcd_clicked()) { // we need the loop to move the nozzle based on the encoder wheel here!
|
|
952
|
+ idle();
|
|
953
|
+ if (encoder_diff) {
|
|
954
|
+ do_blocking_move_to_z(current_position[Z_AXIS] + 0.01 * float(encoder_diff));
|
|
955
|
+ encoder_diff = 0;
|
|
956
|
+ }
|
934
|
957
|
}
|
|
958
|
+ KEEPALIVE_STATE(IN_HANDLER);
|
|
959
|
+ return current_position[Z_AXIS];
|
935
|
960
|
}
|
936
|
|
- KEEPALIVE_STATE(IN_HANDLER);
|
937
|
|
- return current_position[Z_AXIS];
|
938
|
|
- }
|
939
|
961
|
|
940
|
|
- static void echo_and_take_a_measurement() { SERIAL_PROTOCOLLNPGM(" and take a measurement."); }
|
|
962
|
+ static void echo_and_take_a_measurement() { SERIAL_PROTOCOLLNPGM(" and take a measurement."); }
|
941
|
963
|
|
942
|
|
- float unified_bed_leveling::measure_business_card_thickness(float &in_height) {
|
943
|
|
- has_control_of_lcd_panel = true;
|
944
|
|
- save_ubl_active_state_and_disable(); // Disable bed level correction for probing
|
|
964
|
+ float unified_bed_leveling::measure_business_card_thickness(float &in_height) {
|
|
965
|
+ has_control_of_lcd_panel = true;
|
|
966
|
+ save_ubl_active_state_and_disable(); // Disable bed level correction for probing
|
945
|
967
|
|
946
|
|
- do_blocking_move_to_z(in_height);
|
947
|
|
- do_blocking_move_to_xy(0.5 * (UBL_MESH_MAX_X - (UBL_MESH_MIN_X)), 0.5 * (UBL_MESH_MAX_Y - (UBL_MESH_MIN_Y)));
|
948
|
|
- //, min(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]) / 2.0);
|
949
|
|
- stepper.synchronize();
|
|
968
|
+ do_blocking_move_to_z(in_height);
|
|
969
|
+ do_blocking_move_to_xy(0.5 * (UBL_MESH_MAX_X - (UBL_MESH_MIN_X)), 0.5 * (UBL_MESH_MAX_Y - (UBL_MESH_MIN_Y)));
|
|
970
|
+ //, min(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS]) / 2.0);
|
|
971
|
+ stepper.synchronize();
|
950
|
972
|
|
951
|
|
- SERIAL_PROTOCOLPGM("Place shim under nozzle");
|
952
|
|
- LCD_MESSAGEPGM("Place shim & measure"); // TODO: Make translatable string
|
953
|
|
- lcd_return_to_status();
|
954
|
|
- echo_and_take_a_measurement();
|
|
973
|
+ SERIAL_PROTOCOLPGM("Place shim under nozzle");
|
|
974
|
+ LCD_MESSAGEPGM("Place shim & measure"); // TODO: Make translatable string
|
|
975
|
+ lcd_return_to_status();
|
|
976
|
+ echo_and_take_a_measurement();
|
955
|
977
|
|
956
|
|
- const float z1 = measure_point_with_encoder();
|
957
|
|
- do_blocking_move_to_z(current_position[Z_AXIS] + SIZE_OF_LITTLE_RAISE);
|
958
|
|
- stepper.synchronize();
|
|
978
|
+ const float z1 = measure_point_with_encoder();
|
|
979
|
+ do_blocking_move_to_z(current_position[Z_AXIS] + SIZE_OF_LITTLE_RAISE);
|
|
980
|
+ stepper.synchronize();
|
959
|
981
|
|
960
|
|
- SERIAL_PROTOCOLPGM("Remove shim");
|
961
|
|
- LCD_MESSAGEPGM("Remove & measure bed"); // TODO: Make translatable string
|
962
|
|
- echo_and_take_a_measurement();
|
|
982
|
+ SERIAL_PROTOCOLPGM("Remove shim");
|
|
983
|
+ LCD_MESSAGEPGM("Remove & measure bed"); // TODO: Make translatable string
|
|
984
|
+ echo_and_take_a_measurement();
|
963
|
985
|
|
964
|
|
- const float z2 = measure_point_with_encoder();
|
|
986
|
+ const float z2 = measure_point_with_encoder();
|
965
|
987
|
|
966
|
|
- do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES);
|
|
988
|
+ do_blocking_move_to_z(current_position[Z_AXIS] + Z_CLEARANCE_BETWEEN_PROBES);
|
967
|
989
|
|
968
|
|
- const float thickness = abs(z1 - z2);
|
|
990
|
+ const float thickness = abs(z1 - z2);
|
969
|
991
|
|
970
|
|
- if (g29_verbose_level > 1) {
|
971
|
|
- SERIAL_PROTOCOLPGM("Business Card is ");
|
972
|
|
- SERIAL_PROTOCOL_F(thickness, 4);
|
973
|
|
- SERIAL_PROTOCOLLNPGM("mm thick.");
|
974
|
|
- }
|
|
992
|
+ if (g29_verbose_level > 1) {
|
|
993
|
+ SERIAL_PROTOCOLPGM("Business Card is ");
|
|
994
|
+ SERIAL_PROTOCOL_F(thickness, 4);
|
|
995
|
+ SERIAL_PROTOCOLLNPGM("mm thick.");
|
|
996
|
+ }
|
975
|
997
|
|
976
|
|
- in_height = current_position[Z_AXIS]; // do manual probing at lower height
|
|
998
|
+ in_height = current_position[Z_AXIS]; // do manual probing at lower height
|
977
|
999
|
|
978
|
|
- has_control_of_lcd_panel = false;
|
|
1000
|
+ has_control_of_lcd_panel = false;
|
979
|
1001
|
|
980
|
|
- restore_ubl_active_state_and_leave();
|
|
1002
|
+ restore_ubl_active_state_and_leave();
|
981
|
1003
|
|
982
|
|
- return thickness;
|
983
|
|
- }
|
|
1004
|
+ return thickness;
|
|
1005
|
+ }
|
984
|
1006
|
|
985
|
|
- void unified_bed_leveling::manually_probe_remaining_mesh(const float &lx, const float &ly, const float &z_clearance, const float &thick, const bool do_ubl_mesh_map) {
|
|
1007
|
+ void unified_bed_leveling::manually_probe_remaining_mesh(const float &lx, const float &ly, const float &z_clearance, const float &thick, const bool do_ubl_mesh_map) {
|
986
|
1008
|
|
987
|
|
- has_control_of_lcd_panel = true;
|
988
|
|
- save_ubl_active_state_and_disable(); // we don't do bed level correction because we want the raw data when we probe
|
989
|
|
- do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
990
|
|
- do_blocking_move_to_xy(lx, ly);
|
|
1009
|
+ has_control_of_lcd_panel = true;
|
991
|
1010
|
|
992
|
|
- lcd_return_to_status();
|
993
|
|
- mesh_index_pair location;
|
994
|
|
- do {
|
995
|
|
- location = find_closest_mesh_point_of_type(INVALID, lx, ly, USE_NOZZLE_AS_REFERENCE, NULL, false);
|
996
|
|
- // It doesn't matter if the probe can't reach the NAN location. This is a manual probe.
|
997
|
|
- if (location.x_index < 0 && location.y_index < 0) continue;
|
|
1011
|
+ save_ubl_active_state_and_disable(); // we don't do bed level correction because we want the raw data when we probe
|
|
1012
|
+ do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
|
1013
|
+ do_blocking_move_to_xy(lx, ly);
|
998
|
1014
|
|
999
|
|
- const float rawx = mesh_index_to_xpos(location.x_index),
|
1000
|
|
- rawy = mesh_index_to_ypos(location.y_index),
|
1001
|
|
- xProbe = LOGICAL_X_POSITION(rawx),
|
1002
|
|
- yProbe = LOGICAL_Y_POSITION(rawy);
|
|
1015
|
+ lcd_return_to_status();
|
1003
|
1016
|
|
1004
|
|
- if (!position_is_reachable_raw_xy(rawx, rawy)) break; // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points)
|
|
1017
|
+ mesh_index_pair location;
|
|
1018
|
+ do {
|
|
1019
|
+ location = find_closest_mesh_point_of_type(INVALID, lx, ly, USE_NOZZLE_AS_REFERENCE, NULL, false);
|
|
1020
|
+ // It doesn't matter if the probe can't reach the NAN location. This is a manual probe.
|
|
1021
|
+ if (location.x_index < 0 && location.y_index < 0) continue;
|
1005
|
1022
|
|
1006
|
|
- do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
|
1023
|
+ const float rawx = mesh_index_to_xpos(location.x_index),
|
|
1024
|
+ rawy = mesh_index_to_ypos(location.y_index),
|
|
1025
|
+ xProbe = LOGICAL_X_POSITION(rawx),
|
|
1026
|
+ yProbe = LOGICAL_Y_POSITION(rawy);
|
1007
|
1027
|
|
1008
|
|
- LCD_MESSAGEPGM("Moving to next"); // TODO: Make translatable string
|
|
1028
|
+ if (!position_is_reachable_raw_xy(rawx, rawy)) break; // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points)
|
1009
|
1029
|
|
1010
|
|
- do_blocking_move_to_xy(xProbe, yProbe);
|
1011
|
|
- do_blocking_move_to_z(z_clearance);
|
|
1030
|
+ do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
1012
|
1031
|
|
1013
|
|
- KEEPALIVE_STATE(PAUSED_FOR_USER);
|
1014
|
|
- has_control_of_lcd_panel = true;
|
|
1032
|
+ LCD_MESSAGEPGM("Moving to next"); // TODO: Make translatable string
|
|
1033
|
+
|
|
1034
|
+ do_blocking_move_to_xy(xProbe, yProbe);
|
|
1035
|
+ do_blocking_move_to_z(z_clearance);
|
1015
|
1036
|
|
1016
|
|
- if (do_ubl_mesh_map) display_map(g29_map_type); // show user where we're probing
|
|
1037
|
+ KEEPALIVE_STATE(PAUSED_FOR_USER);
|
|
1038
|
+ has_control_of_lcd_panel = true;
|
1017
|
1039
|
|
1018
|
|
- serialprintPGM(parser.seen('B') ? PSTR("Place shim & measure") : PSTR("Measure")); // TODO: Make translatable strings
|
|
1040
|
+ if (do_ubl_mesh_map) display_map(g29_map_type); // show user where we're probing
|
1019
|
1041
|
|
1020
|
|
- const float z_step = 0.01; // existing behavior: 0.01mm per click, occasionally step
|
1021
|
|
- //const float z_step = 1.0 / planner.axis_steps_per_mm[Z_AXIS]; // approx one step each click
|
|
1042
|
+ serialprintPGM(parser.seen('B') ? PSTR("Place shim & measure") : PSTR("Measure")); // TODO: Make translatable strings
|
1022
|
1043
|
|
1023
|
|
- while (ubl_lcd_clicked()) delay(50); // wait for user to release encoder wheel
|
1024
|
|
- delay(50); // debounce
|
1025
|
|
- while (!ubl_lcd_clicked()) { // we need the loop to move the nozzle based on the encoder wheel here!
|
1026
|
|
- idle();
|
1027
|
|
- if (encoder_diff) {
|
1028
|
|
- do_blocking_move_to_z(current_position[Z_AXIS] + float(encoder_diff) * z_step);
|
1029
|
|
- encoder_diff = 0;
|
|
1044
|
+ const float z_step = 0.01; // existing behavior: 0.01mm per click, occasionally step
|
|
1045
|
+ //const float z_step = 1.0 / planner.axis_steps_per_mm[Z_AXIS]; // approx one step each click
|
|
1046
|
+
|
|
1047
|
+ while (ubl_lcd_clicked()) delay(50); // wait for user to release encoder wheel
|
|
1048
|
+ delay(50); // debounce
|
|
1049
|
+ while (!ubl_lcd_clicked()) { // we need the loop to move the nozzle based on the encoder wheel here!
|
|
1050
|
+ idle();
|
|
1051
|
+ if (encoder_diff) {
|
|
1052
|
+ do_blocking_move_to_z(current_position[Z_AXIS] + float(encoder_diff) * z_step);
|
|
1053
|
+ encoder_diff = 0;
|
|
1054
|
+ }
|
1030
|
1055
|
}
|
1031
|
|
- }
|
1032
|
1056
|
|
1033
|
|
- // this sequence to detect an ubl_lcd_clicked() debounce it and leave if it is
|
1034
|
|
- // a Press and Hold is repeated in a lot of places (including G26_Mesh_Validation.cpp). This
|
1035
|
|
- // should be redone and compressed.
|
1036
|
|
- const millis_t nxt = millis() + 1500L;
|
1037
|
|
- while (ubl_lcd_clicked()) { // debounce and watch for abort
|
1038
|
|
- idle();
|
1039
|
|
- if (ELAPSED(millis(), nxt)) {
|
1040
|
|
- SERIAL_PROTOCOLLNPGM("\nMesh only partially populated.");
|
1041
|
|
- do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE);
|
1042
|
|
- lcd_quick_feedback();
|
1043
|
|
- while (ubl_lcd_clicked()) idle();
|
1044
|
|
- has_control_of_lcd_panel = false;
|
1045
|
|
- KEEPALIVE_STATE(IN_HANDLER);
|
1046
|
|
- restore_ubl_active_state_and_leave();
|
1047
|
|
- return;
|
|
1057
|
+ // this sequence to detect an ubl_lcd_clicked() debounce it and leave if it is
|
|
1058
|
+ // a Press and Hold is repeated in a lot of places (including G26_Mesh_Validation.cpp). This
|
|
1059
|
+ // should be redone and compressed.
|
|
1060
|
+ const millis_t nxt = millis() + 1500L;
|
|
1061
|
+ while (ubl_lcd_clicked()) { // debounce and watch for abort
|
|
1062
|
+ idle();
|
|
1063
|
+ if (ELAPSED(millis(), nxt)) {
|
|
1064
|
+ SERIAL_PROTOCOLLNPGM("\nMesh only partially populated.");
|
|
1065
|
+ do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE);
|
|
1066
|
+
|
|
1067
|
+ #if ENABLED(NEWPANEL)
|
|
1068
|
+ lcd_quick_feedback();
|
|
1069
|
+ while (ubl_lcd_clicked()) idle();
|
|
1070
|
+ has_control_of_lcd_panel = false;
|
|
1071
|
+ #endif
|
|
1072
|
+
|
|
1073
|
+ KEEPALIVE_STATE(IN_HANDLER);
|
|
1074
|
+ restore_ubl_active_state_and_leave();
|
|
1075
|
+ return;
|
|
1076
|
+ }
|
1048
|
1077
|
}
|
1049
|
|
- }
|
1050
|
1078
|
|
1051
|
|
- z_values[location.x_index][location.y_index] = current_position[Z_AXIS] - thick;
|
1052
|
|
- if (g29_verbose_level > 2) {
|
1053
|
|
- SERIAL_PROTOCOLPGM("Mesh Point Measured at: ");
|
1054
|
|
- SERIAL_PROTOCOL_F(z_values[location.x_index][location.y_index], 6);
|
1055
|
|
- SERIAL_EOL;
|
1056
|
|
- }
|
1057
|
|
- } while (location.x_index >= 0 && location.y_index >= 0);
|
|
1079
|
+ z_values[location.x_index][location.y_index] = current_position[Z_AXIS] - thick;
|
|
1080
|
+ if (g29_verbose_level > 2) {
|
|
1081
|
+ SERIAL_PROTOCOLPGM("Mesh Point Measured at: ");
|
|
1082
|
+ SERIAL_PROTOCOL_F(z_values[location.x_index][location.y_index], 6);
|
|
1083
|
+ SERIAL_EOL;
|
|
1084
|
+ }
|
|
1085
|
+ } while (location.x_index >= 0 && location.y_index >= 0);
|
1058
|
1086
|
|
1059
|
|
- if (do_ubl_mesh_map) display_map(g29_map_type);
|
|
1087
|
+ if (do_ubl_mesh_map) display_map(g29_map_type);
|
1060
|
1088
|
|
1061
|
|
- restore_ubl_active_state_and_leave();
|
1062
|
|
- KEEPALIVE_STATE(IN_HANDLER);
|
1063
|
|
- do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE);
|
1064
|
|
- do_blocking_move_to_xy(lx, ly);
|
1065
|
|
- }
|
|
1089
|
+ restore_ubl_active_state_and_leave();
|
|
1090
|
+ KEEPALIVE_STATE(IN_HANDLER);
|
|
1091
|
+ do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE);
|
|
1092
|
+ do_blocking_move_to_xy(lx, ly);
|
|
1093
|
+ }
|
|
1094
|
+ #endif
|
1066
|
1095
|
|
1067
|
1096
|
bool unified_bed_leveling::g29_parameter_parsing() {
|
1068
|
1097
|
bool err_flag = false;
|
1069
|
1098
|
|
1070
|
|
- LCD_MESSAGEPGM("Doing G29 UBL!"); // TODO: Make translatable string
|
1071
|
|
- lcd_quick_feedback();
|
|
1099
|
+ #if ENABLED(NEWPANEL)
|
|
1100
|
+ LCD_MESSAGEPGM("Doing G29 UBL!"); // TODO: Make translatable string
|
|
1101
|
+ lcd_quick_feedback();
|
|
1102
|
+ #endif
|
1072
|
1103
|
|
1073
|
1104
|
g29_constant = 0.0;
|
1074
|
1105
|
g29_repetition_cnt = 0;
|
|
@@ -1174,8 +1205,12 @@
|
1174
|
1205
|
ubl_state_recursion_chk++;
|
1175
|
1206
|
if (ubl_state_recursion_chk != 1) {
|
1176
|
1207
|
SERIAL_ECHOLNPGM("save_ubl_active_state_and_disabled() called multiple times in a row.");
|
1177
|
|
- LCD_MESSAGEPGM("save_UBL_active() error"); // TODO: Make translatable string
|
1178
|
|
- lcd_quick_feedback();
|
|
1208
|
+
|
|
1209
|
+ #if ENABLED(NEWPANEL)
|
|
1210
|
+ LCD_MESSAGEPGM("save_UBL_active() error"); // TODO: Make translatable string
|
|
1211
|
+ lcd_quick_feedback();
|
|
1212
|
+ #endif
|
|
1213
|
+
|
1179
|
1214
|
return;
|
1180
|
1215
|
}
|
1181
|
1216
|
ubl_state_at_invocation = state.active;
|
|
@@ -1185,8 +1220,12 @@
|
1185
|
1220
|
void unified_bed_leveling::restore_ubl_active_state_and_leave() {
|
1186
|
1221
|
if (--ubl_state_recursion_chk) {
|
1187
|
1222
|
SERIAL_ECHOLNPGM("restore_ubl_active_state_and_leave() called too many times.");
|
1188
|
|
- LCD_MESSAGEPGM("restore_UBL_active() error"); // TODO: Make translatable string
|
1189
|
|
- lcd_quick_feedback();
|
|
1223
|
+
|
|
1224
|
+ #if ENABLED(NEWPANEL)
|
|
1225
|
+ LCD_MESSAGEPGM("restore_UBL_active() error"); // TODO: Make translatable string
|
|
1226
|
+ lcd_quick_feedback();
|
|
1227
|
+ #endif
|
|
1228
|
+
|
1190
|
1229
|
return;
|
1191
|
1230
|
}
|
1192
|
1231
|
set_bed_leveling_enabled(ubl_state_at_invocation);
|
|
@@ -1420,114 +1459,116 @@
|
1420
|
1459
|
return out_mesh;
|
1421
|
1460
|
}
|
1422
|
1461
|
|
1423
|
|
- void unified_bed_leveling::fine_tune_mesh(const float &lx, const float &ly, const bool do_ubl_mesh_map) {
|
1424
|
|
- if (!parser.seen('R')) // fine_tune_mesh() is special. If no repetition count flag is specified
|
1425
|
|
- g29_repetition_cnt = 1; // do exactly one mesh location. Otherwise use what the parser decided.
|
|
1462
|
+ #if ENABLED(NEWPANEL)
|
|
1463
|
+ void unified_bed_leveling::fine_tune_mesh(const float &lx, const float &ly, const bool do_ubl_mesh_map) {
|
|
1464
|
+ if (!parser.seen('R')) // fine_tune_mesh() is special. If no repetition count flag is specified
|
|
1465
|
+ g29_repetition_cnt = 1; // do exactly one mesh location. Otherwise use what the parser decided.
|
1426
|
1466
|
|
1427
|
|
- mesh_index_pair location;
|
1428
|
|
- uint16_t not_done[16];
|
|
1467
|
+ mesh_index_pair location;
|
|
1468
|
+ uint16_t not_done[16];
|
1429
|
1469
|
|
1430
|
|
- if (!position_is_reachable_xy(lx, ly)) {
|
1431
|
|
- SERIAL_PROTOCOLLNPGM("(X,Y) outside printable radius.");
|
1432
|
|
- return;
|
1433
|
|
- }
|
|
1470
|
+ if (!position_is_reachable_xy(lx, ly)) {
|
|
1471
|
+ SERIAL_PROTOCOLLNPGM("(X,Y) outside printable radius.");
|
|
1472
|
+ return;
|
|
1473
|
+ }
|
1434
|
1474
|
|
1435
|
|
- save_ubl_active_state_and_disable();
|
|
1475
|
+ save_ubl_active_state_and_disable();
|
1436
|
1476
|
|
1437
|
|
- memset(not_done, 0xFF, sizeof(not_done));
|
|
1477
|
+ memset(not_done, 0xFF, sizeof(not_done));
|
1438
|
1478
|
|
1439
|
|
- LCD_MESSAGEPGM("Fine Tuning Mesh"); // TODO: Make translatable string
|
|
1479
|
+ LCD_MESSAGEPGM("Fine Tuning Mesh"); // TODO: Make translatable string
|
1440
|
1480
|
|
1441
|
|
- do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
1442
|
|
- do_blocking_move_to_xy(lx, ly);
|
1443
|
|
- do {
|
1444
|
|
- location = find_closest_mesh_point_of_type(SET_IN_BITMAP, lx, ly, USE_NOZZLE_AS_REFERENCE, not_done, false);
|
|
1481
|
+ do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
|
1482
|
+ do_blocking_move_to_xy(lx, ly);
|
|
1483
|
+ do {
|
|
1484
|
+ location = find_closest_mesh_point_of_type(SET_IN_BITMAP, lx, ly, USE_NOZZLE_AS_REFERENCE, not_done, false);
|
1445
|
1485
|
|
1446
|
|
- if (location.x_index < 0) break; // stop when we can't find any more reachable points.
|
|
1486
|
+ if (location.x_index < 0) break; // stop when we can't find any more reachable points.
|
1447
|
1487
|
|
1448
|
|
- bit_clear(not_done, location.x_index, location.y_index); // Mark this location as 'adjusted' so we will find a
|
1449
|
|
- // different location the next time through the loop
|
|
1488
|
+ bit_clear(not_done, location.x_index, location.y_index); // Mark this location as 'adjusted' so we will find a
|
|
1489
|
+ // different location the next time through the loop
|
1450
|
1490
|
|
1451
|
|
- const float rawx = mesh_index_to_xpos(location.x_index),
|
1452
|
|
- rawy = mesh_index_to_ypos(location.y_index);
|
|
1491
|
+ const float rawx = mesh_index_to_xpos(location.x_index),
|
|
1492
|
+ rawy = mesh_index_to_ypos(location.y_index);
|
1453
|
1493
|
|
1454
|
|
- if (!position_is_reachable_raw_xy(rawx, rawy)) // SHOULD NOT OCCUR because find_closest_mesh_point_of_type will only return reachable
|
1455
|
|
- break;
|
|
1494
|
+ if (!position_is_reachable_raw_xy(rawx, rawy)) // SHOULD NOT OCCUR because find_closest_mesh_point_of_type will only return reachable
|
|
1495
|
+ break;
|
1456
|
1496
|
|
1457
|
|
- float new_z = z_values[location.x_index][location.y_index];
|
|
1497
|
+ float new_z = z_values[location.x_index][location.y_index];
|
1458
|
1498
|
|
1459
|
|
- if (isnan(new_z)) // if the mesh point is invalid, set it to 0.0 so it can be edited
|
1460
|
|
- new_z = 0.0;
|
|
1499
|
+ if (isnan(new_z)) // if the mesh point is invalid, set it to 0.0 so it can be edited
|
|
1500
|
+ new_z = 0.0;
|
1461
|
1501
|
|
1462
|
|
- do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES); // Move the nozzle to where we are going to edit
|
1463
|
|
- do_blocking_move_to_xy(LOGICAL_X_POSITION(rawx), LOGICAL_Y_POSITION(rawy));
|
|
1502
|
+ do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES); // Move the nozzle to where we are going to edit
|
|
1503
|
+ do_blocking_move_to_xy(LOGICAL_X_POSITION(rawx), LOGICAL_Y_POSITION(rawy));
|
1464
|
1504
|
|
1465
|
|
- new_z = floor(new_z * 1000.0) * 0.001; // Chop off digits after the 1000ths place
|
|
1505
|
+ new_z = floor(new_z * 1000.0) * 0.001; // Chop off digits after the 1000ths place
|
1466
|
1506
|
|
1467
|
|
- KEEPALIVE_STATE(PAUSED_FOR_USER);
|
1468
|
|
- has_control_of_lcd_panel = true;
|
|
1507
|
+ KEEPALIVE_STATE(PAUSED_FOR_USER);
|
|
1508
|
+ has_control_of_lcd_panel = true;
|
1469
|
1509
|
|
1470
|
|
- if (do_ubl_mesh_map) display_map(g29_map_type); // show the user which point is being adjusted
|
|
1510
|
+ if (do_ubl_mesh_map) display_map(g29_map_type); // show the user which point is being adjusted
|
1471
|
1511
|
|
1472
|
|
- lcd_refresh();
|
|
1512
|
+ lcd_refresh();
|
1473
|
1513
|
|
1474
|
|
- lcd_mesh_edit_setup(new_z);
|
|
1514
|
+ lcd_mesh_edit_setup(new_z);
|
1475
|
1515
|
|
1476
|
|
- do {
|
1477
|
|
- new_z = lcd_mesh_edit();
|
1478
|
|
- #ifdef UBL_MESH_EDIT_MOVES_Z
|
1479
|
|
- do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES + new_z); // Move the nozzle as the point is edited
|
1480
|
|
- #endif
|
1481
|
|
- idle();
|
1482
|
|
- } while (!ubl_lcd_clicked());
|
|
1516
|
+ do {
|
|
1517
|
+ new_z = lcd_mesh_edit();
|
|
1518
|
+ #ifdef UBL_MESH_EDIT_MOVES_Z
|
|
1519
|
+ do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES + new_z); // Move the nozzle as the point is edited
|
|
1520
|
+ #endif
|
|
1521
|
+ idle();
|
|
1522
|
+ } while (!ubl_lcd_clicked());
|
1483
|
1523
|
|
1484
|
|
- lcd_return_to_status();
|
|
1524
|
+ lcd_return_to_status();
|
1485
|
1525
|
|
1486
|
|
- // The technique used here generates a race condition for the encoder click.
|
1487
|
|
- // It could get detected in lcd_mesh_edit (actually _lcd_mesh_fine_tune) or here.
|
1488
|
|
- // Let's work on specifying a proper API for the LCD ASAP, OK?
|
1489
|
|
- has_control_of_lcd_panel = true;
|
|
1526
|
+ // The technique used here generates a race condition for the encoder click.
|
|
1527
|
+ // It could get detected in lcd_mesh_edit (actually _lcd_mesh_fine_tune) or here.
|
|
1528
|
+ // Let's work on specifying a proper API for the LCD ASAP, OK?
|
|
1529
|
+ has_control_of_lcd_panel = true;
|
1490
|
1530
|
|
1491
|
|
- // this sequence to detect an ubl_lcd_clicked() debounce it and leave if it is
|
1492
|
|
- // a Press and Hold is repeated in a lot of places (including G26_Mesh_Validation.cpp). This
|
1493
|
|
- // should be redone and compressed.
|
1494
|
|
- const millis_t nxt = millis() + 1500UL;
|
1495
|
|
- while (ubl_lcd_clicked()) { // debounce and watch for abort
|
1496
|
|
- idle();
|
1497
|
|
- if (ELAPSED(millis(), nxt)) {
|
1498
|
|
- lcd_return_to_status();
|
1499
|
|
- //SERIAL_PROTOCOLLNPGM("\nFine Tuning of Mesh Stopped.");
|
1500
|
|
- do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
1501
|
|
- LCD_MESSAGEPGM("Mesh Editing Stopped"); // TODO: Make translatable string
|
|
1531
|
+ // this sequence to detect an ubl_lcd_clicked() debounce it and leave if it is
|
|
1532
|
+ // a Press and Hold is repeated in a lot of places (including G26_Mesh_Validation.cpp). This
|
|
1533
|
+ // should be redone and compressed.
|
|
1534
|
+ const millis_t nxt = millis() + 1500UL;
|
|
1535
|
+ while (ubl_lcd_clicked()) { // debounce and watch for abort
|
|
1536
|
+ idle();
|
|
1537
|
+ if (ELAPSED(millis(), nxt)) {
|
|
1538
|
+ lcd_return_to_status();
|
|
1539
|
+ //SERIAL_PROTOCOLLNPGM("\nFine Tuning of Mesh Stopped.");
|
|
1540
|
+ do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
|
1541
|
+ LCD_MESSAGEPGM("Mesh Editing Stopped"); // TODO: Make translatable string
|
1502
|
1542
|
|
1503
|
|
- while (ubl_lcd_clicked()) idle();
|
|
1543
|
+ while (ubl_lcd_clicked()) idle();
|
1504
|
1544
|
|
1505
|
|
- goto FINE_TUNE_EXIT;
|
|
1545
|
+ goto FINE_TUNE_EXIT;
|
|
1546
|
+ }
|
1506
|
1547
|
}
|
1507
|
|
- }
|
1508
|
1548
|
|
1509
|
|
- safe_delay(20); // We don't want any switch noise.
|
|
1549
|
+ safe_delay(20); // We don't want any switch noise.
|
1510
|
1550
|
|
1511
|
|
- z_values[location.x_index][location.y_index] = new_z;
|
|
1551
|
+ z_values[location.x_index][location.y_index] = new_z;
|
1512
|
1552
|
|
1513
|
|
- lcd_refresh();
|
|
1553
|
+ lcd_refresh();
|
1514
|
1554
|
|
1515
|
|
- } while (location.x_index >= 0 && --g29_repetition_cnt > 0);
|
|
1555
|
+ } while (location.x_index >= 0 && --g29_repetition_cnt > 0);
|
1516
|
1556
|
|
1517
|
|
- FINE_TUNE_EXIT:
|
|
1557
|
+ FINE_TUNE_EXIT:
|
1518
|
1558
|
|
1519
|
|
- has_control_of_lcd_panel = false;
|
1520
|
|
- KEEPALIVE_STATE(IN_HANDLER);
|
|
1559
|
+ has_control_of_lcd_panel = false;
|
|
1560
|
+ KEEPALIVE_STATE(IN_HANDLER);
|
1521
|
1561
|
|
1522
|
|
- if (do_ubl_mesh_map) display_map(g29_map_type);
|
1523
|
|
- restore_ubl_active_state_and_leave();
|
1524
|
|
- do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
|
1562
|
+ if (do_ubl_mesh_map) display_map(g29_map_type);
|
|
1563
|
+ restore_ubl_active_state_and_leave();
|
|
1564
|
+ do_blocking_move_to_z(Z_CLEARANCE_BETWEEN_PROBES);
|
1525
|
1565
|
|
1526
|
|
- do_blocking_move_to_xy(lx, ly);
|
|
1566
|
+ do_blocking_move_to_xy(lx, ly);
|
1527
|
1567
|
|
1528
|
|
- LCD_MESSAGEPGM("Done Editing Mesh"); // TODO: Make translatable string
|
1529
|
|
- SERIAL_ECHOLNPGM("Done Editing Mesh");
|
1530
|
|
- }
|
|
1568
|
+ LCD_MESSAGEPGM("Done Editing Mesh"); // TODO: Make translatable string
|
|
1569
|
+ SERIAL_ECHOLNPGM("Done Editing Mesh");
|
|
1570
|
+ }
|
|
1571
|
+ #endif
|
1531
|
1572
|
|
1532
|
1573
|
/**
|
1533
|
1574
|
* 'Smart Fill': Scan from the outward edges of the mesh towards the center.
|