My Marlin configs for Fabrikator Mini and CTC i3 Pro B
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #include "../../MarlinCore.h"
  23. #if ENABLED(CALIBRATION_GCODE)
  24. #include "../gcode.h"
  25. #if ENABLED(BACKLASH_GCODE)
  26. #include "../../feature/backlash.h"
  27. #endif
  28. #include "../../lcd/marlinui.h"
  29. #include "../../module/motion.h"
  30. #include "../../module/planner.h"
  31. #include "../../module/tool_change.h"
  32. #include "../../module/endstops.h"
  33. #include "../../feature/bedlevel/bedlevel.h"
  34. #if !AXIS_CAN_CALIBRATE(X)
  35. #undef CALIBRATION_MEASURE_LEFT
  36. #undef CALIBRATION_MEASURE_RIGHT
  37. #endif
  38. #if !AXIS_CAN_CALIBRATE(Y)
  39. #undef CALIBRATION_MEASURE_FRONT
  40. #undef CALIBRATION_MEASURE_BACK
  41. #endif
  42. #if !AXIS_CAN_CALIBRATE(Z)
  43. #undef CALIBRATION_MEASURE_AT_TOP_EDGES
  44. #endif
  45. /**
  46. * G425 backs away from the calibration object by various distances
  47. * depending on the confidence level:
  48. *
  49. * UNKNOWN - No real notion on where the calibration object is on the bed
  50. * UNCERTAIN - Measurement may be uncertain due to backlash
  51. * CERTAIN - Measurement obtained with backlash compensation
  52. */
  53. #ifndef CALIBRATION_MEASUREMENT_UNKNOWN
  54. #define CALIBRATION_MEASUREMENT_UNKNOWN 5.0 // mm
  55. #endif
  56. #ifndef CALIBRATION_MEASUREMENT_UNCERTAIN
  57. #define CALIBRATION_MEASUREMENT_UNCERTAIN 1.0 // mm
  58. #endif
  59. #ifndef CALIBRATION_MEASUREMENT_CERTAIN
  60. #define CALIBRATION_MEASUREMENT_CERTAIN 0.5 // mm
  61. #endif
  62. #if BOTH(CALIBRATION_MEASURE_LEFT, CALIBRATION_MEASURE_RIGHT)
  63. #define HAS_X_CENTER 1
  64. #endif
  65. #if HAS_Y_AXIS && BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK)
  66. #define HAS_Y_CENTER 1
  67. #endif
  68. #if LINEAR_AXES >= 4 && BOTH(CALIBRATION_MEASURE_IMIN, CALIBRATION_MEASURE_IMAX)
  69. #define HAS_I_CENTER 1
  70. #endif
  71. #if LINEAR_AXES >= 5 && BOTH(CALIBRATION_MEASURE_JMIN, CALIBRATION_MEASURE_JMAX)
  72. #define HAS_J_CENTER 1
  73. #endif
  74. #if LINEAR_AXES >= 6 && BOTH(CALIBRATION_MEASURE_KMIN, CALIBRATION_MEASURE_KMAX)
  75. #define HAS_K_CENTER 1
  76. #endif
  77. enum side_t : uint8_t {
  78. TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES,
  79. LIST_N(DOUBLE(SUB3(LINEAR_AXES)), IMINIMUM, IMAXIMUM, JMINIMUM, JMAXIMUM, KMINIMUM, KMAXIMUM)
  80. };
  81. static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER;
  82. static constexpr xyz_float_t dimensions CALIBRATION_OBJECT_DIMENSIONS;
  83. static constexpr xy_float_t nod = { CALIBRATION_NOZZLE_OUTER_DIAMETER, CALIBRATION_NOZZLE_OUTER_DIAMETER };
  84. struct measurements_t {
  85. xyz_pos_t obj_center = true_center; // Non-static must be assigned from xyz_pos_t
  86. float obj_side[NUM_SIDES], backlash[NUM_SIDES];
  87. xyz_float_t pos_error;
  88. xy_float_t nozzle_outer_dimension = nod;
  89. };
  90. #if ENABLED(BACKLASH_GCODE)
  91. #define TEMPORARY_BACKLASH_CORRECTION(value) REMEMBER(tbst, backlash.correction, value)
  92. #else
  93. #define TEMPORARY_BACKLASH_CORRECTION(value)
  94. #endif
  95. #if ENABLED(BACKLASH_GCODE) && defined(BACKLASH_SMOOTHING_MM)
  96. #define TEMPORARY_BACKLASH_SMOOTHING(value) REMEMBER(tbsm, backlash.smoothing_mm, value)
  97. #else
  98. #define TEMPORARY_BACKLASH_SMOOTHING(value)
  99. #endif
  100. inline void calibration_move() {
  101. do_blocking_move_to((xyz_pos_t)current_position, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
  102. }
  103. /**
  104. * Move to the exact center above the calibration object
  105. *
  106. * m in - Measurement record
  107. * uncertainty in - How far away from the object top to park
  108. */
  109. inline void park_above_object(measurements_t &m, const float uncertainty) {
  110. // Move to safe distance above calibration object
  111. current_position.z = m.obj_center.z + dimensions.z / 2 + uncertainty;
  112. calibration_move();
  113. // Move to center of calibration object in XY
  114. current_position = xy_pos_t(m.obj_center);
  115. calibration_move();
  116. }
  117. #if HAS_MULTI_HOTEND
  118. inline void set_nozzle(measurements_t &m, const uint8_t extruder) {
  119. if (extruder != active_extruder) {
  120. park_above_object(m, CALIBRATION_MEASUREMENT_UNKNOWN);
  121. tool_change(extruder);
  122. }
  123. }
  124. #endif
  125. #if HAS_HOTEND_OFFSET
  126. inline void normalize_hotend_offsets() {
  127. LOOP_S_L_N(e, 1, HOTENDS)
  128. hotend_offset[e] -= hotend_offset[0];
  129. hotend_offset[0].reset();
  130. }
  131. #endif
  132. #if !PIN_EXISTS(CALIBRATION)
  133. #include "../../module/probe.h"
  134. #endif
  135. inline bool read_calibration_pin() {
  136. return (
  137. #if PIN_EXISTS(CALIBRATION)
  138. READ(CALIBRATION_PIN) != CALIBRATION_PIN_INVERTING
  139. #else
  140. PROBE_TRIGGERED()
  141. #endif
  142. );
  143. }
  144. /**
  145. * Move along axis in the specified dir until the probe value becomes stop_state,
  146. * then return the axis value.
  147. *
  148. * axis in - Axis along which the measurement will take place
  149. * dir in - Direction along that axis (-1 or 1)
  150. * stop_state in - Move until probe pin becomes this value
  151. * fast in - Fast vs. precise measurement
  152. */
  153. float measuring_movement(const AxisEnum axis, const int dir, const bool stop_state, const bool fast) {
  154. const float step = fast ? 0.25 : CALIBRATION_MEASUREMENT_RESOLUTION;
  155. const feedRate_t mms = fast ? MMM_TO_MMS(CALIBRATION_FEEDRATE_FAST) : MMM_TO_MMS(CALIBRATION_FEEDRATE_SLOW);
  156. const float limit = fast ? 50 : 5;
  157. destination = current_position;
  158. for (float travel = 0; travel < limit; travel += step) {
  159. destination[axis] += dir * step;
  160. do_blocking_move_to((xyz_pos_t)destination, mms);
  161. planner.synchronize();
  162. if (read_calibration_pin() == stop_state) break;
  163. }
  164. return destination[axis];
  165. }
  166. /**
  167. * Move along axis until the probe is triggered. Move toolhead to its starting
  168. * point and return the measured value.
  169. *
  170. * axis in - Axis along which the measurement will take place
  171. * dir in - Direction along that axis (-1 or 1)
  172. * stop_state in - Move until probe pin becomes this value
  173. * backlash_ptr in/out - When not nullptr, measure and record axis backlash
  174. * uncertainty in - If uncertainty is CALIBRATION_MEASUREMENT_UNKNOWN, do a fast probe.
  175. */
  176. inline float measure(const AxisEnum axis, const int dir, const bool stop_state, float * const backlash_ptr, const float uncertainty) {
  177. const bool fast = uncertainty == CALIBRATION_MEASUREMENT_UNKNOWN;
  178. // Save the current position of the specified axis
  179. const float start_pos = current_position[axis];
  180. // Take a measurement. Only the specified axis will be affected.
  181. const float measured_pos = measuring_movement(axis, dir, stop_state, fast);
  182. // Measure backlash
  183. if (backlash_ptr && !fast) {
  184. const float release_pos = measuring_movement(axis, -dir, !stop_state, fast);
  185. *backlash_ptr = ABS(release_pos - measured_pos);
  186. }
  187. // Move back to the starting position
  188. destination = current_position;
  189. destination[axis] = start_pos;
  190. do_blocking_move_to((xyz_pos_t)destination, MMM_TO_MMS(CALIBRATION_FEEDRATE_TRAVEL));
  191. return measured_pos;
  192. }
  193. /**
  194. * Probe one side of the calibration object
  195. *
  196. * m in/out - Measurement record, m.obj_center and m.obj_side will be updated.
  197. * uncertainty in - How far away from the calibration object to begin probing
  198. * side in - Side of probe where probe will occur
  199. * probe_top_at_edge in - When probing sides, probe top of calibration object nearest edge
  200. * to find out height of edge
  201. */
  202. inline void probe_side(measurements_t &m, const float uncertainty, const side_t side, const bool probe_top_at_edge=false) {
  203. const xyz_float_t dimensions = CALIBRATION_OBJECT_DIMENSIONS;
  204. AxisEnum axis;
  205. float dir = 1;
  206. park_above_object(m, uncertainty);
  207. switch (side) {
  208. #if AXIS_CAN_CALIBRATE(X)
  209. case RIGHT: dir = -1;
  210. case LEFT: axis = X_AXIS; break;
  211. #endif
  212. #if LINEAR_AXES >= 2 && AXIS_CAN_CALIBRATE(Y)
  213. case BACK: dir = -1;
  214. case FRONT: axis = Y_AXIS; break;
  215. #endif
  216. #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
  217. case TOP: {
  218. const float measurement = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
  219. m.obj_center.z = measurement - dimensions.z / 2;
  220. m.obj_side[TOP] = measurement;
  221. return;
  222. }
  223. #endif
  224. #if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I)
  225. case IMINIMUM: dir = -1;
  226. case IMAXIMUM: axis = I_AXIS; break;
  227. #endif
  228. #if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J)
  229. case JMINIMUM: dir = -1;
  230. case JMAXIMUM: axis = J_AXIS; break;
  231. #endif
  232. #if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K)
  233. case KMINIMUM: dir = -1;
  234. case KMAXIMUM: axis = K_AXIS; break;
  235. #endif
  236. default: return;
  237. }
  238. if (probe_top_at_edge) {
  239. #if AXIS_CAN_CALIBRATE(Z)
  240. // Probe top nearest the side we are probing
  241. current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 - m.nozzle_outer_dimension[axis]);
  242. calibration_move();
  243. m.obj_side[TOP] = measure(Z_AXIS, -1, true, &m.backlash[TOP], uncertainty);
  244. m.obj_center.z = m.obj_side[TOP] - dimensions.z / 2;
  245. #endif
  246. }
  247. if ((AXIS_CAN_CALIBRATE(X) && axis == X_AXIS) || (AXIS_CAN_CALIBRATE(Y) && axis == Y_AXIS)) {
  248. // Move to safe distance to the side of the calibration object
  249. current_position[axis] = m.obj_center[axis] + (-dir) * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2 + uncertainty);
  250. calibration_move();
  251. // Plunge below the side of the calibration object and measure
  252. current_position.z = m.obj_side[TOP] - (CALIBRATION_NOZZLE_TIP_HEIGHT) * 0.7f;
  253. calibration_move();
  254. const float measurement = measure(axis, dir, true, &m.backlash[side], uncertainty);
  255. m.obj_center[axis] = measurement + dir * (dimensions[axis] / 2 + m.nozzle_outer_dimension[axis] / 2);
  256. m.obj_side[side] = measurement;
  257. }
  258. }
  259. /**
  260. * Probe all sides of the calibration calibration object
  261. *
  262. * m in/out - Measurement record: center, backlash and error values be updated.
  263. * uncertainty in - How far away from the calibration object to begin probing
  264. */
  265. inline void probe_sides(measurements_t &m, const float uncertainty) {
  266. #if ENABLED(CALIBRATION_MEASURE_AT_TOP_EDGES)
  267. constexpr bool probe_top_at_edge = true;
  268. #else
  269. // Probing at the exact center only works if the center is flat. Probing on a washer
  270. // or bolt will require probing the top near the side edges, away from the center.
  271. constexpr bool probe_top_at_edge = false;
  272. probe_side(m, uncertainty, TOP);
  273. #endif
  274. TERN_(CALIBRATION_MEASURE_RIGHT, probe_side(m, uncertainty, RIGHT, probe_top_at_edge));
  275. TERN_(CALIBRATION_MEASURE_FRONT, probe_side(m, uncertainty, FRONT, probe_top_at_edge));
  276. TERN_(CALIBRATION_MEASURE_LEFT, probe_side(m, uncertainty, LEFT, probe_top_at_edge));
  277. TERN_(CALIBRATION_MEASURE_BACK, probe_side(m, uncertainty, BACK, probe_top_at_edge));
  278. TERN_(CALIBRATION_MEASURE_IMIN, probe_side(m, uncertainty, IMINIMUM, probe_top_at_edge));
  279. TERN_(CALIBRATION_MEASURE_IMAX, probe_side(m, uncertainty, IMAXIMUM, probe_top_at_edge));
  280. TERN_(CALIBRATION_MEASURE_JMIN, probe_side(m, uncertainty, JMINIMUM, probe_top_at_edge));
  281. TERN_(CALIBRATION_MEASURE_JMAX, probe_side(m, uncertainty, JMAXIMUM, probe_top_at_edge));
  282. TERN_(CALIBRATION_MEASURE_KMIN, probe_side(m, uncertainty, KMINIMUM, probe_top_at_edge));
  283. TERN_(CALIBRATION_MEASURE_KMAX, probe_side(m, uncertainty, KMAXIMUM, probe_top_at_edge));
  284. // Compute the measured center of the calibration object.
  285. TERN_(HAS_X_CENTER, m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2);
  286. TERN_(HAS_Y_CENTER, m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2);
  287. TERN_(HAS_I_CENTER, m.obj_center.i = (m.obj_side[IMINIMUM] + m.obj_side[IMAXIMUM]) / 2);
  288. TERN_(HAS_J_CENTER, m.obj_center.j = (m.obj_side[JMINIMUM] + m.obj_side[JMAXIMUM]) / 2);
  289. TERN_(HAS_K_CENTER, m.obj_center.k = (m.obj_side[KMINIMUM] + m.obj_side[KMAXIMUM]) / 2);
  290. // Compute the outside diameter of the nozzle at the height
  291. // at which it makes contact with the calibration object
  292. TERN_(HAS_X_CENTER, m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x);
  293. TERN_(HAS_Y_CENTER, m.nozzle_outer_dimension.y = m.obj_side[BACK] - m.obj_side[FRONT] - dimensions.y);
  294. park_above_object(m, uncertainty);
  295. // The difference between the known and the measured location
  296. // of the calibration object is the positional error
  297. LINEAR_AXIS_CODE(
  298. m.pos_error.x = TERN0(HAS_X_CENTER, true_center.x - m.obj_center.x),
  299. m.pos_error.y = TERN0(HAS_Y_CENTER, true_center.y - m.obj_center.y),
  300. m.pos_error.z = true_center.z - m.obj_center.z,
  301. m.pos_error.i = TERN0(HAS_I_CENTER, true_center.i - m.obj_center.i),
  302. m.pos_error.j = TERN0(HAS_J_CENTER, true_center.j - m.obj_center.j),
  303. m.pos_error.k = TERN0(HAS_K_CENTER, true_center.k - m.obj_center.k)
  304. );
  305. }
  306. #if ENABLED(CALIBRATION_REPORTING)
  307. inline void report_measured_faces(const measurements_t &m) {
  308. SERIAL_ECHOLNPGM("Sides:");
  309. #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
  310. SERIAL_ECHOLNPGM(" Top: ", m.obj_side[TOP]);
  311. #endif
  312. #if ENABLED(CALIBRATION_MEASURE_LEFT)
  313. SERIAL_ECHOLNPGM(" Left: ", m.obj_side[LEFT]);
  314. #endif
  315. #if ENABLED(CALIBRATION_MEASURE_RIGHT)
  316. SERIAL_ECHOLNPGM(" Right: ", m.obj_side[RIGHT]);
  317. #endif
  318. #if HAS_Y_AXIS
  319. #if ENABLED(CALIBRATION_MEASURE_FRONT)
  320. SERIAL_ECHOLNPGM(" Front: ", m.obj_side[FRONT]);
  321. #endif
  322. #if ENABLED(CALIBRATION_MEASURE_BACK)
  323. SERIAL_ECHOLNPGM(" Back: ", m.obj_side[BACK]);
  324. #endif
  325. #endif
  326. #if LINEAR_AXES >= 4
  327. #if ENABLED(CALIBRATION_MEASURE_IMIN)
  328. SERIAL_ECHOLNPGM(" " STR_I_MIN ": ", m.obj_side[IMINIMUM]);
  329. #endif
  330. #if ENABLED(CALIBRATION_MEASURE_IMAX)
  331. SERIAL_ECHOLNPGM(" " STR_I_MAX ": ", m.obj_side[IMAXIMUM]);
  332. #endif
  333. #endif
  334. #if LINEAR_AXES >= 5
  335. #if ENABLED(CALIBRATION_MEASURE_JMIN)
  336. SERIAL_ECHOLNPGM(" " STR_J_MIN ": ", m.obj_side[JMINIMUM]);
  337. #endif
  338. #if ENABLED(CALIBRATION_MEASURE_JMAX)
  339. SERIAL_ECHOLNPGM(" " STR_J_MAX ": ", m.obj_side[JMAXIMUM]);
  340. #endif
  341. #endif
  342. #if LINEAR_AXES >= 6
  343. #if ENABLED(CALIBRATION_MEASURE_KMIN)
  344. SERIAL_ECHOLNPGM(" " STR_K_MIN ": ", m.obj_side[KMINIMUM]);
  345. #endif
  346. #if ENABLED(CALIBRATION_MEASURE_KMAX)
  347. SERIAL_ECHOLNPGM(" " STR_K_MAX ": ", m.obj_side[KMAXIMUM]);
  348. #endif
  349. #endif
  350. SERIAL_EOL();
  351. }
  352. inline void report_measured_center(const measurements_t &m) {
  353. SERIAL_ECHOLNPGM("Center:");
  354. #if HAS_X_CENTER
  355. SERIAL_ECHOLNPGM_P(SP_X_STR, m.obj_center.x);
  356. #endif
  357. #if HAS_Y_CENTER
  358. SERIAL_ECHOLNPGM_P(SP_Y_STR, m.obj_center.y);
  359. #endif
  360. SERIAL_ECHOLNPGM_P(SP_Z_STR, m.obj_center.z);
  361. #if HAS_I_CENTER
  362. SERIAL_ECHOLNPGM_P(SP_I_STR, m.obj_center.i);
  363. #endif
  364. #if HAS_J_CENTER
  365. SERIAL_ECHOLNPGM_P(SP_J_STR, m.obj_center.j);
  366. #endif
  367. #if HAS_K_CENTER
  368. SERIAL_ECHOLNPGM_P(SP_K_STR, m.obj_center.k);
  369. #endif
  370. SERIAL_EOL();
  371. }
  372. inline void report_measured_backlash(const measurements_t &m) {
  373. SERIAL_ECHOLNPGM("Backlash:");
  374. #if AXIS_CAN_CALIBRATE(X)
  375. #if ENABLED(CALIBRATION_MEASURE_LEFT)
  376. SERIAL_ECHOLNPGM(" Left: ", m.backlash[LEFT]);
  377. #endif
  378. #if ENABLED(CALIBRATION_MEASURE_RIGHT)
  379. SERIAL_ECHOLNPGM(" Right: ", m.backlash[RIGHT]);
  380. #endif
  381. #endif
  382. #if HAS_Y_AXIS && AXIS_CAN_CALIBRATE(Y)
  383. #if ENABLED(CALIBRATION_MEASURE_FRONT)
  384. SERIAL_ECHOLNPGM(" Front: ", m.backlash[FRONT]);
  385. #endif
  386. #if ENABLED(CALIBRATION_MEASURE_BACK)
  387. SERIAL_ECHOLNPGM(" Back: ", m.backlash[BACK]);
  388. #endif
  389. #endif
  390. #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
  391. SERIAL_ECHOLNPGM(" Top: ", m.backlash[TOP]);
  392. #endif
  393. #if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I)
  394. #if ENABLED(CALIBRATION_MEASURE_IMIN)
  395. SERIAL_ECHOLNPGM(" " STR_I_MIN ": ", m.backlash[IMINIMUM]);
  396. #endif
  397. #if ENABLED(CALIBRATION_MEASURE_IMAX)
  398. SERIAL_ECHOLNPGM(" " STR_I_MAX ": ", m.backlash[IMAXIMUM]);
  399. #endif
  400. #endif
  401. #if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J)
  402. #if ENABLED(CALIBRATION_MEASURE_JMIN)
  403. SERIAL_ECHOLNPGM(" " STR_J_MIN ": ", m.backlash[JMINIMUM]);
  404. #endif
  405. #if ENABLED(CALIBRATION_MEASURE_JMAX)
  406. SERIAL_ECHOLNPGM(" " STR_J_MAX ": ", m.backlash[JMAXIMUM]);
  407. #endif
  408. #endif
  409. #if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K)
  410. #if ENABLED(CALIBRATION_MEASURE_KMIN)
  411. SERIAL_ECHOLNPGM(" " STR_K_MIN ": ", m.backlash[KMINIMUM]);
  412. #endif
  413. #if ENABLED(CALIBRATION_MEASURE_KMAX)
  414. SERIAL_ECHOLNPGM(" " STR_K_MAX ": ", m.backlash[KMAXIMUM]);
  415. #endif
  416. #endif
  417. SERIAL_EOL();
  418. }
  419. inline void report_measured_positional_error(const measurements_t &m) {
  420. SERIAL_CHAR('T');
  421. SERIAL_ECHO(active_extruder);
  422. SERIAL_ECHOLNPGM(" Positional Error:");
  423. #if HAS_X_CENTER && AXIS_CAN_CALIBRATE(X)
  424. SERIAL_ECHOLNPGM_P(SP_X_STR, m.pos_error.x);
  425. #endif
  426. #if HAS_Y_CENTER && AXIS_CAN_CALIBRATE(Y)
  427. SERIAL_ECHOLNPGM_P(SP_Y_STR, m.pos_error.y);
  428. #endif
  429. #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z)
  430. SERIAL_ECHOLNPGM_P(SP_Z_STR, m.pos_error.z);
  431. #endif
  432. #if HAS_I_CENTER && AXIS_CAN_CALIBRATE(I)
  433. SERIAL_ECHOLNPGM_P(SP_I_STR, m.pos_error.i);
  434. #endif
  435. #if HAS_J_CENTER && AXIS_CAN_CALIBRATE(J)
  436. SERIAL_ECHOLNPGM_P(SP_J_STR, m.pos_error.j);
  437. #endif
  438. #if HAS_K_CENTER && AXIS_CAN_CALIBRATE(K)
  439. SERIAL_ECHOLNPGM_P(SP_Z_STR, m.pos_error.z);
  440. #endif
  441. SERIAL_EOL();
  442. }
  443. inline void report_measured_nozzle_dimensions(const measurements_t &m) {
  444. SERIAL_ECHOLNPGM("Nozzle Tip Outer Dimensions:");
  445. #if HAS_X_CENTER
  446. SERIAL_ECHOLNPGM_P(SP_X_STR, m.nozzle_outer_dimension.x);
  447. #endif
  448. #if HAS_Y_CENTER
  449. SERIAL_ECHOLNPGM_P(SP_Y_STR, m.nozzle_outer_dimension.y);
  450. #endif
  451. SERIAL_EOL();
  452. UNUSED(m);
  453. }
  454. #if HAS_HOTEND_OFFSET
  455. //
  456. // This function requires normalize_hotend_offsets() to be called
  457. //
  458. inline void report_hotend_offsets() {
  459. LOOP_S_L_N(e, 1, HOTENDS)
  460. SERIAL_ECHOLNPGM_P(PSTR("T"), e, PSTR(" Hotend Offset X"), hotend_offset[e].x, SP_Y_STR, hotend_offset[e].y, SP_Z_STR, hotend_offset[e].z);
  461. }
  462. #endif
  463. #endif // CALIBRATION_REPORTING
  464. /**
  465. * Probe around the calibration object to measure backlash
  466. *
  467. * m in/out - Measurement record, updated with new readings
  468. * uncertainty in - How far away from the object to begin probing
  469. */
  470. inline void calibrate_backlash(measurements_t &m, const float uncertainty) {
  471. // Backlash compensation should be off while measuring backlash
  472. {
  473. // New scope for TEMPORARY_BACKLASH_CORRECTION
  474. TEMPORARY_BACKLASH_CORRECTION(all_off);
  475. TEMPORARY_BACKLASH_SMOOTHING(0.0f);
  476. probe_sides(m, uncertainty);
  477. #if ENABLED(BACKLASH_GCODE)
  478. #if HAS_X_CENTER
  479. backlash.distance_mm.x = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2;
  480. #elif ENABLED(CALIBRATION_MEASURE_LEFT)
  481. backlash.distance_mm.x = m.backlash[LEFT];
  482. #elif ENABLED(CALIBRATION_MEASURE_RIGHT)
  483. backlash.distance_mm.x = m.backlash[RIGHT];
  484. #endif
  485. #if HAS_Y_CENTER
  486. backlash.distance_mm.y = (m.backlash[FRONT] + m.backlash[BACK]) / 2;
  487. #elif ENABLED(CALIBRATION_MEASURE_FRONT)
  488. backlash.distance_mm.y = m.backlash[FRONT];
  489. #elif ENABLED(CALIBRATION_MEASURE_BACK)
  490. backlash.distance_mm.y = m.backlash[BACK];
  491. #endif
  492. TERN_(HAS_Z_AXIS, if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP]);
  493. #if HAS_I_CENTER
  494. backlash.distance_mm.i = (m.backlash[IMINIMUM] + m.backlash[IMAXIMUM]) / 2;
  495. #elif ENABLED(CALIBRATION_MEASURE_IMIN)
  496. backlash.distance_mm.i = m.backlash[IMINIMUM];
  497. #elif ENABLED(CALIBRATION_MEASURE_IMAX)
  498. backlash.distance_mm.i = m.backlash[IMAXIMUM];
  499. #endif
  500. #if HAS_J_CENTER
  501. backlash.distance_mm.j = (m.backlash[JMINIMUM] + m.backlash[JMAXIMUM]) / 2;
  502. #elif ENABLED(CALIBRATION_MEASURE_JMIN)
  503. backlash.distance_mm.j = m.backlash[JMINIMUM];
  504. #elif ENABLED(CALIBRATION_MEASURE_JMAX)
  505. backlash.distance_mm.j = m.backlash[JMAXIMUM];
  506. #endif
  507. #if HAS_K_CENTER
  508. backlash.distance_mm.k = (m.backlash[KMINIMUM] + m.backlash[KMAXIMUM]) / 2;
  509. #elif ENABLED(CALIBRATION_MEASURE_KMIN)
  510. backlash.distance_mm.k = m.backlash[KMINIMUM];
  511. #elif ENABLED(CALIBRATION_MEASURE_KMAX)
  512. backlash.distance_mm.k = m.backlash[KMAXIMUM];
  513. #endif
  514. #endif // BACKLASH_GCODE
  515. }
  516. #if ENABLED(BACKLASH_GCODE)
  517. // Turn on backlash compensation and move in all
  518. // allowed directions to take up any backlash
  519. {
  520. // New scope for TEMPORARY_BACKLASH_CORRECTION
  521. TEMPORARY_BACKLASH_CORRECTION(all_on);
  522. TEMPORARY_BACKLASH_SMOOTHING(0.0f);
  523. const xyz_float_t move = LINEAR_AXIS_ARRAY(
  524. AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3,
  525. AXIS_CAN_CALIBRATE(I) * 3, AXIS_CAN_CALIBRATE(J) * 3, AXIS_CAN_CALIBRATE(K) * 3
  526. );
  527. current_position += move; calibration_move();
  528. current_position -= move; calibration_move();
  529. }
  530. #endif
  531. }
  532. inline void update_measurements(measurements_t &m, const AxisEnum axis) {
  533. current_position[axis] += m.pos_error[axis];
  534. m.obj_center[axis] = true_center[axis];
  535. m.pos_error[axis] = 0;
  536. }
  537. /**
  538. * Probe around the calibration object. Adjust the position and toolhead offset
  539. * using the deviation from the known position of the calibration object.
  540. *
  541. * m in/out - Measurement record, updated with new readings
  542. * uncertainty in - How far away from the object to begin probing
  543. * extruder in - What extruder to probe
  544. *
  545. * Prerequisites:
  546. * - Call calibrate_backlash() beforehand for best accuracy
  547. */
  548. inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const uint8_t extruder) {
  549. TEMPORARY_BACKLASH_CORRECTION(all_on);
  550. TEMPORARY_BACKLASH_SMOOTHING(0.0f);
  551. TERN(HAS_MULTI_HOTEND, set_nozzle(m, extruder), UNUSED(extruder));
  552. probe_sides(m, uncertainty);
  553. // Adjust the hotend offset
  554. #if HAS_HOTEND_OFFSET
  555. if (ENABLED(HAS_X_CENTER) && AXIS_CAN_CALIBRATE(X)) hotend_offset[extruder].x += m.pos_error.x;
  556. if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) hotend_offset[extruder].y += m.pos_error.y;
  557. if (AXIS_CAN_CALIBRATE(Z)) hotend_offset[extruder].z += m.pos_error.z;
  558. normalize_hotend_offsets();
  559. #endif
  560. // Correct for positional error, so the object
  561. // is at the known actual spot
  562. planner.synchronize();
  563. if (ENABLED(HAS_X_CENTER) && AXIS_CAN_CALIBRATE(X)) update_measurements(m, X_AXIS);
  564. if (ENABLED(HAS_Y_CENTER) && AXIS_CAN_CALIBRATE(Y)) update_measurements(m, Y_AXIS);
  565. if (AXIS_CAN_CALIBRATE(Z)) update_measurements(m, Z_AXIS);
  566. TERN_(HAS_I_CENTER, update_measurements(m, I_AXIS));
  567. TERN_(HAS_J_CENTER, update_measurements(m, J_AXIS));
  568. TERN_(HAS_K_CENTER, update_measurements(m, K_AXIS));
  569. sync_plan_position();
  570. }
  571. /**
  572. * Probe around the calibration object for all toolheads, adjusting the coordinate
  573. * system for the first nozzle and the nozzle offset for subsequent nozzles.
  574. *
  575. * m in/out - Measurement record, updated with new readings
  576. * uncertainty in - How far away from the object to begin probing
  577. */
  578. inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty) {
  579. TEMPORARY_BACKLASH_CORRECTION(all_on);
  580. TEMPORARY_BACKLASH_SMOOTHING(0.0f);
  581. HOTEND_LOOP() calibrate_toolhead(m, uncertainty, e);
  582. TERN_(HAS_HOTEND_OFFSET, normalize_hotend_offsets());
  583. TERN_(HAS_MULTI_HOTEND, set_nozzle(m, 0));
  584. }
  585. /**
  586. * Perform a full auto-calibration routine:
  587. *
  588. * 1) For each nozzle, touch top and sides of object to determine object position and
  589. * nozzle offsets. Do a fast but rough search over a wider area.
  590. * 2) With the first nozzle, touch top and sides of object to determine backlash values
  591. * for all axis (if BACKLASH_GCODE is enabled)
  592. * 3) For each nozzle, touch top and sides of object slowly to determine precise
  593. * position of object. Adjust coordinate system and nozzle offsets so probed object
  594. * location corresponds to known object location with a high degree of precision.
  595. */
  596. inline void calibrate_all() {
  597. measurements_t m;
  598. TERN_(HAS_HOTEND_OFFSET, reset_hotend_offsets());
  599. TEMPORARY_BACKLASH_CORRECTION(all_on);
  600. TEMPORARY_BACKLASH_SMOOTHING(0.0f);
  601. // Do a fast and rough calibration of the toolheads
  602. calibrate_all_toolheads(m, CALIBRATION_MEASUREMENT_UNKNOWN);
  603. TERN_(BACKLASH_GCODE, calibrate_backlash(m, CALIBRATION_MEASUREMENT_UNCERTAIN));
  604. // Cycle the toolheads so the servos settle into their "natural" positions
  605. #if HAS_MULTI_HOTEND
  606. HOTEND_LOOP() set_nozzle(m, e);
  607. #endif
  608. // Do a slow and precise calibration of the toolheads
  609. calibrate_all_toolheads(m, CALIBRATION_MEASUREMENT_UNCERTAIN);
  610. current_position.x = X_CENTER;
  611. calibration_move(); // Park nozzle away from calibration object
  612. }
  613. /**
  614. * G425: Perform calibration with calibration object.
  615. *
  616. * B - Perform calibration of backlash only.
  617. * T<extruder> - Perform calibration of toolhead only.
  618. * V - Probe object and print position, error, backlash and hotend offset.
  619. * U - Uncertainty, how far to start probe away from the object (mm)
  620. *
  621. * no args - Perform entire calibration sequence (backlash + position on all toolheads)
  622. */
  623. void GcodeSuite::G425() {
  624. #ifdef CALIBRATION_SCRIPT_PRE
  625. GcodeSuite::process_subcommands_now_P(PSTR(CALIBRATION_SCRIPT_PRE));
  626. #endif
  627. if (homing_needed_error()) return;
  628. TEMPORARY_BED_LEVELING_STATE(false);
  629. SET_SOFT_ENDSTOP_LOOSE(true);
  630. measurements_t m;
  631. const float uncertainty = parser.floatval('U', CALIBRATION_MEASUREMENT_UNCERTAIN);
  632. if (parser.seen_test('B'))
  633. calibrate_backlash(m, uncertainty);
  634. else if (parser.seen_test('T'))
  635. calibrate_toolhead(m, uncertainty, parser.intval('T', active_extruder));
  636. #if ENABLED(CALIBRATION_REPORTING)
  637. else if (parser.seen('V')) {
  638. probe_sides(m, uncertainty);
  639. SERIAL_EOL();
  640. report_measured_faces(m);
  641. report_measured_center(m);
  642. report_measured_backlash(m);
  643. report_measured_nozzle_dimensions(m);
  644. report_measured_positional_error(m);
  645. #if HAS_HOTEND_OFFSET
  646. normalize_hotend_offsets();
  647. report_hotend_offsets();
  648. #endif
  649. }
  650. #endif
  651. else
  652. calibrate_all();
  653. SET_SOFT_ENDSTOP_LOOSE(false);
  654. #ifdef CALIBRATION_SCRIPT_POST
  655. GcodeSuite::process_subcommands_now_P(PSTR(CALIBRATION_SCRIPT_POST));
  656. #endif
  657. }
  658. #endif // CALIBRATION_GCODE