#include #include "config.h" #include "config_pins.h" #include "lcd.h" #if (defined(USE_20X4_TEXT_LCD) && defined(USE_FULL_GRAPHIC_LCD)) || (!defined(USE_20X4_TEXT_LCD) && !defined(USE_FULL_GRAPHIC_LCD)) #error define one of USE_20X4_TEXT_LCD or USE_FULL_GRAPHIC_LCD #endif #ifdef USE_20X4_TEXT_LCD #include #define LCD_HEADING_LINES 1 #define LCD_HEADING_WIDTH 20 #define LCD_TEXT_LINES 3 #define LCD_TEXT_WIDTH 20 LiquidCrystal lcd(LCD_PINS_RS, LCD_PINS_ENABLE, LCD_PINS_D4, LCD_PINS_D5, LCD_PINS_D6, LCD_PINS_D7); #endif // USE_20X4_TEXT_LCD #ifdef USE_FULL_GRAPHIC_LCD #include //#define ENABLE_DYNAMIC_SCROLLING #define LCD_HEADING_LINES 1 #define LCD_HEADING_WIDTH 15 #define LCD_TEXT_LINES 5 #define LCD_TEXT_WIDTH 25 #define LCD_HEADING_FONT u8g2_font_torussansbold8_8r #define LCD_TEXT_FONT u8g2_font_5x7_tr #define LCD_HEADING_TEXT_OFFSET 3 #define LCD_HEADING_HEIGHT 8 #define LCD_TEXT_HEIGHT 9 U8G2_ST7920_128X64_1_SW_SPI lcd(U8G2_R0, LCD_PINS_SCK, LCD_PINS_MOSI, LCD_PINS_CS); #endif // USE_FULL_GRAPHIC_LCD String heading[LCD_HEADING_LINES]; String text[LCD_TEXT_LINES]; String heading_pre, heading_post; String text_pre, text_post; int heading_direction, text_direction; String menu_pre[LCD_TEXT_LINES], menu_post[LCD_TEXT_LINES]; int menu_dir[LCD_TEXT_LINES]; boolean redraw = false; enum lcd_text_mode { lcd_mode_none, lcd_mode_multiline, lcd_mode_menu }; enum lcd_text_mode text_mode = lcd_mode_none; #define SCROLL_DELAY 500 unsigned long last_scroll_time = 0; #define IS_TEXT_CHAR(x) (((x >= 'a') && (x <= 'z')) || ((x >= 'A') && (x <= 'Z'))) void lcd_shorten_multiline(String s[], int n, int w, String *remainder) { String for_next_line = ""; for (int i = 0; i < n; i++) { // take previously stored string and put in front of line s[i] = for_next_line + s[i]; for_next_line = ""; // if a line is longer then w, take end off and store if (s[i].length() > w) { for_next_line += s[i].substring(w); s[i] = s[i].substring(0, w); } // if a newline is in this line, end the text there and split it for (int j = 0; j < s[i].length(); j++) { if (s[i][j] == '\n') { for_next_line = s[i].substring(j + 1) + for_next_line; s[i] = s[i].substring(0, j); break; } } // if the last char of this line and the first char of for_next_line // exist and are both upper/lowercase characters, check if one of the // previous 5 chars is a space, and instead split there if ((n > 1) && (for_next_line.length() > 0) && IS_TEXT_CHAR(s[i][s[i].length() - 1]) && IS_TEXT_CHAR(for_next_line[0])) { for (int j = s[i].length() - 2; (j >= 0) && (j >= s[i].length() - 2 - 5); j--) { if (s[i][j] == ' ') { for_next_line = s[i].substring(j + 1) + for_next_line; s[i] = s[i].substring(0, j + 1); break; } } } } // if something remains at end, store remainder, if given, otherwise leave at last line if (remainder != NULL) { *remainder = for_next_line; } else { s[n - 1] = s[n - 1] + for_next_line; } last_scroll_time = millis(); } #ifdef ENABLE_DYNAMIC_SCROLLING void lcd_scroll_multiline(String s[], int n, int w, String &pre, String &post, int &dir) { if (dir == 0) { // switch directions if done scrolling in current direction if (post.length() == 0) { dir = 1; return; } // take first char of first line, append to pre pre = pre + s[0][0]; s[0] = s[0].substring(1); // shift now empty spot to the left through lines for (int i = 1; i < n; i++) { s[i - 1] = s[i - 1] + s[i][0]; s[i] = s[i].substring(1); } // fill empty spot at end of last line with first char of post s[n - 1] = s[n - 1] + post[0]; post = post.substring(1); } else { // switch directions if done scrolling in current direction if (pre.length() == 0) { dir = 0; return; } // take last char of last line, prepend to post post = s[n - 1][s[n - 1].length() - 1] + post; s[n - 1] = s[n - 1].substring(0, s[n - 1].length() - 1); // shift now empty sport to the right through lines for (int i = n - 1; i >= 1; i--) { s[i] = s[i - 1][s[i - 1].length() - 1] + s[i]; s[i - 1] = s[i - 1].substring(0, s[i - 1].length() - 1); } // fill empty spot at beginning with last char of pre s[0] = pre[pre.length() - 1] + s[0]; pre = pre.substring(0, pre.length() - 1); } redraw = true; } void lcd_scrolling(void) { if (text_mode != lcd_mode_none) { if ((heading_pre.length() > 0) || (heading_post.length() > 0)) { lcd_scroll_multiline(heading, LCD_HEADING_LINES, LCD_HEADING_WIDTH, heading_pre, heading_post, heading_direction); } } if (text_mode == lcd_mode_multiline) { if ((text_pre.length() > 0) || (text_post.length() > 0)) { lcd_scroll_multiline(text, LCD_TEXT_LINES, LCD_TEXT_WIDTH, text_pre, text_post, text_direction); } } else if (text_mode == lcd_mode_menu) { for (int i = 0; i < LCD_TEXT_LINES; i++) { if ((menu_pre[i].length() > 0) || (menu_post[i].length() > 0)) { lcd_scroll_multiline(text + i, 1, LCD_TEXT_WIDTH, menu_pre[i], menu_post[i], menu_dir[i]); } } } } #endif // ENABLE_DYNAMIC_SCROLLING int lcd_text_lines(void) { return LCD_TEXT_LINES; } void lcd_init(void) { #ifdef USE_20X4_TEXT_LCD lcd.begin(20, 4); lcd.clear(); lcd.print(F(" Fuellfix v2 ")); lcd.print(F(" Initializing.... ")); lcd.print(F("Software Version ")); lcd.print(F(FIRMWARE_VERSION)); lcd.print(F("made by: xythobuz.de")); #endif // USE_20X4_TEXT_LCD #ifdef USE_FULL_GRAPHIC_LCD lcd.begin(); lcd.firstPage(); do { lcd.setFont(u8g2_font_luBS12_tr); String s = F("Fuellfix v2"); lcd.drawStr(0, 14 * 1, s.c_str()); s = F("Initializing"); lcd.drawStr(0, 14 * 2, s.c_str()); lcd.setFont(u8g2_font_t0_12b_tr); s = F("Software Version " FIRMWARE_VERSION); lcd.drawStr(0, 14 * 2 + 8 + 12 * 1, s.c_str()); s = F("made by: xythobuz.de"); lcd.drawStr(0, 14 * 2 + 8 + 12 * 2, s.c_str()); } while (lcd.nextPage()); #endif // USE_FULL_GRAPHIC_LCD } void lcd_loop(void) { #ifdef ENABLE_DYNAMIC_SCROLLING if ((millis() - last_scroll_time) >= SCROLL_DELAY) { last_scroll_time = millis(); lcd_scrolling(); } #endif // ENABLE_DYNAMIC_SCROLLING #ifdef USE_FULL_GRAPHIC_LCD if (redraw) { lcd.firstPage(); do { lcd.setFont(LCD_HEADING_FONT); for (int i = 0; i < LCD_HEADING_LINES; i++) { lcd.drawStr(0, LCD_HEADING_HEIGHT * (i + 1), heading[i].c_str()); } lcd.setFont(LCD_TEXT_FONT); for (int i = 0; i < LCD_TEXT_LINES; i++) { lcd.drawStr(0, LCD_HEADING_HEIGHT * LCD_HEADING_LINES + LCD_HEADING_TEXT_OFFSET + LCD_TEXT_HEIGHT * (i + 1), text[i].c_str()); } } while (lcd.nextPage()); redraw = false; } #endif // USE_FULL_GRAPHIC_LCD } void lcd_clear(void) { Serial.println(); text_mode = lcd_mode_none; #ifdef USE_20X4_TEXT_LCD lcd.clear(); #endif // USE_20X4_TEXT_LCD #ifdef USE_FULL_GRAPHIC_LCD for (int i = 0; i < LCD_HEADING_LINES; i++) { heading[i] = ""; } for (int i = 0; i < LCD_TEXT_LINES; i++) { text[i] = ""; } redraw = true; #endif // USE_FULL_GRAPHIC_LCD } void lcd_set_heading(const char *h) { Serial.println(h); #ifdef USE_20X4_TEXT_LCD lcd.setCursor(0, 0); lcd.print(h); #endif // USE_20X4_TEXT_LCD #ifdef USE_FULL_GRAPHIC_LCD heading[0] = h; heading_pre = ""; heading_direction = 0; lcd_shorten_multiline(heading, LCD_HEADING_LINES, LCD_HEADING_WIDTH, &heading_post); redraw = true; #endif // USE_FULL_GRAPHIC_LCD } void lcd_set_text(const char *t) { Serial.println(t); text_mode = lcd_mode_multiline; #ifdef USE_20X4_TEXT_LCD lcd.setCursor(0, LCD_HEADING_LINES); lcd.print(t); #endif // USE_20X4_TEXT_LCD #ifdef USE_FULL_GRAPHIC_LCD text[0] = t; text_pre = ""; text_direction = 0; lcd_shorten_multiline(text, LCD_TEXT_LINES, LCD_TEXT_WIDTH, &text_post); redraw = true; #endif // USE_FULL_GRAPHIC_LCD } void lcd_set_menu_text(int line, const char *t) { Serial.print(line); Serial.print(F(": ")); Serial.println(t); text_mode = lcd_mode_menu; #ifdef USE_20X4_TEXT_LCD lcd.setCursor(0, LCD_HEADING_LINES + line); lcd.print(t); #endif // USE_20X4_TEXT_LCD #ifdef USE_FULL_GRAPHIC_LCD text[line] = t; menu_pre[line] = ""; menu_dir[line] = 0; lcd_shorten_multiline(text + line, 1, LCD_TEXT_WIDTH, menu_post + line); redraw = true; #endif // USE_FULL_GRAPHIC_LCD }