|
@@ -0,0 +1,269 @@
|
|
|
+/*
|
|
|
+ * display.cpp
|
|
|
+ *
|
|
|
+ * Created on: Sep 26, 2017
|
|
|
+ * Author: Philipp Hinz
|
|
|
+ */
|
|
|
+#include <stdlib.h>
|
|
|
+#include <pthread.h>
|
|
|
+#include <time.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#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;
|
|
|
+int 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", 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();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (scale5Hz) {
|
|
|
+ switch (coffeeState) {
|
|
|
+ case STATE_BREW:
|
|
|
+ case STATE_BREWMANUAL:
|
|
|
+ displayRefresh();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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 <= REFRESH_RATE) {
|
|
|
+ displayPrintLn(0, "CoffeePi", true);
|
|
|
+ displayPrintLn(1, displayGetString(str_brewing), true);
|
|
|
+ } else
|
|
|
+ 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(int state) {
|
|
|
+ if (state != coffeeState) {
|
|
|
+ coffeeState = state;
|
|
|
+ timerScaler--;
|
|
|
+ displayTimer.call();
|
|
|
+ }
|
|
|
+}
|