remove on_config_reload event
[clinton/Smoothieware.git] / src / modules / tools / zprobe / ZProbe.cpp
CommitLineData
88443c6b
JM
1/*
2 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
3 Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4 Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
6*/
7
8#include "ZProbe.h"
9
10#include "Kernel.h"
11#include "BaseSolution.h"
12#include "Config.h"
13#include "Robot.h"
14#include "StepperMotor.h"
15#include "StreamOutputPool.h"
16#include "Gcode.h"
17#include "Conveyor.h"
18#include "Stepper.h"
19#include "checksumm.h"
20#include "ConfigValue.h"
21#include "SlowTicker.h"
22#include "Planner.h"
037c350d 23#include "SerialMessage.h"
9f6f04a5
JM
24#include "PublicDataRequest.h"
25#include "EndstopsPublicAccess.h"
26#include "PublicData.h"
88443c6b 27
681a62d7
JM
28#include <tuple>
29#include <algorithm>
30
88443c6b
JM
31#define zprobe_checksum CHECKSUM("zprobe")
32#define enable_checksum CHECKSUM("enable")
33#define probe_pin_checksum CHECKSUM("probe_pin")
34#define debounce_count_checksum CHECKSUM("debounce_count")
681a62d7
JM
35#define slow_feedrate_checksum CHECKSUM("slow_feedrate")
36#define fast_feedrate_checksum CHECKSUM("fast_feedrate")
37#define probe_radius_checksum CHECKSUM("probe_radius")
681a62d7 38#define probe_height_checksum CHECKSUM("probe_height")
88443c6b 39
681a62d7 40// from endstop section
b7cd847e 41#define delta_homing_checksum CHECKSUM("delta_homing")
88443c6b
JM
42
43#define X_AXIS 0
44#define Y_AXIS 1
45#define Z_AXIS 2
46
dd0a7cfa
JM
47#define STEPPER THEKERNEL->robot->actuators
48#define STEPS_PER_MM(a) (STEPPER[a]->get_steps_per_mm())
56ce2b5a
JM
49#define Z_STEPS_PER_MM STEPS_PER_MM(Z_AXIS)
50
7d6fe308
JM
51#define abs(a) ((a<0) ? -a : a)
52
88443c6b
JM
53void ZProbe::on_module_loaded()
54{
55 // if the module is disabled -> do nothing
56ce2b5a 56 if(!THEKERNEL->config->value( zprobe_checksum, enable_checksum )->by_default(false)->as_bool()) {
88443c6b
JM
57 // as this module is not needed free up the resource
58 delete this;
59 return;
60 }
681a62d7 61 this->running = false;
88443c6b
JM
62
63 // load settings
64 this->on_config_reload(this);
65 // register event-handlers
88443c6b
JM
66 register_for_event(ON_GCODE_RECEIVED);
67 register_for_event(ON_IDLE);
68
38bf9a1c 69 THEKERNEL->slow_ticker->attach( THEKERNEL->stepper->get_acceleration_ticks_per_second() , this, &ZProbe::acceleration_tick );
88443c6b
JM
70}
71
72void ZProbe::on_config_reload(void *argument)
73{
681a62d7
JM
74 this->pin.from_string( THEKERNEL->config->value(zprobe_checksum, probe_pin_checksum)->by_default("nc" )->as_string())->as_input();
75 this->debounce_count = THEKERNEL->config->value(zprobe_checksum, debounce_count_checksum)->by_default(0 )->as_number();
76
037c350d
JM
77 // see what type of arm solution we need to use
78 this->is_delta = THEKERNEL->config->value(delta_homing_checksum)->by_default(false)->as_bool();
79 if(this->is_delta) {
80 // default is probably wrong
81 this->probe_radius = THEKERNEL->config->value(zprobe_checksum, probe_radius_checksum)->by_default(100.0F)->as_number();
82 }
681a62d7 83
681a62d7 84 this->probe_height = THEKERNEL->config->value(zprobe_checksum, probe_height_checksum)->by_default(5.0F)->as_number();
681a62d7
JM
85 this->slow_feedrate = THEKERNEL->config->value(zprobe_checksum, slow_feedrate_checksum)->by_default(5)->as_number(); // feedrate in mm/sec
86 this->fast_feedrate = THEKERNEL->config->value(zprobe_checksum, fast_feedrate_checksum)->by_default(100)->as_number(); // feedrate in mm/sec
88443c6b
JM
87}
88
681a62d7 89bool ZProbe::wait_for_probe(int steps[3])
88443c6b
JM
90{
91 unsigned int debounce = 0;
92 while(true) {
93 THEKERNEL->call_event(ON_IDLE);
94 // if no stepper is moving, moves are finished and there was no touch
dd0a7cfa 95 if( !STEPPER[X_AXIS]->is_moving() && !STEPPER[Y_AXIS]->is_moving() && !STEPPER[Z_AXIS]->is_moving() ) {
88443c6b
JM
96 return false;
97 }
98
99 // if the touchprobe is active...
100 if( this->pin.get() ) {
101 //...increase debounce counter...
102 if( debounce < debounce_count) {
103 // ...but only if the counter hasn't reached the max. value
104 debounce++;
105 } else {
106 // ...otherwise stop the steppers, return its remaining steps
107 for( int i = X_AXIS; i <= Z_AXIS; i++ ) {
108 steps[i] = 0;
dd0a7cfa
JM
109 if ( STEPPER[i]->is_moving() ) {
110 steps[i] = STEPPER[i]->get_stepped();
111 STEPPER[i]->move(0, 0);
88443c6b
JM
112 }
113 }
114 return true;
115 }
116 } else {
117 // The probe was not hit yet, reset debounce counter
118 debounce = 0;
119 }
120 }
121}
122
123void ZProbe::on_idle(void *argument)
124{
125}
126
127// single probe and report amount moved
681a62d7 128bool ZProbe::run_probe(int& steps, bool fast)
88443c6b
JM
129{
130 // Enable the motors
131 THEKERNEL->stepper->turn_enable_pins_on();
56ce2b5a 132 this->current_feedrate = (fast ? this->fast_feedrate : this->slow_feedrate) * Z_STEPS_PER_MM; // steps/sec
88443c6b
JM
133
134 // move Z down
dd0a7cfa
JM
135 STEPPER[Z_AXIS]->set_speed(0); // will be increased by acceleration tick
136 STEPPER[Z_AXIS]->move(true, 1000 * Z_STEPS_PER_MM); // always probes down, no more than 1000mm TODO should be 2*maxz
b7cd847e
JM
137 if(this->is_delta) {
138 // for delta need to move all three actuators
dd0a7cfa
JM
139 STEPPER[X_AXIS]->set_speed(0);
140 STEPPER[X_AXIS]->move(true, 1000 * STEPS_PER_MM(X_AXIS));
141 STEPPER[Y_AXIS]->set_speed(0);
142 STEPPER[Y_AXIS]->move(true, 1000 * STEPS_PER_MM(Y_AXIS));
b7cd847e
JM
143 }
144
7d6fe308
JM
145 this->running = true;
146
681a62d7
JM
147 int s[3];
148 bool r = wait_for_probe(s);
56ce2b5a 149 steps= s[Z_AXIS]; // only need z
681a62d7 150 this->running = false;
88443c6b
JM
151 return r;
152}
153
681a62d7
JM
154bool ZProbe::return_probe(int steps)
155{
156 // move probe back to where it was
56ce2b5a 157 this->current_feedrate = this->fast_feedrate * Z_STEPS_PER_MM; // feedrate in steps/sec
681a62d7
JM
158 bool dir= steps < 0;
159 steps= abs(steps);
160
dd0a7cfa
JM
161 STEPPER[Z_AXIS]->set_speed(0); // will be increased by acceleration tick
162 STEPPER[Z_AXIS]->move(dir, steps);
681a62d7 163 if(this->is_delta) {
dd0a7cfa
JM
164 STEPPER[X_AXIS]->set_speed(0);
165 STEPPER[X_AXIS]->move(dir, steps);
166 STEPPER[Y_AXIS]->set_speed(0);
167 STEPPER[Y_AXIS]->move(dir, steps);
681a62d7 168 }
7d6fe308
JM
169
170 this->running = true;
dd0a7cfa 171 while(STEPPER[X_AXIS]->is_moving() || STEPPER[Y_AXIS]->is_moving() || STEPPER[Z_AXIS]->is_moving()) {
681a62d7
JM
172 // wait for it to complete
173 THEKERNEL->call_event(ON_IDLE);
174 }
175
176 this->running = false;
177
178 return true;
179}
180
181// calculate the X and Y positions for the three towers given the radius from the center
182static std::tuple<float, float, float, float, float, float> getCoordinates(float radius)
183{
184 float px = 0.866F * radius; // ~sin(60)
185 float py = 0.5F * radius; // cos(60)
186 float t1x = -px, t1y = -py; // X Tower
187 float t2x = px, t2y = -py; // Y Tower
188 float t3x = 0.0F, t3y = radius; // Z Tower
189 return std::make_tuple(t1x, t1y, t2x, t2y, t3x, t3y);
190}
191
192bool ZProbe::probe_delta_tower(int& steps, float x, float y)
193{
194 int s;
195 // move to tower
196 coordinated_move(x, y, NAN, this->fast_feedrate);
197 if(!run_probe(s)) return false;
198
199 // return to original Z
200 return_probe(s);
201 steps= s;
202
203 return true;
204}
205
fc7b9a7b
JM
206/* Run a calibration routine for a delta
207 1. Home
208 2. probe for z bed
681a62d7
JM
209 3. probe initial tower positions
210 4. set initial trims such that trims will be minimal negative values
211 5. home, probe three towers again
212 6. calculate trim offset and apply to all trims
9f6f04a5 213 7. repeat 5, 6 until it converges on a solution
fc7b9a7b
JM
214*/
215
037c350d 216bool ZProbe::calibrate_delta_endstops(Gcode *gcode)
fc7b9a7b 217{
9f6f04a5
JM
218 float target= 0.03F;
219 if(gcode->has_letter('I')) target= gcode->get_value('I'); // override default target
220 if(gcode->has_letter('J')) this->probe_radius= gcode->get_value('J'); // override default probe radius
221
222 bool keep= false;
223 if(gcode->has_letter('K')) keep= true; // keep current settings
224
225 gcode->stream->printf("Calibrating Endstops: target %fmm, radius %fmm\n", target, this->probe_radius);
226
037c350d
JM
227 // get probe points
228 float t1x, t1y, t2x, t2y, t3x, t3y;
229 std::tie(t1x, t1y, t2x, t2y, t3x, t3y) = getCoordinates(this->probe_radius);
230
9f6f04a5
JM
231 float trimx= 0.0F, trimy= 0.0F, trimz= 0.0F;
232 if(!keep) {
233 // zero trim values
69bd2d7f 234 if(!set_trim(0, 0, 0, gcode->stream)) return false;
4553f13b 235
9f6f04a5
JM
236 }else{
237 // get current trim, and continue from that
7d6fe308
JM
238 if (get_trim(trimx, trimy, trimz)) {
239 gcode->stream->printf("Current Trim X: %f, Y: %f, Z: %f\r\n", trimx, trimy, trimz);
9f6f04a5
JM
240
241 } else {
242 gcode->stream->printf("Could not get current trim, are endstops enabled?\n");
243 return false;
244 }
245 }
681a62d7
JM
246
247 // home
248 home();
249
250 // find bed, run at fast rate
251 int s;
252 if(!run_probe(s, true)) return false;
253
4553f13b
JM
254 float bedht= s/Z_STEPS_PER_MM - this->probe_height; // distance to move from home to 5mm above bed
255 gcode->stream->printf("Bed ht is %f mm\n", bedht);
681a62d7
JM
256
257 // move to start position
258 home();
4553f13b 259 coordinated_move(NAN, NAN, -bedht, this->fast_feedrate, true); // do a relative move from home to the point above the bed
681a62d7
JM
260
261 // get initial probes
262 // probe the base of the X tower
263 if(!probe_delta_tower(s, t1x, t1y)) return false;
56ce2b5a 264 float t1z= s / Z_STEPS_PER_MM;
9f6f04a5 265 gcode->stream->printf("T1-0 Z:%1.4f C:%d\n", t1z, s);
681a62d7
JM
266
267 // probe the base of the Y tower
268 if(!probe_delta_tower(s, t2x, t2y)) return false;
56ce2b5a 269 float t2z= s / Z_STEPS_PER_MM;
9f6f04a5 270 gcode->stream->printf("T2-0 Z:%1.4f C:%d\n", t2z, s);
681a62d7
JM
271
272 // probe the base of the Z tower
273 if(!probe_delta_tower(s, t3x, t3y)) return false;
56ce2b5a 274 float t3z= s / Z_STEPS_PER_MM;
9f6f04a5 275 gcode->stream->printf("T3-0 Z:%1.4f C:%d\n", t3z, s);
681a62d7
JM
276
277 float trimscale= 1.2522F; // empirically determined
278
9f6f04a5
JM
279 auto mm= std::minmax({t1z, t2z, t3z});
280 if((mm.second-mm.first) <= target) {
281 gcode->stream->printf("trim already set within required parameters: delta %f\n", mm.second-mm.first);
282 return true;
283 }
681a62d7 284
9f6f04a5
JM
285 // set trims to worst case so we always have a negative trim
286 trimx += (mm.first-t1z)*trimscale;
287 trimy += (mm.first-t2z)*trimscale;
288 trimz += (mm.first-t3z)*trimscale;
56ce2b5a
JM
289
290 for (int i = 1; i <= 10; ++i) {
9f6f04a5 291 // set trim
7d6fe308 292 if(!set_trim(trimx, trimy, trimz, gcode->stream)) return false;
9f6f04a5 293
681a62d7
JM
294 // home and move probe to start position just above the bed
295 home();
4553f13b 296 coordinated_move(NAN, NAN, -bedht, this->fast_feedrate, true); // do a relative move from home to the point above the bed
681a62d7
JM
297
298 // probe the base of the X tower
299 if(!probe_delta_tower(s, t1x, t1y)) return false;
56ce2b5a 300 t1z= s / Z_STEPS_PER_MM;
9f6f04a5 301 gcode->stream->printf("T1-%d Z:%1.4f C:%d\n", i, t1z, s);
681a62d7
JM
302
303 // probe the base of the Y tower
304 if(!probe_delta_tower(s, t2x, t2y)) return false;
56ce2b5a 305 t2z= s / Z_STEPS_PER_MM;
9f6f04a5 306 gcode->stream->printf("T2-%d Z:%1.4f C:%d\n", i, t2z, s);
681a62d7
JM
307
308 // probe the base of the Z tower
309 if(!probe_delta_tower(s, t3x, t3y)) return false;
56ce2b5a 310 t3z= s / Z_STEPS_PER_MM;
9f6f04a5 311 gcode->stream->printf("T3-%d Z:%1.4f C:%d\n", i, t3z, s);
681a62d7 312
9f6f04a5
JM
313 mm= std::minmax({t1z, t2z, t3z});
314 if((mm.second-mm.first) <= target) {
315 gcode->stream->printf("trim set to within required parameters: delta %f\n", mm.second-mm.first);
316 break;
317 }
681a62d7
JM
318
319 // set new trim values based on min difference
9f6f04a5
JM
320 trimx += (mm.first-t1z)*trimscale;
321 trimy += (mm.first-t2z)*trimscale;
322 trimz += (mm.first-t3z)*trimscale;
681a62d7
JM
323
324 // flush the output
325 THEKERNEL->call_event(ON_IDLE);
326 }
327
9f6f04a5
JM
328 if((mm.second-mm.first) > target) {
329 gcode->stream->printf("WARNING: trim did not resolve to within required parameters: delta %f\n", mm.second-mm.first);
330 }
681a62d7 331
037c350d
JM
332 return true;
333}
334
9f6f04a5
JM
335/*
336 probe edges to get outer positions, then probe center
337 modify the delta radius until center and X converge
338*/
339
037c350d
JM
340bool ZProbe::calibrate_delta_radius(Gcode *gcode)
341{
9f6f04a5
JM
342 float target= 0.03F;
343 if(gcode->has_letter('I')) target= gcode->get_value('I'); // override default target
344 if(gcode->has_letter('J')) this->probe_radius= gcode->get_value('J'); // override default probe radius
345
346 gcode->stream->printf("Calibrating delta radius: target %f, radius %f\n", target, this->probe_radius);
037c350d
JM
347
348 // get probe points
349 float t1x, t1y, t2x, t2y, t3x, t3y;
350 std::tie(t1x, t1y, t2x, t2y, t3x, t3y) = getCoordinates(this->probe_radius);
351
352 home();
353 // find bed, then move to a point 5mm above it
354 int s;
355 if(!run_probe(s, true)) return false;
56ce2b5a 356 float bedht= s/Z_STEPS_PER_MM - this->probe_height; // distance to move from home to 5mm above bed
037c350d
JM
357 gcode->stream->printf("Bed ht is %f mm\n", bedht);
358
359 home();
360 coordinated_move(NAN, NAN, -bedht, this->fast_feedrate, true); // do a relative move from home to the point above the bed
361
7d6fe308
JM
362 // probe center to get reference point at this Z height
363 int dc;
681a62d7 364 if(!probe_delta_tower(dc, 0, 0)) return false;
56ce2b5a 365 gcode->stream->printf("CT Z:%1.3f C:%d\n", dc / Z_STEPS_PER_MM, dc);
56ce2b5a 366 float cmm= dc / Z_STEPS_PER_MM;
681a62d7 367
037c350d
JM
368 // get current delta radius
369 float delta_radius= 0.0F;
370 BaseSolution::arm_options_t options;
371 if(THEKERNEL->robot->arm_solution->get_optional(options)) {
372 delta_radius= options['R'];
373 }
374 if(delta_radius == 0.0F) {
375 gcode->stream->printf("This appears to not be a delta arm solution\n");
376 return false;
377 }
378 options.clear();
fc7b9a7b 379
037c350d
JM
380 float drinc= 2.5F; // approx
381 for (int i = 1; i <= 10; ++i) {
7d6fe308
JM
382 // probe t1, t2, t3 and get average, but use coordinated moves, probing center won't change
383 int dx, dy, dz;
384 if(!probe_delta_tower(dx, t1x, t1y)) return false;
385 gcode->stream->printf("T1-%d Z:%1.3f C:%d\n", i, dx / Z_STEPS_PER_MM, dx);
386 if(!probe_delta_tower(dy, t2x, t2y)) return false;
387 gcode->stream->printf("T2-%d Z:%1.3f C:%d\n", i, dy / Z_STEPS_PER_MM, dy);
388 if(!probe_delta_tower(dz, t3x, t3y)) return false;
389 gcode->stream->printf("T3-%d Z:%1.3f C:%d\n", i, dz / Z_STEPS_PER_MM, dz);
390
391 // now look at the difference and reduce it by adjusting delta radius
392 float m= ((dx+dy+dz)/3.0F) / Z_STEPS_PER_MM;
393 float d= cmm-m;
394 gcode->stream->printf("C-%d Z-ave:%1.4f delta: %1.3f\n", i, m, d);
395
396 if(abs(d) <= target) break; // resolution of success
397
398 // increase delta radius to adjust for low center
399 // decrease delta radius to adjust for high center
400 delta_radius += (d*drinc);
401
037c350d
JM
402 // set the new delta radius
403 options['R']= delta_radius;
404 THEKERNEL->robot->arm_solution->set_optional(options);
405 gcode->stream->printf("Setting delta radius to: %1.4f\n", delta_radius);
406
407 home();
408 coordinated_move(NAN, NAN, -bedht, this->fast_feedrate, true); // needs to be a relative coordinated move
037c350d 409
7d6fe308
JM
410 // flush the output
411 THEKERNEL->call_event(ON_IDLE);
037c350d 412 }
fc7b9a7b
JM
413 return true;
414}
415
88443c6b
JM
416void ZProbe::on_gcode_received(void *argument)
417{
418 Gcode *gcode = static_cast<Gcode *>(argument);
88443c6b
JM
419
420 if( gcode->has_g) {
421 // G code processing
681a62d7 422 if( gcode->g == 30 ) { // simple Z probe
bd96f4d7 423 gcode->mark_as_taken();
88443c6b
JM
424 // first wait for an empty queue i.e. no moves left
425 THEKERNEL->conveyor->wait_for_empty_queue();
426
45f42ae2 427 // make sure the probe is not already triggered before moving motors
37137511 428 if(this->pin.get()) {
45f42ae2
SF
429 gcode->stream->printf("ZProbe triggered before move, aborting command.\n");
430 return;
431 }
432
681a62d7
JM
433 int steps;
434 if(run_probe(steps)) {
56ce2b5a 435 gcode->stream->printf("Z:%1.4f C:%d\n", steps / Z_STEPS_PER_MM, steps);
bd96f4d7
JM
436 // move back to where it started, unless a Z is specified
437 if(gcode->has_letter('Z')) {
438 // set Z to the specified value, and leave probe where it is
439 THEKERNEL->robot->reset_axis_position(gcode->get_value('Z'), Z_AXIS);
681a62d7
JM
440 } else {
441 return_probe(steps);
bd96f4d7 442 }
681a62d7 443 } else {
bd96f4d7 444 gcode->stream->printf("ZProbe not triggered\n");
88443c6b 445 }
fc7b9a7b 446
681a62d7
JM
447 } else if( gcode->g == 32 ) { // auto calibration for delta, Z bed mapping for cartesian
448 // first wait for an empty queue i.e. no moves left
449 THEKERNEL->conveyor->wait_for_empty_queue();
fc7b9a7b 450 gcode->mark_as_taken();
45f42ae2
SF
451
452 // make sure the probe is not already triggered before moving motors
37137511 453 if(this->pin.get()) {
45f42ae2
SF
454 gcode->stream->printf("ZProbe triggered before move, aborting command.\n");
455 return;
dd0a7cfa
JM
456 }
457
fc7b9a7b 458 if(is_delta) {
037c350d
JM
459 if(!gcode->has_letter('R')){
460 if(!calibrate_delta_endstops(gcode)) {
461 gcode->stream->printf("Calibration failed to complete, probe not triggered\n");
462 return;
463 }
681a62d7 464 }
037c350d
JM
465 if(!gcode->has_letter('E')){
466 if(!calibrate_delta_radius(gcode)) {
467 gcode->stream->printf("Calibration failed to complete, probe not triggered\n");
468 return;
469 }
470 }
471 gcode->stream->printf("Calibration complete, save settings with M500\n");
472
681a62d7
JM
473 } else {
474 // TODO create Z height map for bed
475 gcode->stream->printf("Not supported yet\n");
fc7b9a7b 476 }
88443c6b
JM
477 }
478
479 } else if(gcode->has_m) {
480 // M code processing here
bd96f4d7 481 if(gcode->m == 119) {
681a62d7 482 int c = this->pin.get();
bd96f4d7
JM
483 gcode->stream->printf(" Probe: %d", c);
484 gcode->add_nl = true;
485 gcode->mark_as_taken();
681a62d7 486
7d6fe308
JM
487 } else if (gcode->m == 557) { // P0 Xxxx Yyyy sets probe points for G32
488 // TODO will override the automatically calculated probe points for a delta, required for a cartesian
489
490 gcode->mark_as_taken();
bd96f4d7 491 }
88443c6b
JM
492 }
493}
494
495#define max(a,b) (((a) > (b)) ? (a) : (b))
496// Called periodically to change the speed to match acceleration
497uint32_t ZProbe::acceleration_tick(uint32_t dummy)
498{
499 if(!this->running) return(0); // nothing to do
500
501 // foreach stepper that is moving
b7cd847e 502 for ( int c = X_AXIS; c <= Z_AXIS; c++ ) {
dd0a7cfa 503 if( !STEPPER[c]->is_moving() ) continue;
88443c6b 504
dd0a7cfa 505 uint32_t current_rate = STEPPER[c]->get_steps_per_second();
681a62d7 506 uint32_t target_rate = int(floor(this->current_feedrate));
88443c6b 507
681a62d7 508 if( current_rate < target_rate ) {
38bf9a1c 509 uint32_t rate_increase = int(floor((THEKERNEL->planner->get_acceleration() / THEKERNEL->stepper->get_acceleration_ticks_per_second()) * STEPS_PER_MM(c)));
88443c6b
JM
510 current_rate = min( target_rate, current_rate + rate_increase );
511 }
681a62d7
JM
512 if( current_rate > target_rate ) {
513 current_rate = target_rate;
514 }
88443c6b
JM
515
516 // steps per second
dd0a7cfa 517 STEPPER[c]->set_speed(max(current_rate, THEKERNEL->stepper->get_minimum_steps_per_second()));
88443c6b
JM
518 }
519
520 return 0;
521}
681a62d7
JM
522
523// issue a coordinated move directly to robot, and return when done
524// Only move the coordinates that are passed in as not nan
037c350d 525void ZProbe::coordinated_move(float x, float y, float z, float feedrate, bool relative)
681a62d7
JM
526{
527 char buf[32];
037c350d
JM
528 char cmd[64];
529
530 if(relative) strcpy(cmd, "G91 G0 ");
531 else strcpy(cmd, "G0 ");
532
681a62d7 533 if(!isnan(x)) {
037c350d 534 int n = snprintf(buf, sizeof(buf), " X%1.3f", x);
681a62d7
JM
535 strncat(cmd, buf, n);
536 }
537 if(!isnan(y)) {
037c350d 538 int n = snprintf(buf, sizeof(buf), " Y%1.3f", y);
681a62d7
JM
539 strncat(cmd, buf, n);
540 }
541 if(!isnan(z)) {
037c350d 542 int n = snprintf(buf, sizeof(buf), " Z%1.3f", z);
681a62d7
JM
543 strncat(cmd, buf, n);
544 }
545
546 // use specified feedrate (mm/sec)
037c350d 547 int n = snprintf(buf, sizeof(buf), " F%1.1f", feedrate * 60); // feed rate is converted to mm/min
681a62d7 548 strncat(cmd, buf, n);
037c350d
JM
549 if(relative) strcat(cmd, " G90");
550
551 //THEKERNEL->streams->printf("DEBUG: move: %s\n", cmd);
681a62d7 552
037c350d
JM
553 // send as a command line as may have multiple G codes in it
554 struct SerialMessage message;
555 message.message = cmd;
556 message.stream = &(StreamOutput::NullStream);
557 THEKERNEL->call_event(ON_CONSOLE_LINE_RECEIVED, &message );
681a62d7
JM
558 THEKERNEL->conveyor->wait_for_empty_queue();
559}
560
561// issue home command
562void ZProbe::home()
563{
564 Gcode gc("G28", &(StreamOutput::NullStream));
565 THEKERNEL->call_event(ON_GCODE_RECEIVED, &gc);
566}
567
7d6fe308 568bool ZProbe::set_trim(float x, float y, float z, StreamOutput *stream)
681a62d7 569{
7d6fe308 570 float t[3]{x, y, z};
75e6428d 571 bool ok= PublicData::set_value( endstops_checksum, trim_checksum, t);
7d6fe308
JM
572
573 if (ok) {
574 stream->printf("set trim to X:%f Y:%f Z:%f\n", x, y, z);
575 } else {
576 stream->printf("unable to set trim, is endstops enabled?\n");
577 }
578
579 return ok;
580}
581
582bool ZProbe::get_trim(float& x, float& y, float& z)
583{
584 void *returned_data;
75e6428d 585 bool ok = PublicData::get_value( endstops_checksum, trim_checksum, &returned_data );
7d6fe308
JM
586
587 if (ok) {
588 float *trim = static_cast<float *>(returned_data);
589 x= trim[0];
590 y= trim[1];
591 z= trim[2];
592 return true;
593 }
594 return false;
681a62d7 595}