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.

bio_status_screen.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*************************
  2. * bio_status_screen.cpp *
  3. *************************/
  4. /****************************************************************************
  5. * Written By Mark Pelletier 2017 - Aleph Objects, Inc. *
  6. * Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
  7. * Written By Marcio Teixeira 2019 - Cocoa Press *
  8. * *
  9. * This program is free software: you can redistribute it and/or modify *
  10. * it under the terms of the GNU General Public License as published by *
  11. * the Free Software Foundation, either version 3 of the License, or *
  12. * (at your option) any later version. *
  13. * *
  14. * This program is distributed in the hope that it will be useful, *
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  17. * GNU General Public License for more details. *
  18. * *
  19. * To view a copy of the GNU General Public License, go to the following *
  20. * location: <https://www.gnu.org/licenses/>. *
  21. ****************************************************************************/
  22. #include "../config.h"
  23. #if ENABLED(TOUCH_UI_FTDI_EVE) && ANY(TOUCH_UI_LULZBOT_BIO, TOUCH_UI_COCOA_PRESS)
  24. #include "screens.h"
  25. #include "../ftdi_eve_lib/extras/poly_ui.h"
  26. #if ENABLED(TOUCH_UI_COCOA_PRESS)
  27. #include "cocoa_press_ui.h"
  28. #elif ENABLED(TOUCH_UI_PORTRAIT)
  29. #include "bio_printer_ui_portrait.h"
  30. #else
  31. #include "bio_printer_ui_landscape.h"
  32. #endif
  33. #define GRID_COLS 2
  34. #define GRID_ROWS 9
  35. #define POLY(A) PolyUI::poly_reader_t(A, sizeof(A)/sizeof(A[0]))
  36. const uint8_t shadow_depth = 5;
  37. const float max_speed = 1.00;
  38. const float min_speed = 0.02;
  39. const float emax_speed = 2.00;
  40. const float emin_speed = 0.70;
  41. using namespace FTDI;
  42. using namespace Theme;
  43. using namespace ExtUI;
  44. float StatusScreen::increment;
  45. bool StatusScreen::jog_xy;
  46. bool StatusScreen::fine_motion;
  47. void StatusScreen::unlockMotors() {
  48. injectCommands_P(PSTR("M84 XY"));
  49. jog_xy = false;
  50. }
  51. void StatusScreen::draw_temperature(draw_mode_t what) {
  52. CommandProcessor cmd;
  53. PolyUI ui(cmd, what);
  54. int16_t x, y, h, v;
  55. cmd.tag(15);
  56. if (what & BACKGROUND) {
  57. cmd.cmd(COLOR_RGB(bg_color));
  58. #if ENABLED(TOUCH_UI_LULZBOT_BIO)
  59. // The LulzBot Bio shows the temperature for
  60. // the bed.
  61. #ifdef TOUCH_UI_PORTRAIT
  62. // Draw touch surfaces
  63. ui.bounds(POLY(target_temp), x, y, h, v);
  64. cmd.rectangle(x, y, h, v);
  65. ui.bounds(POLY(actual_temp), x, y, h, v);
  66. cmd.rectangle(x, y, h, v);
  67. #else
  68. ui.bounds(POLY(bed_temp), x, y, h, v);
  69. cmd.rectangle(x, y, h, v);
  70. #endif
  71. ui.bounds(POLY(bed_icon), x, y, h, v);
  72. cmd.rectangle(x, y, h, v);
  73. // Draw bed icon
  74. cmd.cmd(BITMAP_SOURCE(Bed_Heat_Icon_Info))
  75. .cmd(BITMAP_LAYOUT(Bed_Heat_Icon_Info))
  76. .cmd(BITMAP_SIZE (Bed_Heat_Icon_Info))
  77. .cmd(COLOR_RGB(shadow_rgb))
  78. .icon (x + 2, y + 2, h, v, Bed_Heat_Icon_Info, icon_scale * 2)
  79. .cmd(COLOR_RGB(bg_text_enabled))
  80. .icon (x, y, h, v, Bed_Heat_Icon_Info, icon_scale * 2);
  81. #elif ENABLED(TOUCH_UI_COCOA_PRESS) && DISABLED(TOUCH_UI_PORTRAIT)
  82. // The CocoaPress shows the temperature for two
  83. // heating zones, but has no bed temperature
  84. cmd.cmd(COLOR_RGB(bg_text_enabled));
  85. cmd.font(font_xsmall);
  86. ui.bounds(POLY(h0_label), x, y, h, v);
  87. cmd.text(x, y, h, v, GET_TEXT_F(MSG_ZONE_1));
  88. ui.bounds(POLY(h1_label), x, y, h, v);
  89. cmd.text(x, y, h, v, GET_TEXT_F(MSG_ZONE_2));
  90. ui.bounds(POLY(h2_label), x, y, h, v);
  91. cmd.text(x, y, h, v, GET_TEXT_F(MSG_ZONE_3));
  92. ui.bounds(POLY(h3_label), x, y, h, v);
  93. cmd.text(x, y, h, v, GET_TEXT_F(MSG_CHAMBER));
  94. #else
  95. UNUSED(x);
  96. UNUSED(y);
  97. UNUSED(h);
  98. UNUSED(v);
  99. #endif
  100. #ifdef TOUCH_UI_USE_UTF8
  101. load_utf8_bitmaps(cmd); // Restore font bitmap handles
  102. #endif
  103. }
  104. if (what & FOREGROUND) {
  105. char str[15];
  106. cmd.cmd(COLOR_RGB(bg_text_enabled));
  107. #if ENABLED(TOUCH_UI_LULZBOT_BIO)
  108. cmd.font(font_medium);
  109. #ifdef TOUCH_UI_PORTRAIT
  110. if (!isHeaterIdle(BED) && getTargetTemp_celsius(BED) > 0)
  111. format_temp(str, getTargetTemp_celsius(BED));
  112. else
  113. strcpy_P(str, GET_TEXT(MSG_BED));
  114. ui.bounds(POLY(target_temp), x, y, h, v);
  115. cmd.text(x, y, h, v, str);
  116. format_temp(str, getActualTemp_celsius(BED));
  117. ui.bounds(POLY(actual_temp), x, y, h, v);
  118. cmd.text(x, y, h, v, str);
  119. #else
  120. if (!isHeaterIdle(BED) && getTargetTemp_celsius(BED) > 0)
  121. format_temp_and_temp(str, getActualTemp_celsius(BED), getTargetTemp_celsius(BED));
  122. else
  123. format_temp_and_idle(str, getActualTemp_celsius(BED));
  124. ui.bounds(POLY(bed_temp), x, y, h, v);
  125. cmd.text(x, y, h, v, str);
  126. #endif
  127. #elif ENABLED(TOUCH_UI_COCOA_PRESS) && DISABLED(TOUCH_UI_PORTRAIT)
  128. // The CocoaPress shows the temperature for two
  129. // heating zones, but has no bed temperature
  130. cmd.font(font_large);
  131. if (!isHeaterIdle(E0) && getTargetTemp_celsius(E0) > 0)
  132. format_temp_and_temp(str, getActualTemp_celsius(E0), getTargetTemp_celsius(E0));
  133. else
  134. format_temp_and_idle(str, getActualTemp_celsius(E0));
  135. ui.bounds(POLY(h0_temp), x, y, h, v);
  136. cmd.text(x, y, h, v, str);
  137. if (!isHeaterIdle(E1) && getTargetTemp_celsius(E1) > 0)
  138. format_temp_and_temp(str, getActualTemp_celsius(E1), getTargetTemp_celsius(E1));
  139. else
  140. format_temp_and_idle(str, getActualTemp_celsius(E1));
  141. ui.bounds(POLY(h1_temp), x, y, h, v);
  142. cmd.text(x, y, h, v, str);
  143. if (!isHeaterIdle(E2) && getTargetTemp_celsius(E2) > 0)
  144. format_temp_and_temp(str, getActualTemp_celsius(E2), getTargetTemp_celsius(E2));
  145. else
  146. format_temp_and_idle(str, getActualTemp_celsius(E2));
  147. ui.bounds(POLY(h2_temp), x, y, h, v);
  148. cmd.text(x, y, h, v, str);
  149. if (!isHeaterIdle(CHAMBER) && getTargetTemp_celsius(CHAMBER) > 0)
  150. format_temp_and_temp(str, getActualTemp_celsius(CHAMBER), getTargetTemp_celsius(CHAMBER));
  151. else
  152. format_temp_and_idle(str, getActualTemp_celsius(CHAMBER));
  153. ui.bounds(POLY(h3_temp), x, y, h, v);
  154. cmd.text(x, y, h, v, str);
  155. #else
  156. UNUSED(str);
  157. #endif
  158. }
  159. }
  160. void StatusScreen::draw_syringe(draw_mode_t what) {
  161. int16_t x, y, h, v;
  162. const float fill_level = (
  163. #ifdef E_MAX_POS
  164. 1.0 - min(1.0, max(0.0, getAxisPosition_mm(E0) / E_MAX_POS))
  165. #else
  166. 0.75
  167. #endif
  168. );
  169. const bool e_homed = TERN0(TOUCH_UI_LULZBOT_BIO, isAxisPositionKnown(E0));
  170. CommandProcessor cmd;
  171. PolyUI ui(cmd, what);
  172. if (what & BACKGROUND) {
  173. // Paint the shadow for the syringe
  174. ui.color(shadow_rgb);
  175. ui.shadow(POLY(syringe_outline), shadow_depth);
  176. }
  177. if (what & FOREGROUND && e_homed) {
  178. // Paint the syringe icon
  179. ui.color(syringe_rgb);
  180. ui.fill(POLY(syringe_outline));
  181. ui.color(fluid_rgb);
  182. ui.bounds(POLY(syringe_fluid), x, y, h, v);
  183. cmd.cmd(SAVE_CONTEXT());
  184. cmd.cmd(SCISSOR_XY(x,y + v * (1.0 - fill_level)));
  185. cmd.cmd(SCISSOR_SIZE(h, v * fill_level));
  186. ui.fill(POLY(syringe_fluid), false);
  187. cmd.cmd(RESTORE_CONTEXT());
  188. ui.color(stroke_rgb);
  189. ui.fill(POLY(syringe));
  190. }
  191. }
  192. void StatusScreen::draw_arrows(draw_mode_t what) {
  193. const bool e_homed = TERN1(TOUCH_UI_LULZBOT_BIO, isAxisPositionKnown(E0)),
  194. z_homed = isAxisPositionKnown(Z);
  195. CommandProcessor cmd;
  196. PolyUI ui(cmd, what);
  197. ui.button_fill (fill_rgb);
  198. ui.button_stroke(stroke_rgb, 28);
  199. ui.button_shadow(shadow_rgb, shadow_depth);
  200. constexpr uint8_t style = TERN(TOUCH_UI_COCOA_PRESS, PolyUI::FILL | PolyUI::SHADOW, PolyUI::REGULAR);
  201. if ((what & BACKGROUND) || jog_xy) {
  202. ui.button(1, POLY(x_neg), style);
  203. ui.button(2, POLY(x_pos), style);
  204. ui.button(3, POLY(y_neg), style);
  205. ui.button(4, POLY(y_pos), style);
  206. }
  207. if ((what & BACKGROUND) || z_homed) {
  208. ui.button(5, POLY(z_neg), style);
  209. ui.button(6, POLY(z_pos), style);
  210. }
  211. if ((what & BACKGROUND) || e_homed) {
  212. #if DISABLED(TOUCH_UI_COCOA_PRESS)
  213. ui.button(7, POLY(e_neg), style);
  214. #endif
  215. ui.button(8, POLY(e_pos), style);
  216. }
  217. }
  218. void StatusScreen::draw_fine_motion(draw_mode_t what) {
  219. int16_t x, y, h, v;
  220. CommandProcessor cmd;
  221. PolyUI ui(cmd, what);
  222. cmd.font(
  223. #ifdef TOUCH_UI_PORTRAIT
  224. font_medium
  225. #else
  226. font_small
  227. #endif
  228. )
  229. .tag(16);
  230. if (what & BACKGROUND) {
  231. ui.bounds(POLY(fine_label), x, y, h, v);
  232. cmd.cmd(COLOR_RGB(bg_text_enabled))
  233. .text(x, y, h, v, GET_TEXT_F(MSG_FINE_MOTION));
  234. }
  235. if (what & FOREGROUND) {
  236. ui.bounds(POLY(fine_toggle), x, y, h, v);
  237. cmd.colors(ui_toggle)
  238. .toggle2(x, y, h, v, GET_TEXT_F(MSG_NO), GET_TEXT_F(MSG_YES), fine_motion);
  239. }
  240. }
  241. void StatusScreen::draw_overlay_icons(draw_mode_t what) {
  242. const bool e_homed = TERN1(TOUCH_UI_LULZBOT_BIO, isAxisPositionKnown(E0)),
  243. z_homed = isAxisPositionKnown(Z);
  244. CommandProcessor cmd;
  245. PolyUI ui(cmd, what);
  246. if (what & FOREGROUND) {
  247. ui.button_fill (TERN(TOUCH_UI_COCOA_PRESS, stroke_rgb, fill_rgb));
  248. ui.button_stroke(stroke_rgb, 28);
  249. ui.button_shadow(shadow_rgb, shadow_depth);
  250. constexpr uint8_t style = TERN(TOUCH_UI_COCOA_PRESS, PolyUI::FILL | PolyUI::SHADOW, PolyUI::REGULAR);
  251. if (!jog_xy) ui.button(12, POLY(padlock), style);
  252. if (!e_homed) ui.button(13, POLY(home_e), style);
  253. if (!z_homed) ui.button(14, POLY(home_z), style);
  254. }
  255. }
  256. void StatusScreen::draw_buttons(draw_mode_t what) {
  257. int16_t x, y, h, v;
  258. const bool has_media = isMediaInserted() && !isPrintingFromMedia();
  259. CommandProcessor cmd;
  260. PolyUI ui(cmd, what);
  261. ui.bounds(POLY(usb_btn), x, y, h, v);
  262. cmd.font(font_medium)
  263. .colors(normal_btn)
  264. .enabled(has_media)
  265. .colors(has_media ? action_btn : normal_btn)
  266. .tag(9).button(x, y, h, v,
  267. isPrintingFromMedia() ?
  268. GET_TEXT_F(MSG_PRINTING) :
  269. GET_TEXT_F(MSG_BUTTON_MEDIA)
  270. );
  271. ui.bounds(POLY(menu_btn), x, y, h, v);
  272. cmd.colors(!has_media ? action_btn : normal_btn).tag(10).button(x, y, h, v, GET_TEXT_F(MSG_BUTTON_MENU));
  273. }
  274. void StatusScreen::loadBitmaps() {
  275. // Load the bitmaps for the status screen
  276. constexpr uint32_t base = ftdi_memory_map::RAM_G;
  277. CLCD::mem_write_pgm(base + Bed_Heat_Icon_Info.RAMG_offset, Bed_Heat_Icon, sizeof(Bed_Heat_Icon));
  278. // Load fonts for internationalization
  279. #ifdef TOUCH_UI_USE_UTF8
  280. load_utf8_data(base + UTF8_FONT_OFFSET);
  281. #endif
  282. }
  283. void StatusScreen::onRedraw(draw_mode_t what) {
  284. if (what & BACKGROUND) {
  285. CommandProcessor cmd;
  286. cmd.cmd(CLEAR_COLOR_RGB(bg_color))
  287. .cmd(CLEAR(true,true,true))
  288. .tag(0);
  289. }
  290. draw_syringe(what);
  291. draw_temperature(what);
  292. draw_arrows(what);
  293. draw_overlay_icons(what);
  294. draw_buttons(what);
  295. draw_fine_motion(what);
  296. }
  297. bool StatusScreen::onTouchStart(uint8_t) {
  298. increment = 0;
  299. return true;
  300. }
  301. bool StatusScreen::onTouchEnd(uint8_t tag) {
  302. switch (tag) {
  303. case 1:
  304. case 2:
  305. case 3:
  306. case 4:
  307. case 12:
  308. if (!jog_xy) {
  309. jog_xy = true;
  310. injectCommands_P(PSTR("M17"));
  311. }
  312. jog({ 0, 0, 0 });
  313. break;
  314. case 5:
  315. case 6:
  316. jog({ 0, 0, 0 });
  317. break;
  318. case 9: GOTO_SCREEN(FilesScreen); break;
  319. case 10: GOTO_SCREEN(MainMenu); break;
  320. #if ENABLED(TOUCH_UI_LULZBOT_BIO)
  321. case 13: GOTO_SCREEN(BioConfirmHomeE); break;
  322. #endif
  323. case 14: SpinnerDialogBox::enqueueAndWait_P(F("G28 Z")); break;
  324. case 15: GOTO_SCREEN(TemperatureScreen); break;
  325. case 16: fine_motion = !fine_motion; break;
  326. default: return false;
  327. }
  328. // If a passcode is enabled, the LockScreen will prevent the
  329. // user from proceeding.
  330. LockScreen::check_passcode();
  331. return true;
  332. }
  333. bool StatusScreen::onTouchHeld(uint8_t tag) {
  334. if (tag >= 1 && tag <= 4 && !jog_xy) return false;
  335. const float s = min_speed + (fine_motion ? 0 : (max_speed - min_speed) * sq(increment));
  336. switch (tag) {
  337. case 1: jog({-s, 0, 0}); break;
  338. case 2: jog({ s, 0, 0}); break;
  339. case 4: jog({ 0, -s, 0}); break; // NOTE: Y directions inverted because bed rather than needle moves
  340. case 3: jog({ 0, s, 0}); break;
  341. case 5: jog({ 0, 0, -s}); break;
  342. case 6: jog({ 0, 0, s}); break;
  343. case 7: case 8:
  344. {
  345. if (ExtUI::isMoving()) return false;
  346. const feedRate_t feedrate = emin_speed + (fine_motion ? 0 : (emax_speed - emin_speed) * sq(increment));
  347. const float increment = 0.25 * feedrate * (tag == 7 ? -1 : 1);
  348. MoveAxisScreen::setManualFeedrate(E0, feedrate);
  349. UI_INCREMENT(AxisPosition_mm, E0);
  350. current_screen.onRefresh();
  351. break;
  352. }
  353. default:
  354. return false;
  355. }
  356. increment = min(1.0f, increment + 0.1f);
  357. return false;
  358. }
  359. void StatusScreen::setStatusMessage(progmem_str pstr) {
  360. #ifdef TOUCH_UI_LULZBOT_BIO
  361. BioPrintingDialogBox::setStatusMessage(pstr);
  362. #else
  363. UNUSED(pstr);
  364. #endif
  365. }
  366. void StatusScreen::setStatusMessage(const char * const str) {
  367. #ifdef TOUCH_UI_LULZBOT_BIO
  368. BioPrintingDialogBox::setStatusMessage(str);
  369. #else
  370. UNUSED(str);
  371. #endif
  372. }
  373. void StatusScreen::onIdle() {
  374. reset_menu_timeout();
  375. if (refresh_timer.elapsed(STATUS_UPDATE_INTERVAL)) {
  376. if (!EventLoop::is_touch_held())
  377. onRefresh();
  378. #ifdef TOUCH_UI_LULZBOT_BIO
  379. if (isPrintingFromMedia())
  380. BioPrintingDialogBox::show();
  381. #endif
  382. refresh_timer.start();
  383. }
  384. }
  385. #endif // TOUCH_UI_FTDI_EVE