Commit | Line | Data |
---|---|---|
6c972e51 JL |
1 | /* |
2 | This code is derived from (and mostly copied from) Johann Rocholls code at https://github.com/jcrocholl/Marlin/blob/deltabot/Marlin/Marlin_main.cpp | |
3 | license is the same as his code. | |
4 | ||
5 | Summary | |
6 | ------- | |
7 | Probes grid_size points in X and Y (total probes grid_size * grid_size) and stores the relative offsets from the 0,0 Z height | |
8 | When enabled every move will calculate the Z offset based on interpolating the height offset within the grids nearest 4 points. | |
9 | ||
10 | Configuration | |
11 | ------------- | |
12 | The strategy must be enabled in the config as well as zprobe. | |
13 | ||
c77455c3 | 14 | leveling-strategy.rectangular-grid.enable true |
6c972e51 JL |
15 | |
16 | The size of the grid can be set with... | |
17 | ||
258f176c VB |
18 | leveling-strategy.rectangular-grid.grid_x_size 7 |
19 | leveling-strategy.rectangular-grid.grid_y_size 7 | |
6c972e51 JL |
20 | |
21 | this is the X and Y size of the grid, it must be an odd number, the default is 7 which is 49 probe points | |
22 | ||
5d3233cd | 23 | The width and length of the rectangle that is probed is set with... |
c77455c3 | 24 | |
5d3233cd JM |
25 | leveling-strategy.rectangular-grid.x_size 100 |
26 | leveling-strategy.rectangular-grid.y_size 90 | |
c77455c3 | 27 | |
6c972e51 JL |
28 | Optionally probe offsets from the nozzle or tool head can be defined with... |
29 | ||
c77455c3 | 30 | leveling-strategy.rectangular-grid.probe_offsets 0,0,0 # probe offsetrs x,y,z |
6c972e51 JL |
31 | |
32 | they may also be set with M565 X0 Y0 Z0 | |
33 | ||
34 | If the saved grid is to be loaded on boot then this must be set in the config... | |
35 | ||
c77455c3 | 36 | leveling-strategy.rectangular-grid.save true |
6c972e51 JL |
37 | |
38 | Then when M500 is issued it will save M375 which will cause the grid to be loaded on boot. The default is to not autoload the grid on boot | |
39 | ||
40 | Optionally an initial_height can be set that tell the intial probe where to stop the fast decent before it probes, this should be around 5-10mm above the bed | |
c77455c3 | 41 | leveling-strategy.rectangular-grid.initial_height 10 |
6c972e51 | 42 | |
825e3420 VB |
43 | If two corners rectangular mode activated using "leveling-strategy.rectangular-grid.only_by_two_corners true" then G29/31/32 will not work without providing XYAB parameters |
44 | XY - start point, AB rectangle size from starting point | |
45 | ||
6c972e51 JL |
46 | |
47 | Usage | |
48 | ----- | |
c77455c3 | 49 | G29 test probes a rectangle which defaults to the width and height, can be overidden with Xnnn and Ynnn |
6c972e51 JL |
50 | |
51 | G31 probes the grid and turns the compensation on, this will remain in effect until reset or M561/M370 | |
c77455c3 | 52 | optional parameters {{Xn}} {{Yn}} sets the size for this rectangular probe, which gets saved with M375 |
6c972e51 JL |
53 | |
54 | M370 clears the grid and turns off compensation | |
c77455c3 JM |
55 | M374 Save grid to /sd/cartesian.grid |
56 | M374.1 delete /sd/cartesian.grid | |
57 | M375 Load the grid from /sd/cartesian.grid and enable compensation | |
6c972e51 JL |
58 | M375.1 display the current grid |
59 | M561 clears the grid and turns off compensation | |
60 | M565 defines the probe offsets from the nozzle or tool head | |
61 | ||
62 | ||
63 | M500 saves the probe points | |
64 | M503 displays the current settings | |
65 | */ | |
66 | ||
67 | #include "CartGridStrategy.h" | |
68 | ||
69 | #include "Kernel.h" | |
70 | #include "Config.h" | |
71 | #include "Robot.h" | |
72 | #include "StreamOutputPool.h" | |
73 | #include "Gcode.h" | |
74 | #include "checksumm.h" | |
75 | #include "ConfigValue.h" | |
76 | #include "PublicDataRequest.h" | |
77 | #include "PublicData.h" | |
78 | #include "Conveyor.h" | |
79 | #include "ZProbe.h" | |
80 | #include "nuts_bolts.h" | |
81 | #include "utils.h" | |
82 | #include "platform_memory.h" | |
83 | ||
84 | #include <string> | |
85 | #include <algorithm> | |
86 | #include <cstdlib> | |
87 | #include <cmath> | |
88 | #include <fastmath.h> | |
89 | ||
608a0f26 | 90 | #define grid_size_checksum CHECKSUM("size") |
258f176c VB |
91 | #define grid_x_size_checksum CHECKSUM("grid_x_size") |
92 | #define grid_y_size_checksum CHECKSUM("grid_y_size") | |
6c972e51 JL |
93 | #define tolerance_checksum CHECKSUM("tolerance") |
94 | #define save_checksum CHECKSUM("save") | |
95 | #define probe_offsets_checksum CHECKSUM("probe_offsets") | |
96 | #define initial_height_checksum CHECKSUM("initial_height") | |
5d3233cd JM |
97 | #define x_size_checksum CHECKSUM("x_size") |
98 | #define y_size_checksum CHECKSUM("y_size") | |
6c972e51 | 99 | #define do_home_checksum CHECKSUM("do_home") |
825e3420 | 100 | #define only_by_two_corners_checksum CHECKSUM("only_by_two_corners") |
6c972e51 JL |
101 | |
102 | #define GRIDFILE "/sd/cartesian.grid" | |
bf2abd76 | 103 | #define GRIDFILE_NM "/sd/cartesian_nm.grid" |
6c972e51 JL |
104 | |
105 | CartGridStrategy::CartGridStrategy(ZProbe *zprobe) : LevelingStrategy(zprobe) | |
106 | { | |
107 | grid = nullptr; | |
108 | } | |
109 | ||
110 | CartGridStrategy::~CartGridStrategy() | |
111 | { | |
112 | if(grid != nullptr) AHB0.dealloc(grid); | |
113 | } | |
114 | ||
115 | bool CartGridStrategy::handleConfig() | |
116 | { | |
608a0f26 VB |
117 | |
118 | uint8_t grid_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_size_checksum)->by_default(7)->as_number(); | |
423e6cc9 VB |
119 | current_grid_x_size = configured_grid_x_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_x_size_checksum)->by_default(grid_size)->as_number(); |
120 | current_grid_y_size = configured_grid_y_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, grid_y_size_checksum)->by_default(grid_size)->as_number(); | |
6c972e51 JL |
121 | tolerance = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, tolerance_checksum)->by_default(0.03F)->as_number(); |
122 | save = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, save_checksum)->by_default(false)->as_bool(); | |
123 | do_home = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, do_home_checksum)->by_default(true)->as_bool(); | |
825e3420 | 124 | only_by_two_corners = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, only_by_two_corners_checksum)->by_default(false)->as_bool(); |
6c972e51 | 125 | |
825e3420 VB |
126 | x_start = 0.0F; |
127 | y_start = 0.0F; | |
6c972e51 JL |
128 | x_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, x_size_checksum)->by_default(0.0F)->as_number(); |
129 | y_size = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, y_size_checksum)->by_default(0.0F)->as_number(); | |
130 | if (x_size == 0.0F || y_size == 0.0F) { | |
131 | THEKERNEL->streams->printf("Error: Invalid config, x_size and y_size must be defined\n"); | |
132 | return false; | |
133 | } | |
134 | ||
135 | // the initial height above the bed we stop the intial move down after home to find the bed | |
136 | // this should be a height that is enough that the probe will not hit the bed and is an offset from max_z (can be set to 0 if max_z takes into account the probe offset) | |
137 | this->initial_height = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, initial_height_checksum)->by_default(10)->as_number(); | |
138 | ||
139 | // Probe offsets xxx,yyy,zzz | |
140 | { | |
141 | std::string po = THEKERNEL->config->value(leveling_strategy_checksum, cart_grid_leveling_strategy_checksum, probe_offsets_checksum)->by_default("0,0,0")->as_string(); | |
142 | std::vector<float> v = parse_number_list(po.c_str()); | |
143 | if(v.size() >= 3) { | |
144 | this->probe_offsets = std::make_tuple(v[0], v[1], v[2]); | |
145 | } | |
146 | } | |
147 | ||
148 | // allocate in AHB0 | |
258f176c | 149 | grid = (float *)AHB0.alloc(configured_grid_x_size * configured_grid_y_size * sizeof(float)); |
6c972e51 | 150 | |
fc6c9aac JM |
151 | if(grid == nullptr) { |
152 | THEKERNEL->streams->printf("Error: Not enough memory\n"); | |
153 | return false; | |
154 | } | |
155 | ||
6c972e51 JL |
156 | reset_bed_level(); |
157 | ||
158 | return true; | |
159 | } | |
160 | ||
161 | void CartGridStrategy::save_grid(StreamOutput *stream) | |
162 | { | |
825e3420 VB |
163 | if(only_by_two_corners){ |
164 | stream->printf("error:Unable to save grid in only_by_two_corners mode\n"); | |
165 | return; | |
166 | } | |
167 | ||
6c972e51 JL |
168 | if(isnan(grid[0])) { |
169 | stream->printf("error:No grid to save\n"); | |
170 | return; | |
171 | } | |
172 | ||
258f176c VB |
173 | if((current_grid_x_size != configured_grid_x_size) || (current_grid_y_size != configured_grid_y_size)) { |
174 | stream->printf("error:Unable to save grid with size different from configured\n"); | |
175 | return; | |
176 | } | |
177 | ||
bf2abd76 | 178 | FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "w"):fopen(GRIDFILE_NM, "w"); |
6c972e51 JL |
179 | if(fp == NULL) { |
180 | stream->printf("error:Failed to open grid file %s\n", GRIDFILE); | |
181 | return; | |
182 | } | |
44b5bc10 VB |
183 | uint8_t tmp_configured_grid_size = configured_grid_x_size; |
184 | if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) { | |
258f176c VB |
185 | stream->printf("error:Failed to write grid x size\n"); |
186 | fclose(fp); | |
187 | return; | |
188 | } | |
189 | ||
44b5bc10 | 190 | tmp_configured_grid_size = configured_grid_y_size; |
bf2abd76 | 191 | if(configured_grid_y_size != configured_grid_x_size){ |
44b5bc10 | 192 | if(fwrite(&tmp_configured_grid_size, sizeof(uint8_t), 1, fp) != 1) { |
bf2abd76 VB |
193 | stream->printf("error:Failed to write grid y size\n"); |
194 | fclose(fp); | |
195 | return; | |
196 | } | |
6c972e51 JL |
197 | } |
198 | ||
199 | if(fwrite(&x_size, sizeof(float), 1, fp) != 1) { | |
200 | stream->printf("error:Failed to write x_size\n"); | |
201 | fclose(fp); | |
202 | return; | |
203 | } | |
204 | ||
205 | if(fwrite(&y_size, sizeof(float), 1, fp) != 1) { | |
206 | stream->printf("error:Failed to write y_size\n"); | |
207 | fclose(fp); | |
208 | return; | |
209 | } | |
210 | ||
258f176c VB |
211 | for (int y = 0; y < configured_grid_y_size; y++) { |
212 | for (int x = 0; x < configured_grid_x_size; x++) { | |
213 | if(fwrite(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) { | |
6c972e51 JL |
214 | stream->printf("error:Failed to write grid\n"); |
215 | fclose(fp); | |
216 | return; | |
217 | } | |
218 | } | |
219 | } | |
220 | stream->printf("grid saved to %s\n", GRIDFILE); | |
221 | fclose(fp); | |
222 | } | |
223 | ||
224 | bool CartGridStrategy::load_grid(StreamOutput *stream) | |
225 | { | |
825e3420 VB |
226 | if(only_by_two_corners){ |
227 | stream->printf("error:Unable to load grid in only_by_two_corners mode\n"); | |
228 | return false; | |
229 | } | |
230 | ||
bf2abd76 | 231 | FILE *fp = (configured_grid_x_size == configured_grid_y_size)?fopen(GRIDFILE, "r"):fopen(GRIDFILE_NM, "r"); |
6c972e51 JL |
232 | if(fp == NULL) { |
233 | stream->printf("error:Failed to open grid %s\n", GRIDFILE); | |
234 | return false; | |
235 | } | |
236 | ||
258f176c | 237 | uint8_t load_grid_x_size, load_grid_y_size; |
6c972e51 JL |
238 | float x, y; |
239 | ||
258f176c VB |
240 | if(fread(&load_grid_x_size, sizeof(uint8_t), 1, fp) != 1) { |
241 | stream->printf("error:Failed to read grid size\n"); | |
242 | fclose(fp); | |
243 | return false; | |
244 | } | |
245 | ||
246 | if(load_grid_x_size != configured_grid_x_size) { | |
247 | stream->printf("error:grid size x is different read %d - config %d\n", load_grid_x_size, configured_grid_x_size); | |
248 | fclose(fp); | |
249 | return false; | |
250 | } | |
251 | ||
bf2abd76 | 252 | load_grid_y_size = load_grid_x_size; |
6c972e51 | 253 | |
bf2abd76 VB |
254 | if(configured_grid_x_size != configured_grid_y_size){ |
255 | if(fread(&load_grid_y_size, sizeof(uint8_t), 1, fp) != 1) { | |
256 | stream->printf("error:Failed to read grid size\n"); | |
257 | fclose(fp); | |
258 | return false; | |
259 | } | |
260 | ||
261 | if(load_grid_y_size != configured_grid_y_size) { | |
262 | stream->printf("error:grid size y is different read %d - config %d\n", load_grid_y_size, configured_grid_x_size); | |
263 | fclose(fp); | |
264 | return false; | |
265 | } | |
6c972e51 JL |
266 | } |
267 | ||
268 | if(fread(&x, sizeof(float), 1, fp) != 1) { | |
269 | stream->printf("error:Failed to read grid x size\n"); | |
270 | fclose(fp); | |
271 | return false; | |
272 | } | |
273 | ||
274 | if(fread(&y, sizeof(float), 1, fp) != 1) { | |
275 | stream->printf("error:Failed to read grid y size\n"); | |
276 | fclose(fp); | |
277 | return false; | |
278 | } | |
279 | ||
280 | if(x != x_size || y != y_size) { | |
281 | stream->printf("error:bed dimensions changed read (%f, %f) - config (%f,%f)\n", x, y, x_size, y_size); | |
282 | fclose(fp); | |
283 | return false; | |
284 | } | |
285 | ||
258f176c VB |
286 | for (int y = 0; y < configured_grid_y_size; y++) { |
287 | for (int x = 0; x < configured_grid_x_size; x++) { | |
288 | if(fread(&grid[x + (configured_grid_x_size * y)], sizeof(float), 1, fp) != 1) { | |
6c972e51 JL |
289 | stream->printf("error:Failed to read grid\n"); |
290 | fclose(fp); | |
291 | return false; | |
292 | } | |
293 | } | |
294 | } | |
258f176c | 295 | stream->printf("grid loaded, grid: (%f, %f), size: %d x %d\n", x_size, y_size, load_grid_x_size, load_grid_y_size); |
6c972e51 JL |
296 | fclose(fp); |
297 | return true; | |
298 | } | |
299 | ||
825e3420 | 300 | bool CartGridStrategy::probe_grid(int n, int m, float x_start, float y_start, float x_size, float y_size, StreamOutput *stream) |
6c972e51 | 301 | { |
608a0f26 | 302 | if((n < 5)||(m < 5)) { |
6c972e51 JL |
303 | stream->printf("Need at least a 5x5 grid to probe\n"); |
304 | return true; | |
305 | } | |
306 | ||
307 | float initial_z = findBed(); | |
308 | if(isnan(initial_z)) return false; | |
309 | ||
825e3420 VB |
310 | float x_step = x_size / n; |
311 | float y_step = y_size / m; | |
6c972e51 | 312 | for (int c = 0; c < n; ++c) { |
825e3420 | 313 | float y = y_start + y_step * c; |
6c972e51 | 314 | for (int r = 0; r < n; ++r) { |
825e3420 | 315 | float x = x_start + x_step * r; |
6c972e51 | 316 | float z = 0.0F; |
c77455c3 JM |
317 | float mm; |
318 | if(!zprobe->doProbeAt(mm, x, y)) return false; | |
319 | z = zprobe->getProbeHeight() - mm; | |
953d8b14 | 320 | stream->printf("%1.4f ", z); |
6c972e51 JL |
321 | } |
322 | stream->printf("\n"); | |
323 | } | |
324 | return true; | |
325 | } | |
326 | ||
327 | bool CartGridStrategy::handleGcode(Gcode *gcode) | |
328 | { | |
329 | if(gcode->has_g) { | |
330 | if (gcode->g == 29) { // do a probe to test flatness | |
331 | // first wait for an empty queue i.e. no moves left | |
332 | THEKERNEL->conveyor->wait_for_idle(); | |
333 | ||
ed691bbf VB |
334 | int n = gcode->has_letter('I') ? gcode->get_value('I') : configured_grid_x_size; |
335 | int m = gcode->has_letter('J') ? gcode->get_value('J') : configured_grid_y_size; | |
825e3420 VB |
336 | |
337 | float x_size = this->x_size, y_size = this->x_size; | |
338 | float x_start = this->x_start, y_start = this->y_start; | |
339 | ||
340 | if(only_by_two_corners){ | |
341 | if(gcode->has_letter('X') && gcode->has_letter('X') && gcode->has_letter('A') && gcode->has_letter('B')){ | |
342 | x_start = gcode->get_value('X'); // override default probe start point | |
343 | y_start = gcode->get_value('Y'); // override default probe start point | |
344 | x_size = gcode->get_value('A'); // override default probe width | |
345 | y_size = gcode->get_value('B'); // override default probe length | |
346 | } else { | |
347 | gcode->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n"); | |
348 | return true; | |
349 | } | |
350 | } else { | |
351 | if(gcode->has_letter('X')) x_size = gcode->get_value('X'); // override default probe width | |
352 | if(gcode->has_letter('Y')) y_size = gcode->get_value('Y'); // override default probe length | |
353 | } | |
354 | ||
355 | probe_grid(n, m, x_start, y_start, x_size, y_size, gcode->stream); | |
c77455c3 | 356 | |
6c972e51 JL |
357 | return true; |
358 | ||
737e15be | 359 | } else if( gcode->g == 31 || gcode->g == 32) { // do a grid probe |
6c972e51 JL |
360 | // first wait for an empty queue i.e. no moves left |
361 | THEKERNEL->conveyor->wait_for_idle(); | |
362 | ||
363 | if(!doProbe(gcode)) { | |
364 | gcode->stream->printf("Probe failed to complete, check the initial probe height and/or initial_height settings\n"); | |
365 | } else { | |
366 | gcode->stream->printf("Probe completed\n"); | |
367 | } | |
368 | return true; | |
369 | } | |
370 | ||
371 | } else if(gcode->has_m) { | |
372 | if(gcode->m == 370 || gcode->m == 561) { // M370: Clear bed, M561: Set Identity Transform | |
373 | // delete the compensationTransform in robot | |
374 | setAdjustFunction(false); | |
375 | reset_bed_level(); | |
376 | gcode->stream->printf("grid cleared and disabled\n"); | |
377 | return true; | |
378 | ||
379 | } else if(gcode->m == 374) { // M374: Save grid, M374.1: delete saved grid | |
380 | if(gcode->subcode == 1) { | |
381 | remove(GRIDFILE); | |
382 | gcode->stream->printf("%s deleted\n", GRIDFILE); | |
383 | } else { | |
384 | save_grid(gcode->stream); | |
385 | } | |
386 | ||
387 | return true; | |
388 | ||
389 | } else if(gcode->m == 375) { // M375: load grid, M375.1 display grid | |
390 | if(gcode->subcode == 1) { | |
391 | print_bed_level(gcode->stream); | |
392 | } else { | |
393 | if(load_grid(gcode->stream)) setAdjustFunction(true); | |
394 | } | |
395 | return true; | |
396 | ||
397 | } else if(gcode->m == 565) { // M565: Set Z probe offsets | |
398 | float x = 0, y = 0, z = 0; | |
399 | if(gcode->has_letter('X')) x = gcode->get_value('X'); | |
400 | if(gcode->has_letter('Y')) y = gcode->get_value('Y'); | |
401 | if(gcode->has_letter('Z')) z = gcode->get_value('Z'); | |
402 | probe_offsets = std::make_tuple(x, y, z); | |
403 | return true; | |
404 | ||
405 | } else if(gcode->m == 500 || gcode->m == 503) { // M500 save, M503 display | |
406 | float x, y, z; | |
407 | std::tie(x, y, z) = probe_offsets; | |
408 | gcode->stream->printf(";Probe offsets:\nM565 X%1.5f Y%1.5f Z%1.5f\n", x, y, z); | |
409 | if(save) { | |
410 | if(!isnan(grid[0])) gcode->stream->printf(";Load saved grid\nM375\n"); | |
411 | else if(gcode->m == 503) gcode->stream->printf(";WARNING No grid to save\n"); | |
412 | } | |
413 | return true; | |
414 | } | |
415 | } | |
416 | ||
417 | return false; | |
418 | } | |
419 | ||
420 | // These are convenience defines to keep the code as close to the original as possible it also saves memory and flash | |
421 | // set the rectangle in which to probe | |
825e3420 | 422 | #define LEFT_PROBE_BED_POSITION (x_start) |
6c972e51 JL |
423 | #define RIGHT_PROBE_BED_POSITION (x_size) |
424 | #define BACK_PROBE_BED_POSITION (y_size) | |
825e3420 | 425 | #define FRONT_PROBE_BED_POSITION (y_start) |
6c972e51 JL |
426 | |
427 | // probe at the points of a lattice grid | |
825e3420 VB |
428 | #define AUTO_BED_LEVELING_GRID_X (RIGHT_PROBE_BED_POSITION / (current_grid_x_size - 1)) |
429 | #define AUTO_BED_LEVELING_GRID_Y (BACK_PROBE_BED_POSITION / (current_grid_y_size - 1)) | |
6c972e51 JL |
430 | |
431 | #define X_PROBE_OFFSET_FROM_EXTRUDER std::get<0>(probe_offsets) | |
432 | #define Y_PROBE_OFFSET_FROM_EXTRUDER std::get<1>(probe_offsets) | |
433 | #define Z_PROBE_OFFSET_FROM_EXTRUDER std::get<2>(probe_offsets) | |
434 | ||
435 | void CartGridStrategy::setAdjustFunction(bool on) | |
436 | { | |
437 | if(on) { | |
438 | // set the compensationTransform in robot | |
439 | using std::placeholders::_1; | |
440 | using std::placeholders::_2; | |
441 | THEROBOT->compensationTransform = std::bind(&CartGridStrategy::doCompensation, this, _1, _2); // [this](float *target, bool inverse) { doCompensation(target, inverse); }; | |
442 | } else { | |
443 | // clear it | |
444 | THEROBOT->compensationTransform = nullptr; | |
445 | } | |
446 | } | |
447 | ||
448 | float CartGridStrategy::findBed() | |
449 | { | |
450 | if (do_home) zprobe->home(); | |
451 | // move to an initial position fast so as to not take all day, we move down max_z - initial_height, which is set in config, default 10mm | |
452 | float deltaz = initial_height; | |
825e3420 VB |
453 | zprobe->coordinated_move(NAN, NAN, deltaz, zprobe->getFastFeedrate()); //move Z only to initial_height |
454 | zprobe->coordinated_move(x_start, y_start, NAN, zprobe->getFastFeedrate()); // move at initial_height to x_start, y_start | |
6c972e51 JL |
455 | |
456 | // find bed at 0,0 run at slow rate so as to not hit bed hard | |
457 | float mm; | |
458 | if(!zprobe->run_probe_return(mm, zprobe->getSlowFeedrate())) return NAN; | |
459 | ||
460 | float dz = zprobe->getProbeHeight() - mm; | |
461 | zprobe->coordinated_move(NAN, NAN, dz, zprobe->getFastFeedrate(), true); // relative move | |
462 | ||
463 | return mm + deltaz - zprobe->getProbeHeight(); // distance to move from home to 5mm above bed | |
464 | } | |
465 | ||
466 | bool CartGridStrategy::doProbe(Gcode *gc) | |
467 | { | |
4138ce97 | 468 | gc->stream->printf("Rectangular Grid Probe...\n"); |
825e3420 VB |
469 | |
470 | if(only_by_two_corners){ | |
471 | if(gc->has_letter('X') && gc->has_letter('X') && gc->has_letter('A') && gc->has_letter('B')){ | |
472 | x_start = gc->get_value('X'); // override default probe start point, will get saved | |
473 | y_start = gc->get_value('Y'); // override default probe start point, will get saved | |
474 | x_size = gc->get_value('A'); // override default probe width, will get saved | |
475 | y_size = gc->get_value('B'); // override default probe length, will get saved | |
476 | } else { | |
477 | gc->stream->printf("In only_by_two_corners mode all XYAB parameters needed\n"); | |
478 | return false; | |
479 | } | |
480 | } else { | |
481 | if(gc->has_letter('X')) x_size = gc->get_value('X'); // override default probe width, will get saved | |
482 | if(gc->has_letter('Y')) y_size = gc->get_value('Y'); // override default probe length, will get saved | |
483 | } | |
484 | ||
6c972e51 JL |
485 | setAdjustFunction(false); |
486 | reset_bed_level(); | |
487 | ||
258f176c | 488 | if(gc->has_letter('I')) current_grid_x_size = gc->get_value('I'); // override default grid x size |
258f176c | 489 | if(gc->has_letter('J')) current_grid_y_size = gc->get_value('J'); // override default grid y size |
423e6cc9 VB |
490 | |
491 | if((current_grid_x_size * current_grid_y_size) > (configured_grid_x_size * configured_grid_y_size)){ | |
825e3420 | 492 | gc->stream->printf("Grid size (%d x %d = %d) bigger than configured (%d x %d = %d). Change configuration.\n", current_grid_x_size, current_grid_y_size, current_grid_x_size*current_grid_x_size, configured_grid_x_size, configured_grid_y_size, configured_grid_x_size*configured_grid_y_size); |
258f176c VB |
493 | return false; |
494 | } | |
495 | ||
6c972e51 JL |
496 | // find bed, and leave probe probe height above bed |
497 | float initial_z = findBed(); | |
498 | if(isnan(initial_z)) { | |
499 | gc->stream->printf("Finding bed failed, check the maxz and initial height settings\n"); | |
500 | return false; | |
501 | } | |
502 | ||
258f176c | 503 | gc->stream->printf("Probe start ht is %f mm, rectangular bed width %fmm, height %fmm, grid size is %dx%d\n", initial_z, x_size, y_size, current_grid_x_size, current_grid_y_size); |
6c972e51 JL |
504 | |
505 | // do first probe for 0,0 | |
506 | float mm; | |
825e3420 | 507 | if(!zprobe->doProbeAt(mm, x_start - X_PROBE_OFFSET_FROM_EXTRUDER, y_start - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false; |
6c972e51 JL |
508 | float z_reference = zprobe->getProbeHeight() - mm; // this should be zero |
509 | gc->stream->printf("probe at 0,0 is %f mm\n", z_reference); | |
510 | ||
c77455c3 | 511 | // probe all the points of the grid |
258f176c | 512 | for (int yCount = 0; yCount < current_grid_y_size; yCount++) { |
6c972e51 JL |
513 | float yProbe = FRONT_PROBE_BED_POSITION + AUTO_BED_LEVELING_GRID_Y * yCount; |
514 | int xStart, xStop, xInc; | |
515 | if (yCount % 2) { | |
258f176c | 516 | xStart = current_grid_x_size - 1; |
6c972e51 JL |
517 | xStop = -1; |
518 | xInc = -1; | |
b7b6110a VB |
519 | } else { |
520 | xStart = 0; | |
521 | xStop = current_grid_x_size; | |
522 | xInc = 1; | |
6c972e51 JL |
523 | } |
524 | ||
525 | for (int xCount = xStart; xCount != xStop; xCount += xInc) { | |
526 | float xProbe = LEFT_PROBE_BED_POSITION + AUTO_BED_LEVELING_GRID_X * xCount; | |
527 | ||
528 | if(!zprobe->doProbeAt(mm, xProbe - X_PROBE_OFFSET_FROM_EXTRUDER, yProbe - Y_PROBE_OFFSET_FROM_EXTRUDER)) return false; | |
529 | float measured_z = zprobe->getProbeHeight() - mm - z_reference; // this is the delta z from bed at 0,0 | |
953d8b14 | 530 | gc->stream->printf("DEBUG: X%1.4f, Y%1.4f, Z%1.4f\n", xProbe, yProbe, measured_z); |
258f176c | 531 | grid[xCount + (current_grid_x_size * yCount)] = measured_z; |
6c972e51 JL |
532 | } |
533 | } | |
534 | ||
6c972e51 JL |
535 | print_bed_level(gc->stream); |
536 | ||
537 | setAdjustFunction(true); | |
538 | ||
539 | return true; | |
540 | } | |
541 | ||
6c972e51 JL |
542 | void CartGridStrategy::doCompensation(float *target, bool inverse) |
543 | { | |
544 | // Adjust print surface height by linear interpolation over the bed_level array. | |
825e3420 VB |
545 | if ((std::min(x_start, x_start + x_size) <= target[X_AXIS]) && (target[X_AXIS] <= std::max(x_start, x_start + x_size)) && |
546 | (std::min(y_start, y_start + y_size) <= target[Y_AXIS]) && (target[Y_AXIS] <= std::max(y_start, y_start + y_size))) { | |
547 | ||
548 | float grid_x = std::max(0.001F, (target[X_AXIS] - x_start) / AUTO_BED_LEVELING_GRID_X); | |
549 | float grid_y = std::max(0.001F, (target[Y_AXIS] - y_start) / AUTO_BED_LEVELING_GRID_Y); | |
550 | int floor_x = floorf(grid_x); | |
551 | int floor_y = floorf(grid_y); | |
552 | float ratio_x = grid_x - floor_x; | |
553 | float ratio_y = grid_y - floor_y; | |
554 | float z1 = grid[(floor_x) + ((floor_y) * current_grid_x_size)]; | |
555 | float z2 = grid[(floor_x) + ((floor_y + 1) * current_grid_x_size)]; | |
556 | float z3 = grid[(floor_x + 1) + ((floor_y) * current_grid_x_size)]; | |
557 | float z4 = grid[(floor_x + 1) + ((floor_y + 1) * current_grid_x_size)]; | |
558 | float left = (1 - ratio_y) * z1 + ratio_y * z2; | |
559 | float right = (1 - ratio_y) * z3 + ratio_y * z4; | |
560 | float offset = (1 - ratio_x) * left + ratio_x * right; | |
561 | ||
562 | if(inverse) | |
563 | target[Z_AXIS] -= offset; | |
564 | else | |
565 | target[Z_AXIS] += offset; | |
6c972e51 JL |
566 | |
567 | ||
568 | /* | |
569 | THEKERNEL->streams->printf("//DEBUG: TARGET: %f, %f, %f\n", target[0], target[1], target[2]); | |
570 | THEKERNEL->streams->printf("//DEBUG: grid_x= %f\n", grid_x); | |
571 | THEKERNEL->streams->printf("//DEBUG: grid_y= %f\n", grid_y); | |
572 | THEKERNEL->streams->printf("//DEBUG: floor_x= %d\n", floor_x); | |
573 | THEKERNEL->streams->printf("//DEBUG: floor_y= %d\n", floor_y); | |
574 | THEKERNEL->streams->printf("//DEBUG: ratio_x= %f\n", ratio_x); | |
575 | THEKERNEL->streams->printf("//DEBUG: ratio_y= %f\n", ratio_y); | |
576 | THEKERNEL->streams->printf("//DEBUG: z1= %f\n", z1); | |
577 | THEKERNEL->streams->printf("//DEBUG: z2= %f\n", z2); | |
578 | THEKERNEL->streams->printf("//DEBUG: z3= %f\n", z3); | |
579 | THEKERNEL->streams->printf("//DEBUG: z4= %f\n", z4); | |
580 | THEKERNEL->streams->printf("//DEBUG: left= %f\n", left); | |
581 | THEKERNEL->streams->printf("//DEBUG: right= %f\n", right); | |
582 | THEKERNEL->streams->printf("//DEBUG: offset= %f\n", offset); | |
583 | */ | |
825e3420 | 584 | } |
6c972e51 JL |
585 | } |
586 | ||
587 | ||
588 | // Print calibration results for plotting or manual frame adjustment. | |
589 | void CartGridStrategy::print_bed_level(StreamOutput *stream) | |
590 | { | |
258f176c VB |
591 | for (int y = 0; y < current_grid_y_size; y++) { |
592 | for (int x = 0; x < current_grid_x_size; x++) { | |
953d8b14 | 593 | stream->printf("%1.4f ", grid[x + (current_grid_x_size * y)]); |
6c972e51 JL |
594 | } |
595 | stream->printf("\n"); | |
596 | } | |
597 | } | |
598 | ||
599 | // Reset calibration results to zero. | |
600 | void CartGridStrategy::reset_bed_level() | |
601 | { | |
258f176c VB |
602 | for (int y = 0; y < current_grid_y_size; y++) { |
603 | for (int x = 0; x < current_grid_x_size; x++) { | |
604 | grid[x + (current_grid_x_size * y)] = NAN; | |
6c972e51 JL |
605 | } |
606 | } | |
607 | } |