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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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 <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. //
  23. // Filament Change Menu
  24. //
  25. #include "../../inc/MarlinConfigPre.h"
  26. #if BOTH(HAS_LCD_MENU, ADVANCED_PAUSE_FEATURE)
  27. #include "menu.h"
  28. #include "../../module/temperature.h"
  29. #include "../../feature/pause.h"
  30. #include "../../gcode/queue.h"
  31. #if HAS_FILAMENT_SENSOR
  32. #include "../../feature/runout.h"
  33. #endif
  34. //
  35. // Change Filament > Change/Unload/Load Filament
  36. //
  37. static PauseMode _change_filament_temp_mode; // =PAUSE_MODE_PAUSE_PRINT
  38. static int8_t _change_filament_temp_extruder; // =0
  39. inline PGM_P _change_filament_temp_command() {
  40. switch (_change_filament_temp_mode) {
  41. case PAUSE_MODE_LOAD_FILAMENT:
  42. return PSTR("M701 T%d");
  43. case PAUSE_MODE_UNLOAD_FILAMENT:
  44. return _change_filament_temp_extruder >= 0 ? PSTR("M702 T%d") : PSTR("M702 ;%d");
  45. case PAUSE_MODE_CHANGE_FILAMENT:
  46. case PAUSE_MODE_PAUSE_PRINT:
  47. default:
  48. return PSTR("M600 B0 T%d");
  49. }
  50. return GET_TEXT(MSG_FILAMENTCHANGE);
  51. }
  52. // Initiate Filament Load/Unload/Change at the specified temperature
  53. static void _change_filament_temp(const uint16_t temperature) {
  54. char cmd[11];
  55. sprintf_P(cmd, _change_filament_temp_command(), _change_filament_temp_extruder);
  56. thermalManager.setTargetHotend(temperature, _change_filament_temp_extruder);
  57. queue.inject(cmd);
  58. }
  59. //
  60. // Menu to choose the temperature and start Filament Change
  61. //
  62. inline PGM_P change_filament_header(const PauseMode mode) {
  63. switch (mode) {
  64. case PAUSE_MODE_LOAD_FILAMENT:
  65. return GET_TEXT(MSG_FILAMENTLOAD);
  66. case PAUSE_MODE_UNLOAD_FILAMENT:
  67. return GET_TEXT(MSG_FILAMENTUNLOAD);
  68. default: break;
  69. }
  70. return GET_TEXT(MSG_FILAMENTCHANGE);
  71. }
  72. void _menu_temp_filament_op(const PauseMode mode, const int8_t extruder) {
  73. _change_filament_temp_mode = mode;
  74. _change_filament_temp_extruder = extruder;
  75. START_MENU();
  76. if (LCD_HEIGHT >= 4) STATIC_ITEM_P(change_filament_header(mode), SS_CENTER|SS_INVERT);
  77. BACK_ITEM(MSG_BACK);
  78. ACTION_ITEM(MSG_PREHEAT_1, []{ _change_filament_temp(ui.preheat_hotend_temp[0]); });
  79. ACTION_ITEM(MSG_PREHEAT_2, []{ _change_filament_temp(ui.preheat_hotend_temp[1]); });
  80. EDIT_ITEM_FAST(int3, MSG_PREHEAT_CUSTOM, &thermalManager.temp_hotend[_change_filament_temp_extruder].target, EXTRUDE_MINTEMP, heater_maxtemp[extruder] - 15, []{
  81. _change_filament_temp(thermalManager.temp_hotend[_change_filament_temp_extruder].target);
  82. });
  83. END_MENU();
  84. }
  85. /**
  86. *
  87. * "Change Filament" submenu
  88. *
  89. */
  90. #if E_STEPPERS > 1 || ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
  91. void menu_change_filament() {
  92. START_MENU();
  93. BACK_ITEM(MSG_MAIN);
  94. // Say "filament change" when no print is active
  95. editable.int8 = printingIsPaused() ? PAUSE_MODE_PAUSE_PRINT : PAUSE_MODE_CHANGE_FILAMENT;
  96. // Change filament
  97. #if E_STEPPERS == 1
  98. PGM_P const msg = GET_TEXT(MSG_FILAMENTCHANGE);
  99. if (thermalManager.targetTooColdToExtrude(active_extruder))
  100. SUBMENU_P(msg, []{ _menu_temp_filament_op(PAUSE_MODE_CHANGE_FILAMENT, 0); });
  101. else
  102. GCODES_ITEM_P(msg, PSTR("M600 B0"));
  103. #else
  104. PGM_P const msg = GET_TEXT(MSG_FILAMENTCHANGE_E);
  105. LOOP_L_N(s, E_STEPPERS) {
  106. if (thermalManager.targetTooColdToExtrude(s))
  107. SUBMENU_N_P(s, msg, []{ _menu_temp_filament_op(PAUSE_MODE_CHANGE_FILAMENT, MenuItemBase::itemIndex); });
  108. else {
  109. ACTION_ITEM_N_P(s, msg, []{
  110. char cmd[13];
  111. sprintf_P(cmd, PSTR("M600 B0 T%i"), int(MenuItemBase::itemIndex));
  112. queue.inject(cmd);
  113. });
  114. }
  115. }
  116. #endif
  117. #if ENABLED(FILAMENT_LOAD_UNLOAD_GCODES)
  118. if (!printer_busy()) {
  119. // Load filament
  120. #if E_STEPPERS == 1
  121. PGM_P const msg_load = GET_TEXT(MSG_FILAMENTLOAD);
  122. if (thermalManager.targetTooColdToExtrude(active_extruder))
  123. SUBMENU_P(msg_load, []{ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, 0); });
  124. else
  125. GCODES_ITEM_P(msg_load, PSTR("M701"));
  126. #else
  127. PGM_P const msg_load = GET_TEXT(MSG_FILAMENTLOAD_E);
  128. LOOP_L_N(s, E_STEPPERS) {
  129. if (thermalManager.targetTooColdToExtrude(s))
  130. SUBMENU_N_P(s, msg_load, []{ _menu_temp_filament_op(PAUSE_MODE_LOAD_FILAMENT, MenuItemBase::itemIndex); });
  131. else {
  132. ACTION_ITEM_N_P(s, msg_load, []{
  133. char cmd[12];
  134. sprintf_P(cmd, PSTR("M701 T%i"), int(MenuItemBase::itemIndex));
  135. queue.inject(cmd);
  136. });
  137. }
  138. }
  139. #endif
  140. // Unload filament
  141. #if E_STEPPERS == 1
  142. PGM_P const msg_unload = GET_TEXT(MSG_FILAMENTUNLOAD);
  143. if (thermalManager.targetTooColdToExtrude(active_extruder))
  144. SUBMENU_P(msg_unload, []{ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, 0); });
  145. else
  146. GCODES_ITEM_P(msg_unload, PSTR("M702"));
  147. #else
  148. #if ENABLED(FILAMENT_UNLOAD_ALL_EXTRUDERS)
  149. {
  150. bool too_cold = false;
  151. LOOP_L_N(s, E_STEPPERS) {
  152. if (thermalManager.targetTooColdToExtrude(s)) {
  153. too_cold = true; break;
  154. }
  155. }
  156. if (!too_cold)
  157. GCODES_ITEM(MSG_FILAMENTUNLOAD_ALL, PSTR("M702"));
  158. else
  159. SUBMENU(MSG_FILAMENTUNLOAD_ALL, []{ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, -1); });
  160. }
  161. #endif
  162. PGM_P const msg_unload = GET_TEXT(MSG_FILAMENTUNLOAD_E);
  163. LOOP_L_N(s, E_STEPPERS) {
  164. if (thermalManager.targetTooColdToExtrude(s))
  165. SUBMENU_N_P(s, msg_unload, []{ _menu_temp_filament_op(PAUSE_MODE_UNLOAD_FILAMENT, MenuItemBase::itemIndex); });
  166. else {
  167. ACTION_ITEM_N_P(s, msg_unload, []{
  168. char cmd[12];
  169. sprintf_P(cmd, PSTR("M702 T%i"), int(MenuItemBase::itemIndex));
  170. queue.inject(cmd);
  171. });
  172. }
  173. }
  174. #endif
  175. } // !printer_busy
  176. #endif
  177. END_MENU();
  178. }
  179. #endif
  180. static uint8_t hotend_status_extruder = 0;
  181. static PGM_P pause_header() {
  182. switch (pause_mode) {
  183. case PAUSE_MODE_CHANGE_FILAMENT:
  184. return GET_TEXT(MSG_FILAMENT_CHANGE_HEADER);
  185. case PAUSE_MODE_LOAD_FILAMENT:
  186. return GET_TEXT(MSG_FILAMENT_CHANGE_HEADER_LOAD);
  187. case PAUSE_MODE_UNLOAD_FILAMENT:
  188. return GET_TEXT(MSG_FILAMENT_CHANGE_HEADER_UNLOAD);
  189. default: break;
  190. }
  191. return GET_TEXT(MSG_FILAMENT_CHANGE_HEADER_PAUSE);
  192. }
  193. // Portions from STATIC_ITEM...
  194. #define HOTEND_STATUS_ITEM() do { \
  195. if (_menuLineNr == _thisItemNr) { \
  196. if (ui.should_draw()) { \
  197. MenuItem_static::draw(_lcdLineNr, GET_TEXT(MSG_FILAMENT_CHANGE_NOZZLE), SS_INVERT); \
  198. ui.draw_hotend_status(_lcdLineNr, hotend_status_extruder); \
  199. } \
  200. if (_skipStatic && encoderLine <= _thisItemNr) { \
  201. ui.encoderPosition += ENCODER_STEPS_PER_MENU_ITEM; \
  202. ++encoderLine; \
  203. } \
  204. ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); \
  205. } \
  206. ++_thisItemNr; \
  207. }while(0)
  208. void menu_pause_option() {
  209. START_MENU();
  210. #if LCD_HEIGHT > 2
  211. STATIC_ITEM(MSG_FILAMENT_CHANGE_OPTION_HEADER);
  212. #endif
  213. ACTION_ITEM(MSG_FILAMENT_CHANGE_OPTION_PURGE, []{ pause_menu_response = PAUSE_RESPONSE_EXTRUDE_MORE; });
  214. #if HAS_FILAMENT_SENSOR
  215. if (runout.filament_ran_out)
  216. EDIT_ITEM(bool, MSG_RUNOUT_SENSOR, &runout.enabled, runout.reset);
  217. #endif
  218. ACTION_ITEM(MSG_FILAMENT_CHANGE_OPTION_RESUME, []{ pause_menu_response = PAUSE_RESPONSE_RESUME_PRINT; });
  219. END_MENU();
  220. }
  221. //
  222. // ADVANCED_PAUSE_FEATURE message screens
  223. //
  224. // Warning: msg must have three null bytes to delimit lines!
  225. //
  226. void _lcd_pause_message(PGM_P const msg) {
  227. PGM_P const msg1 = msg;
  228. PGM_P const msg2 = msg1 + strlen_P(msg1) + 1;
  229. PGM_P const msg3 = msg2 + strlen_P(msg2) + 1;
  230. const bool has2 = msg2[0], has3 = msg3[0],
  231. skip1 = !has2 && (LCD_HEIGHT) >= 5;
  232. START_SCREEN();
  233. STATIC_ITEM_P(pause_header(), SS_CENTER|SS_INVERT); // 1: Header
  234. if (skip1) SKIP_ITEM(); // Move a single-line message down
  235. STATIC_ITEM_P(msg1); // 2: Message Line 1
  236. if (has2) STATIC_ITEM_P(msg2); // 3: Message Line 2
  237. if (has3 && (LCD_HEIGHT) >= 5) STATIC_ITEM_P(msg3); // 4: Message Line 3 (if LCD has 5 lines)
  238. if (skip1 + 1 + has2 + has3 < (LCD_HEIGHT) - 2) SKIP_ITEM(); // Push Hotend Status down, if needed
  239. HOTEND_STATUS_ITEM(); // 5: Hotend Status
  240. END_SCREEN();
  241. }
  242. void lcd_pause_parking_message() { _lcd_pause_message(GET_TEXT(MSG_PAUSE_PRINT_PARKING)); }
  243. void lcd_pause_changing_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_INIT)); }
  244. void lcd_pause_unload_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_UNLOAD)); }
  245. void lcd_pause_heating_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_HEATING)); }
  246. void lcd_pause_heat_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_HEAT)); }
  247. void lcd_pause_insert_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_INSERT)); }
  248. void lcd_pause_load_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_LOAD)); }
  249. void lcd_pause_waiting_message() { _lcd_pause_message(GET_TEXT(MSG_ADVANCED_PAUSE_WAITING)); }
  250. void lcd_pause_resume_message() { _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_RESUME)); }
  251. void lcd_pause_purge_message() {
  252. #if ENABLED(ADVANCED_PAUSE_CONTINUOUS_PURGE)
  253. _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_CONT_PURGE));
  254. #else
  255. _lcd_pause_message(GET_TEXT(MSG_FILAMENT_CHANGE_PURGE));
  256. #endif
  257. }
  258. FORCE_INLINE screenFunc_t ap_message_screen(const PauseMessage message) {
  259. switch (message) {
  260. case PAUSE_MESSAGE_PARKING: return lcd_pause_parking_message;
  261. case PAUSE_MESSAGE_CHANGING: return lcd_pause_changing_message;
  262. case PAUSE_MESSAGE_UNLOAD: return lcd_pause_unload_message;
  263. case PAUSE_MESSAGE_WAITING: return lcd_pause_waiting_message;
  264. case PAUSE_MESSAGE_INSERT: return lcd_pause_insert_message;
  265. case PAUSE_MESSAGE_LOAD: return lcd_pause_load_message;
  266. case PAUSE_MESSAGE_PURGE: return lcd_pause_purge_message;
  267. case PAUSE_MESSAGE_RESUME: return lcd_pause_resume_message;
  268. case PAUSE_MESSAGE_HEAT: return lcd_pause_heat_message;
  269. case PAUSE_MESSAGE_HEATING: return lcd_pause_heating_message;
  270. case PAUSE_MESSAGE_OPTION: pause_menu_response = PAUSE_RESPONSE_WAIT_FOR;
  271. return menu_pause_option;
  272. case PAUSE_MESSAGE_STATUS:
  273. default: break;
  274. }
  275. return nullptr;
  276. }
  277. void lcd_pause_show_message(
  278. const PauseMessage message,
  279. const PauseMode mode/*=PAUSE_MODE_SAME*/,
  280. const uint8_t extruder/*=active_extruder*/
  281. ) {
  282. if (mode != PAUSE_MODE_SAME) pause_mode = mode;
  283. hotend_status_extruder = extruder;
  284. const screenFunc_t next_screen = ap_message_screen(message);
  285. if (next_screen) {
  286. ui.defer_status_screen();
  287. ui.goto_screen(next_screen);
  288. }
  289. else
  290. ui.return_to_status();
  291. }
  292. #endif // HAS_LCD_MENU && ADVANCED_PAUSE_FEATURE