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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. #if E_MANUAL
  143. static void _lcd_move_e(
  144. #if E_MANUAL > 1
  145. const int8_t eindex=-1
  146. #endif
  147. ) {
  148. if (ui.use_click()) return ui.goto_previous_screen_no_defer();
  149. if (ui.encoderPosition) {
  150. if (!ui.processing_manual_move) {
  151. const float diff = float(int16_t(ui.encoderPosition)) * move_menu_scale;
  152. #if IS_KINEMATIC
  153. manual_move_offset += diff;
  154. #else
  155. current_position[E_AXIS] += diff;
  156. #endif
  157. manual_move_to_current(E_AXIS
  158. #if E_MANUAL > 1
  159. , eindex
  160. #endif
  161. );
  162. ui.refresh(LCDVIEW_REDRAW_NOW);
  163. }
  164. ui.encoderPosition = 0;
  165. }
  166. if (ui.should_draw()) {
  167. PGM_P pos_label;
  168. #if E_MANUAL == 1
  169. pos_label = PSTR(MSG_MOVE_E);
  170. #else
  171. switch (eindex) {
  172. default: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E1); break;
  173. case 1: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E2); break;
  174. #if E_MANUAL > 2
  175. case 2: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E3); break;
  176. #if E_MANUAL > 3
  177. case 3: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E4); break;
  178. #if E_MANUAL > 4
  179. case 4: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E5); break;
  180. #if E_MANUAL > 5
  181. case 5: pos_label = PSTR(MSG_MOVE_E MSG_MOVE_E6); break;
  182. #endif // E_MANUAL > 5
  183. #endif // E_MANUAL > 4
  184. #endif // E_MANUAL > 3
  185. #endif // E_MANUAL > 2
  186. }
  187. #endif // E_MANUAL > 1
  188. draw_edit_screen(pos_label, ftostr41sign(current_position[E_AXIS]
  189. #if IS_KINEMATIC
  190. + manual_move_offset
  191. #endif
  192. #if ENABLED(MANUAL_E_MOVES_RELATIVE)
  193. - manual_move_e_origin
  194. #endif
  195. ));
  196. }
  197. }
  198. inline void lcd_move_e() { _lcd_move_e(); }
  199. #if E_MANUAL > 1
  200. inline void lcd_move_e0() { _lcd_move_e(0); }
  201. inline void lcd_move_e1() { _lcd_move_e(1); }
  202. #if E_MANUAL > 2
  203. inline void lcd_move_e2() { _lcd_move_e(2); }
  204. #if E_MANUAL > 3
  205. inline void lcd_move_e3() { _lcd_move_e(3); }
  206. #if E_MANUAL > 4
  207. inline void lcd_move_e4() { _lcd_move_e(4); }
  208. #if E_MANUAL > 5
  209. inline void lcd_move_e5() { _lcd_move_e(5); }
  210. #endif // E_MANUAL > 5
  211. #endif // E_MANUAL > 4
  212. #endif // E_MANUAL > 3
  213. #endif // E_MANUAL > 2
  214. #endif // E_MANUAL > 1
  215. #endif // E_MANUAL
  216. //
  217. // "Motion" > "Move Xmm" > "Move XYZ" submenu
  218. //
  219. #ifndef SHORT_MANUAL_Z_MOVE
  220. #define SHORT_MANUAL_Z_MOVE 0.025
  221. #endif
  222. screenFunc_t _manual_move_func_ptr;
  223. void _goto_manual_move(const float scale) {
  224. ui.defer_status_screen();
  225. move_menu_scale = scale;
  226. ui.goto_screen(_manual_move_func_ptr);
  227. }
  228. void menu_move_10mm() { _goto_manual_move(10); }
  229. void menu_move_1mm() { _goto_manual_move( 1); }
  230. void menu_move_01mm() { _goto_manual_move( 0.1f); }
  231. void _menu_move_distance(const AxisEnum axis, const screenFunc_t func, const int8_t eindex=-1) {
  232. _manual_move_func_ptr = func;
  233. START_MENU();
  234. if (LCD_HEIGHT >= 4) {
  235. switch (axis) {
  236. case X_AXIS: STATIC_ITEM(MSG_MOVE_X, true, true); break;
  237. case Y_AXIS: STATIC_ITEM(MSG_MOVE_Y, true, true); break;
  238. case Z_AXIS: STATIC_ITEM(MSG_MOVE_Z, true, true); break;
  239. default:
  240. #if ENABLED(MANUAL_E_MOVES_RELATIVE)
  241. manual_move_e_origin = current_position[E_AXIS];
  242. #endif
  243. STATIC_ITEM(MSG_MOVE_E, true, true);
  244. break;
  245. }
  246. }
  247. #if ENABLED(PREVENT_COLD_EXTRUSION)
  248. if (axis == E_AXIS && thermalManager.tooColdToExtrude(eindex >= 0 ? eindex : active_extruder))
  249. MENU_BACK(MSG_HOTEND_TOO_COLD);
  250. else
  251. #endif
  252. {
  253. MENU_BACK(MSG_MOVE_AXIS);
  254. MENU_ITEM(submenu, MSG_MOVE_10MM, menu_move_10mm);
  255. MENU_ITEM(submenu, MSG_MOVE_1MM, menu_move_1mm);
  256. MENU_ITEM(submenu, MSG_MOVE_01MM, menu_move_01mm);
  257. if (axis == Z_AXIS && (SHORT_MANUAL_Z_MOVE) > 0.0f && (SHORT_MANUAL_Z_MOVE) < 0.1f) {
  258. MENU_ITEM(submenu, "", []{ _goto_manual_move(float(SHORT_MANUAL_Z_MOVE)); });
  259. MENU_ITEM_ADDON_START(1);
  260. char tmp[20], numstr[10];
  261. // Determine digits needed right of decimal
  262. const uint8_t digs = !UNEAR_ZERO((SHORT_MANUAL_Z_MOVE) * 1000 - int((SHORT_MANUAL_Z_MOVE) * 1000)) ? 4 :
  263. !UNEAR_ZERO((SHORT_MANUAL_Z_MOVE) * 100 - int((SHORT_MANUAL_Z_MOVE) * 100)) ? 3 : 2;
  264. sprintf_P(tmp, PSTR(MSG_MOVE_Z_DIST), dtostrf(SHORT_MANUAL_Z_MOVE, 1, digs, numstr));
  265. LCDPRINT(tmp);
  266. MENU_ITEM_ADDON_END();
  267. }
  268. }
  269. END_MENU();
  270. }
  271. void lcd_move_get_x_amount() { _menu_move_distance(X_AXIS, lcd_move_x); }
  272. void lcd_move_get_y_amount() { _menu_move_distance(Y_AXIS, lcd_move_y); }
  273. void lcd_move_get_z_amount() { _menu_move_distance(Z_AXIS, lcd_move_z); }
  274. #if E_MANUAL
  275. void lcd_move_get_e_amount() { _menu_move_distance(E_AXIS, lcd_move_e, -1); }
  276. #if E_MANUAL > 1
  277. void lcd_move_get_e0_amount() { _menu_move_distance(E_AXIS, lcd_move_e0, 0); }
  278. void lcd_move_get_e1_amount() { _menu_move_distance(E_AXIS, lcd_move_e1, 1); }
  279. #if E_MANUAL > 2
  280. void lcd_move_get_e2_amount() { _menu_move_distance(E_AXIS, lcd_move_e2, 2); }
  281. #if E_MANUAL > 3
  282. void lcd_move_get_e3_amount() { _menu_move_distance(E_AXIS, lcd_move_e3, 3); }
  283. #if E_MANUAL > 4
  284. void lcd_move_get_e4_amount() { _menu_move_distance(E_AXIS, lcd_move_e4, 4); }
  285. #if E_MANUAL > 5
  286. void lcd_move_get_e5_amount() { _menu_move_distance(E_AXIS, lcd_move_e5, 5); }
  287. #endif // E_MANUAL > 5
  288. #endif // E_MANUAL > 4
  289. #endif // E_MANUAL > 3
  290. #endif // E_MANUAL > 2
  291. #endif // E_MANUAL > 1
  292. #endif // E_MANUAL
  293. #if ENABLED(DELTA)
  294. void lcd_lower_z_to_clip_height() {
  295. line_to_z(delta_clip_start_height);
  296. ui.synchronize();
  297. }
  298. #endif
  299. void menu_move() {
  300. START_MENU();
  301. MENU_BACK(MSG_MOTION);
  302. #if HAS_SOFTWARE_ENDSTOPS && ENABLED(SOFT_ENDSTOPS_MENU_ITEM)
  303. MENU_ITEM_EDIT(bool, MSG_LCD_SOFT_ENDSTOPS, &soft_endstops_enabled);
  304. #endif
  305. if (
  306. #if IS_KINEMATIC || ENABLED(NO_MOTION_BEFORE_HOMING)
  307. all_axes_homed()
  308. #else
  309. true
  310. #endif
  311. ) {
  312. if (
  313. #if ENABLED(DELTA)
  314. current_position[Z_AXIS] <= delta_clip_start_height
  315. #else
  316. true
  317. #endif
  318. ) {
  319. MENU_ITEM(submenu, MSG_MOVE_X, lcd_move_get_x_amount);
  320. MENU_ITEM(submenu, MSG_MOVE_Y, lcd_move_get_y_amount);
  321. }
  322. #if ENABLED(DELTA)
  323. else
  324. MENU_ITEM(function, MSG_FREE_XY, lcd_lower_z_to_clip_height);
  325. #endif
  326. MENU_ITEM(submenu, MSG_MOVE_Z, lcd_move_get_z_amount);
  327. }
  328. else
  329. MENU_ITEM(gcode, MSG_AUTO_HOME, PSTR("G28"));
  330. #if ANY(SWITCHING_EXTRUDER, SWITCHING_NOZZLE, MAGNETIC_SWITCHING_TOOLHEAD)
  331. #if EXTRUDERS == 6
  332. switch (active_extruder) {
  333. case 0: MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1")); break;
  334. case 1: MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0")); break;
  335. case 2: MENU_ITEM(gcode, MSG_SELECT " " MSG_E4, PSTR("T3")); break;
  336. case 3: MENU_ITEM(gcode, MSG_SELECT " " MSG_E3, PSTR("T2")); break;
  337. case 4: MENU_ITEM(gcode, MSG_SELECT " " MSG_E6, PSTR("T5")); break;
  338. case 5: MENU_ITEM(gcode, MSG_SELECT " " MSG_E5, PSTR("T4")); break;
  339. }
  340. #elif EXTRUDERS == 5 || EXTRUDERS == 4
  341. switch (active_extruder) {
  342. case 0: MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1")); break;
  343. case 1: MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0")); break;
  344. case 2: MENU_ITEM(gcode, MSG_SELECT " " MSG_E4, PSTR("T3")); break;
  345. case 3: MENU_ITEM(gcode, MSG_SELECT " " MSG_E3, PSTR("T2")); break;
  346. }
  347. #elif EXTRUDERS == 3
  348. if (active_extruder < 2) {
  349. if (active_extruder)
  350. MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0"));
  351. else
  352. MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1"));
  353. }
  354. #else
  355. if (active_extruder)
  356. MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0"));
  357. else
  358. MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1"));
  359. #endif
  360. #elif ENABLED(DUAL_X_CARRIAGE)
  361. if (active_extruder)
  362. MENU_ITEM(gcode, MSG_SELECT " " MSG_E1, PSTR("T0"));
  363. else
  364. MENU_ITEM(gcode, MSG_SELECT " " MSG_E2, PSTR("T1"));
  365. #endif
  366. #if E_MANUAL
  367. #if EITHER(SWITCHING_EXTRUDER, SWITCHING_NOZZLE)
  368. // Only the current...
  369. MENU_ITEM(submenu, MSG_MOVE_E, lcd_move_get_e_amount);
  370. // ...and the non-switching
  371. #if E_MANUAL == 5
  372. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E5, lcd_move_get_e4_amount);
  373. #elif E_MANUAL == 3
  374. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E3, lcd_move_get_e2_amount);
  375. #endif
  376. #else
  377. // Independent extruders with one E-stepper per hotend
  378. MENU_ITEM(submenu, MSG_MOVE_E, lcd_move_get_e_amount);
  379. #if E_MANUAL > 1
  380. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E1, lcd_move_get_e0_amount);
  381. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E2, lcd_move_get_e1_amount);
  382. #if E_MANUAL > 2
  383. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E3, lcd_move_get_e2_amount);
  384. #if E_MANUAL > 3
  385. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E4, lcd_move_get_e3_amount);
  386. #if E_MANUAL > 4
  387. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E5, lcd_move_get_e4_amount);
  388. #if E_MANUAL > 5
  389. MENU_ITEM(submenu, MSG_MOVE_E MSG_MOVE_E6, lcd_move_get_e5_amount);
  390. #endif // E_MANUAL > 5
  391. #endif // E_MANUAL > 4
  392. #endif // E_MANUAL > 3
  393. #endif // E_MANUAL > 2
  394. #endif // E_MANUAL > 1
  395. #endif
  396. #endif // E_MANUAL
  397. END_MENU();
  398. }
  399. #if ENABLED(AUTO_BED_LEVELING_UBL)
  400. void _lcd_ubl_level_bed();
  401. #elif ENABLED(LCD_BED_LEVELING)
  402. void menu_bed_leveling();
  403. #endif
  404. void menu_motion() {
  405. START_MENU();
  406. //
  407. // ^ Main
  408. //
  409. MENU_BACK(MSG_MAIN);
  410. //
  411. // Move Axis
  412. //
  413. #if ENABLED(DELTA)
  414. if (all_axes_homed())
  415. #endif
  416. MENU_ITEM(submenu, MSG_MOVE_AXIS, menu_move);
  417. //
  418. // Auto Home
  419. //
  420. MENU_ITEM(gcode, MSG_AUTO_HOME, PSTR("G28"));
  421. #if ENABLED(INDIVIDUAL_AXIS_HOMING_MENU)
  422. MENU_ITEM(gcode, MSG_AUTO_HOME_X, PSTR("G28 X"));
  423. MENU_ITEM(gcode, MSG_AUTO_HOME_Y, PSTR("G28 Y"));
  424. MENU_ITEM(gcode, MSG_AUTO_HOME_Z, PSTR("G28 Z"));
  425. #endif
  426. //
  427. // Auto Z-Align
  428. //
  429. #if ENABLED(Z_STEPPER_AUTO_ALIGN)
  430. MENU_ITEM(gcode, MSG_AUTO_Z_ALIGN, PSTR("G34"));
  431. #endif
  432. //
  433. // Level Bed
  434. //
  435. #if ENABLED(AUTO_BED_LEVELING_UBL)
  436. MENU_ITEM(submenu, MSG_UBL_LEVEL_BED, _lcd_ubl_level_bed);
  437. #elif ENABLED(LCD_BED_LEVELING)
  438. if (!g29_in_progress) MENU_ITEM(submenu, MSG_BED_LEVELING, menu_bed_leveling);
  439. #elif HAS_LEVELING && DISABLED(SLIM_LCD_MENUS)
  440. #if DISABLED(PROBE_MANUALLY)
  441. MENU_ITEM(gcode, MSG_LEVEL_BED, PSTR("G28\nG29"));
  442. #endif
  443. if (all_axes_homed() && leveling_is_valid()) {
  444. bool new_level_state = planner.leveling_active;
  445. MENU_ITEM_EDIT_CALLBACK(bool, MSG_BED_LEVELING, &new_level_state, _lcd_toggle_bed_leveling);
  446. }
  447. #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
  448. MENU_MULTIPLIER_ITEM_EDIT_CALLBACK(float3, MSG_Z_FADE_HEIGHT, &lcd_z_fade_height, 0, 100, _lcd_set_z_fade_height);
  449. #endif
  450. #endif
  451. #if ENABLED(LEVEL_BED_CORNERS) && DISABLED(LCD_BED_LEVELING)
  452. MENU_ITEM(function, MSG_LEVEL_CORNERS, _lcd_level_bed_corners);
  453. #endif
  454. #if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)
  455. MENU_ITEM(gcode, MSG_M48_TEST, PSTR("G28\nM48 P10"));
  456. #endif
  457. //
  458. // Disable Steppers
  459. //
  460. MENU_ITEM(gcode, MSG_DISABLE_STEPPERS, PSTR("M84"));
  461. END_MENU();
  462. }
  463. #endif // HAS_LCD_MENU