/* * display.cpp * * Created on: Sep 26, 2017 * Author: Philipp Hinz */ #include #include #include #include #include #include "global.h" #include "display.h" #include "logger.h" #include "database.h" #include "timer.h" #include "lcd.h" #include "coffee.h" #include "hal.h" display_lang_t displayLang; timer displayTimer(displayTimerHandler); int lcd = 0; volatile int timerScaler = 0; volatile int elapsedCnt = 0; coffee_status_t coffeeState = STATE_OFF; /** * Prints out the current time in a centered position * @param line Target line in display */ void displayPrintTime(int line) { time_t rawtime; struct tm * timeinfo; if (line > DISPLAY_ROWS) line = 0; time(&rawtime); timeinfo = localtime(&rawtime); lcdPosition(lcd, 0, line); lcdPrintf(lcd, " %.2d:%.2d:%.2d ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); } /** * Prints out the total volume flow * @param line Target line in display */ void displayPrintFlow(int line) { float flow = halGetFlow(); lcdPosition(lcd, 0, line); lcdPrintf(lcd, "%s: %.0f ml ", displayGetString(str_flow), flow); } /** * Prints a string to a specific line, optionally centered. * This function also fills out the remaining row of the display with spaces, * to ensure there is no old data left. * @param line Target line in display * @param *str String to print * @param centered Print centered or not */ void displayPrintLn(int line, const char* str, bool centered) { char buf[DISPLAY_COLS + 1]; int len = strlen(str); int i = 0; int spaces = 0; if (len > DISPLAY_COLS) len = DISPLAY_COLS; if (line > DISPLAY_ROWS) line = 0; if (centered) { spaces = (DISPLAY_COLS - len) / 2; if (spaces) { for (i = 0; i < spaces; i++) { buf[i] = ' '; } } } for (i = 0; i < len; i++) { buf[spaces + i] = str[i]; } if ((len + spaces) < DISPLAY_COLS) { // fill remaining space for (i = i + spaces; i < DISPLAY_COLS; i++) { buf[i] = ' '; } } lcdPosition(lcd, 0, line); buf[DISPLAY_COLS] = '\0'; lcdPrintf(lcd, buf); //logger(V_HAL, "Printed out on display: \"%s\"\n", buf); } /** * Main thread to handle display data * @param *threadid Thread ID */ void* displayThread(void* threadid) { sleep(1); // Wait for other components to initialize int tmp = sqlGetConf(CFGdisplaylang); if (!tmp || tmp >= lang_last) tmp = DEFAULT_LANG; displaySetLang((display_lang_t) tmp); displayTimer.start(); while (1) { pause(); if (1) { timerScaler--; displayTimer.call(); } } pthread_exit(EXIT_SUCCESS); } /** * Timer handler for display update * @param *threadid Thread ID */ void* displayTimerHandler(void* threadid) { int scale1Hz = 0; if (timerScaler++ >= REFRESH_RATE) { // 1s elapsed, reset timerScaler = 0; scale1Hz = 1; } int scale5Hz = (timerScaler == (REFRESH_RATE / 5) ? 0 : 1); if (scale1Hz) { switch (coffeeState) { case STATE_OFF: case STATE_HEATING: case STATE_INITALHEATING: case STATE_IDLE: case STATE_CLEANING: case STATE_ERROR: displayRefresh(); break; default: break; } } if (scale5Hz) { switch (coffeeState) { case STATE_BREW: case STATE_BREWMANUAL: displayRefresh(); break; default: break; } } if (elapsedCnt < (5 * REFRESH_RATE)) // Don't let it grow too large elapsedCnt++; pthread_exit(EXIT_SUCCESS); } /** * Initializes display */ void displayInit(void) { lcd = lcdInit(); if (lcd < 0) logger_error("Error: unable to init LCD (%d)\n", lcd); lcdClear(lcd); lcdHome(lcd); displayPrintLn(0, (char*) "CoffeePi", true); displayPrintLn(1, (char*) "booting...", true); //lcdPrintf(lcd, " CoffeePi booting..."); timerScaler = 0; displayTimer.setDivider((1000000 / TIMER_DELAY_US) / REFRESH_RATE); logger(V_BASIC, "Initialized display with a refresh rate of %d Hz\n", REFRESH_RATE); } /** * Handles cleanup before program termination */ void displayTerminate(void) { displayPrintLn(0, "CoffeePi", true); displayPrintLn(1, displayGetString(str_bye), true); } /** * Sets the language of the display text * @param lang New language */ void displaySetLang(display_lang_t lang) { displayLang = lang; } /** * Refreshed the display content and outputs it */ void displayRefresh(void) { switch (coffeeState) { //coffeeGetState() case STATE_IDLE: displayPrintLn(0, "CoffeePi", true); displayPrintLn(1, displayGetString(str_ready), true); break; case STATE_INITALHEATING: displayPrintLn(0, "CoffeePi", true); displayPrintLn(1, displayGetString(str_heating), true); break; case STATE_HEATING: displayPrintLn(0, "CoffeePi", true); displayPrintLn(1, displayGetString(str_heatingready), true); break; case STATE_BREW: if (elapsedCnt <= 2*REFRESH_RATE) { displayPrintLn(0, "CoffeePi", true); displayPrintLn(1, displayGetString(str_brewing), true); } else { displayPrintLn(0, displayGetString(str_brewing), true); displayPrintFlow(1); } break; case STATE_BREWMANUAL: displayPrintLn(0, displayGetString(str_brewing), true); displayPrintFlow(1); break; case STATE_CLEANING: displayPrintLn(0, "CoffeePi", true); displayPrintLn(1, displayGetString(str_cleaning), true); break; case STATE_ERROR: displayPrintLn(0, "CoffeePi", true); displayPrintLn(1, displayGetString(str_error), true); break; case STATE_OFF: default: displayPrintLn(0, "CoffeePi", true); displayPrintTime(1); break; } } /** * Returns the matching translation of a string * @param string Requested string * @return Translated string */ const char* displayGetString(display_strings_t string) { if (displayLang >= lang_last) displayLang = DEFAULT_LANG; return display_strings[string].text[displayLang]; } /** * Updates the display state to the matching coffee state * @param state New state */ void displayPushState(coffee_status_t state) { if (state != coffeeState) { coffeeState = state; timerScaler--; elapsedCnt = 0; displayTimer.call(); } }