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 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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. //
  23. // Motion Menu
  24. //
  25. #include "../../inc/MarlinConfigPre.h"
  26. #if HAS_LCD_MENU
  27. #include "menu.h"
  28. #include "menu_addon.h"
  29. #include "../../module/motion.h"
  30. #if ENABLED(DELTA)
  31. #include "../../module/delta.h"
  32. #endif
  33. #if ENABLED(PREVENT_COLD_EXTRUSION)
  34. #include "../../module/temperature.h"
  35. #endif
  36. #if HAS_LEVELING
  37. #include "../../module/planner.h"
  38. #include "../../feature/bedlevel/bedlevel.h"
  39. #endif
  40. #if ENABLED(MANUAL_E_MOVES_RELATIVE)
  41. float manual_move_e_origin = 0;
  42. #endif
  43. //
  44. // "Motion" > "Move Axis" submenu
  45. //
  46. static void _lcd_move_xyz(PGM_P const name, const AxisEnum axis) {
  47. if (ui.use_click()) return ui.goto_previous_screen_no_defer();
  48. if (ui.encoderPosition && !ui.manual_move.processing) {
  49. // Start with no limits to movement
  50. float min = current_position[axis] - 1000,
  51. max = current_position[axis] + 1000;
  52. // Limit to software endstops, if enabled
  53. #if HAS_SOFTWARE_ENDSTOPS
  54. if (soft_endstops_enabled) switch (axis) {
  55. case X_AXIS:
  56. TERN_(MIN_SOFTWARE_ENDSTOP_X, min = soft_endstop.min.x);
  57. TERN_(MAX_SOFTWARE_ENDSTOP_X, max = soft_endstop.max.x);
  58. break;
  59. case Y_AXIS:
  60. TERN_(MIN_SOFTWARE_ENDSTOP_Y, min = soft_endstop.min.y);
  61. TERN_(MAX_SOFTWARE_ENDSTOP_Y, max = soft_endstop.max.y);
  62. break;
  63. case Z_AXIS:
  64. TERN_(MIN_SOFTWARE_ENDSTOP_Z, min = soft_endstop.min.z);
  65. TERN_(MAX_SOFTWARE_ENDSTOP_Z, max = soft_endstop.max.z);
  66. default: break;
  67. }
  68. #endif // HAS_SOFTWARE_ENDSTOPS
  69. // Delta limits XY based on the current offset from center
  70. // This assumes the center is 0,0
  71. #if ENABLED(DELTA)
  72. if (axis != Z_AXIS) {
  73. max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis
  74. min = -max;
  75. }
  76. #endif
  77. // Get the new position
  78. const float diff = float(int32_t(ui.encoderPosition)) * ui.manual_move.menu_scale;
  79. #if IS_KINEMATIC
  80. ui.manual_move.offset += diff;
  81. if (int32_t(ui.encoderPosition) < 0)
  82. NOLESS(ui.manual_move.offset, min - current_position[axis]);
  83. else
  84. NOMORE(ui.manual_move.offset, max - current_position[axis]);
  85. #else
  86. current_position[axis] += diff;
  87. if (int32_t(ui.encoderPosition) < 0)
  88. NOLESS(current_position[axis], min);
  89. else
  90. NOMORE(current_position[axis], max);
  91. #endif
  92. ui.manual_move.soon(axis);
  93. ui.refresh(LCDVIEW_REDRAW_NOW);
  94. }
  95. ui.encoderPosition = 0;
  96. if (ui.should_draw()) {
  97. const float pos = NATIVE_TO_LOGICAL(
  98. ui.manual_move.processing ? destination[axis] : current_position[axis] + TERN0(IS_KINEMATIC, ui.manual_move.offset),
  99. axis
  100. );
  101. MenuEditItemBase::draw_edit_screen(name, ui.manual_move.menu_scale >= 0.1f ? ftostr41sign(pos) : ftostr63(pos));
  102. }
  103. }
  104. void lcd_move_x() { _lcd_move_xyz(GET_TEXT(MSG_MOVE_X), X_AXIS); }
  105. void lcd_move_y() { _lcd_move_xyz(GET_TEXT(MSG_MOVE_Y), Y_AXIS); }
  106. void lcd_move_z() { _lcd_move_xyz(GET_TEXT(MSG_MOVE_Z), Z_AXIS); }
  107. #if E_MANUAL
  108. static void lcd_move_e(TERN_(MULTI_MANUAL, const int8_t eindex=-1)) {
  109. if (ui.use_click()) return ui.goto_previous_screen_no_defer();
  110. if (ui.encoderPosition) {
  111. if (!ui.manual_move.processing) {
  112. const float diff = float(int32_t(ui.encoderPosition)) * ui.manual_move.menu_scale;
  113. TERN(IS_KINEMATIC, ui.manual_move.offset, current_position.e) += diff;
  114. ui.manual_move.soon(E_AXIS
  115. #if MULTI_MANUAL
  116. , eindex
  117. #endif
  118. );
  119. ui.refresh(LCDVIEW_REDRAW_NOW);
  120. }
  121. ui.encoderPosition = 0;
  122. }
  123. if (ui.should_draw()) {
  124. TERN_(MULTI_MANUAL, MenuItemBase::init(eindex));
  125. MenuEditItemBase::draw_edit_screen(
  126. GET_TEXT(TERN(MULTI_MANUAL, MSG_MOVE_EN, MSG_MOVE_E)),
  127. ftostr41sign(current_position.e
  128. + TERN0(IS_KINEMATIC, ui.manual_move.offset)
  129. - TERN0(MANUAL_E_MOVES_RELATIVE, manual_move_e_origin)
  130. )
  131. );
  132. } // should_draw
  133. }
  134. #endif // E_MANUAL
  135. //
  136. // "Motion" > "Move Xmm" > "Move XYZ" submenu
  137. //
  138. #ifndef SHORT_MANUAL_Z_MOVE
  139. #define SHORT_MANUAL_Z_MOVE 0.025
  140. #endif
  141. screenFunc_t _manual_move_func_ptr;
  142. void _goto_manual_move(const float scale) {
  143. ui.defer_status_screen();
  144. ui.manual_move.menu_scale = scale;
  145. ui.goto_screen(_manual_move_func_ptr);
  146. }
  147. void _menu_move_distance(const AxisEnum axis, const screenFunc_t func, const int8_t eindex=-1) {
  148. _manual_move_func_ptr = func;
  149. #if ENABLED(PREVENT_COLD_EXTRUSION)
  150. const bool too_cold = axis == E_AXIS && thermalManager.tooColdToExtrude(eindex >= 0 ? eindex : active_extruder);
  151. #else
  152. constexpr bool too_cold = false;
  153. #endif
  154. START_MENU();
  155. if (LCD_HEIGHT >= 4) {
  156. switch (axis) {
  157. case X_AXIS: STATIC_ITEM(MSG_MOVE_X, SS_DEFAULT|SS_INVERT); break;
  158. case Y_AXIS: STATIC_ITEM(MSG_MOVE_Y, SS_DEFAULT|SS_INVERT); break;
  159. case Z_AXIS: STATIC_ITEM(MSG_MOVE_Z, SS_DEFAULT|SS_INVERT); break;
  160. default:
  161. TERN_(MANUAL_E_MOVES_RELATIVE, manual_move_e_origin = current_position.e);
  162. STATIC_ITEM(MSG_MOVE_E, SS_DEFAULT|SS_INVERT);
  163. break;
  164. }
  165. }
  166. if (too_cold)
  167. BACK_ITEM(MSG_HOTEND_TOO_COLD);
  168. else {
  169. BACK_ITEM(MSG_MOVE_AXIS);
  170. SUBMENU(MSG_MOVE_10MM, []{ _goto_manual_move(10); });
  171. SUBMENU(MSG_MOVE_1MM, []{ _goto_manual_move( 1); });
  172. SUBMENU(MSG_MOVE_01MM, []{ _goto_manual_move( 0.1f); });
  173. if (axis == Z_AXIS && (SHORT_MANUAL_Z_MOVE) > 0.0f && (SHORT_MANUAL_Z_MOVE) < 0.1f) {
  174. char tmp[20], numstr[10];
  175. // Determine digits needed right of decimal
  176. const uint8_t digs = !UNEAR_ZERO((SHORT_MANUAL_Z_MOVE) * 1000 - int((SHORT_MANUAL_Z_MOVE) * 1000)) ? 4 :
  177. !UNEAR_ZERO((SHORT_MANUAL_Z_MOVE) * 100 - int((SHORT_MANUAL_Z_MOVE) * 100)) ? 3 : 2;
  178. sprintf_P(tmp, GET_TEXT(MSG_MOVE_Z_DIST), dtostrf(SHORT_MANUAL_Z_MOVE, 1, digs, numstr));
  179. #if DISABLED(HAS_GRAPHICAL_TFT)
  180. extern const char NUL_STR[];
  181. SUBMENU_P(NUL_STR, []{ _goto_manual_move(float(SHORT_MANUAL_Z_MOVE)); });
  182. MENU_ITEM_ADDON_START(0 + ENABLED(HAS_CHARACTER_LCD));
  183. lcd_put_u8str(tmp);
  184. MENU_ITEM_ADDON_END();
  185. #else
  186. SUBMENU_P(tmp, []{ _goto_manual_move(float(SHORT_MANUAL_Z_MOVE)); });
  187. #endif
  188. }
  189. }
  190. END_MENU();
  191. }
  192. void menu_move() {
  193. START_MENU();
  194. BACK_ITEM(MSG_MOTION);
  195. #if BOTH(HAS_SOFTWARE_ENDSTOPS, SOFT_ENDSTOPS_MENU_ITEM)
  196. EDIT_ITEM(bool, MSG_LCD_SOFT_ENDSTOPS, &soft_endstops_enabled);
  197. #endif
  198. if (NONE(IS_KINEMATIC, NO_MOTION_BEFORE_HOMING) || all_axes_homed()) {
  199. if (TERN1(DELTA, current_position.z <= delta_clip_start_height)) {
  200. SUBMENU(MSG_MOVE_X, []{ _menu_move_distance(X_AXIS, lcd_move_x); });
  201. SUBMENU(MSG_MOVE_Y, []{ _menu_move_distance(Y_AXIS, lcd_move_y); });
  202. }
  203. #if ENABLED(DELTA)
  204. else
  205. ACTION_ITEM(MSG_FREE_XY, []{ line_to_z(delta_clip_start_height); ui.synchronize(); });
  206. #endif
  207. SUBMENU(MSG_MOVE_Z, []{ _menu_move_distance(Z_AXIS, lcd_move_z); });
  208. }
  209. else
  210. GCODES_ITEM(MSG_AUTO_HOME, G28_STR);
  211. #if ANY(SWITCHING_EXTRUDER, SWITCHING_NOZZLE, MAGNETIC_SWITCHING_TOOLHEAD)
  212. #if EXTRUDERS >= 4
  213. switch (active_extruder) {
  214. case 0: GCODES_ITEM_N(1, MSG_SELECT_E, PSTR("T1")); break;
  215. case 1: GCODES_ITEM_N(0, MSG_SELECT_E, PSTR("T0")); break;
  216. case 2: GCODES_ITEM_N(3, MSG_SELECT_E, PSTR("T3")); break;
  217. case 3: GCODES_ITEM_N(2, MSG_SELECT_E, PSTR("T2")); break;
  218. #if EXTRUDERS == 6
  219. case 4: GCODES_ITEM_N(5, MSG_SELECT_E, PSTR("T5")); break;
  220. case 5: GCODES_ITEM_N(4, MSG_SELECT_E, PSTR("T4")); break;
  221. #endif
  222. }
  223. #elif EXTRUDERS == 3
  224. if (active_extruder < 2) {
  225. if (active_extruder)
  226. GCODES_ITEM_N(0, MSG_SELECT_E, PSTR("T0"));
  227. else
  228. GCODES_ITEM_N(1, MSG_SELECT_E, PSTR("T1"));
  229. }
  230. #else
  231. if (active_extruder)
  232. GCODES_ITEM_N(0, MSG_SELECT_E, PSTR("T0"));
  233. else
  234. GCODES_ITEM_N(1, MSG_SELECT_E, PSTR("T1"));
  235. #endif
  236. #elif ENABLED(DUAL_X_CARRIAGE)
  237. if (active_extruder)
  238. GCODES_ITEM_N(0, MSG_SELECT_E, PSTR("T0"));
  239. else
  240. GCODES_ITEM_N(1, MSG_SELECT_E, PSTR("T1"));
  241. #endif
  242. #if E_MANUAL
  243. // The current extruder
  244. SUBMENU(MSG_MOVE_E, []{ _menu_move_distance(E_AXIS, []{ lcd_move_e(); }, -1); });
  245. #define SUBMENU_MOVE_E(N) SUBMENU_N(N, MSG_MOVE_EN, []{ _menu_move_distance(E_AXIS, []{ lcd_move_e(MenuItemBase::itemIndex); }, MenuItemBase::itemIndex); });
  246. #if EITHER(SWITCHING_EXTRUDER, SWITCHING_NOZZLE)
  247. // ...and the non-switching
  248. #if E_MANUAL == 7 || E_MANUAL == 5 || E_MANUAL == 3
  249. SUBMENU_MOVE_E(E_MANUAL - 1);
  250. #endif
  251. #elif MULTI_MANUAL
  252. // Independent extruders with one E-stepper per hotend
  253. LOOP_L_N(n, E_MANUAL) SUBMENU_MOVE_E(n);
  254. #endif
  255. #endif // E_MANUAL
  256. END_MENU();
  257. }
  258. #if ENABLED(AUTO_BED_LEVELING_UBL)
  259. void _lcd_ubl_level_bed();
  260. #elif ENABLED(LCD_BED_LEVELING)
  261. void menu_bed_leveling();
  262. #endif
  263. void menu_motion() {
  264. START_MENU();
  265. //
  266. // ^ Main
  267. //
  268. BACK_ITEM(MSG_MAIN);
  269. //
  270. // Move Axis
  271. //
  272. if (TERN1(DELTA, all_axes_homed()))
  273. SUBMENU(MSG_MOVE_AXIS, menu_move);
  274. //
  275. // Auto Home
  276. //
  277. GCODES_ITEM(MSG_AUTO_HOME, G28_STR);
  278. #if ENABLED(INDIVIDUAL_AXIS_HOMING_MENU)
  279. GCODES_ITEM(MSG_AUTO_HOME_X, PSTR("G28X"));
  280. GCODES_ITEM(MSG_AUTO_HOME_Y, PSTR("G28Y"));
  281. GCODES_ITEM(MSG_AUTO_HOME_Z, PSTR("G28Z"));
  282. #endif
  283. //
  284. // Auto-calibration
  285. //
  286. #if ENABLED(CALIBRATION_GCODE)
  287. GCODES_ITEM(MSG_AUTO_CALIBRATE, PSTR("G425"));
  288. #endif
  289. //
  290. // Auto Z-Align
  291. //
  292. #if ENABLED(Z_STEPPER_AUTO_ALIGN)
  293. GCODES_ITEM(MSG_AUTO_Z_ALIGN, PSTR("G34"));
  294. #endif
  295. //
  296. // Assisted Bed Tramming
  297. //
  298. #if ENABLED(ASSISTED_TRAMMING)
  299. GCODES_ITEM(MSG_ASSISTED_TRAMMING, PSTR("G35"));
  300. #endif
  301. //
  302. // Level Bed
  303. //
  304. #if ENABLED(AUTO_BED_LEVELING_UBL)
  305. SUBMENU(MSG_UBL_LEVEL_BED, _lcd_ubl_level_bed);
  306. #elif ENABLED(LCD_BED_LEVELING)
  307. if (!g29_in_progress)
  308. SUBMENU(MSG_BED_LEVELING, menu_bed_leveling);
  309. #elif HAS_LEVELING && DISABLED(SLIM_LCD_MENUS)
  310. #if DISABLED(PROBE_MANUALLY)
  311. GCODES_ITEM(MSG_LEVEL_BED, PSTR("G28\nG29"));
  312. #endif
  313. if (all_axes_homed() && leveling_is_valid()) {
  314. bool show_state = planner.leveling_active;
  315. EDIT_ITEM(bool, MSG_BED_LEVELING, &show_state, _lcd_toggle_bed_leveling);
  316. }
  317. #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
  318. editable.decimal = planner.z_fade_height;
  319. EDIT_ITEM_FAST(float3, MSG_Z_FADE_HEIGHT, &editable.decimal, 0, 100, []{ set_z_fade_height(editable.decimal); });
  320. #endif
  321. #endif
  322. #if ENABLED(LEVEL_BED_CORNERS) && DISABLED(LCD_BED_LEVELING)
  323. ACTION_ITEM(MSG_LEVEL_CORNERS, _lcd_level_bed_corners);
  324. #endif
  325. #if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)
  326. GCODES_ITEM(MSG_M48_TEST, PSTR("G28\nM48 P10"));
  327. #endif
  328. //
  329. // Disable Steppers
  330. //
  331. GCODES_ITEM(MSG_DISABLE_STEPPERS, PSTR("M84"));
  332. END_MENU();
  333. }
  334. #endif // HAS_LCD_MENU