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.

ultralcd_impl_HD44780.h 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (C) 2016 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. #ifndef ULTRALCD_IMPL_HD44780_H
  23. #define ULTRALCD_IMPL_HD44780_H
  24. /**
  25. * Implementation of the LCD display routines for a Hitachi HD44780 display.
  26. * These are the most common LCD character displays.
  27. */
  28. #include "utility.h"
  29. #include "duration_t.h"
  30. #if ENABLED(AUTO_BED_LEVELING_UBL)
  31. #include "ubl.h"
  32. #endif
  33. extern volatile uint8_t buttons; //an extended version of the last checked buttons in a bit array.
  34. ////////////////////////////////////
  35. // Setup button and encode mappings for each panel (into 'buttons' variable
  36. //
  37. // This is just to map common functions (across different panels) onto the same
  38. // macro name. The mapping is independent of whether the button is directly connected or
  39. // via a shift/i2c register.
  40. #if ENABLED(ULTIPANEL)
  41. //
  42. // Setup other button mappings of each panel
  43. //
  44. #if ENABLED(LCD_I2C_VIKI)
  45. #define B_I2C_BTN_OFFSET 3 // (the first three bit positions reserved for EN_A, EN_B, EN_C)
  46. // button and encoder bit positions within 'buttons'
  47. #define B_LE (BUTTON_LEFT<<B_I2C_BTN_OFFSET) // The remaining normalized buttons are all read via I2C
  48. #define B_UP (BUTTON_UP<<B_I2C_BTN_OFFSET)
  49. #define B_MI (BUTTON_SELECT<<B_I2C_BTN_OFFSET)
  50. #define B_DW (BUTTON_DOWN<<B_I2C_BTN_OFFSET)
  51. #define B_RI (BUTTON_RIGHT<<B_I2C_BTN_OFFSET)
  52. #if BUTTON_EXISTS(ENC)
  53. // the pause/stop/restart button is connected to BTN_ENC when used
  54. #define B_ST (EN_C) // Map the pause/stop/resume button into its normalized functional name
  55. #undef LCD_CLICKED
  56. #define LCD_CLICKED (buttons&(B_MI|B_RI|B_ST)) // pause/stop button also acts as click until we implement proper pause/stop.
  57. #else
  58. #undef LCD_CLICKED
  59. #define LCD_CLICKED (buttons&(B_MI|B_RI))
  60. #endif
  61. // I2C buttons take too long to read inside an interrupt context and so we read them during lcd_update
  62. #define LCD_HAS_SLOW_BUTTONS
  63. #elif ENABLED(LCD_I2C_PANELOLU2)
  64. #if !BUTTON_EXISTS(ENC) // Use I2C if not directly connected to a pin
  65. #define B_I2C_BTN_OFFSET 3 // (the first three bit positions reserved for EN_A, EN_B, EN_C)
  66. #define B_MI (PANELOLU2_ENCODER_C<<B_I2C_BTN_OFFSET) // requires LiquidTWI2 library v1.2.3 or later
  67. #undef LCD_CLICKED
  68. #define LCD_CLICKED (buttons & B_MI)
  69. // I2C buttons take too long to read inside an interrupt context and so we read them during lcd_update
  70. #define LCD_HAS_SLOW_BUTTONS
  71. #endif
  72. #elif DISABLED(NEWPANEL) // old style ULTIPANEL
  73. // Shift register bits correspond to buttons:
  74. #define BL_LE 7 // Left
  75. #define BL_UP 6 // Up
  76. #define BL_MI 5 // Middle
  77. #define BL_DW 4 // Down
  78. #define BL_RI 3 // Right
  79. #define BL_ST 2 // Red Button
  80. #define B_LE (_BV(BL_LE))
  81. #define B_UP (_BV(BL_UP))
  82. #define B_MI (_BV(BL_MI))
  83. #define B_DW (_BV(BL_DW))
  84. #define B_RI (_BV(BL_RI))
  85. #define B_ST (_BV(BL_ST))
  86. #define LCD_CLICKED ((buttons & B_MI) || (buttons & B_ST))
  87. #endif
  88. #endif // ULTIPANEL
  89. ////////////////////////////////////
  90. // Create LCD class instance and chipset-specific information
  91. #if ENABLED(LCD_I2C_TYPE_PCF8575)
  92. // note: these are register mapped pins on the PCF8575 controller not Arduino pins
  93. #define LCD_I2C_PIN_BL 3
  94. #define LCD_I2C_PIN_EN 2
  95. #define LCD_I2C_PIN_RW 1
  96. #define LCD_I2C_PIN_RS 0
  97. #define LCD_I2C_PIN_D4 4
  98. #define LCD_I2C_PIN_D5 5
  99. #define LCD_I2C_PIN_D6 6
  100. #define LCD_I2C_PIN_D7 7
  101. #include <Wire.h>
  102. #include <LCD.h>
  103. #include <LiquidCrystal_I2C.h>
  104. #define LCD_CLASS LiquidCrystal_I2C
  105. LCD_CLASS lcd(LCD_I2C_ADDRESS, LCD_I2C_PIN_EN, LCD_I2C_PIN_RW, LCD_I2C_PIN_RS, LCD_I2C_PIN_D4, LCD_I2C_PIN_D5, LCD_I2C_PIN_D6, LCD_I2C_PIN_D7);
  106. #elif ENABLED(LCD_I2C_TYPE_MCP23017)
  107. //for the LED indicators (which maybe mapped to different things in lcd_implementation_update_indicators())
  108. #define LED_A 0x04 //100
  109. #define LED_B 0x02 //010
  110. #define LED_C 0x01 //001
  111. #define LCD_HAS_STATUS_INDICATORS
  112. #include <Wire.h>
  113. #include <LiquidTWI2.h>
  114. #define LCD_CLASS LiquidTWI2
  115. #if ENABLED(DETECT_DEVICE)
  116. LCD_CLASS lcd(LCD_I2C_ADDRESS, 1);
  117. #else
  118. LCD_CLASS lcd(LCD_I2C_ADDRESS);
  119. #endif
  120. #elif ENABLED(LCD_I2C_TYPE_MCP23008)
  121. #include <Wire.h>
  122. #include <LiquidTWI2.h>
  123. #define LCD_CLASS LiquidTWI2
  124. #if ENABLED(DETECT_DEVICE)
  125. LCD_CLASS lcd(LCD_I2C_ADDRESS, 1);
  126. #else
  127. LCD_CLASS lcd(LCD_I2C_ADDRESS);
  128. #endif
  129. #elif ENABLED(LCD_I2C_TYPE_PCA8574)
  130. #include <LiquidCrystal_I2C.h>
  131. #define LCD_CLASS LiquidCrystal_I2C
  132. LCD_CLASS lcd(LCD_I2C_ADDRESS, LCD_WIDTH, LCD_HEIGHT);
  133. // 2 wire Non-latching LCD SR from:
  134. // https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/schematics#!shiftregister-connection
  135. #elif ENABLED(SR_LCD_2W_NL)
  136. extern "C" void __cxa_pure_virtual() { while (1); }
  137. #include <LCD.h>
  138. #include <LiquidCrystal_SR.h>
  139. #define LCD_CLASS LiquidCrystal_SR
  140. #if PIN_EXISTS(SR_STROBE)
  141. LCD_CLASS lcd(SR_DATA_PIN, SR_CLK_PIN, SR_STROBE_PIN);
  142. #else
  143. LCD_CLASS lcd(SR_DATA_PIN, SR_CLK_PIN);
  144. #endif
  145. #elif ENABLED(LCM1602)
  146. #include <Wire.h>
  147. #include <LCD.h>
  148. #include <LiquidCrystal_I2C.h>
  149. #define LCD_CLASS LiquidCrystal_I2C
  150. LCD_CLASS lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
  151. #else
  152. // Standard directly connected LCD implementations
  153. #include <LiquidCrystal.h>
  154. #define LCD_CLASS LiquidCrystal
  155. LCD_CLASS lcd(LCD_PINS_RS, LCD_PINS_ENABLE, LCD_PINS_D4, LCD_PINS_D5, LCD_PINS_D6, LCD_PINS_D7); //RS,Enable,D4,D5,D6,D7
  156. #endif
  157. #include "utf_mapper.h"
  158. #if ENABLED(LCD_PROGRESS_BAR)
  159. static millis_t progress_bar_ms = 0;
  160. #if PROGRESS_MSG_EXPIRE > 0
  161. static millis_t expire_status_ms = 0;
  162. #endif
  163. #define LCD_STR_PROGRESS "\x03\x04\x05"
  164. #endif
  165. #if ENABLED(LCD_HAS_STATUS_INDICATORS)
  166. static void lcd_implementation_update_indicators();
  167. #endif
  168. static void createChar_P(const char c, const byte * const ptr) {
  169. byte temp[8];
  170. for (uint8_t i = 0; i < 8; i++)
  171. temp[i] = pgm_read_byte(&ptr[i]);
  172. lcd.createChar(c, temp);
  173. }
  174. static void lcd_set_custom_characters(
  175. #if ENABLED(LCD_PROGRESS_BAR)
  176. const bool info_screen_charset = true
  177. #endif
  178. ) {
  179. const static PROGMEM byte bedTemp[8] = {
  180. B00000,
  181. B11111,
  182. B10101,
  183. B10001,
  184. B10101,
  185. B11111,
  186. B00000,
  187. B00000
  188. };
  189. const static PROGMEM byte degree[8] = {
  190. B01100,
  191. B10010,
  192. B10010,
  193. B01100,
  194. B00000,
  195. B00000,
  196. B00000,
  197. B00000
  198. };
  199. const static PROGMEM byte thermometer[8] = {
  200. B00100,
  201. B01010,
  202. B01010,
  203. B01010,
  204. B01010,
  205. B10001,
  206. B10001,
  207. B01110
  208. };
  209. const static PROGMEM byte uplevel[8] = {
  210. B00100,
  211. B01110,
  212. B11111,
  213. B00100,
  214. B11100,
  215. B00000,
  216. B00000,
  217. B00000
  218. };
  219. const static PROGMEM byte feedrate[8] = {
  220. B11100,
  221. B10000,
  222. B11000,
  223. B10111,
  224. B00101,
  225. B00110,
  226. B00101,
  227. B00000
  228. };
  229. const static PROGMEM byte clock[8] = {
  230. B00000,
  231. B01110,
  232. B10011,
  233. B10101,
  234. B10001,
  235. B01110,
  236. B00000,
  237. B00000
  238. };
  239. #if ENABLED(SDSUPPORT)
  240. const static PROGMEM byte refresh[8] = {
  241. B00000,
  242. B00110,
  243. B11001,
  244. B11000,
  245. B00011,
  246. B10011,
  247. B01100,
  248. B00000,
  249. };
  250. const static PROGMEM byte folder[8] = {
  251. B00000,
  252. B11100,
  253. B11111,
  254. B10001,
  255. B10001,
  256. B11111,
  257. B00000,
  258. B00000
  259. };
  260. #if ENABLED(LCD_PROGRESS_BAR)
  261. const static PROGMEM byte progress[3][8] = { {
  262. B00000,
  263. B10000,
  264. B10000,
  265. B10000,
  266. B10000,
  267. B10000,
  268. B10000,
  269. B00000
  270. }, {
  271. B00000,
  272. B10100,
  273. B10100,
  274. B10100,
  275. B10100,
  276. B10100,
  277. B10100,
  278. B00000
  279. }, {
  280. B00000,
  281. B10101,
  282. B10101,
  283. B10101,
  284. B10101,
  285. B10101,
  286. B10101,
  287. B00000
  288. } };
  289. #endif
  290. #endif
  291. createChar_P(LCD_BEDTEMP_CHAR, bedTemp);
  292. createChar_P(LCD_DEGREE_CHAR, degree);
  293. createChar_P(LCD_STR_THERMOMETER[0], thermometer);
  294. createChar_P(LCD_FEEDRATE_CHAR, feedrate);
  295. createChar_P(LCD_CLOCK_CHAR, clock);
  296. #if ENABLED(SDSUPPORT)
  297. #if ENABLED(LCD_PROGRESS_BAR)
  298. static bool char_mode = false;
  299. if (info_screen_charset != char_mode) {
  300. char_mode = info_screen_charset;
  301. if (info_screen_charset) { // Progress bar characters for info screen
  302. for (int16_t i = 3; i--;) createChar_P(LCD_STR_PROGRESS[i], progress[i]);
  303. }
  304. else { // Custom characters for submenus
  305. createChar_P(LCD_UPLEVEL_CHAR, uplevel);
  306. createChar_P(LCD_STR_REFRESH[0], refresh);
  307. createChar_P(LCD_STR_FOLDER[0], folder);
  308. }
  309. }
  310. #else
  311. createChar_P(LCD_UPLEVEL_CHAR, uplevel);
  312. createChar_P(LCD_STR_REFRESH[0], refresh);
  313. createChar_P(LCD_STR_FOLDER[0], folder);
  314. #endif
  315. #else
  316. createChar_P(LCD_UPLEVEL_CHAR, uplevel);
  317. #endif
  318. }
  319. static void lcd_implementation_init(
  320. #if ENABLED(LCD_PROGRESS_BAR)
  321. const bool info_screen_charset = true
  322. #endif
  323. ) {
  324. #if ENABLED(LCD_I2C_TYPE_PCF8575)
  325. lcd.begin(LCD_WIDTH, LCD_HEIGHT);
  326. #ifdef LCD_I2C_PIN_BL
  327. lcd.setBacklightPin(LCD_I2C_PIN_BL, POSITIVE);
  328. lcd.setBacklight(HIGH);
  329. #endif
  330. #elif ENABLED(LCD_I2C_TYPE_MCP23017)
  331. lcd.setMCPType(LTI_TYPE_MCP23017);
  332. lcd.begin(LCD_WIDTH, LCD_HEIGHT);
  333. lcd_implementation_update_indicators();
  334. #elif ENABLED(LCD_I2C_TYPE_MCP23008)
  335. lcd.setMCPType(LTI_TYPE_MCP23008);
  336. lcd.begin(LCD_WIDTH, LCD_HEIGHT);
  337. #elif ENABLED(LCD_I2C_TYPE_PCA8574)
  338. lcd.init();
  339. lcd.backlight();
  340. #else
  341. lcd.begin(LCD_WIDTH, LCD_HEIGHT);
  342. #endif
  343. lcd_set_custom_characters(
  344. #if ENABLED(LCD_PROGRESS_BAR)
  345. info_screen_charset
  346. #endif
  347. );
  348. lcd.clear();
  349. }
  350. void lcd_implementation_clear() { lcd.clear(); }
  351. void lcd_print(const char c) { charset_mapper(c); }
  352. void lcd_print(const char *str) { while (*str) lcd.print(*str++); }
  353. void lcd_printPGM(const char *str) { while (const char c = pgm_read_byte(str)) lcd.print(c), ++str; }
  354. void lcd_print_utf(const char *str, uint8_t n=LCD_WIDTH) {
  355. char c;
  356. while (n && (c = *str)) n -= charset_mapper(c), ++str;
  357. }
  358. void lcd_printPGM_utf(const char *str, uint8_t n=LCD_WIDTH) {
  359. char c;
  360. while (n && (c = pgm_read_byte(str))) n -= charset_mapper(c), ++str;
  361. }
  362. #if ENABLED(SHOW_BOOTSCREEN)
  363. void lcd_erase_line(const int16_t line) {
  364. lcd.setCursor(0, line);
  365. for (uint8_t i = LCD_WIDTH + 1; --i;)
  366. lcd.write(' ');
  367. }
  368. // Scroll the PSTR 'text' in a 'len' wide field for 'time' milliseconds at position col,line
  369. void lcd_scroll(const int16_t col, const int16_t line, const char* const text, const int16_t len, const int16_t time) {
  370. char tmp[LCD_WIDTH + 1] = {0};
  371. int16_t n = max(lcd_strlen_P(text) - len, 0);
  372. for (int16_t i = 0; i <= n; i++) {
  373. strncpy_P(tmp, text + i, min(len, LCD_WIDTH));
  374. lcd.setCursor(col, line);
  375. lcd_print(tmp);
  376. delay(time / max(n, 1));
  377. }
  378. }
  379. static void logo_lines(const char* const extra) {
  380. int16_t indent = (LCD_WIDTH - 8 - lcd_strlen_P(extra)) / 2;
  381. lcd.setCursor(indent, 0); lcd.print('\x00'); lcd_printPGM(PSTR( "------" )); lcd.write('\x01');
  382. lcd.setCursor(indent, 1); lcd_printPGM(PSTR("|Marlin|")); lcd_printPGM(extra);
  383. lcd.setCursor(indent, 2); lcd.write('\x02'); lcd_printPGM(PSTR( "------" )); lcd.write('\x03');
  384. }
  385. void lcd_bootscreen() {
  386. const static PROGMEM byte corner[4][8] = { {
  387. B00000,
  388. B00000,
  389. B00000,
  390. B00000,
  391. B00001,
  392. B00010,
  393. B00100,
  394. B00100
  395. }, {
  396. B00000,
  397. B00000,
  398. B00000,
  399. B11100,
  400. B11100,
  401. B01100,
  402. B00100,
  403. B00100
  404. }, {
  405. B00100,
  406. B00010,
  407. B00001,
  408. B00000,
  409. B00000,
  410. B00000,
  411. B00000,
  412. B00000
  413. }, {
  414. B00100,
  415. B01000,
  416. B10000,
  417. B00000,
  418. B00000,
  419. B00000,
  420. B00000,
  421. B00000
  422. } };
  423. for (uint8_t i = 0; i < 4; i++)
  424. createChar_P(i, corner[i]);
  425. lcd.clear();
  426. #define LCD_EXTRA_SPACE (LCD_WIDTH-8)
  427. #define CENTER_OR_SCROLL(STRING,DELAY) \
  428. lcd_erase_line(3); \
  429. if (strlen(STRING) <= LCD_WIDTH) { \
  430. lcd.setCursor((LCD_WIDTH - lcd_strlen_P(PSTR(STRING))) / 2, 3); \
  431. lcd_printPGM(PSTR(STRING)); \
  432. safe_delay(DELAY); \
  433. } \
  434. else { \
  435. lcd_scroll(0, 3, PSTR(STRING), LCD_WIDTH, DELAY); \
  436. }
  437. #ifdef STRING_SPLASH_LINE1
  438. //
  439. // Show the Marlin logo with splash line 1
  440. //
  441. if (LCD_EXTRA_SPACE >= strlen(STRING_SPLASH_LINE1) + 1) {
  442. //
  443. // Show the Marlin logo, splash line1, and splash line 2
  444. //
  445. logo_lines(PSTR(" " STRING_SPLASH_LINE1));
  446. #ifdef STRING_SPLASH_LINE2
  447. CENTER_OR_SCROLL(STRING_SPLASH_LINE2, 2000);
  448. #else
  449. safe_delay(2000);
  450. #endif
  451. }
  452. else {
  453. //
  454. // Show the Marlin logo with splash line 1
  455. // After a delay show splash line 2, if it exists
  456. //
  457. #ifdef STRING_SPLASH_LINE2
  458. #define _SPLASH_WAIT_1 1500
  459. #else
  460. #define _SPLASH_WAIT_1 2000
  461. #endif
  462. logo_lines(PSTR(""));
  463. CENTER_OR_SCROLL(STRING_SPLASH_LINE1, _SPLASH_WAIT_1);
  464. #ifdef STRING_SPLASH_LINE2
  465. CENTER_OR_SCROLL(STRING_SPLASH_LINE2, 1500);
  466. #endif
  467. }
  468. #elif defined(STRING_SPLASH_LINE2)
  469. //
  470. // Show splash line 2 only, alongside the logo if possible
  471. //
  472. if (LCD_EXTRA_SPACE >= strlen(STRING_SPLASH_LINE2) + 1) {
  473. logo_lines(PSTR(" " STRING_SPLASH_LINE2));
  474. safe_delay(2000);
  475. }
  476. else {
  477. logo_lines(PSTR(""));
  478. CENTER_OR_SCROLL(STRING_SPLASH_LINE2, 2000);
  479. }
  480. #else
  481. //
  482. // Show only the Marlin logo
  483. //
  484. logo_lines(PSTR(""));
  485. safe_delay(2000);
  486. #endif
  487. lcd.clear();
  488. safe_delay(100);
  489. lcd_set_custom_characters(
  490. #if ENABLED(LCD_PROGRESS_BAR)
  491. false
  492. #endif
  493. );
  494. }
  495. #endif // SHOW_BOOTSCREEN
  496. void lcd_kill_screen() {
  497. lcd.setCursor(0, 0);
  498. lcd_print_utf(lcd_status_message);
  499. #if LCD_HEIGHT < 4
  500. lcd.setCursor(0, 2);
  501. #else
  502. lcd.setCursor(0, 2);
  503. lcd_printPGM(PSTR(MSG_HALTED));
  504. lcd.setCursor(0, 3);
  505. #endif
  506. lcd_printPGM(PSTR(MSG_PLEASE_RESET));
  507. }
  508. FORCE_INLINE void _draw_axis_label(const AxisEnum axis, const char* const pstr, const bool blink) {
  509. if (blink)
  510. lcd_printPGM(pstr);
  511. else {
  512. if (!axis_homed[axis])
  513. lcd.write('?');
  514. else {
  515. #if DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
  516. if (!axis_known_position[axis])
  517. lcd.write(' ');
  518. else
  519. #endif
  520. lcd_printPGM(pstr);
  521. }
  522. }
  523. }
  524. FORCE_INLINE void _draw_heater_status(const int8_t heater, const char prefix, const bool blink) {
  525. const bool isBed = heater < 0;
  526. const float t1 = (isBed ? thermalManager.degBed() : thermalManager.degHotend(heater)),
  527. t2 = (isBed ? thermalManager.degTargetBed() : thermalManager.degTargetHotend(heater));
  528. if (prefix >= 0) lcd.print(prefix);
  529. lcd.print(itostr3(t1 + 0.5));
  530. lcd.write('/');
  531. #if HEATER_IDLE_HANDLER
  532. const bool is_idle = (!isBed ? thermalManager.is_heater_idle(heater) :
  533. #if HAS_TEMP_BED
  534. thermalManager.is_bed_idle()
  535. #else
  536. false
  537. #endif
  538. );
  539. if (!blink && is_idle) {
  540. lcd.write(' ');
  541. if (t2 >= 10) lcd.write(' ');
  542. if (t2 >= 100) lcd.write(' ');
  543. }
  544. else
  545. #endif
  546. lcd.print(itostr3left(t2 + 0.5));
  547. if (prefix >= 0) {
  548. lcd.print((char)LCD_DEGREE_CHAR);
  549. lcd.write(' ');
  550. if (t2 < 10) lcd.write(' ');
  551. }
  552. }
  553. #if ENABLED(LCD_PROGRESS_BAR)
  554. inline void lcd_draw_progress_bar(const uint8_t percent) {
  555. const int16_t tix = (int16_t)(percent * (LCD_WIDTH) * 3) / 100,
  556. cel = tix / 3,
  557. rem = tix % 3;
  558. uint8_t i = LCD_WIDTH;
  559. char msg[LCD_WIDTH + 1], b = ' ';
  560. msg[LCD_WIDTH] = '\0';
  561. while (i--) {
  562. if (i == cel - 1)
  563. b = LCD_STR_PROGRESS[2];
  564. else if (i == cel && rem != 0)
  565. b = LCD_STR_PROGRESS[rem - 1];
  566. msg[i] = b;
  567. }
  568. lcd.print(msg);
  569. }
  570. #endif // LCD_PROGRESS_BAR
  571. /**
  572. Possible status screens:
  573. 16x2 |000/000 B000/000|
  574. |0123456789012345|
  575. 16x4 |000/000 B000/000|
  576. |SD100% Z 000.00|
  577. |F100% T--:--|
  578. |0123456789012345|
  579. 20x2 |T000/000D B000/000D |
  580. |01234567890123456789|
  581. 20x4 |T000/000D B000/000D |
  582. |X 000 Y 000 Z 000.00|
  583. |F100% SD100% T--:--|
  584. |01234567890123456789|
  585. 20x4 |T000/000D B000/000D |
  586. |T000/000D Z 000.00|
  587. |F100% SD100% T--:--|
  588. |01234567890123456789|
  589. */
  590. static void lcd_implementation_status_screen() {
  591. const bool blink = lcd_blink();
  592. //
  593. // Line 1
  594. //
  595. lcd.setCursor(0, 0);
  596. #if LCD_WIDTH < 20
  597. //
  598. // Hotend 0 Temperature
  599. //
  600. _draw_heater_status(0, -1, blink);
  601. //
  602. // Hotend 1 or Bed Temperature
  603. //
  604. #if HOTENDS > 1 || TEMP_SENSOR_BED != 0
  605. lcd.setCursor(8, 0);
  606. #if HOTENDS > 1
  607. lcd.print((CHAR)LCD_STR_THERMOMETER[0]);
  608. _draw_heater_status(1, -1, blink);
  609. #else
  610. lcd.print((CHAR)LCD_BEDTEMP_CHAR);
  611. _draw_heater_status(-1, -1, blink);
  612. #endif
  613. #endif // HOTENDS > 1 || TEMP_SENSOR_BED != 0
  614. #else // LCD_WIDTH >= 20
  615. //
  616. // Hotend 0 Temperature
  617. //
  618. _draw_heater_status(0, LCD_STR_THERMOMETER[0], blink);
  619. //
  620. // Hotend 1 or Bed Temperature
  621. //
  622. #if HOTENDS > 1 || TEMP_SENSOR_BED != 0
  623. lcd.setCursor(10, 0);
  624. #if HOTENDS > 1
  625. _draw_heater_status(1, LCD_STR_THERMOMETER[0], blink);
  626. #else
  627. _draw_heater_status(-1, LCD_BEDTEMP_CHAR, blink);
  628. #endif
  629. #endif // HOTENDS > 1 || TEMP_SENSOR_BED != 0
  630. #endif // LCD_WIDTH >= 20
  631. //
  632. // Line 2
  633. //
  634. #if LCD_HEIGHT > 2
  635. #if LCD_WIDTH < 20
  636. #if ENABLED(SDSUPPORT)
  637. lcd.setCursor(0, 2);
  638. lcd_printPGM(PSTR("SD"));
  639. if (IS_SD_PRINTING)
  640. lcd.print(itostr3(card.percentDone()));
  641. else
  642. lcd_printPGM(PSTR("---"));
  643. lcd.write('%');
  644. #endif // SDSUPPORT
  645. #else // LCD_WIDTH >= 20
  646. lcd.setCursor(0, 1);
  647. #if HOTENDS > 1 && TEMP_SENSOR_BED != 0
  648. // If we both have a 2nd extruder and a heated bed,
  649. // show the heated bed temp on the left,
  650. // since the first line is filled with extruder temps
  651. _draw_heater_status(-1, LCD_BEDTEMP_CHAR, blink);
  652. #else
  653. // Before homing the axis letters are blinking 'X' <-> '?'.
  654. // When axis is homed but axis_known_position is false the axis letters are blinking 'X' <-> ' '.
  655. // When everything is ok you see a constant 'X'.
  656. _draw_axis_label(X_AXIS, PSTR(MSG_X), blink);
  657. lcd.print(ftostr4sign(current_position[X_AXIS]));
  658. lcd.write(' ');
  659. _draw_axis_label(Y_AXIS, PSTR(MSG_Y), blink);
  660. lcd.print(ftostr4sign(current_position[Y_AXIS]));
  661. #endif // HOTENDS > 1 || TEMP_SENSOR_BED != 0
  662. #endif // LCD_WIDTH >= 20
  663. lcd.setCursor(LCD_WIDTH - 8, 1);
  664. _draw_axis_label(Z_AXIS, PSTR(MSG_Z), blink);
  665. lcd.print(ftostr52sp(FIXFLOAT(current_position[Z_AXIS])));
  666. #endif // LCD_HEIGHT > 2
  667. //
  668. // Line 3
  669. //
  670. #if LCD_HEIGHT > 3
  671. lcd.setCursor(0, 2);
  672. lcd.print((char)LCD_FEEDRATE_CHAR);
  673. lcd.print(itostr3(feedrate_percentage));
  674. lcd.write('%');
  675. #if LCD_WIDTH >= 20 && ENABLED(SDSUPPORT)
  676. lcd.setCursor(7, 2);
  677. lcd_printPGM(PSTR("SD"));
  678. if (IS_SD_PRINTING)
  679. lcd.print(itostr3(card.percentDone()));
  680. else
  681. lcd_printPGM(PSTR("---"));
  682. lcd.write('%');
  683. #endif // LCD_WIDTH >= 20 && SDSUPPORT
  684. char buffer[10];
  685. duration_t elapsed = print_job_timer.duration();
  686. uint8_t len = elapsed.toDigital(buffer);
  687. lcd.setCursor(LCD_WIDTH - len - 1, 2);
  688. lcd.print((char)LCD_CLOCK_CHAR);
  689. lcd_print(buffer);
  690. #endif // LCD_HEIGHT > 3
  691. //
  692. // Last Line
  693. // Status Message (which may be a Progress Bar or Filament display)
  694. //
  695. lcd.setCursor(0, LCD_HEIGHT - 1);
  696. #if ENABLED(LCD_PROGRESS_BAR)
  697. // Draw the progress bar if the message has shown long enough
  698. // or if there is no message set.
  699. if (card.isFileOpen() && (ELAPSED(millis(), progress_bar_ms + PROGRESS_BAR_MSG_TIME) || !lcd_status_message[0])) {
  700. const uint8_t percent = card.percentDone();
  701. if (percent) return lcd_draw_progress_bar(percent);
  702. }
  703. #elif ENABLED(FILAMENT_LCD_DISPLAY) && ENABLED(SDSUPPORT)
  704. // Show Filament Diameter and Volumetric Multiplier %
  705. // After allowing lcd_status_message to show for 5 seconds
  706. if (ELAPSED(millis(), previous_lcd_status_ms + 5000UL)) {
  707. lcd_printPGM(PSTR("Dia "));
  708. lcd.print(ftostr12ns(filament_width_meas));
  709. lcd_printPGM(PSTR(" V"));
  710. lcd.print(itostr3(100.0 * volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]));
  711. lcd.write('%');
  712. return;
  713. }
  714. #endif // FILAMENT_LCD_DISPLAY && SDSUPPORT
  715. #if ENABLED(STATUS_MESSAGE_SCROLLING)
  716. static bool last_blink = false;
  717. const uint8_t slen = lcd_strlen(lcd_status_message);
  718. const char *stat = lcd_status_message + status_scroll_pos;
  719. if (slen <= LCD_WIDTH)
  720. lcd_print_utf(stat); // The string isn't scrolling
  721. else {
  722. if (status_scroll_pos <= slen - LCD_WIDTH)
  723. lcd_print_utf(stat); // The string fills the screen
  724. else {
  725. uint8_t chars = LCD_WIDTH;
  726. if (status_scroll_pos < slen) { // First string still visible
  727. lcd_print_utf(stat); // The string leaves space
  728. chars -= slen - status_scroll_pos; // Amount of space left
  729. }
  730. lcd.write('.'); // Always at 1+ spaces left, draw a dot
  731. if (--chars) {
  732. if (status_scroll_pos < slen + 1) // Draw a second dot if there's space
  733. --chars, lcd.write('.');
  734. if (chars) lcd_print_utf(lcd_status_message, chars); // Print a second copy of the message
  735. }
  736. }
  737. if (last_blink != blink) {
  738. last_blink = blink;
  739. // Skip any non-printing bytes
  740. if (status_scroll_pos < slen) while (!PRINTABLE(lcd_status_message[status_scroll_pos])) status_scroll_pos++;
  741. if (++status_scroll_pos >= slen + 2) status_scroll_pos = 0;
  742. }
  743. }
  744. #else
  745. lcd_print_utf(lcd_status_message);
  746. #endif
  747. }
  748. #if ENABLED(ULTIPANEL)
  749. #if ENABLED(ADVANCED_PAUSE_FEATURE)
  750. static void lcd_implementation_hotend_status(const uint8_t row) {
  751. if (row < LCD_HEIGHT) {
  752. lcd.setCursor(LCD_WIDTH - 9, row);
  753. _draw_heater_status(active_extruder, LCD_STR_THERMOMETER[0], lcd_blink());
  754. }
  755. }
  756. #endif // ADVANCED_PAUSE_FEATURE
  757. static void lcd_implementation_drawmenu_static(const uint8_t row, const char* pstr, const bool center=true, const bool invert=false, const char *valstr=NULL) {
  758. UNUSED(invert);
  759. char c;
  760. int8_t n = LCD_WIDTH;
  761. lcd.setCursor(0, row);
  762. if (center && !valstr) {
  763. int8_t pad = (LCD_WIDTH - lcd_strlen_P(pstr)) / 2;
  764. while (--pad >= 0) { lcd.write(' '); n--; }
  765. }
  766. while (n > 0 && (c = pgm_read_byte(pstr))) {
  767. n -= charset_mapper(c);
  768. pstr++;
  769. }
  770. if (valstr) while (n > 0 && (c = *valstr)) {
  771. n -= charset_mapper(c);
  772. valstr++;
  773. }
  774. while (n-- > 0) lcd.write(' ');
  775. }
  776. static void lcd_implementation_drawmenu_generic(const bool sel, const uint8_t row, const char* pstr, const char pre_char, const char post_char) {
  777. char c;
  778. uint8_t n = LCD_WIDTH - 2;
  779. lcd.setCursor(0, row);
  780. lcd.print(sel ? pre_char : ' ');
  781. while ((c = pgm_read_byte(pstr)) && n > 0) {
  782. n -= charset_mapper(c);
  783. pstr++;
  784. }
  785. while (n--) lcd.write(' ');
  786. lcd.print(post_char);
  787. }
  788. static void lcd_implementation_drawmenu_setting_edit_generic(const bool sel, const uint8_t row, const char* pstr, const char pre_char, const char* const data) {
  789. char c;
  790. uint8_t n = LCD_WIDTH - 2 - lcd_strlen(data);
  791. lcd.setCursor(0, row);
  792. lcd.print(sel ? pre_char : ' ');
  793. while ((c = pgm_read_byte(pstr)) && n > 0) {
  794. n -= charset_mapper(c);
  795. pstr++;
  796. }
  797. lcd.write(':');
  798. while (n--) lcd.write(' ');
  799. lcd_print(data);
  800. }
  801. static void lcd_implementation_drawmenu_setting_edit_generic_P(const bool sel, const uint8_t row, const char* pstr, const char pre_char, const char* const data) {
  802. char c;
  803. uint8_t n = LCD_WIDTH - 2 - lcd_strlen_P(data);
  804. lcd.setCursor(0, row);
  805. lcd.print(sel ? pre_char : ' ');
  806. while ((c = pgm_read_byte(pstr)) && n > 0) {
  807. n -= charset_mapper(c);
  808. pstr++;
  809. }
  810. lcd.write(':');
  811. while (n--) lcd.write(' ');
  812. lcd_printPGM(data);
  813. }
  814. #define DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(_type, _name, _strFunc) \
  815. inline void lcd_implementation_drawmenu_setting_edit_ ## _name (const bool sel, const uint8_t row, const char* pstr, const char* pstr2, _type * const data, ...) { \
  816. lcd_implementation_drawmenu_setting_edit_generic(sel, row, pstr, '>', _strFunc(*(data))); \
  817. } \
  818. inline void lcd_implementation_drawmenu_setting_edit_callback_ ## _name (const bool sel, const uint8_t row, const char* pstr, const char* pstr2, _type * const data, ...) { \
  819. lcd_implementation_drawmenu_setting_edit_generic(sel, row, pstr, '>', _strFunc(*(data))); \
  820. } \
  821. inline void lcd_implementation_drawmenu_setting_edit_accessor_ ## _name (const bool sel, const uint8_t row, const char* pstr, const char* pstr2, _type (*pget)(), void (*pset)(_type), ...) { \
  822. lcd_implementation_drawmenu_setting_edit_generic(sel, row, pstr, '>', _strFunc(pget())); \
  823. } \
  824. typedef void _name##_void
  825. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(int16_t, int3, itostr3);
  826. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(uint8_t, int8, i8tostr3);
  827. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(float, float3, ftostr3);
  828. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(float, float32, ftostr32);
  829. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(float, float43, ftostr43sign);
  830. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(float, float5, ftostr5rj);
  831. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(float, float51, ftostr51sign);
  832. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(float, float52, ftostr52sign);
  833. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(float, float62, ftostr62rj);
  834. DEFINE_LCD_IMPLEMENTATION_DRAWMENU_SETTING_EDIT_TYPE(uint32_t, long5, ftostr5rj);
  835. #define lcd_implementation_drawmenu_setting_edit_bool(sel, row, pstr, pstr2, data) lcd_implementation_drawmenu_setting_edit_generic_P(sel, row, pstr, '>', (*(data))?PSTR(MSG_ON):PSTR(MSG_OFF))
  836. #define lcd_implementation_drawmenu_setting_edit_callback_bool(sel, row, pstr, pstr2, data, callback) lcd_implementation_drawmenu_setting_edit_generic_P(sel, row, pstr, '>', (*(data))?PSTR(MSG_ON):PSTR(MSG_OFF))
  837. #define lcd_implementation_drawmenu_setting_edit_accessor_bool(sel, row, pstr, pstr2, pget, pset, callback) lcd_implementation_drawmenu_setting_edit_generic_P(sel, row, pstr, '>', (*(data))?PSTR(MSG_ON):PSTR(MSG_OFF))
  838. void lcd_implementation_drawedit(const char* pstr, const char* const value=NULL) {
  839. lcd.setCursor(1, 1);
  840. lcd_printPGM(pstr);
  841. if (value != NULL) {
  842. lcd.write(':');
  843. const uint8_t valrow = (lcd_strlen_P(pstr) + 1 + lcd_strlen(value) + 1) > (LCD_WIDTH - 2) ? 2 : 1; // Value on the next row if it won't fit
  844. lcd.setCursor((LCD_WIDTH - 1) - (lcd_strlen(value) + 1), valrow); // Right-justified, padded by spaces
  845. lcd.write(' '); // overwrite char if value gets shorter
  846. lcd_print(value);
  847. }
  848. }
  849. #if ENABLED(SDSUPPORT)
  850. static void lcd_implementation_drawmenu_sd(const bool sel, const uint8_t row, const char* const pstr, const char* filename, char* const longFilename, const uint8_t concat, const char post_char) {
  851. UNUSED(pstr);
  852. char c;
  853. uint8_t n = LCD_WIDTH - concat;
  854. lcd.setCursor(0, row);
  855. lcd.print(sel ? '>' : ' ');
  856. if (longFilename[0]) {
  857. filename = longFilename;
  858. longFilename[n] = '\0';
  859. }
  860. while ((c = *filename) && n > 0) {
  861. n -= charset_mapper(c);
  862. filename++;
  863. }
  864. while (n--) lcd.write(' ');
  865. lcd.print(post_char);
  866. }
  867. static void lcd_implementation_drawmenu_sdfile(const bool sel, const uint8_t row, const char* pstr, const char* filename, char* const longFilename) {
  868. lcd_implementation_drawmenu_sd(sel, row, pstr, filename, longFilename, 2, ' ');
  869. }
  870. static void lcd_implementation_drawmenu_sddirectory(const bool sel, const uint8_t row, const char* pstr, const char* filename, char* const longFilename) {
  871. lcd_implementation_drawmenu_sd(sel, row, pstr, filename, longFilename, 2, LCD_STR_FOLDER[0]);
  872. }
  873. #endif // SDSUPPORT
  874. #define lcd_implementation_drawmenu_back(sel, row, pstr, dummy) lcd_implementation_drawmenu_generic(sel, row, pstr, LCD_UPLEVEL_CHAR, LCD_UPLEVEL_CHAR)
  875. #define lcd_implementation_drawmenu_submenu(sel, row, pstr, data) lcd_implementation_drawmenu_generic(sel, row, pstr, '>', LCD_STR_ARROW_RIGHT[0])
  876. #define lcd_implementation_drawmenu_gcode(sel, row, pstr, gcode) lcd_implementation_drawmenu_generic(sel, row, pstr, '>', ' ')
  877. #define lcd_implementation_drawmenu_function(sel, row, pstr, data) lcd_implementation_drawmenu_generic(sel, row, pstr, '>', ' ')
  878. #if ENABLED(LCD_HAS_SLOW_BUTTONS)
  879. extern millis_t next_button_update_ms;
  880. static uint8_t lcd_implementation_read_slow_buttons() {
  881. #if ENABLED(LCD_I2C_TYPE_MCP23017)
  882. // Reading these buttons this is likely to be too slow to call inside interrupt context
  883. // so they are called during normal lcd_update
  884. uint8_t slow_bits = lcd.readButtons() << B_I2C_BTN_OFFSET;
  885. #if ENABLED(LCD_I2C_VIKI)
  886. if ((slow_bits & (B_MI | B_RI)) && PENDING(millis(), next_button_update_ms)) // LCD clicked
  887. slow_bits &= ~(B_MI | B_RI); // Disable LCD clicked buttons if screen is updated
  888. #endif // LCD_I2C_VIKI
  889. return slow_bits;
  890. #endif // LCD_I2C_TYPE_MCP23017
  891. }
  892. #endif // LCD_HAS_SLOW_BUTTONS
  893. #endif // ULTIPANEL
  894. #if ENABLED(LCD_HAS_STATUS_INDICATORS)
  895. static void lcd_implementation_update_indicators() {
  896. // Set the LEDS - referred to as backlights by the LiquidTWI2 library
  897. static uint8_t ledsprev = 0;
  898. uint8_t leds = 0;
  899. if (thermalManager.degTargetBed() > 0) leds |= LED_A;
  900. if (thermalManager.degTargetHotend(0) > 0) leds |= LED_B;
  901. #if FAN_COUNT > 0
  902. if (0
  903. #if HAS_FAN0
  904. || fanSpeeds[0]
  905. #endif
  906. #if HAS_FAN1
  907. || fanSpeeds[1]
  908. #endif
  909. #if HAS_FAN2
  910. || fanSpeeds[2]
  911. #endif
  912. ) leds |= LED_C;
  913. #endif // FAN_COUNT > 0
  914. #if HOTENDS > 1
  915. if (thermalManager.degTargetHotend(1) > 0) leds |= LED_C;
  916. #endif
  917. if (leds != ledsprev) {
  918. lcd.setBacklight(leds);
  919. ledsprev = leds;
  920. }
  921. }
  922. #endif // LCD_HAS_STATUS_INDICATORS
  923. #if ENABLED(AUTO_BED_LEVELING_UBL)
  924. /**
  925. * These are just basic data for the 20x4 LCD work that
  926. * is coming up very soon.
  927. * Soon this will morph into a map code.
  928. */
  929. /**
  930. Possible map screens:
  931. 16x2 |X000.00 Y000.00|
  932. |(00,00) Z00.000|
  933. 20x2 | X:000.00 Y:000.00 |
  934. | (00,00) Z:00.000 |
  935. 16x4 |+-------+(00,00)|
  936. || |X000.00|
  937. || |Y000.00|
  938. |+-------+Z00.000|
  939. 20x4 | +-------+ (00,00) |
  940. | | | X:000.00|
  941. | | | Y:000.00|
  942. | +-------+ Z:00.000|
  943. */
  944. void lcd_set_ubl_map_plot_chars() {
  945. #if LCD_HEIGHT > 3
  946. //#include "_ubl_lcd_map_characters.h"
  947. const static byte _lcd_box_top[8] PROGMEM = {
  948. B11111,
  949. B00000,
  950. B00000,
  951. B00000,
  952. B00000,
  953. B00000,
  954. B00000,
  955. B00000
  956. };
  957. const static byte _lcd_box_bottom[8] PROGMEM = {
  958. B00000,
  959. B00000,
  960. B00000,
  961. B00000,
  962. B00000,
  963. B00000,
  964. B00000,
  965. B11111
  966. };
  967. createChar_P(LCD_UBL_BOXTOP_CHAR, _lcd_box_top);
  968. createChar_P(LCD_UBL_BOXBOT_CHAR, _lcd_box_bottom);
  969. #endif
  970. }
  971. void lcd_implementation_ubl_plot(const uint8_t x_plot, const uint8_t y_plot) {
  972. #if LCD_WIDTH >= 20
  973. #define _LCD_W_POS 12
  974. #define _PLOT_X 1
  975. #define _MAP_X 3
  976. #define _LABEL(C,X,Y) lcd.setCursor(X, Y); lcd.print(C)
  977. #define _XLABEL(X,Y) _LABEL("X:",X,Y)
  978. #define _YLABEL(X,Y) _LABEL("Y:",X,Y)
  979. #define _ZLABEL(X,Y) _LABEL("Z:",X,Y)
  980. #else
  981. #define _LCD_W_POS 8
  982. #define _PLOT_X 0
  983. #define _MAP_X 1
  984. #define _LABEL(X,Y,C) lcd.setCursor(X, Y); lcd.write(C)
  985. #define _XLABEL(X,Y) _LABEL('X',X,Y)
  986. #define _YLABEL(X,Y) _LABEL('Y',X,Y)
  987. #define _ZLABEL(X,Y) _LABEL('Z',X,Y)
  988. #endif
  989. #if LCD_HEIGHT <= 3 // 16x2 or 20x2 display
  990. /**
  991. * Show X and Y positions
  992. */
  993. _XLABEL(_PLOT_X, 0);
  994. lcd.print(ftostr32(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]))));
  995. _YLABEL(_LCD_W_POS, 0);
  996. lcd.print(ftostr32(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]))));
  997. lcd.setCursor(_PLOT_X, 0);
  998. #else // 16x4 or 20x4 display
  999. /**
  1000. * Draw the Mesh Map Box
  1001. */
  1002. uint8_t m;
  1003. lcd.setCursor(_MAP_X, 0); for (m = 0; m < 5; m++) lcd.write(LCD_UBL_BOXTOP_CHAR); // Top
  1004. lcd.setCursor(_MAP_X, 3); for (m = 0; m < 5; m++) lcd.write(LCD_UBL_BOXBOT_CHAR); // Bottom
  1005. for (m = 0; m <= 3; m++) {
  1006. lcd.setCursor(2, m); lcd.write('|'); // Left
  1007. lcd.setCursor(8, m); lcd.write('|'); // Right
  1008. }
  1009. lcd.setCursor(_LCD_W_POS, 0);
  1010. #endif
  1011. /**
  1012. * Print plot position
  1013. */
  1014. lcd.write('(');
  1015. lcd.print(x_plot);
  1016. lcd.write(',');
  1017. lcd.print(y_plot);
  1018. lcd.write(')');
  1019. #if LCD_HEIGHT <= 3 // 16x2 or 20x2 display
  1020. /**
  1021. * Print Z values
  1022. */
  1023. _ZLABEL(_LCD_W_POS, 1);
  1024. if (!isnan(ubl.z_values[x_plot][y_plot]))
  1025. lcd.print(ftostr43sign(ubl.z_values[x_plot][y_plot]));
  1026. else
  1027. lcd_printPGM(PSTR(" -----"));
  1028. #else // 16x4 or 20x4 display
  1029. /**
  1030. * Show all values at right of screen
  1031. */
  1032. _XLABEL(_LCD_W_POS, 1);
  1033. lcd.print(ftostr32(LOGICAL_X_POSITION(pgm_read_float(&ubl._mesh_index_to_xpos[x_plot]))));
  1034. _YLABEL(_LCD_W_POS, 2);
  1035. lcd.print(ftostr32(LOGICAL_Y_POSITION(pgm_read_float(&ubl._mesh_index_to_ypos[y_plot]))));
  1036. /**
  1037. * Show the location value
  1038. */
  1039. _ZLABEL(_LCD_W_POS, 3);
  1040. if (!isnan(ubl.z_values[x_plot][y_plot]))
  1041. lcd.print(ftostr43sign(ubl.z_values[x_plot][y_plot]));
  1042. else
  1043. lcd_printPGM(PSTR(" -----"));
  1044. #endif // LCD_HEIGHT > 3
  1045. }
  1046. #endif // AUTO_BED_LEVELING_UBL
  1047. #endif // ULTRALCD_IMPL_HD44780_H