My Marlin configs for Fabrikator Mini and CTC i3 Pro B
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

menu_filament.cpp 13KB

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