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_filament.cpp 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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. // Filament Change Menu
  24. //
  25. #include "../../inc/MarlinConfigPre.h"
  26. #if HAS_LCD_MENU && ENABLED(ADVANCED_PAUSE_FEATURE)
  27. #include "menu.h"
  28. #include "../../module/temperature.h"
  29. #include "../../feature/pause.h"
  30. #if HAS_FILAMENT_SENSOR
  31. #include "../../feature/runout.h"
  32. #endif
  33. //
  34. // Change Filament > Change/Unload/Load Filament
  35. //
  36. static PauseMode _change_filament_temp_mode; // =PAUSE_MODE_PAUSE_PRINT
  37. static int8_t _change_filament_temp_extruder; // =0
  38. inline PGM_P _change_filament_temp_command() {
  39. switch (_change_filament_temp_mode) {
  40. case PAUSE_MODE_LOAD_FILAMENT:
  41. return PSTR("M701 T%d");
  42. case PAUSE_MODE_UNLOAD_FILAMENT:
  43. return _change_filament_temp_extruder >= 0 ? PSTR("M702 T%d") : PSTR("M702 ;%d");
  44. case PAUSE_MODE_CHANGE_FILAMENT:
  45. case PAUSE_MODE_PAUSE_PRINT:
  46. default:
  47. return PSTR("M600 B0 T%d");
  48. }
  49. return GET_TEXT(MSG_FILAMENTCHANGE);
  50. }
  51. // Initiate Filament Load/Unload/Change at the specified temperature
  52. static void _change_filament_temp(const uint16_t temperature) {
  53. char cmd[11];
  54. sprintf_P(cmd, _change_filament_temp_command(), _change_filament_temp_extruder);
  55. thermalManager.setTargetHotend(temperature, _change_filament_temp_extruder);
  56. lcd_enqueue_one_now(cmd);
  57. }
  58. //
  59. // Menu to choose the temperature and start Filament Change
  60. //
  61. inline PGM_P change_filament_header(const PauseMode mode) {
  62. switch (mode) {
  63. case PAUSE_MODE_LOAD_FILAMENT:
  64. return GET_TEXT(MSG_FILAMENTLOAD);
  65. case PAUSE_MODE_UNLOAD_FILAMENT:
  66. return GET_TEXT(MSG_FILAMENTUNLOAD);
  67. default: break;
  68. }
  69. return GET_TEXT(MSG_FILAMENTCHANGE);
  70. }
  71. void _menu_temp_filament_op(const PauseMode mode, const int8_t extruder) {
  72. _change_filament_temp_mode = mode;
  73. _change_filament_temp_extruder = extruder;
  74. START_MENU();
  75. if (LCD_HEIGHT >= 4) STATIC_ITEM_P(change_filament_header(mode), SS_CENTER|SS_INVERT);
  76. BACK_ITEM(MSG_BACK);
  77. ACTION_ITEM(MSG_PREHEAT_1, [](){ _change_filament_temp(ui.preheat_hotend_temp[0]); });
  78. ACTION_ITEM(MSG_PREHEAT_2, [](){ _change_filament_temp(ui.preheat_hotend_temp[1]); });
  79. EDIT_ITEM_FAST(int3, MSG_PREHEAT_CUSTOM, &thermalManager.temp_hotend[_change_filament_temp_extruder].target, EXTRUDE_MINTEMP, heater_maxtemp[extruder] - 15, [](){
  80. _change_filament_temp(thermalManager.temp_hotend[_change_filament_temp_extruder].target);
  81. });
  82. END_MENU();
  83. }
  84. /**
  85. *
  86. * "Change Filament" submenu
  87. *
  88. */
  89. #if E_STEPPERS > 1 || ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
  90. void menu_change_filament() {
  91. START_MENU();
  92. BACK_ITEM(MSG_MAIN);
  93. // Say "filament change" when no print is active
  94. editable.int8 = printingIsPaused() ? PAUSE_MODE_PAUSE_PRINT : PAUSE_MODE_CHANGE_FILAMENT;
  95. // Change filament
  96. #if E_STEPPERS == 1
  97. PGM_P const msg0 = GET_TEXT(MSG_FILAMENTCHANGE);
  98. if (thermalManager.targetTooColdToExtrude(active_extruder))
  99. MENU_ITEM_P(submenu, msg0, [](){ _menu_temp_filament_op(PauseMode(editable.int8), 0); });
  100. else
  101. MENU_ITEM_P(gcode, msg0, PSTR("M600 B0"));
  102. #else
  103. PGM_P const msg0 = GET_TEXT(MSG_FILAMENTCHANGE_E0);
  104. PGM_P const msg1 = GET_TEXT(MSG_FILAMENTCHANGE_E1);
  105. if (thermalManager.targetTooColdToExtrude(0))
  106. MENU_ITEM_P(submenu, msg0, [](){ _menu_temp_filament_op(PauseMode(editable.int8), 0); });
  107. else
  108. MENU_ITEM_P(gcode, msg0, PSTR("M600 B0 T0"));
  109. if (thermalManager.targetTooColdToExtrude(1))
  110. MENU_ITEM_P(submenu, msg1, [](){ _menu_temp_filament_op(PauseMode(editable.int8), 1); });
  111. else
  112. MENU_ITEM_P(gcode, msg1, PSTR("M600 B0 T1"));
  113. #if E_STEPPERS > 2
  114. PGM_P const msg2 = GET_TEXT(MSG_FILAMENTCHANGE_E2);
  115. if (thermalManager.targetTooColdToExtrude(2))
  116. MENU_ITEM_P(submenu, msg2, [](){ _menu_temp_filament_op(PauseMode(editable.int8), 2); });
  117. else
  118. MENU_ITEM_P(gcode, msg2, PSTR("M600 B0 T2"));
  119. #if E_STEPPERS > 3
  120. PGM_P const msg3 = GET_TEXT(MSG_FILAMENTCHANGE_E3);
  121. if (thermalManager.targetTooColdToExtrude(3))
  122. MENU_ITEM_P(submenu, msg3, [](){ _menu_temp_filament_op(PauseMode(editable.int8), 3); });
  123. else
  124. MENU_ITEM_P(gcode, msg3, PSTR("M600 B0 T3"));
  125. #if E_STEPPERS > 4
  126. PGM_P const msg4 = GET_TEXT(MSG_FILAMENTCHANGE_E4);
  127. if (thermalManager.targetTooColdToExtrude(4))
  128. MENU_ITEM_P(submenu, msg4, [](){ _menu_temp_filament_op(PauseMode(editable.int8), 4); });
  129. else
  130. MENU_ITEM_P(gcode, msg4, PSTR("M600 B0 T4"));
  131. #if E_STEPPERS > 5
  132. PGM_P const msg5 = GET_TEXT(MSG_FILAMENTCHANGE_E5);
  133. if (thermalManager.targetTooColdToExtrude(5))
  134. MENU_ITEM_P(submenu, msg5, [](){ _menu_temp_filament_op(PauseMode(editable.int8), 5); });
  135. else
  136. MENU_ITEM_P(gcode, msg5, PSTR("M600 B0 T5"));
  137. #endif // E_STEPPERS > 5
  138. #endif // E_STEPPERS > 4
  139. #endif // E_STEPPERS > 3
  140. #endif // E_STEPPERS > 2
  141. #endif // E_STEPPERS == 1
  142. #if ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
  143. if (!printer_busy()) {
  144. // Load filament
  145. #if E_STEPPERS == 1
  146. PGM_P const msg0 = GET_TEXT(MSG_FILAMENTLOAD);
  147. if (thermalManager.targetTooColdToExtrude(active_extruder))
  148. MENU_ITEM_P(submenu, msg0, [](){ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, 0); });
  149. else
  150. MENU_ITEM_P(gcode, msg0, PSTR("M701"));
  151. #else
  152. PGM_P const msg0 = GET_TEXT(MSG_FILAMENTLOAD_E0);
  153. PGM_P const msg1 = GET_TEXT(MSG_FILAMENTLOAD_E1);
  154. if (thermalManager.targetTooColdToExtrude(0))
  155. MENU_ITEM_P(submenu, msg0, [](){ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, 0); });
  156. else
  157. MENU_ITEM_P(gcode, msg0, PSTR("M701 T0"));
  158. if (thermalManager.targetTooColdToExtrude(1))
  159. MENU_ITEM_P(submenu, msg1, [](){ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, 1); });
  160. else
  161. MENU_ITEM_P(gcode, msg1, PSTR("M701 T1"));
  162. #if E_STEPPERS > 2
  163. PGM_P const msg2 = GET_TEXT(MSG_FILAMENTLOAD_E2);
  164. if (thermalManager.targetTooColdToExtrude(2))
  165. MENU_ITEM_P(submenu, msg2, [](){ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, 2); });
  166. else
  167. MENU_ITEM_P(gcode, msg2, PSTR("M701 T2"));
  168. #if E_STEPPERS > 3
  169. PGM_P const msg3 = GET_TEXT(MSG_FILAMENTLOAD_E3);
  170. if (thermalManager.targetTooColdToExtrude(3))
  171. MENU_ITEM_P(submenu, msg3, [](){ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, 3); });
  172. else
  173. MENU_ITEM_P(gcode, msg3, PSTR("M701 T3"));
  174. #if E_STEPPERS > 4
  175. PGM_P const msg4 = GET_TEXT(MSG_FILAMENTLOAD_E4);
  176. if (thermalManager.targetTooColdToExtrude(4))
  177. MENU_ITEM_P(submenu, msg4, [](){ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, 4); });
  178. else
  179. MENU_ITEM_P(gcode, msg4, PSTR("M701 T4"));
  180. #if E_STEPPERS > 5
  181. PGM_P const msg5 = GET_TEXT(MSG_FILAMENTLOAD_E5);
  182. if (thermalManager.targetTooColdToExtrude(5))
  183. MENU_ITEM_P(submenu, msg5, [](){ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, 5); });
  184. else
  185. MENU_ITEM_P(gcode, msg5, PSTR("M701 T5"));
  186. #endif // E_STEPPERS > 5
  187. #endif // E_STEPPERS > 4
  188. #endif // E_STEPPERS > 3
  189. #endif // E_STEPPERS > 2
  190. #endif // E_STEPPERS == 1
  191. // Unload filament
  192. #if E_STEPPERS == 1
  193. if (thermalManager.targetHotEnoughToExtrude(active_extruder))
  194. GCODES_ITEM(MSG_FILAMENTUNLOAD, PSTR("M702"));
  195. else
  196. SUBMENU(MSG_FILAMENTUNLOAD, [](){ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, 0); });
  197. #else
  198. #if ENABLED(FILAMENT_UNLOAD_ALL_EXTRUDERS)
  199. if (JOIN_N(E_STEPPERS, &&,
  200. thermalManager.targetHotEnoughToExtrude(0),
  201. thermalManager.targetHotEnoughToExtrude(1),
  202. thermalManager.targetHotEnoughToExtrude(2),
  203. thermalManager.targetHotEnoughToExtrude(3),
  204. thermalManager.targetHotEnoughToExtrude(4),
  205. thermalManager.targetHotEnoughToExtrude(5))
  206. ) GCODES_ITEM(MSG_FILAMENTUNLOAD_ALL, PSTR("M702"));
  207. else
  208. SUBMENU(MSG_FILAMENTUNLOAD_ALL, [](){ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, -1); });
  209. #endif
  210. if (thermalManager.targetHotEnoughToExtrude(0))
  211. GCODES_ITEM(MSG_FILAMENTUNLOAD_E0, PSTR("M702 T0"));
  212. else
  213. SUBMENU(MSG_FILAMENTUNLOAD_E0, [](){ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, 0); });
  214. if (thermalManager.targetHotEnoughToExtrude(1))
  215. GCODES_ITEM(MSG_FILAMENTUNLOAD_E1, PSTR("M702 T1"));
  216. else
  217. SUBMENU(MSG_FILAMENTUNLOAD_E1, [](){ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, 1); });
  218. #if E_STEPPERS > 2
  219. if (thermalManager.targetHotEnoughToExtrude(2))
  220. GCODES_ITEM(MSG_FILAMENTUNLOAD_E2, PSTR("M702 T2"));
  221. else
  222. SUBMENU(MSG_FILAMENTUNLOAD_E2, [](){ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, 2); });
  223. #if E_STEPPERS > 3
  224. if (thermalManager.targetHotEnoughToExtrude(3))
  225. GCODES_ITEM(MSG_FILAMENTUNLOAD_E3, PSTR("M702 T3"));
  226. else
  227. SUBMENU(MSG_FILAMENTUNLOAD_E3, [](){ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, 3); });
  228. #if E_STEPPERS > 4
  229. if (thermalManager.targetHotEnoughToExtrude(4))
  230. GCODES_ITEM(MSG_FILAMENTUNLOAD_E4, PSTR("M702 T4"));
  231. else
  232. SUBMENU(MSG_FILAMENTUNLOAD_E4, [](){ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, 4); });
  233. #if E_STEPPERS > 5
  234. if (thermalManager.targetHotEnoughToExtrude(5))
  235. GCODES_ITEM(MSG_FILAMENTUNLOAD_E5, PSTR("M702 T5"));
  236. else
  237. SUBMENU(MSG_FILAMENTUNLOAD_E5, [](){ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, 5); });
  238. #endif // E_STEPPERS > 5
  239. #endif // E_STEPPERS > 4
  240. #endif // E_STEPPERS > 3
  241. #endif // E_STEPPERS > 2
  242. #endif // E_STEPPERS == 1
  243. }
  244. #endif
  245. END_MENU();
  246. }
  247. #endif
  248. static uint8_t hotend_status_extruder = 0;
  249. static PGM_P pause_header() {
  250. switch (pause_mode) {
  251. case PAUSE_MODE_CHANGE_FILAMENT:
  252. return GET_TEXT(MSG_FILAMENT_CHANGE_HEADER);
  253. case PAUSE_MODE_LOAD_FILAMENT:
  254. return GET_TEXT(MSG_FILAMENT_CHANGE_HEADER_LOAD);
  255. case PAUSE_MODE_UNLOAD_FILAMENT:
  256. return GET_TEXT(MSG_FILAMENT_CHANGE_HEADER_UNLOAD);
  257. default: break;
  258. }
  259. return GET_TEXT(MSG_FILAMENT_CHANGE_HEADER_PAUSE);
  260. }
  261. // Portions from STATIC_ITEM...
  262. #define HOTEND_STATUS_ITEM() do { \
  263. if (_menuLineNr == _thisItemNr) { \
  264. if (ui.should_draw()) { \
  265. draw_menu_item_static(_lcdLineNr, GET_TEXT(MSG_FILAMENT_CHANGE_NOZZLE), SS_INVERT); \
  266. ui.draw_hotend_status(_lcdLineNr, hotend_status_extruder); \
  267. } \
  268. if (_skipStatic && encoderLine <= _thisItemNr) { \
  269. ui.encoderPosition += ENCODER_STEPS_PER_MENU_ITEM; \
  270. ++encoderLine; \
  271. } \
  272. ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); \
  273. } \
  274. ++_thisItemNr; \
  275. }while(0)
  276. void menu_pause_option() {
  277. START_MENU();
  278. #if LCD_HEIGHT > 2
  279. STATIC_ITEM(MSG_FILAMENT_CHANGE_OPTION_HEADER);
  280. #endif
  281. ACTION_ITEM(MSG_FILAMENT_CHANGE_OPTION_PURGE, [](){ pause_menu_response = PAUSE_RESPONSE_EXTRUDE_MORE; });
  282. #if HAS_FILAMENT_SENSOR
  283. if (runout.filament_ran_out)
  284. EDIT_ITEM(bool, MSG_RUNOUT_SENSOR, &runout.enabled, runout.reset);
  285. else
  286. #endif
  287. ACTION_ITEM(MSG_FILAMENT_CHANGE_OPTION_RESUME, [](){ pause_menu_response = PAUSE_RESPONSE_RESUME_PRINT; });
  288. END_MENU();
  289. }
  290. //
  291. // ADVANCED_PAUSE_FEATURE message screens
  292. //
  293. // Warning: msg must have three null bytes to delimit lines!
  294. //
  295. void _lcd_pause_message(PGM_P const msg) {
  296. PGM_P const msg1 = msg;
  297. PGM_P const msg2 = msg1 + strlen_P(msg1) + 1;
  298. PGM_P const msg3 = msg2 + strlen_P(msg2) + 1;
  299. const bool has2 = msg2[0], has3 = msg3[0],
  300. skip1 = !has2 && (LCD_HEIGHT) >= 5;
  301. START_SCREEN();
  302. STATIC_ITEM_P(pause_header(), SS_CENTER|SS_INVERT); // 1: Header
  303. if (skip1) SKIP_ITEM(); // Move a single-line message down
  304. STATIC_ITEM_P(msg1); // 2: Message Line 1
  305. if (has2) STATIC_ITEM_P(msg2); // 3: Message Line 2
  306. if (has3 && (LCD_HEIGHT) >= 5) STATIC_ITEM_P(msg3); // 4: Message Line 3 (if LCD has 5 lines)
  307. if (skip1 + 1 + has2 + has3 < (LCD_HEIGHT) - 2) SKIP_ITEM(); // Push Hotend Status down, if needed
  308. HOTEND_STATUS_ITEM(); // 5: Hotend Status
  309. END_SCREEN();
  310. }
  311. void lcd_pause_pausing_message() { _lcd_pause_message(GET_TEXT(MSG_PAUSE_PRINT_INIT)); }
  312. void lcd_pause_changing_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_INIT)); }
  313. void lcd_pause_unload_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_UNLOAD)); }
  314. void lcd_pause_heating_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_HEATING)); }
  315. void lcd_pause_heat_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_HEAT)); }
  316. void lcd_pause_insert_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_INSERT)); }
  317. void lcd_pause_load_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_LOAD)); }
  318. void lcd_pause_waiting_message() { _lcd_pause_message(GET_TEXT(MSG_ADVANCED_PAUSE_WAITING)); }
  319. void lcd_pause_resume_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_RESUME)); }
  320. void lcd_pause_purge_message() {
  321. #if ENABLED(ADVANCED_PAUSE_CONTINUOUS_PURGE)
  322. _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_CONT_PURGE));
  323. #else
  324. _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_PURGE));
  325. #endif
  326. }
  327. FORCE_INLINE screenFunc_t ap_message_screen(const PauseMessage message) {
  328. switch (message) {
  329. case PAUSE_MESSAGE_PAUSING: return lcd_pause_pausing_message;
  330. case PAUSE_MESSAGE_CHANGING: return lcd_pause_changing_message;
  331. case PAUSE_MESSAGE_UNLOAD: return lcd_pause_unload_message;
  332. case PAUSE_MESSAGE_WAITING: return lcd_pause_waiting_message;
  333. case PAUSE_MESSAGE_INSERT: return lcd_pause_insert_message;
  334. case PAUSE_MESSAGE_LOAD: return lcd_pause_load_message;
  335. case PAUSE_MESSAGE_PURGE: return lcd_pause_purge_message;
  336. case PAUSE_MESSAGE_RESUME: return lcd_pause_resume_message;
  337. case PAUSE_MESSAGE_HEAT: return lcd_pause_heat_message;
  338. case PAUSE_MESSAGE_HEATING: return lcd_pause_heating_message;
  339. case PAUSE_MESSAGE_OPTION: pause_menu_response = PAUSE_RESPONSE_WAIT_FOR;
  340. return menu_pause_option;
  341. case PAUSE_MESSAGE_STATUS:
  342. default: break;
  343. }
  344. return nullptr;
  345. }
  346. void lcd_pause_show_message(
  347. const PauseMessage message,
  348. const PauseMode mode/*=PAUSE_MODE_SAME*/,
  349. const uint8_t extruder/*=active_extruder*/
  350. ) {
  351. if (mode != PAUSE_MODE_SAME) pause_mode = mode;
  352. hotend_status_extruder = extruder;
  353. const screenFunc_t next_screen = ap_message_screen(message);
  354. if (next_screen) {
  355. ui.defer_status_screen();
  356. ui.goto_screen(next_screen);
  357. }
  358. else
  359. ui.return_to_status();
  360. }
  361. #endif // HAS_LCD_MENU && ADVANCED_PAUSE_FEATURE