My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

menu_motion.cpp 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2019 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 <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. //
  23. // Motion Menu
  24. //
  25. #include "../../inc/MarlinConfigPre.h"
  26. #if HAS_LCD_MENU
  27. #include "menu.h"
  28. #include "../lcdprint.h"
  29. #if HAS_GRAPHICAL_LCD
  30. #include "../dogm/ultralcd_DOGM.h"
  31. #endif
  32. #include "../../module/motion.h"
  33. #if ENABLED(DELTA)
  34. #include "../../module/delta.h"
  35. #endif
  36. #if ENABLED(PREVENT_COLD_EXTRUSION)
  37. #include "../../module/temperature.h"
  38. #endif
  39. #if HAS_LEVELING
  40. #include "../../module/planner.h"
  41. #include "../../feature/bedlevel/bedlevel.h"
  42. #endif
  43. extern millis_t manual_move_start_time;
  44. extern int8_t manual_move_axis;
  45. #if ENABLED(MANUAL_E_MOVES_RELATIVE)
  46. float manual_move_e_origin = 0;
  47. #endif
  48. #if IS_KINEMATIC
  49. extern float manual_move_offset;
  50. #endif
  51. //
  52. // Tell ui.update() to start a move to current_position" after a short delay.
  53. //
  54. inline void manual_move_to_current(AxisEnum axis
  55. #if E_MANUAL > 1
  56. , const int8_t eindex=-1
  57. #endif
  58. ) {
  59. #if E_MANUAL > 1
  60. if (axis == E_AXIS) ui.manual_move_e_index = eindex >= 0 ? eindex : active_extruder;
  61. #endif
  62. manual_move_start_time = millis() + (move_menu_scale < 0.99f ? 0UL : 250UL); // delay for bigger moves
  63. manual_move_axis = (int8_t)axis;
  64. }
  65. //
  66. // "Motion" > "Move Axis" submenu
  67. //
  68. static void _lcd_move_xyz(PGM_P name, AxisEnum axis) {
  69. if (ui.use_click()) return ui.goto_previous_screen_no_defer();
  70. if (ui.encoderPosition && !ui.processing_manual_move) {
  71. // Start with no limits to movement
  72. float min = current_position[axis] - 1000,
  73. max = current_position[axis] + 1000;
  74. // Limit to software endstops, if enabled
  75. #if HAS_SOFTWARE_ENDSTOPS
  76. if (soft_endstops_enabled) switch (axis) {
  77. case X_AXIS:
  78. #if ENABLED(MIN_SOFTWARE_ENDSTOP_X)
  79. min = soft_endstop[X_AXIS].min;
  80. #endif
  81. #if ENABLED(MAX_SOFTWARE_ENDSTOP_X)
  82. max = soft_endstop[X_AXIS].max;
  83. #endif
  84. break;
  85. case Y_AXIS:
  86. #if ENABLED(MIN_SOFTWARE_ENDSTOP_Y)
  87. min = soft_endstop[Y_AXIS].min;
  88. #endif
  89. #if ENABLED(MAX_SOFTWARE_ENDSTOP_Y)
  90. max = soft_endstop[Y_AXIS].max;
  91. #endif
  92. break;
  93. case Z_AXIS:
  94. #if ENABLED(MIN_SOFTWARE_ENDSTOP_Z)
  95. min = soft_endstop[Z_AXIS].min;
  96. #endif
  97. #if ENABLED(MAX_SOFTWARE_ENDSTOP_Z)
  98. max = soft_endstop[Z_AXIS].max;
  99. #endif
  100. default: break;
  101. }
  102. #endif // HAS_SOFTWARE_ENDSTOPS
  103. // Delta limits XY based on the current offset from center
  104. // This assumes the center is 0,0
  105. #if ENABLED(DELTA)
  106. if (axis != Z_AXIS) {
  107. max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis
  108. min = -max;
  109. }
  110. #endif
  111. // Get the new position
  112. const float diff = float(int16_t(ui.encoderPosition)) * move_menu_scale;
  113. #if IS_KINEMATIC
  114. manual_move_offset += diff;
  115. if (int16_t(ui.encoderPosition) < 0)
  116. NOLESS(manual_move_offset, min - current_position[axis]);
  117. else
  118. NOMORE(manual_move_offset, max - current_position[axis]);
  119. #else
  120. current_position[axis] += diff;
  121. if (int16_t(ui.encoderPosition) < 0)
  122. NOLESS(current_position[axis], min);
  123. else
  124. NOMORE(current_position[axis], max);
  125. #endif
  126. manual_move_to_current(axis);
  127. ui.refresh(LCDVIEW_REDRAW_NOW);
  128. }
  129. ui.encoderPosition = 0;
  130. if (ui.should_draw()) {
  131. const float pos = NATIVE_TO_LOGICAL(ui.processing_manual_move ? destination[axis] : current_position[axis]
  132. #if IS_KINEMATIC
  133. + manual_move_offset
  134. #endif
  135. , axis);
  136. draw_edit_screen(name, move_menu_scale >= 0.1f ? ftostr41sign(pos) : ftostr43sign(pos));
  137. }
  138. }
  139. void lcd_move_x() { _lcd_move_xyz(PSTR(MSG_MOVE_X), X_AXIS); }
  140. void lcd_move_y() { _lcd_move_xyz(PSTR(MSG_MOVE_Y), Y_AXIS); }
  141. void lcd_move_z() { _lcd_move_xyz(PSTR(MSG_MOVE_Z), Z_AXIS); }
  142. static void _lcd_move_e(
  143. #if E_MANUAL > 1
  144. const int8_t eindex=-1
  145. #endif
  146. ) {
  147. if (ui.use_click()) return ui.goto_previous_screen_no_defer();
  148. if (ui.encoderPosition) {
  149. if (!ui.processing_manual_move) {
  150. const float diff = float(int16_t(ui.encoderPosition)) * move_menu_scale;
  151. #if IS_KINEMATIC
  152. manual_move_offset += diff;
  153. #else
  154. current_position[E_AXIS] += diff;
  155. #endif
  156. manual_move_to_current(E_AXIS
  157. #if E_MANUAL > 1
  158. , eindex
  159. #endif
  160. );
  161. ui.refresh(LCDVIEW_REDRAW_NOW);
  162. }
  163. ui.encoderPosition = 0;
  164. }
  165. if (ui.should_draw()) {
  166. PGM_P pos_label;
  167. #if E_MANUAL == 1
  168. pos_label = PSTR(MSG_MOVE_E);
  169. #else
  170. switch (eindex) {
  171. default: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E1); break;
  172. case 1: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E2); break;
  173. #if E_MANUAL > 2
  174. case 2: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E3); break;
  175. #if E_MANUAL > 3
  176. case 3: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E4); break;
  177. #if E_MANUAL > 4
  178. case 4: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E5); break;
  179. #if E_MANUAL > 5
  180. case 5: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E6); break;
  181. #endif // E_MANUAL > 5
  182. #endif // E_MANUAL > 4
  183. #endif // E_MANUAL > 3
  184. #endif // E_MANUAL > 2
  185. }
  186. #endif // E_MANUAL > 1
  187. draw_edit_screen(pos_label, ftostr41sign(current_position[E_AXIS]
  188. #if IS_KINEMATIC
  189. + manual_move_offset
  190. #endif
  191. #if ENABLED(MANUAL_E_MOVES_RELATIVE)
  192. - manual_move_e_origin
  193. #endif
  194. ));
  195. }
  196. }
  197. inline void lcd_move_e() { _lcd_move_e(); }
  198. #if E_MANUAL > 1
  199. inline void lcd_move_e0() { _lcd_move_e(0); }
  200. inline void lcd_move_e1() { _lcd_move_e(1); }
  201. #if E_MANUAL > 2
  202. inline void lcd_move_e2() { _lcd_move_e(2); }
  203. #if E_MANUAL > 3
  204. inline void lcd_move_e3() { _lcd_move_e(3); }
  205. #if E_MANUAL > 4
  206. inline void lcd_move_e4() { _lcd_move_e(4); }
  207. #if E_MANUAL > 5
  208. inline void lcd_move_e5() { _lcd_move_e(5); }
  209. #endif // E_MANUAL > 5
  210. #endif // E_MANUAL > 4
  211. #endif // E_MANUAL > 3
  212. #endif // E_MANUAL > 2
  213. #endif // E_MANUAL > 1
  214. //
  215. // "Motion" > "Move Xmm" > "Move XYZ" submenu
  216. //
  217. #ifndef SHORT_MANUAL_Z_MOVE
  218. #define SHORT_MANUAL_Z_MOVE 0.025
  219. #endif
  220. screenFunc_t _manual_move_func_ptr;
  221. void _goto_manual_move(const float scale) {
  222. ui.defer_status_screen();
  223. move_menu_scale = scale;
  224. ui.goto_screen(_manual_move_func_ptr);
  225. }
  226. void menu_move_10mm() { _goto_manual_move(10); }
  227. void menu_move_1mm() { _goto_manual_move( 1); }
  228. void menu_move_01mm() { _goto_manual_move( 0.1f); }
  229. void _menu_move_distance(const AxisEnum axis, const screenFunc_t func, const int8_t eindex=-1) {
  230. _manual_move_func_ptr = func;
  231. START_MENU();
  232. if (LCD_HEIGHT >= 4) {
  233. switch (axis) {
  234. case X_AXIS: STATIC_ITEM(MSG_MOVE_X, true, true); break;
  235. case Y_AXIS: STATIC_ITEM(MSG_MOVE_Y, true, true); break;
  236. case Z_AXIS: STATIC_ITEM(MSG_MOVE_Z, true, true); break;
  237. default:
  238. #if ENABLED(MANUAL_E_MOVES_RELATIVE)
  239. manual_move_e_origin = current_position[E_AXIS];
  240. #endif
  241. STATIC_ITEM(MSG_MOVE_E, true, true);
  242. break;
  243. }
  244. }
  245. #if ENABLED(PREVENT_COLD_EXTRUSION)
  246. if (axis == E_AXIS && thermalManager.tooColdToExtrude(eindex >= 0 ? eindex : active_extruder))
  247. MENU_BACK(MSG_HOTEND_TOO_COLD);
  248. else
  249. #endif
  250. {
  251. MENU_BACK(MSG_MOVE_AXIS);
  252. MENU_ITEM(submenu, MSG_MOVE_10MM, menu_move_10mm);
  253. MENU_ITEM(submenu, MSG_MOVE_1MM, menu_move_1mm);
  254. MENU_ITEM(submenu, MSG_MOVE_01MM, menu_move_01mm);
  255. if (axis == Z_AXIS && (SHORT_MANUAL_Z_MOVE) > 0.0f && (SHORT_MANUAL_Z_MOVE) < 0.1f) {
  256. MENU_ITEM(submenu, "", []{ _goto_manual_move(float(SHORT_MANUAL_Z_MOVE)); });
  257. MENU_ITEM_ADDON_START(1);
  258. char tmp[20], numstr[10];
  259. // Determine digits needed right of decimal
  260. const uint8_t digs = !UNEAR_ZERO((SHORT_MANUAL_Z_MOVE) * 1000 - int((SHORT_MANUAL_Z_MOVE) * 1000)) ? 4 :
  261. !UNEAR_ZERO((SHORT_MANUAL_Z_MOVE) * 100 - int((SHORT_MANUAL_Z_MOVE) * 100)) ? 3 : 2;
  262. sprintf_P(tmp, PSTR(MSG_MOVE_Z_DIST), dtostrf(SHORT_MANUAL_Z_MOVE, 1, digs, numstr));
  263. LCDPRINT(tmp);
  264. MENU_ITEM_ADDON_END();
  265. }
  266. }
  267. END_MENU();
  268. }
  269. void lcd_move_get_x_amount() { _menu_move_distance(X_AXIS, lcd_move_x); }
  270. void lcd_move_get_y_amount() { _menu_move_distance(Y_AXIS, lcd_move_y); }
  271. void lcd_move_get_z_amount() { _menu_move_distance(Z_AXIS, lcd_move_z); }
  272. void lcd_move_get_e_amount() { _menu_move_distance(E_AXIS, lcd_move_e, -1); }
  273. #if E_MANUAL > 1
  274. void lcd_move_get_e0_amount() { _menu_move_distance(E_AXIS, lcd_move_e0, 0); }
  275. void lcd_move_get_e1_amount() { _menu_move_distance(E_AXIS, lcd_move_e1, 1); }
  276. #if E_MANUAL > 2
  277. void lcd_move_get_e2_amount() { _menu_move_distance(E_AXIS, lcd_move_e2, 2); }
  278. #if E_MANUAL > 3
  279. void lcd_move_get_e3_amount() { _menu_move_distance(E_AXIS, lcd_move_e3, 3); }
  280. #if E_MANUAL > 4
  281. void lcd_move_get_e4_amount() { _menu_move_distance(E_AXIS, lcd_move_e4, 4); }
  282. #if E_MANUAL > 5
  283. void lcd_move_get_e5_amount() { _menu_move_distance(E_AXIS, lcd_move_e5, 5); }
  284. #endif // E_MANUAL > 5
  285. #endif // E_MANUAL > 4
  286. #endif // E_MANUAL > 3
  287. #endif // E_MANUAL > 2
  288. #endif // E_MANUAL > 1
  289. #if ENABLED(DELTA)
  290. void lcd_lower_z_to_clip_height() {
  291. line_to_z(delta_clip_start_height);
  292. ui.synchronize();
  293. }
  294. #endif
  295. void menu_move() {
  296. START_MENU();
  297. MENU_BACK(MSG_MOTION);
  298. #if HAS_SOFTWARE_ENDSTOPS && ENABLED(SOFT_ENDSTOPS_MENU_ITEM)
  299. MENU_ITEM_EDIT(bool, MSG_LCD_SOFT_ENDSTOPS, &soft_endstops_enabled);
  300. #endif
  301. if (
  302. #if IS_KINEMATIC || ENABLED(NO_MOTION_BEFORE_HOMING)
  303. all_axes_homed()
  304. #else
  305. true
  306. #endif
  307. ) {
  308. if (
  309. #if ENABLED(DELTA)
  310. current_position[Z_AXIS] <= delta_clip_start_height
  311. #else
  312. true
  313. #endif
  314. ) {
  315. MENU_ITEM(submenu, MSG_MOVE_X, lcd_move_get_x_amount);
  316. MENU_ITEM(submenu, MSG_MOVE_Y, lcd_move_get_y_amount);
  317. }
  318. #if ENABLED(DELTA)
  319. else
  320. MENU_ITEM(function, MSG_FREE_XY, lcd_lower_z_to_clip_height);
  321. #endif
  322. MENU_ITEM(submenu, MSG_MOVE_Z, lcd_move_get_z_amount);
  323. }
  324. else
  325. MENU_ITEM(gcode, MSG_AUTO_HOME, PSTR("G28"));
  326. #if ANY(SWITCHING_EXTRUDER, SWITCHING_NOZZLE, MAGNETIC_SWITCHING_TOOLHEAD)
  327. #if EXTRUDERS == 6
  328. switch (active_extruder) {
  329. case 0: MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1")); break;
  330. case 1: MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0")); break;
  331. case 2: MENU_ITEM(gcode, MSG_SELECT " " MSG_E4, PSTR("T3")); break;
  332. case 3: MENU_ITEM(gcode, MSG_SELECT " " MSG_E3, PSTR("T2")); break;
  333. case 4: MENU_ITEM(gcode, MSG_SELECT " " MSG_E6, PSTR("T5")); break;
  334. case 5: MENU_ITEM(gcode, MSG_SELECT " " MSG_E5, PSTR("T4")); break;
  335. }
  336. #elif EXTRUDERS == 5 || EXTRUDERS == 4
  337. switch (active_extruder) {
  338. case 0: MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1")); break;
  339. case 1: MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0")); break;
  340. case 2: MENU_ITEM(gcode, MSG_SELECT " " MSG_E4, PSTR("T3")); break;
  341. case 3: MENU_ITEM(gcode, MSG_SELECT " " MSG_E3, PSTR("T2")); break;
  342. }
  343. #elif EXTRUDERS == 3
  344. if (active_extruder < 2) {
  345. if (active_extruder)
  346. MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0"));
  347. else
  348. MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1"));
  349. }
  350. #else
  351. if (active_extruder)
  352. MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0"));
  353. else
  354. MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1"));
  355. #endif
  356. #elif ENABLED(DUAL_X_CARRIAGE)
  357. if (active_extruder)
  358. MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0"));
  359. else
  360. MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1"));
  361. #endif
  362. #if EITHER(SWITCHING_EXTRUDER, SWITCHING_NOZZLE)
  363. // Only the current...
  364. MENU_ITEM(submenu, MSG_MOVE_E, lcd_move_get_e_amount);
  365. // ...and the non-switching
  366. #if E_MANUAL == 5
  367. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E5, lcd_move_get_e4_amount);
  368. #elif E_MANUAL == 3
  369. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E3, lcd_move_get_e2_amount);
  370. #endif
  371. #else
  372. // Independent extruders with one E-stepper per hotend
  373. MENU_ITEM(submenu, MSG_MOVE_E, lcd_move_get_e_amount);
  374. #if E_MANUAL > 1
  375. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E1, lcd_move_get_e0_amount);
  376. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E2, lcd_move_get_e1_amount);
  377. #if E_MANUAL > 2
  378. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E3, lcd_move_get_e2_amount);
  379. #if E_MANUAL > 3
  380. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E4, lcd_move_get_e3_amount);
  381. #if E_MANUAL > 4
  382. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E5, lcd_move_get_e4_amount);
  383. #if E_MANUAL > 5
  384. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E6, lcd_move_get_e5_amount);
  385. #endif // E_MANUAL > 5
  386. #endif // E_MANUAL > 4
  387. #endif // E_MANUAL > 3
  388. #endif // E_MANUAL > 2
  389. #endif // E_MANUAL > 1
  390. #endif
  391. END_MENU();
  392. }
  393. #if ENABLED(AUTO_BED_LEVELING_UBL)
  394. void _lcd_ubl_level_bed();
  395. #elif ENABLED(LCD_BED_LEVELING)
  396. void menu_bed_leveling();
  397. #endif
  398. void menu_motion() {
  399. START_MENU();
  400. //
  401. // ^ Main
  402. //
  403. MENU_BACK(MSG_MAIN);
  404. //
  405. // Move Axis
  406. //
  407. #if ENABLED(DELTA)
  408. if (all_axes_homed())
  409. #endif
  410. MENU_ITEM(submenu, MSG_MOVE_AXIS, menu_move);
  411. //
  412. // Auto Home
  413. //
  414. MENU_ITEM(gcode, MSG_AUTO_HOME, PSTR("G28"));
  415. #if ENABLED(INDIVIDUAL_AXIS_HOMING_MENU)
  416. MENU_ITEM(gcode, MSG_AUTO_HOME_X, PSTR("G28 X"));
  417. MENU_ITEM(gcode, MSG_AUTO_HOME_Y, PSTR("G28 Y"));
  418. MENU_ITEM(gcode, MSG_AUTO_HOME_Z, PSTR("G28 Z"));
  419. #endif
  420. //
  421. // Auto Z-Align
  422. //
  423. #if ENABLED(Z_STEPPER_AUTO_ALIGN)
  424. MENU_ITEM(gcode, MSG_AUTO_Z_ALIGN, PSTR("G34"));
  425. #endif
  426. //
  427. // Level Bed
  428. //
  429. #if ENABLED(AUTO_BED_LEVELING_UBL)
  430. MENU_ITEM(submenu, MSG_UBL_LEVEL_BED, _lcd_ubl_level_bed);
  431. #elif ENABLED(LCD_BED_LEVELING)
  432. if (!g29_in_progress) MENU_ITEM(submenu, MSG_BED_LEVELING, menu_bed_leveling);
  433. #elif HAS_LEVELING && DISABLED(SLIM_LCD_MENUS)
  434. #if DISABLED(PROBE_MANUALLY)
  435. MENU_ITEM(gcode, MSG_LEVEL_BED, PSTR("G28\nG29"));
  436. #endif
  437. if (all_axes_homed() && leveling_is_valid()) {
  438. bool new_level_state = planner.leveling_active;
  439. MENU_ITEM_EDIT_CALLBACK(bool, MSG_BED_LEVELING, &new_level_state, _lcd_toggle_bed_leveling);
  440. }
  441. #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
  442. MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float3, MSG_Z_FADE_HEIGHT, &lcd_z_fade_height, 0, 100, _lcd_set_z_fade_height);
  443. #endif
  444. #endif
  445. #if ENABLED(LEVEL_BED_CORNERS) && DISABLED(LCD_BED_LEVELING)
  446. MENU_ITEM(function, MSG_LEVEL_CORNERS, _lcd_level_bed_corners);
  447. #endif
  448. #if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)
  449. MENU_ITEM(gcode, MSG_M48_TEST, PSTR("G28\nM48 P10"));
  450. #endif
  451. //
  452. // Disable Steppers
  453. //
  454. MENU_ITEM(gcode, MSG_DISABLE_STEPPERS, PSTR("M84"));
  455. END_MENU();
  456. }
  457. #endif // HAS_LCD_MENU