|
|
@@ -20,12 +20,14 @@
|
|
20
|
20
|
*
|
|
21
|
21
|
*/
|
|
22
|
22
|
|
|
23
|
|
-#include "Marlin.h"
|
|
|
23
|
+#include "MarlinConfig.h"
|
|
|
24
|
+
|
|
24
|
25
|
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
|
25
|
26
|
//#include "vector_3.h"
|
|
26
|
27
|
//#include "qr_solve.h"
|
|
27
|
28
|
|
|
28
|
29
|
#include "UBL.h"
|
|
|
30
|
+ #include "Marlin.h"
|
|
29
|
31
|
#include "hex_print_routines.h"
|
|
30
|
32
|
#include "configuration_store.h"
|
|
31
|
33
|
#include "planner.h"
|
|
|
@@ -49,12 +51,13 @@
|
|
49
|
51
|
#define DEPLOY_PROBE() set_probe_deployed(true)
|
|
50
|
52
|
#define STOW_PROBE() set_probe_deployed(false)
|
|
51
|
53
|
bool ProbeStay = true;
|
|
52
|
|
- float ubl_3_point_1_X = UBL_PROBE_PT_1_X;
|
|
53
|
|
- float ubl_3_point_1_Y = UBL_PROBE_PT_1_Y;
|
|
54
|
|
- float ubl_3_point_2_X = UBL_PROBE_PT_2_X;
|
|
55
|
|
- float ubl_3_point_2_Y = UBL_PROBE_PT_2_Y;
|
|
56
|
|
- float ubl_3_point_3_X = UBL_PROBE_PT_3_X;
|
|
57
|
|
- float ubl_3_point_3_Y = UBL_PROBE_PT_3_Y;
|
|
|
54
|
+
|
|
|
55
|
+ constexpr float ubl_3_point_1_X = UBL_PROBE_PT_1_X,
|
|
|
56
|
+ ubl_3_point_1_Y = UBL_PROBE_PT_1_Y,
|
|
|
57
|
+ ubl_3_point_2_X = UBL_PROBE_PT_2_X,
|
|
|
58
|
+ ubl_3_point_2_Y = UBL_PROBE_PT_2_Y,
|
|
|
59
|
+ ubl_3_point_3_X = UBL_PROBE_PT_3_X,
|
|
|
60
|
+ ubl_3_point_3_Y = UBL_PROBE_PT_3_Y;
|
|
58
|
61
|
|
|
59
|
62
|
#define SIZE_OF_LITTLE_RAISE 0
|
|
60
|
63
|
#define BIG_RAISE_NOT_NEEDED 0
|
|
|
@@ -293,19 +296,16 @@
|
|
293
|
296
|
volatile uint8_t ubl_encoderDiff = 0; // Volatile because it's changed by Temperature ISR button update
|
|
294
|
297
|
|
|
295
|
298
|
// The simple parameter flags and values are 'static' so parameter parsing can be in a support routine.
|
|
296
|
|
- static int g29_verbose_level = 0, test_value = 0,
|
|
297
|
|
- phase_value = -1, repetition_cnt = 1;
|
|
|
299
|
+ static int g29_verbose_level = 0, phase_value = -1, repetition_cnt = 1,
|
|
|
300
|
+ storage_slot = 0, test_pattern = 0;
|
|
298
|
301
|
static bool repeat_flag = UBL_OK, c_flag = false, x_flag = UBL_OK, y_flag = UBL_OK, statistics_flag = UBL_OK, business_card_mode = false;
|
|
299
|
302
|
static float x_pos = 0.0, y_pos = 0.0, height_value = 5.0, measured_z, card_thickness = 0.0, constant = 0.0;
|
|
300
|
|
- static int storage_slot = 0, test_pattern = 0;
|
|
301
|
303
|
|
|
302
|
304
|
#if ENABLED(ULTRA_LCD)
|
|
303
|
305
|
void lcd_setstatus(const char* message, bool persist);
|
|
304
|
306
|
#endif
|
|
305
|
307
|
|
|
306
|
308
|
void gcode_G29() {
|
|
307
|
|
- mesh_index_pair location;
|
|
308
|
|
- int j, k;
|
|
309
|
309
|
float Z1, Z2, Z3;
|
|
310
|
310
|
|
|
311
|
311
|
g29_verbose_level = 0; // These may change, but let's get some reasonable values into them.
|
|
|
@@ -331,7 +331,7 @@
|
|
331
|
331
|
if (code_seen('I')) {
|
|
332
|
332
|
repetition_cnt = code_has_value() ? code_value_int() : 1;
|
|
333
|
333
|
while (repetition_cnt--) {
|
|
334
|
|
- location = find_closest_mesh_point_of_type(REAL, x_pos, y_pos, 0, NULL); // The '0' says we want to use the nozzle's position
|
|
|
334
|
+ const mesh_index_pair location = find_closest_mesh_point_of_type(REAL, x_pos, y_pos, 0, NULL); // The '0' says we want to use the nozzle's position
|
|
335
|
335
|
if (location.x_index < 0) {
|
|
336
|
336
|
SERIAL_PROTOCOLLNPGM("Entire Mesh invalidated.\n");
|
|
337
|
337
|
break; // No more invalid Mesh Points to populate
|
|
|
@@ -409,8 +409,8 @@
|
|
409
|
409
|
SERIAL_ECHOPAIR(",", y_pos);
|
|
410
|
410
|
SERIAL_PROTOCOLLNPGM(")\n");
|
|
411
|
411
|
}
|
|
412
|
|
- probe_entire_mesh( x_pos+X_PROBE_OFFSET_FROM_EXTRUDER, y_pos+Y_PROBE_OFFSET_FROM_EXTRUDER,
|
|
413
|
|
- code_seen('O') || code_seen('M'), code_seen('E'));
|
|
|
412
|
+ probe_entire_mesh(x_pos + X_PROBE_OFFSET_FROM_EXTRUDER, y_pos + Y_PROBE_OFFSET_FROM_EXTRUDER,
|
|
|
413
|
+ code_seen('O') || code_seen('M'), code_seen('E'));
|
|
414
|
414
|
break;
|
|
415
|
415
|
//
|
|
416
|
416
|
// Manually Probe Mesh in areas that can not be reached by the probe
|
|
|
@@ -455,7 +455,7 @@
|
|
455
|
455
|
// If no repetition is specified, do the whole Mesh
|
|
456
|
456
|
if (!repeat_flag) repetition_cnt = 9999;
|
|
457
|
457
|
while (repetition_cnt--) {
|
|
458
|
|
- location = find_closest_mesh_point_of_type(INVALID, x_pos, y_pos, 0, NULL); // The '0' says we want to use the nozzle's position
|
|
|
458
|
+ const mesh_index_pair location = find_closest_mesh_point_of_type(INVALID, x_pos, y_pos, 0, NULL); // The '0' says we want to use the nozzle's position
|
|
459
|
459
|
if (location.x_index < 0) break; // No more invalid Mesh Points to populate
|
|
460
|
460
|
z_values[location.x_index][location.y_index] = height_value;
|
|
461
|
461
|
}
|
|
|
@@ -534,8 +534,7 @@
|
|
534
|
534
|
if (code_seen('L')) { // Load Current Mesh Data
|
|
535
|
535
|
storage_slot = code_has_value() ? code_value_int() : ubl.state.eeprom_storage_slot;
|
|
536
|
536
|
|
|
537
|
|
- k = E2END - sizeof(ubl.state);
|
|
538
|
|
- j = (k - ubl_eeprom_start) / sizeof(z_values);
|
|
|
537
|
+ const int16_t j = (UBL_LAST_EEPROM_INDEX - ubl_eeprom_start) / sizeof(z_values);
|
|
539
|
538
|
|
|
540
|
539
|
if (storage_slot < 0 || storage_slot >= j || ubl_eeprom_start <= 0) {
|
|
541
|
540
|
SERIAL_PROTOCOLLNPGM("?EEPROM storage not available for use.\n");
|
|
|
@@ -569,8 +568,7 @@
|
|
569
|
568
|
return;
|
|
570
|
569
|
}
|
|
571
|
570
|
|
|
572
|
|
- int k = E2END - sizeof(ubl.state),
|
|
573
|
|
- j = (k - ubl_eeprom_start) / sizeof(z_values);
|
|
|
571
|
+ const int16_t j = (UBL_LAST_EEPROM_INDEX - ubl_eeprom_start) / sizeof(z_values);
|
|
574
|
572
|
|
|
575
|
573
|
if (storage_slot < 0 || storage_slot >= j || ubl_eeprom_start <= 0) {
|
|
576
|
574
|
SERIAL_PROTOCOLLNPGM("?EEPROM storage not available for use.\n");
|
|
|
@@ -691,7 +689,7 @@
|
|
691
|
689
|
z_values[x][y] -= mean + constant;
|
|
692
|
690
|
}
|
|
693
|
691
|
|
|
694
|
|
- void shift_mesh_height( ) {
|
|
|
692
|
+ void shift_mesh_height() {
|
|
695
|
693
|
for (uint8_t x = 0; x < UBL_MESH_NUM_X_POINTS; x++)
|
|
696
|
694
|
for (uint8_t y = 0; y < UBL_MESH_NUM_Y_POINTS; y++)
|
|
697
|
695
|
if (!isnan(z_values[x][y]))
|
|
|
@@ -702,9 +700,8 @@
|
|
702
|
700
|
* Probe all invalidated locations of the mesh that can be reached by the probe.
|
|
703
|
701
|
* This attempts to fill in locations closest to the nozzle's start location first.
|
|
704
|
702
|
*/
|
|
705
|
|
- void probe_entire_mesh(float x_pos, float y_pos, bool do_ubl_mesh_map, bool stow_probe) {
|
|
|
703
|
+ void probe_entire_mesh(const float &lx, const float &ly, const bool do_ubl_mesh_map, const bool stow_probe) {
|
|
706
|
704
|
mesh_index_pair location;
|
|
707
|
|
- float xProbe, yProbe, measured_z;
|
|
708
|
705
|
|
|
709
|
706
|
ubl_has_control_of_lcd_panel++;
|
|
710
|
707
|
save_ubl_active_state_and_disable(); // we don't do bed level correction because we want the raw data when we probe
|
|
|
@@ -720,20 +717,22 @@
|
|
720
|
717
|
restore_ubl_active_state_and_leave();
|
|
721
|
718
|
return;
|
|
722
|
719
|
}
|
|
723
|
|
- location = find_closest_mesh_point_of_type(INVALID, x_pos, y_pos, 1, NULL); // the '1' says we want the location to be relative to the probe
|
|
|
720
|
+
|
|
|
721
|
+ location = find_closest_mesh_point_of_type(INVALID, lx, ly, 1, NULL); // the '1' says we want the location to be relative to the probe
|
|
724
|
722
|
if (location.x_index >= 0 && location.y_index >= 0) {
|
|
725
|
|
- xProbe = ubl.map_x_index_to_bed_location(location.x_index);
|
|
726
|
|
- yProbe = ubl.map_y_index_to_bed_location(location.y_index);
|
|
|
723
|
+ const float xProbe = ubl.map_x_index_to_bed_location(location.x_index),
|
|
|
724
|
+ yProbe = ubl.map_y_index_to_bed_location(location.y_index);
|
|
727
|
725
|
if (xProbe < MIN_PROBE_X || xProbe > MAX_PROBE_X || yProbe < MIN_PROBE_Y || yProbe > MAX_PROBE_Y) {
|
|
728
|
726
|
SERIAL_PROTOCOLLNPGM("?Error: Attempt to probe off the bed.");
|
|
729
|
727
|
ubl_has_control_of_lcd_panel = false;
|
|
730
|
728
|
goto LEAVE;
|
|
731
|
729
|
}
|
|
732
|
|
- measured_z = probe_pt(xProbe, yProbe, stow_probe, g29_verbose_level);
|
|
|
730
|
+ const float measured_z = probe_pt(xProbe, yProbe, stow_probe, g29_verbose_level);
|
|
733
|
731
|
z_values[location.x_index][location.y_index] = measured_z + Z_PROBE_OFFSET_FROM_EXTRUDER;
|
|
734
|
732
|
}
|
|
735
|
733
|
|
|
736
|
734
|
if (do_ubl_mesh_map) ubl.display_map(1);
|
|
|
735
|
+
|
|
737
|
736
|
} while (location.x_index >= 0 && location.y_index >= 0);
|
|
738
|
737
|
|
|
739
|
738
|
LEAVE:
|
|
|
@@ -742,32 +741,27 @@
|
|
742
|
741
|
STOW_PROBE();
|
|
743
|
742
|
restore_ubl_active_state_and_leave();
|
|
744
|
743
|
|
|
745
|
|
- x_pos = constrain(x_pos - (X_PROBE_OFFSET_FROM_EXTRUDER), X_MIN_POS, X_MAX_POS);
|
|
746
|
|
- y_pos = constrain(y_pos - (Y_PROBE_OFFSET_FROM_EXTRUDER), Y_MIN_POS, Y_MAX_POS);
|
|
747
|
|
-
|
|
748
|
|
- do_blocking_move_to_xy(x_pos, y_pos);
|
|
|
744
|
+ do_blocking_move_to_xy(
|
|
|
745
|
+ constrain(lx - (X_PROBE_OFFSET_FROM_EXTRUDER), X_MIN_POS, X_MAX_POS),
|
|
|
746
|
+ constrain(ly - (Y_PROBE_OFFSET_FROM_EXTRUDER), Y_MIN_POS, Y_MAX_POS)
|
|
|
747
|
+ );
|
|
749
|
748
|
}
|
|
750
|
749
|
|
|
751
|
|
- vector tilt_mesh_based_on_3pts(float pt1, float pt2, float pt3) {
|
|
752
|
|
- vector v1, v2, normal;
|
|
|
750
|
+ vector_3 tilt_mesh_based_on_3pts(const float &pt1, const float &pt2, const float &pt3) {
|
|
753
|
751
|
float c, d, t;
|
|
754
|
752
|
int i, j;
|
|
755
|
753
|
|
|
756
|
|
- v1.dx = (ubl_3_point_1_X - ubl_3_point_2_X);
|
|
757
|
|
- v1.dy = (ubl_3_point_1_Y - ubl_3_point_2_Y);
|
|
758
|
|
- v1.dz = (pt1 - pt2);
|
|
759
|
|
-
|
|
760
|
|
- v2.dx = (ubl_3_point_3_X - ubl_3_point_2_X);
|
|
761
|
|
- v2.dy = (ubl_3_point_3_Y - ubl_3_point_2_Y);
|
|
762
|
|
- v2.dz = (pt3 - pt2);
|
|
|
754
|
+ vector_3 v1 = vector_3( (ubl_3_point_1_X - ubl_3_point_2_X),
|
|
|
755
|
+ (ubl_3_point_1_Y - ubl_3_point_2_Y),
|
|
|
756
|
+ (pt1 - pt2) ),
|
|
763
|
757
|
|
|
764
|
|
- // do cross product
|
|
|
758
|
+ v2 = vector_3( (ubl_3_point_3_X - ubl_3_point_2_X),
|
|
|
759
|
+ (ubl_3_point_3_Y - ubl_3_point_2_Y),
|
|
|
760
|
+ (pt3 - pt2) ),
|
|
765
|
761
|
|
|
766
|
|
- normal.dx = v1.dy * v2.dz - v1.dz * v2.dy;
|
|
767
|
|
- normal.dy = v1.dz * v2.dx - v1.dx * v2.dz;
|
|
768
|
|
- normal.dz = v1.dx * v2.dy - v1.dy * v2.dx;
|
|
|
762
|
+ normal = vector_3::cross(v1, v2);
|
|
769
|
763
|
|
|
770
|
|
- // printf("[%f,%f,%f] ", normal.dx, normal.dy, normal.dz);
|
|
|
764
|
+ // printf("[%f,%f,%f] ", normal.x, normal.y, normal.z);
|
|
771
|
765
|
|
|
772
|
766
|
/**
|
|
773
|
767
|
* This code does two things. This vector is normal to the tilted plane.
|
|
|
@@ -776,31 +770,32 @@
|
|
776
|
770
|
* We also need Z to be unity because we are going to be treating this triangle
|
|
777
|
771
|
* as the sin() and cos() of the bed's tilt
|
|
778
|
772
|
*/
|
|
779
|
|
- normal.dx /= normal.dz;
|
|
780
|
|
- normal.dy /= normal.dz;
|
|
781
|
|
- normal.dz /= normal.dz;
|
|
|
773
|
+ const float inv_z = 1.0 / normal.z;
|
|
|
774
|
+ normal.x *= inv_z;
|
|
|
775
|
+ normal.y *= inv_z;
|
|
|
776
|
+ normal.z = 1.0;
|
|
782
|
777
|
|
|
783
|
778
|
//
|
|
784
|
779
|
// All of 3 of these points should give us the same d constant
|
|
785
|
780
|
//
|
|
786
|
|
- t = normal.dx * ubl_3_point_1_X + normal.dy * ubl_3_point_1_Y;
|
|
787
|
|
- d = t + normal.dz * pt1;
|
|
|
781
|
+ t = normal.x * ubl_3_point_1_X + normal.y * ubl_3_point_1_Y;
|
|
|
782
|
+ d = t + normal.z * pt1;
|
|
788
|
783
|
c = d - t;
|
|
789
|
784
|
SERIAL_ECHOPGM("d from 1st point: ");
|
|
790
|
785
|
SERIAL_ECHO_F(d, 6);
|
|
791
|
786
|
SERIAL_ECHOPGM(" c: ");
|
|
792
|
787
|
SERIAL_ECHO_F(c, 6);
|
|
793
|
788
|
SERIAL_EOL;
|
|
794
|
|
- t = normal.dx * ubl_3_point_2_X + normal.dy * ubl_3_point_2_Y;
|
|
795
|
|
- d = t + normal.dz * pt2;
|
|
|
789
|
+ t = normal.x * ubl_3_point_2_X + normal.y * ubl_3_point_2_Y;
|
|
|
790
|
+ d = t + normal.z * pt2;
|
|
796
|
791
|
c = d - t;
|
|
797
|
792
|
SERIAL_ECHOPGM("d from 2nd point: ");
|
|
798
|
793
|
SERIAL_ECHO_F(d, 6);
|
|
799
|
794
|
SERIAL_ECHOPGM(" c: ");
|
|
800
|
795
|
SERIAL_ECHO_F(c, 6);
|
|
801
|
796
|
SERIAL_EOL;
|
|
802
|
|
- t = normal.dx * ubl_3_point_3_X + normal.dy * ubl_3_point_3_Y;
|
|
803
|
|
- d = t + normal.dz * pt3;
|
|
|
797
|
+ t = normal.x * ubl_3_point_3_X + normal.y * ubl_3_point_3_Y;
|
|
|
798
|
+ d = t + normal.z * pt3;
|
|
804
|
799
|
c = d - t;
|
|
805
|
800
|
SERIAL_ECHOPGM("d from 3rd point: ");
|
|
806
|
801
|
SERIAL_ECHO_F(d, 6);
|
|
|
@@ -810,7 +805,7 @@
|
|
810
|
805
|
|
|
811
|
806
|
for (i = 0; i < UBL_MESH_NUM_X_POINTS; i++) {
|
|
812
|
807
|
for (j = 0; j < UBL_MESH_NUM_Y_POINTS; j++) {
|
|
813
|
|
- c = -((normal.dx * (UBL_MESH_MIN_X + i * (MESH_X_DIST)) + normal.dy * (UBL_MESH_MIN_Y + j * (MESH_Y_DIST))) - d);
|
|
|
808
|
+ c = -((normal.x * (UBL_MESH_MIN_X + i * (MESH_X_DIST)) + normal.y * (UBL_MESH_MIN_Y + j * (MESH_Y_DIST))) - d);
|
|
814
|
809
|
z_values[i][j] += c;
|
|
815
|
810
|
}
|
|
816
|
811
|
}
|
|
|
@@ -829,7 +824,7 @@
|
|
829
|
824
|
return current_position[Z_AXIS];
|
|
830
|
825
|
}
|
|
831
|
826
|
|
|
832
|
|
- float measure_business_card_thickness(float height_value) {
|
|
|
827
|
+ float measure_business_card_thickness(const float &height_value) {
|
|
833
|
828
|
|
|
834
|
829
|
ubl_has_control_of_lcd_panel++;
|
|
835
|
830
|
save_ubl_active_state_and_disable(); // we don't do bed level correction because we want the raw data when we probe
|
|
|
@@ -856,44 +851,45 @@
|
|
856
|
851
|
return abs(Z1 - Z2);
|
|
857
|
852
|
}
|
|
858
|
853
|
|
|
859
|
|
- void manually_probe_remaining_mesh(float x_pos, float y_pos, float z_clearance, float card_thickness, bool do_ubl_mesh_map) {
|
|
860
|
|
- mesh_index_pair location;
|
|
861
|
|
- float last_x, last_y, dx, dy,
|
|
862
|
|
- xProbe, yProbe;
|
|
|
854
|
+ void manually_probe_remaining_mesh(const float &lx, const float &ly, const float &z_clearance, const float &card_thickness, const bool do_ubl_mesh_map) {
|
|
863
|
855
|
|
|
864
|
856
|
ubl_has_control_of_lcd_panel++;
|
|
865
|
|
- last_x = last_y = -9999.99;
|
|
866
|
857
|
save_ubl_active_state_and_disable(); // we don't do bed level correction because we want the raw data when we probe
|
|
867
|
858
|
do_blocking_move_to_z(z_clearance);
|
|
868
|
|
- do_blocking_move_to_xy(x_pos, y_pos);
|
|
|
859
|
+ do_blocking_move_to_xy(lx, ly);
|
|
869
|
860
|
|
|
|
861
|
+ float last_x = -9999.99, last_y = -9999.99;
|
|
|
862
|
+ mesh_index_pair location;
|
|
870
|
863
|
do {
|
|
871
|
864
|
if (do_ubl_mesh_map) ubl.display_map(1);
|
|
872
|
865
|
|
|
873
|
|
- location = find_closest_mesh_point_of_type(INVALID, x_pos, y_pos, 0, NULL); // The '0' says we want to use the nozzle's position
|
|
|
866
|
+ location = find_closest_mesh_point_of_type(INVALID, lx, ly, 0, NULL); // The '0' says we want to use the nozzle's position
|
|
874
|
867
|
// It doesn't matter if the probe can not reach the
|
|
875
|
868
|
// NAN location. This is a manual probe.
|
|
876
|
869
|
if (location.x_index < 0 && location.y_index < 0) continue;
|
|
877
|
870
|
|
|
878
|
|
- xProbe = ubl.map_x_index_to_bed_location(location.x_index);
|
|
879
|
|
- yProbe = ubl.map_y_index_to_bed_location(location.y_index);
|
|
|
871
|
+ const float xProbe = ubl.map_x_index_to_bed_location(location.x_index),
|
|
|
872
|
+ yProbe = ubl.map_y_index_to_bed_location(location.y_index);
|
|
|
873
|
+
|
|
|
874
|
+ // Modify to use if (position_is_reachable(pos[XYZ]))
|
|
880
|
875
|
if (xProbe < (X_MIN_POS) || xProbe > (X_MAX_POS) || yProbe < (Y_MIN_POS) || yProbe > (Y_MAX_POS)) {
|
|
881
|
876
|
SERIAL_PROTOCOLLNPGM("?Error: Attempt to probe off the bed.");
|
|
882
|
877
|
ubl_has_control_of_lcd_panel = false;
|
|
883
|
878
|
goto LEAVE;
|
|
884
|
879
|
}
|
|
885
|
880
|
|
|
886
|
|
- dx = xProbe - last_x;
|
|
887
|
|
- dy = yProbe - last_y;
|
|
|
881
|
+ const float dx = xProbe - last_x,
|
|
|
882
|
+ dy = yProbe - last_y;
|
|
888
|
883
|
|
|
889
|
884
|
if (HYPOT(dx, dy) < BIG_RAISE_NOT_NEEDED)
|
|
890
|
885
|
do_blocking_move_to_z(current_position[Z_AXIS] + SIZE_OF_LITTLE_RAISE);
|
|
891
|
886
|
else
|
|
892
|
887
|
do_blocking_move_to_z(z_clearance);
|
|
893
|
888
|
|
|
|
889
|
+ do_blocking_move_to_xy(xProbe, yProbe);
|
|
|
890
|
+
|
|
894
|
891
|
last_x = xProbe;
|
|
895
|
892
|
last_y = yProbe;
|
|
896
|
|
- do_blocking_move_to_xy(xProbe, yProbe);
|
|
897
|
893
|
|
|
898
|
894
|
wait_for_user = true;
|
|
899
|
895
|
while (wait_for_user) { // we need the loop to move the nozzle based on the encoder wheel here!
|
|
|
@@ -931,7 +927,7 @@
|
|
931
|
927
|
LEAVE:
|
|
932
|
928
|
restore_ubl_active_state_and_leave();
|
|
933
|
929
|
do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE);
|
|
934
|
|
- do_blocking_move_to_xy(x_pos, y_pos);
|
|
|
930
|
+ do_blocking_move_to_xy(lx, ly);
|
|
935
|
931
|
}
|
|
936
|
932
|
|
|
937
|
933
|
bool g29_parameter_parsing() {
|
|
|
@@ -983,7 +979,7 @@
|
|
983
|
979
|
ubl.store_state();
|
|
984
|
980
|
}
|
|
985
|
981
|
|
|
986
|
|
- if ((c_flag = code_seen('C')) && code_has_value())
|
|
|
982
|
+ if ((c_flag = code_seen('C') && code_has_value()))
|
|
987
|
983
|
constant = code_value_float();
|
|
988
|
984
|
|
|
989
|
985
|
if (code_seen('D')) { // Disable the Unified Bed Leveling System
|
|
|
@@ -992,19 +988,17 @@
|
|
992
|
988
|
ubl.store_state();
|
|
993
|
989
|
}
|
|
994
|
990
|
|
|
995
|
|
- if (code_seen('F')) {
|
|
996
|
|
- ubl.state.g29_correction_fade_height = 10.00;
|
|
997
|
|
- if (code_has_value()) {
|
|
998
|
|
- ubl.state.g29_correction_fade_height = code_value_float();
|
|
999
|
|
- ubl.state.g29_fade_height_multiplier = 1.0 / ubl.state.g29_correction_fade_height;
|
|
1000
|
|
- }
|
|
1001
|
|
- if (ubl.state.g29_correction_fade_height < 0.0 || ubl.state.g29_correction_fade_height > 100.0) {
|
|
1002
|
|
- SERIAL_PROTOCOLLNPGM("?Bed Level Correction Fade Height Not Plausible.\n");
|
|
1003
|
|
- ubl.state.g29_correction_fade_height = 10.00;
|
|
1004
|
|
- ubl.state.g29_fade_height_multiplier = 1.0 / ubl.state.g29_correction_fade_height;
|
|
1005
|
|
- return UBL_ERR;
|
|
|
991
|
+ #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
|
|
992
|
+ if (code_seen('F') && code_has_value()) {
|
|
|
993
|
+ const float fh = code_value_float();
|
|
|
994
|
+ if (fh < 0.0 || fh > 100.0) {
|
|
|
995
|
+ SERIAL_PROTOCOLLNPGM("?Bed Level Correction Fade Height Not Plausible.\n");
|
|
|
996
|
+ return UBL_ERR;
|
|
|
997
|
+ }
|
|
|
998
|
+ ubl.state.g29_correction_fade_height = fh;
|
|
|
999
|
+ ubl.state.g29_fade_height_multiplier = 1.0 / fh;
|
|
1006
|
1000
|
}
|
|
1007
|
|
- }
|
|
|
1001
|
+ #endif
|
|
1008
|
1002
|
|
|
1009
|
1003
|
if ((repeat_flag = code_seen('R'))) {
|
|
1010
|
1004
|
repetition_cnt = code_has_value() ? code_value_int() : 9999;
|
|
|
@@ -1020,7 +1014,7 @@
|
|
1020
|
1014
|
* This function goes away after G29 debug is complete. But for right now, it is a handy
|
|
1021
|
1015
|
* routine to dump binary data structures.
|
|
1022
|
1016
|
*/
|
|
1023
|
|
- void dump(char *str, float f) {
|
|
|
1017
|
+ void dump(char * const str, const float &f) {
|
|
1024
|
1018
|
char *ptr;
|
|
1025
|
1019
|
|
|
1026
|
1020
|
SERIAL_PROTOCOL(str);
|
|
|
@@ -1056,7 +1050,6 @@
|
|
1056
|
1050
|
}
|
|
1057
|
1051
|
ubl_state_at_invocation = ubl.state.active;
|
|
1058
|
1052
|
ubl.state.active = 0;
|
|
1059
|
|
- return;
|
|
1060
|
1053
|
}
|
|
1061
|
1054
|
|
|
1062
|
1055
|
void restore_ubl_active_state_and_leave() {
|
|
|
@@ -1075,33 +1068,27 @@
|
|
1075
|
1068
|
* good to have the extra information. Soon... we prune this to just a few items
|
|
1076
|
1069
|
*/
|
|
1077
|
1070
|
void g29_what_command() {
|
|
1078
|
|
- int k = E2END - ubl_eeprom_start;
|
|
|
1071
|
+ const uint16_t k = E2END - ubl_eeprom_start;
|
|
1079
|
1072
|
statistics_flag++;
|
|
1080
|
1073
|
|
|
1081
|
1074
|
SERIAL_PROTOCOLPGM("Unified Bed Leveling System Version 1.00 ");
|
|
1082
|
|
- if (ubl.state.active)
|
|
1083
|
|
- SERIAL_PROTOCOLPGM("Active.\n");
|
|
1084
|
|
- else
|
|
1085
|
|
- SERIAL_PROTOCOLPGM("Inactive.\n");
|
|
1086
|
|
- SERIAL_EOL;
|
|
|
1075
|
+ ubl.state.active ? SERIAL_PROTOCOLCHAR('A') : SERIAL_PROTOCOLPGM("In");
|
|
|
1076
|
+ SERIAL_PROTOCOLLNPGM("ctive.\n");
|
|
1087
|
1077
|
delay(50);
|
|
1088
|
1078
|
|
|
1089
|
|
- if (ubl.state.eeprom_storage_slot == 0xFFFF) {
|
|
|
1079
|
+ if (ubl.state.eeprom_storage_slot == -1)
|
|
1090
|
1080
|
SERIAL_PROTOCOLPGM("No Mesh Loaded.");
|
|
1091
|
|
- }
|
|
1092
|
1081
|
else {
|
|
1093
|
1082
|
SERIAL_PROTOCOLPGM("Mesh: ");
|
|
1094
|
1083
|
prt_hex_word(ubl.state.eeprom_storage_slot);
|
|
1095
|
|
- SERIAL_PROTOCOLPGM(" Loaded. ");
|
|
|
1084
|
+ SERIAL_PROTOCOLPGM(" Loaded.");
|
|
1096
|
1085
|
}
|
|
1097
|
|
-
|
|
1098
|
1086
|
SERIAL_EOL;
|
|
1099
|
|
- delay(50);
|
|
1100
|
1087
|
|
|
1101
|
|
- SERIAL_PROTOCOLPAIR("g29_correction_fade_height : ", ubl.state.g29_correction_fade_height );
|
|
1102
|
|
- SERIAL_EOL;
|
|
1103
|
|
-
|
|
1104
|
|
- idle();
|
|
|
1088
|
+ #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
|
|
|
1089
|
+ SERIAL_PROTOCOLPAIR("g29_correction_fade_height : ", ubl.state.g29_correction_fade_height);
|
|
|
1090
|
+ SERIAL_EOL;
|
|
|
1091
|
+ #endif
|
|
1105
|
1092
|
|
|
1106
|
1093
|
SERIAL_PROTOCOLPGM("z_offset: ");
|
|
1107
|
1094
|
SERIAL_PROTOCOL_F(ubl.state.z_offset, 6);
|
|
|
@@ -1111,28 +1098,20 @@
|
|
1111
|
1098
|
for (uint8_t i = 0; i < UBL_MESH_NUM_X_POINTS; i++) {
|
|
1112
|
1099
|
SERIAL_PROTOCOL_F( ubl.map_x_index_to_bed_location(i), 1);
|
|
1113
|
1100
|
SERIAL_PROTOCOLPGM(" ");
|
|
1114
|
|
- delay(10);
|
|
1115
|
1101
|
}
|
|
1116
|
1102
|
SERIAL_EOL;
|
|
1117
|
|
- delay(50);
|
|
1118
|
|
- idle();
|
|
1119
|
1103
|
|
|
1120
|
1104
|
SERIAL_PROTOCOLPGM("Y-Axis Mesh Points at: ");
|
|
1121
|
1105
|
for (uint8_t i = 0; i < UBL_MESH_NUM_Y_POINTS; i++) {
|
|
1122
|
1106
|
SERIAL_PROTOCOL_F( ubl.map_y_index_to_bed_location(i), 1);
|
|
1123
|
1107
|
SERIAL_PROTOCOLPGM(" ");
|
|
1124
|
|
- delay(10);
|
|
1125
|
1108
|
}
|
|
1126
|
1109
|
SERIAL_EOL;
|
|
1127
|
|
- delay(50);
|
|
1128
|
|
- idle();
|
|
1129
|
1110
|
|
|
1130
|
1111
|
#if HAS_KILL
|
|
1131
|
1112
|
SERIAL_PROTOCOLPAIR("Kill pin on :", KILL_PIN);
|
|
1132
|
1113
|
SERIAL_PROTOCOLLNPAIR(" state:", READ(KILL_PIN));
|
|
1133
|
1114
|
#endif
|
|
1134
|
|
- delay(50);
|
|
1135
|
|
- idle();
|
|
1136
|
1115
|
SERIAL_EOL;
|
|
1137
|
1116
|
|
|
1138
|
1117
|
SERIAL_PROTOCOLLNPAIR("ubl_state_at_invocation :", ubl_state_at_invocation);
|
|
|
@@ -1142,54 +1121,39 @@
|
|
1142
|
1121
|
SERIAL_PROTOCOLPGM("Free EEPROM space starts at: 0x");
|
|
1143
|
1122
|
prt_hex_word(ubl_eeprom_start);
|
|
1144
|
1123
|
SERIAL_EOL;
|
|
1145
|
|
- delay(50);
|
|
1146
|
|
- idle();
|
|
1147
|
1124
|
|
|
1148
|
1125
|
SERIAL_PROTOCOLPGM("end of EEPROM : ");
|
|
1149
|
1126
|
prt_hex_word(E2END);
|
|
1150
|
1127
|
SERIAL_EOL;
|
|
1151
|
|
- delay(50);
|
|
1152
|
|
- idle();
|
|
1153
|
1128
|
|
|
1154
|
1129
|
SERIAL_PROTOCOLLNPAIR("sizeof(ubl) : ", (int)sizeof(ubl));
|
|
1155
|
1130
|
SERIAL_EOL;
|
|
1156
|
1131
|
SERIAL_PROTOCOLLNPAIR("z_value[][] size: ", (int)sizeof(z_values));
|
|
1157
|
1132
|
SERIAL_EOL;
|
|
1158
|
|
- delay(50);
|
|
1159
|
|
- idle();
|
|
1160
|
1133
|
|
|
1161
|
1134
|
SERIAL_PROTOCOLPGM("EEPROM free for UBL: 0x");
|
|
1162
|
1135
|
prt_hex_word(k);
|
|
1163
|
1136
|
SERIAL_EOL;
|
|
1164
|
|
- idle();
|
|
1165
|
1137
|
|
|
1166
|
1138
|
SERIAL_PROTOCOLPGM("EEPROM can hold 0x");
|
|
1167
|
1139
|
prt_hex_word(k / sizeof(z_values));
|
|
1168
|
1140
|
SERIAL_PROTOCOLLNPGM(" meshes.\n");
|
|
1169
|
|
- delay(50);
|
|
1170
|
1141
|
|
|
1171
|
1142
|
SERIAL_PROTOCOLPGM("sizeof(ubl.state) :");
|
|
1172
|
1143
|
prt_hex_word(sizeof(ubl.state));
|
|
1173
|
|
- idle();
|
|
1174
|
1144
|
|
|
1175
|
1145
|
SERIAL_PROTOCOLPAIR("\nUBL_MESH_NUM_X_POINTS ", UBL_MESH_NUM_X_POINTS);
|
|
1176
|
1146
|
SERIAL_PROTOCOLPAIR("\nUBL_MESH_NUM_Y_POINTS ", UBL_MESH_NUM_Y_POINTS);
|
|
1177
|
1147
|
SERIAL_PROTOCOLPAIR("\nUBL_MESH_MIN_X ", UBL_MESH_MIN_X);
|
|
1178
|
|
- delay(50);
|
|
1179
|
|
- idle();
|
|
1180
|
1148
|
SERIAL_PROTOCOLPAIR("\nUBL_MESH_MIN_Y ", UBL_MESH_MIN_Y);
|
|
1181
|
1149
|
SERIAL_PROTOCOLPAIR("\nUBL_MESH_MAX_X ", UBL_MESH_MAX_X);
|
|
1182
|
1150
|
SERIAL_PROTOCOLPAIR("\nUBL_MESH_MAX_Y ", UBL_MESH_MAX_Y);
|
|
1183
|
|
- delay(50);
|
|
1184
|
|
- idle();
|
|
1185
|
1151
|
SERIAL_PROTOCOLPGM("\nMESH_X_DIST ");
|
|
1186
|
1152
|
SERIAL_PROTOCOL_F(MESH_X_DIST, 6);
|
|
1187
|
1153
|
SERIAL_PROTOCOLPGM("\nMESH_Y_DIST ");
|
|
1188
|
1154
|
SERIAL_PROTOCOL_F(MESH_Y_DIST, 6);
|
|
1189
|
1155
|
SERIAL_EOL;
|
|
1190
|
1156
|
|
|
1191
|
|
- idle();
|
|
1192
|
|
-
|
|
1193
|
1157
|
if (!ubl.sanity_check())
|
|
1194
|
1158
|
SERIAL_PROTOCOLLNPGM("Unified Bed Leveling sanity checks passed.");
|
|
1195
|
1159
|
}
|
|
|
@@ -1205,7 +1169,7 @@
|
|
1205
|
1169
|
SERIAL_ECHO_START;
|
|
1206
|
1170
|
SERIAL_ECHOLNPGM("EEPROM Dump:");
|
|
1207
|
1171
|
for (uint16_t i = 0; i < E2END + 1; i += 16) {
|
|
1208
|
|
- if (i & 0x3 == 0) idle();
|
|
|
1172
|
+ if (!(i & 0x3)) idle();
|
|
1209
|
1173
|
prt_hex_word(i);
|
|
1210
|
1174
|
SERIAL_ECHOPGM(": ");
|
|
1211
|
1175
|
for (uint16_t j = 0; j < 16; j++) {
|
|
|
@@ -1217,7 +1181,6 @@
|
|
1217
|
1181
|
SERIAL_EOL;
|
|
1218
|
1182
|
}
|
|
1219
|
1183
|
SERIAL_EOL;
|
|
1220
|
|
- return;
|
|
1221
|
1184
|
}
|
|
1222
|
1185
|
|
|
1223
|
1186
|
/**
|
|
|
@@ -1233,15 +1196,14 @@
|
|
1233
|
1196
|
}
|
|
1234
|
1197
|
storage_slot = code_value_int();
|
|
1235
|
1198
|
|
|
1236
|
|
- uint16_t k = E2END - sizeof(ubl.state),
|
|
1237
|
|
- j = (k - ubl_eeprom_start) / sizeof(tmp_z_values);
|
|
|
1199
|
+ int16_t j = (UBL_LAST_EEPROM_INDEX - ubl_eeprom_start) / sizeof(tmp_z_values);
|
|
1238
|
1200
|
|
|
1239
|
1201
|
if (storage_slot < 0 || storage_slot > j || ubl_eeprom_start <= 0) {
|
|
1240
|
1202
|
SERIAL_PROTOCOLLNPGM("?EEPROM storage not available for use.\n");
|
|
1241
|
1203
|
return;
|
|
1242
|
1204
|
}
|
|
1243
|
1205
|
|
|
1244
|
|
- j = k - (storage_slot + 1) * sizeof(tmp_z_values);
|
|
|
1206
|
+ j = UBL_LAST_EEPROM_INDEX - (storage_slot + 1) * sizeof(tmp_z_values);
|
|
1245
|
1207
|
eeprom_read_block((void *)&tmp_z_values, (void *)j, sizeof(tmp_z_values));
|
|
1246
|
1208
|
|
|
1247
|
1209
|
SERIAL_ECHOPAIR("Subtracting Mesh ", storage_slot);
|
|
|
@@ -1254,23 +1216,19 @@
|
|
1254
|
1216
|
z_values[x][y] = z_values[x][y] - tmp_z_values[x][y];
|
|
1255
|
1217
|
}
|
|
1256
|
1218
|
|
|
1257
|
|
- mesh_index_pair find_closest_mesh_point_of_type(MeshPointType type, float X, float Y, bool probe_as_reference, unsigned int bits[16]) {
|
|
|
1219
|
+ mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType type, const float &lx, const float &ly, const bool probe_as_reference, unsigned int bits[16]) {
|
|
1258
|
1220
|
int i, j;
|
|
1259
|
|
- float f, px, py, mx, my, dx, dy, closest = 99999.99,
|
|
1260
|
|
- current_x, current_y, distance;
|
|
|
1221
|
+ float closest = 99999.99;
|
|
1261
|
1222
|
mesh_index_pair return_val;
|
|
1262
|
1223
|
|
|
1263
|
1224
|
return_val.x_index = return_val.y_index = -1;
|
|
1264
|
1225
|
|
|
1265
|
|
- current_x = current_position[X_AXIS];
|
|
1266
|
|
- current_y = current_position[Y_AXIS];
|
|
|
1226
|
+ const float current_x = current_position[X_AXIS],
|
|
|
1227
|
+ current_y = current_position[Y_AXIS];
|
|
1267
|
1228
|
|
|
1268
|
|
- px = X; // Get our reference position. Either the nozzle or
|
|
1269
|
|
- py = Y; // the probe location.
|
|
1270
|
|
- if (probe_as_reference) {
|
|
1271
|
|
- px -= X_PROBE_OFFSET_FROM_EXTRUDER;
|
|
1272
|
|
- py -= Y_PROBE_OFFSET_FROM_EXTRUDER;
|
|
1273
|
|
- }
|
|
|
1229
|
+ // Get our reference position. Either the nozzle or probe location.
|
|
|
1230
|
+ const float px = lx - (probe_as_reference ? X_PROBE_OFFSET_FROM_EXTRUDER : 0),
|
|
|
1231
|
+ py = ly - (probe_as_reference ? Y_PROBE_OFFSET_FROM_EXTRUDER : 0);
|
|
1274
|
1232
|
|
|
1275
|
1233
|
for (i = 0; i < UBL_MESH_NUM_X_POINTS; i++) {
|
|
1276
|
1234
|
for (j = 0; j < UBL_MESH_NUM_Y_POINTS; j++) {
|
|
|
@@ -1282,24 +1240,20 @@
|
|
1282
|
1240
|
|
|
1283
|
1241
|
// We only get here if we found a Mesh Point of the specified type
|
|
1284
|
1242
|
|
|
1285
|
|
- mx = ubl.map_x_index_to_bed_location(i); // Check if we can probe this mesh location
|
|
1286
|
|
- my = ubl.map_y_index_to_bed_location(j);
|
|
|
1243
|
+ const float mx = LOGICAL_X_POSITION(ubl.map_x_index_to_bed_location(i)), // Check if we can probe this mesh location
|
|
|
1244
|
+ my = LOGICAL_Y_POSITION(ubl.map_y_index_to_bed_location(j));
|
|
1287
|
1245
|
|
|
1288
|
1246
|
// If we are using the probe as the reference there are some locations we can't get to.
|
|
1289
|
1247
|
// We prune these out of the list and ignore them until the next Phase where we do the
|
|
1290
|
1248
|
// manual nozzle probing.
|
|
1291
|
|
-
|
|
|
1249
|
+
|
|
1292
|
1250
|
if (probe_as_reference &&
|
|
1293
|
|
- ( mx < (MIN_PROBE_X) || mx > (MAX_PROBE_X) || my < (MIN_PROBE_Y) || my > (MAX_PROBE_Y) )
|
|
|
1251
|
+ (mx < (MIN_PROBE_X) || mx > (MAX_PROBE_X) || my < (MIN_PROBE_Y) || my > (MAX_PROBE_Y))
|
|
1294
|
1252
|
) continue;
|
|
1295
|
1253
|
|
|
1296
|
|
- dx = px - mx; // We can get to it. Let's see if it is the
|
|
1297
|
|
- dy = py - my; // closest location to the nozzle.
|
|
1298
|
|
- distance = HYPOT(dx, dy);
|
|
1299
|
|
-
|
|
1300
|
|
- dx = current_x - mx; // We are going to add in a weighting factor that considers
|
|
1301
|
|
- dy = current_y - my; // the current location of the nozzle. If two locations are equal
|
|
1302
|
|
- distance += HYPOT(dx, dy) * 0.01; // distance from the measurement location, we are going to give
|
|
|
1254
|
+ // We can get to it. Let's see if it is the closest location to the nozzle.
|
|
|
1255
|
+ // Add in a weighting factor that considers the current location of the nozzle.
|
|
|
1256
|
+ const float distance = HYPOT(px - mx, py - my) + HYPOT(current_x - mx, current_y - my) * 0.01;
|
|
1303
|
1257
|
|
|
1304
|
1258
|
if (distance < closest) {
|
|
1305
|
1259
|
closest = distance; // We found a closer location with
|
|
|
@@ -1313,10 +1267,9 @@
|
|
1313
|
1267
|
return return_val;
|
|
1314
|
1268
|
}
|
|
1315
|
1269
|
|
|
1316
|
|
- void fine_tune_mesh(float x_pos, float y_pos, bool do_ubl_mesh_map) {
|
|
|
1270
|
+ void fine_tune_mesh(const float &lx, const float &ly, const bool do_ubl_mesh_map) {
|
|
1317
|
1271
|
mesh_index_pair location;
|
|
1318
|
|
- float xProbe, yProbe;
|
|
1319
|
|
- uint16_t i, not_done[16];
|
|
|
1272
|
+ uint16_t not_done[16];
|
|
1320
|
1273
|
int32_t round_off;
|
|
1321
|
1274
|
|
|
1322
|
1275
|
save_ubl_active_state_and_disable();
|
|
|
@@ -1327,11 +1280,11 @@
|
|
1327
|
1280
|
#endif
|
|
1328
|
1281
|
|
|
1329
|
1282
|
do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE);
|
|
1330
|
|
- do_blocking_move_to_xy(x_pos, y_pos);
|
|
|
1283
|
+ do_blocking_move_to_xy(lx, ly);
|
|
1331
|
1284
|
do {
|
|
1332
|
1285
|
if (do_ubl_mesh_map) ubl.display_map(1);
|
|
1333
|
1286
|
|
|
1334
|
|
- location = find_closest_mesh_point_of_type( SET_IN_BITMAP, x_pos, y_pos, 0, not_done); // The '0' says we want to use the nozzle's position
|
|
|
1287
|
+ location = find_closest_mesh_point_of_type( SET_IN_BITMAP, lx, ly, 0, not_done); // The '0' says we want to use the nozzle's position
|
|
1335
|
1288
|
// It doesn't matter if the probe can not reach this
|
|
1336
|
1289
|
// location. This is a manual edit of the Mesh Point.
|
|
1337
|
1290
|
if (location.x_index < 0 && location.y_index < 0) continue; // abort if we can't find any more points.
|
|
|
@@ -1339,8 +1292,8 @@
|
|
1339
|
1292
|
bit_clear(not_done, location.x_index, location.y_index); // Mark this location as 'adjusted' so we will find a
|
|
1340
|
1293
|
// different location the next time through the loop
|
|
1341
|
1294
|
|
|
1342
|
|
- xProbe = ubl.map_x_index_to_bed_location(location.x_index);
|
|
1343
|
|
- yProbe = ubl.map_y_index_to_bed_location(location.y_index);
|
|
|
1295
|
+ const float xProbe = ubl.map_x_index_to_bed_location(location.x_index),
|
|
|
1296
|
+ yProbe = ubl.map_y_index_to_bed_location(location.y_index);
|
|
1344
|
1297
|
if (xProbe < X_MIN_POS || xProbe > X_MAX_POS || yProbe < Y_MIN_POS || yProbe > Y_MAX_POS) { // In theory, we don't need this check.
|
|
1345
|
1298
|
SERIAL_PROTOCOLLNPGM("?Error: Attempt to edit off the bed."); // This really can't happen, but for now,
|
|
1346
|
1299
|
ubl_has_control_of_lcd_panel = false; // Let's do the check.
|
|
|
@@ -1406,7 +1359,7 @@
|
|
1406
|
1359
|
restore_ubl_active_state_and_leave();
|
|
1407
|
1360
|
do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE);
|
|
1408
|
1361
|
|
|
1409
|
|
- do_blocking_move_to_xy(x_pos, y_pos);
|
|
|
1362
|
+ do_blocking_move_to_xy(lx, ly);
|
|
1410
|
1363
|
|
|
1411
|
1364
|
#if ENABLED(ULTRA_LCD)
|
|
1412
|
1365
|
lcd_setstatus("Done Editing Mesh", true);
|