display2.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /*
  2. * display2.cpp
  3. *
  4. * Created on: Sep 26, 2017
  5. * Updated:
  6. * Author: Philipp Hinz/Sebastian Vendt
  7. */
  8. #include <stdlib.h>
  9. #include <pthread.h>
  10. #include <time.h>
  11. #include <unistd.h>
  12. #include <string.h>
  13. #include "display2.h"
  14. #include "global.h"
  15. #include "logger.h"
  16. #include "database.h"
  17. #include "timer.h"
  18. #include "lcd.h"
  19. #include "coffee.h"
  20. #include "hal.h"
  21. #include "events.h"
  22. #include "DS18B20.h"
  23. #define CURRENT 0
  24. #define NEXT 1
  25. //timeouts for the transitions in ms
  26. #define TIMEOUT_PREBREW 1000
  27. #define TIMEOUT_POSTBREW 9000
  28. display_lang_t displayLang;
  29. timer displayTimer(displayTimerHandler);
  30. int lcd = 0;
  31. // currently only the history of states is of interest
  32. coffee_status_t coffeeState[] = {STATE_OFF, STATE_NULL}; // current - next state
  33. coffee_mode_t coffeeMode[] = {MODE_STATE, MODE_NULL};
  34. coffee_menuPage_t coffeePage[] = {PAGE_SOFTOFF, PAGE_NULL};
  35. bool coffeeDescaling = false;
  36. refreshRate_t currentRefreshRate = refresh_std;
  37. uint16_t holdNext[3] = {0, 0, 0}; //holdtime of the new {state, Mode, Page} in ms
  38. uint16_t nextWaitTime[3] = {0, 0, 0}; //actual waiting time of the new {state, Mode, Page}
  39. typedef enum {
  40. state_idx = 0,
  41. mode_idx = 1,
  42. page_idx = 2
  43. } idx_t;
  44. void switchToNextPage(coffee_menuPage_t* history);
  45. void switchToNextMode(coffee_mode_t* history);
  46. void switchToNextState(coffee_status_t* history);
  47. void setRefreshRate(refreshRate_t rate);
  48. void setSwitchToNextTimeout(idx_t idx, uint16_t millis);
  49. void displayPrintTemp(int line);
  50. void displayPrintStats(int line);
  51. void displayPrintStats2(int line);
  52. void displayPrintNextDesc(int line);
  53. void displayPrintPostBrew(int line);
  54. void displayPrintBrewManual(int line);
  55. void displayPrintFlow(int line);
  56. void displayPrintAutoResetVal(int line);
  57. void displayPrintCleanCycle(int line);
  58. void displayStateUpdated(event_t *event);
  59. void displayModeUpdated(event_t *event);
  60. void displayPageUpdated(event_t *event);
  61. void displayDescaling(event_t *event);
  62. void displayPrintLogo(void);
  63. void displayTerminate(event_t *e);
  64. void track(idx_t idx);
  65. /**
  66. * Prints out the current time in a centered position
  67. * @param line Target line in display
  68. */
  69. void displayPrintTime(int line) {
  70. time_t rawtime;
  71. struct tm * timeinfo;
  72. if (line > DISPLAY_ROWS)
  73. line = 0;
  74. time(&rawtime);
  75. timeinfo = localtime(&rawtime);
  76. lcdPosition(lcd, 0, line);
  77. lcdPrintf(lcd, " %.2d:%.2d:%.2d ", timeinfo->tm_hour,
  78. timeinfo->tm_min, timeinfo->tm_sec);
  79. }
  80. /**
  81. * Prints out the current temperature in a centered position
  82. * @param line Target line in display
  83. */
  84. void displayPrintTemp(int line) {
  85. //TODO: the temperature is wrong(old value) the first time the page is opened
  86. //after the first refresh the right temperature is displayed
  87. //no idea where this comes from, datasheet says nothing about dummy readouts.
  88. //no old values are used, conversion is started before every readout...
  89. //Quick fix:
  90. DS18B20_readTemp();
  91. if (line > DISPLAY_ROWS)
  92. line = 0;
  93. lcdPosition(lcd, 0, line);
  94. lcdPrintf(lcd, " %d C ", DS18B20_readTemp());
  95. }
  96. /**
  97. * Prints the total epsressi brewed since reset
  98. */
  99. void displayPrintStats(int line) {
  100. char buffer[17];
  101. if (line > DISPLAY_ROWS)
  102. line = 0;
  103. lcdPosition(lcd, 0, line);
  104. sprintf(buffer, "%7d espressi", getBrewCounter());
  105. lcdPrintf(lcd, buffer);
  106. }
  107. /**
  108. * Prints the total heating time in kWh
  109. */
  110. void displayPrintStats2(int line) {
  111. char buffer[17];
  112. if (line > DISPLAY_ROWS)
  113. line = 0;
  114. uint64_t totalkWh = 2 * getTotalHeatingTime()/(60*60);
  115. sprintf(buffer, "%lld kWh", totalkWh);
  116. displayPrintLn(line, buffer, true);
  117. }
  118. /**
  119. * Prints the remaining time or the remaining cups until the next descaling is necessary
  120. */
  121. void displayPrintNextDesc(int line) {
  122. char buffer[17];
  123. if (line > DISPLAY_ROWS) line = 0;
  124. sprintf(buffer, "%d d or %d C", checkDirtyTime(), checkDirtyEspresso());
  125. displayPrintLn(line, buffer, true);
  126. }
  127. /**
  128. *
  129. */
  130. void displayPrintPostBrew(int line) {
  131. char buffer[17];
  132. if (line > DISPLAY_ROWS) line = 0;
  133. float brewTime = (float)halGetLastFlowTime();
  134. sprintf(buffer, "%.1f ml / %.1f s", halGetLastFlow(), brewTime / 1000);
  135. displayPrintLn(line, buffer, true);
  136. }
  137. void displayPrintBrewManual(int line) {
  138. char buffer[17];
  139. if (line > DISPLAY_ROWS) line = 0;
  140. float brewTime = (float)halGetFlowTime();
  141. sprintf(buffer, "%.1f ml / %.1f s", halGetFlow(), brewTime / 1000);
  142. displayPrintLn(line, buffer, true);
  143. }
  144. /**
  145. * Prints out the total volume flow
  146. * @param line Target line in display
  147. */
  148. void displayPrintFlow(int line) {
  149. float flow = halGetFlow();
  150. lcdPosition(lcd, 0, line);
  151. lcdPrintf(lcd, "%s: %.0f ml ", displayGetString(str_flow), flow);
  152. }
  153. /**
  154. *
  155. */
  156. void displayPrintAutoResetVal(int line) {
  157. uint16_t restFlowTime = halGetFlowResetTime();
  158. lcdPosition(lcd, 0, line);
  159. lcdPrintf(lcd, "%.0f ms", restFlowTime);
  160. }
  161. /**
  162. * Prints the current cycle of the cleaning process
  163. */
  164. void displayPrintCleanCycle(int line) {
  165. char buffer[17];
  166. if (line > DISPLAY_ROWS) line = 0;
  167. sprintf(buffer, "%2d of %2d", getCurrentCleanCycle(), CLEANING_CYCLES);
  168. displayPrintLn(line, buffer, true);
  169. }
  170. /**
  171. * Prints a string to a specific line, optionally centered.
  172. * This function also fills out the remaining row of the display with spaces,
  173. * to ensure there is no old data left.
  174. * @param line Target line in display
  175. * @param *str String to print
  176. * @param centered Print centered or not
  177. */
  178. void displayPrintLn(int line, const char* str, bool centered) {
  179. char buf[DISPLAY_COLS + 1];
  180. int len = strlen(str);
  181. int i = 0;
  182. int spaces = 0;
  183. if (len > DISPLAY_COLS)
  184. len = DISPLAY_COLS;
  185. if (line > DISPLAY_ROWS)
  186. line = 0;
  187. if (centered) {
  188. spaces = (DISPLAY_COLS - len) / 2;
  189. if (spaces) {
  190. for (i = 0; i < spaces; i++) {
  191. buf[i] = ' ';
  192. }
  193. }
  194. }
  195. for (i = 0; i < len; i++) {
  196. buf[spaces + i] = str[i];
  197. }
  198. if ((len + spaces) < DISPLAY_COLS) { // fill remaining space
  199. for (i = i + spaces; i < DISPLAY_COLS; i++) {
  200. buf[i] = ' ';
  201. }
  202. }
  203. //draw the descaling star
  204. if(coffeeDescaling && line == 0){
  205. buf[15] = '*';
  206. }
  207. lcdPosition(lcd, 0, line);
  208. buf[DISPLAY_COLS] = '\0';
  209. lcdPrintf(lcd, buf);
  210. //logger(V_HAL, "Printed out on display: \"%s\"\n", buf);
  211. }
  212. /**
  213. * Updates the display state to the matching coffee state
  214. * @param event Event data
  215. */
  216. void displayStateUpdated(event_t *event) {
  217. if (event->len != sizeof(coffee_status_t)) {
  218. logger_error("Invalid use of event %s\n", event->event);
  219. return;
  220. }
  221. coffee_status_t state = *(coffee_status_t*) event->data;
  222. coffeeState[NEXT] = state;
  223. displayTimer.call();
  224. }
  225. /**
  226. * Updates the display state to the matching coffee display mode
  227. * @param event Event data
  228. */
  229. void displayModeUpdated(event_t *event) {
  230. if (event->len != sizeof(coffee_mode_t)) {
  231. logger_error("Invalid use of event %s\n", event->event);
  232. return;
  233. }
  234. coffee_mode_t mode = *(coffee_mode_t*) event->data;
  235. coffeeMode[NEXT] = mode;
  236. displayTimer.call();
  237. }
  238. /**
  239. * Updates the display state to the matching coffee display page
  240. * The new state is put on hold (next) and the display can decided if it immediately makes the
  241. * new state to the current one or if a timeout is specified in which the new state is put on hold and the
  242. * state transition is processed.
  243. * One initial asynchronous call
  244. * @param event Event data
  245. */
  246. void displayPageUpdated(event_t *event) {
  247. if (event->len != sizeof(coffee_menuPage_t)) {
  248. logger_error("Invalid use of event %s\n", event->event);
  249. return;
  250. }
  251. coffee_menuPage_t page = *(coffee_menuPage_t*) event->data;
  252. coffeePage[NEXT] = page;
  253. displayTimer.call();
  254. }
  255. /**
  256. * Updates the current descaling status of the machine
  257. */
  258. void displayDescaling(event_t *event) {
  259. if (event->len != sizeof(bool)) {
  260. logger_error("Invalid use of event %s\n", event->event);
  261. return;
  262. }
  263. coffeeDescaling = *(bool*) event->data;
  264. }
  265. /**
  266. * Prints the logo (CoffeePi) and also the temperature with the logo (CoffeePi@72C) if the machine is on
  267. */
  268. void displayPrintLogo(void) {
  269. char buffer[17];
  270. switch (coffeeState[CURRENT]) {
  271. case STATE_HEATING:
  272. case STATE_INITALHEATING:
  273. case STATE_IDLE:
  274. sprintf(buffer, "CoffeePi @ %dC", DS18B20_readTemp());
  275. break;
  276. default:
  277. sprintf(buffer, "CoffeePi");
  278. break;
  279. }
  280. displayPrintLn(0, buffer, true);
  281. }
  282. /**
  283. * Handles cleanup before program termination
  284. */
  285. void displayTerminate(event_t *e) {
  286. logger(V_BASIC, "display.cpp: Terminating\n");
  287. displayPrintLn(0, "CoffeePi", true);
  288. displayPrintLn(1, displayGetString(str_bye), true);
  289. displayTimer.stop();
  290. }
  291. /**
  292. * Main thread to handle display data
  293. * @param *threadid Thread ID
  294. */
  295. void* displayThread(void* threadid) {
  296. //sleep(1); // Wait for other components to initialize
  297. displayTimer.start();
  298. logger(V_BASIC, "Initialized display Thread. Timer state: %d \n", displayTimer.isActive());
  299. while (1) {
  300. pause();
  301. if (1) {
  302. displayTimer.call();
  303. }
  304. }
  305. pthread_exit(EXIT_SUCCESS);
  306. }
  307. /**
  308. * Timer handler for display update
  309. * @param *threadid Thread ID
  310. */
  311. void* displayTimerHandler(void* threadid) {
  312. //track time of the new state in hold and switch if necessary
  313. track(state_idx);
  314. track(page_idx);
  315. track(mode_idx);
  316. displayRefresh();
  317. pthread_exit(EXIT_SUCCESS);
  318. }
  319. /**
  320. * Tracks the elapsed time in ms of the new state waiting to become the current one if a hold time is specified
  321. */
  322. void track(idx_t idx){
  323. if (holdNext[idx] != 0) {
  324. nextWaitTime[idx] += 1000 / currentRefreshRate;
  325. if (holdNext[idx] <= nextWaitTime[idx]) {
  326. switch(idx){
  327. case state_idx:
  328. switchToNextState(coffeeState);
  329. break;
  330. case page_idx:
  331. switchToNextPage(coffeePage);
  332. break;
  333. case mode_idx:
  334. switchToNextMode(coffeeMode);
  335. break;
  336. }
  337. }
  338. }
  339. }
  340. /**
  341. * Initializes display
  342. */
  343. void displayInit(void) {
  344. lcd = lcdInit();
  345. if (lcd < 0)
  346. logger_error("Error: unable to init LCD (%d)\n", lcd);
  347. lcdClear(lcd);
  348. lcdHome(lcd);
  349. displayPrintLn(0, (char*) "CoffeePi", true);
  350. displayPrintLn(1, (char*) "booting...", true);
  351. //lcdPrintf(lcd, " CoffeePi booting...");
  352. //setRefreshRate(refresh_std);
  353. currentRefreshRate = refresh_std;
  354. displayTimer.setDivider(20 / refresh_std);
  355. /**The following block comes from void* displayThread(void* threadid)
  356. * The idea is that the initialization functions get the component ready to react on external events
  357. * once the threads start, events might be triggered and every component can process them
  358. * This should fix the TO_DO where a descaling event was triggered during startup of the coffeethread and
  359. * the displaythread did not react to it since it hadn't subscribed to the event yet
  360. */
  361. int tmp = sqlGetConf(CFGdisplaylang);
  362. if (!tmp || tmp >= lang_last)
  363. tmp = DEFAULT_LANG;
  364. displaySetLang((display_lang_t) tmp);
  365. event_subscribe("statechange", &displayStateUpdated, "display.cpp");
  366. event_subscribe("modechange", &displayModeUpdated, "display.cpp");
  367. event_subscribe("pagechange", &displayPageUpdated, "display.cpp");
  368. event_subscribe("terminate", &displayTerminate, "display.cpp");
  369. event_subscribe("descaling", &displayDescaling, "display.cpp");
  370. logger(V_BASIC, "display2.cpp Initialized display with a refresh rate of %d Hz\n",
  371. refresh_std);
  372. }
  373. /**
  374. * Sets the language of the display text
  375. * @param lang New language
  376. */
  377. void displaySetLang(display_lang_t lang) {
  378. displayLang = lang;
  379. }
  380. /*
  381. *
  382. */
  383. void switchToNextPage(coffee_menuPage_t* history){
  384. if(history[NEXT] != PAGE_NULL) {
  385. history[CURRENT] = history[NEXT];
  386. history[NEXT] = PAGE_NULL;
  387. holdNext[page_idx] = 0;
  388. nextWaitTime[page_idx] = 0;
  389. }
  390. }
  391. /*
  392. *
  393. */
  394. void switchToNextMode(coffee_mode_t* history){
  395. if(history[NEXT] != MODE_NULL) {
  396. history[CURRENT] = history[NEXT];
  397. history[NEXT] = MODE_NULL;
  398. holdNext[mode_idx] = 0;
  399. nextWaitTime[mode_idx] = 0;
  400. }
  401. }
  402. /*
  403. *
  404. */
  405. void switchToNextState(coffee_status_t* history){
  406. if(history[NEXT] != STATE_NULL) {
  407. history[CURRENT] = history[NEXT];
  408. history[NEXT] = STATE_NULL;
  409. holdNext[state_idx] = 0;
  410. nextWaitTime[state_idx] = 0;
  411. }
  412. }
  413. /**
  414. * sets the refresh rate of the the display
  415. * to one of the predefined refresh rates.
  416. */
  417. void setRefreshRate(refreshRate_t rate) {
  418. if (currentRefreshRate != rate) {
  419. currentRefreshRate = rate;
  420. displayTimer.setDivider(20 / rate);
  421. }
  422. }
  423. /**
  424. * specifies a timeout at which the new state/mode/page will be made to the current one
  425. * This function is enables for special state/mode/page transitions which hold the new state while
  426. * the current transition is displayed. These cases set a timeout specifying how long the state transition should be paused.
  427. * After the time specified in the timeout the timerhandler of the display will switch the states and makes the new one to the current one.
  428. */
  429. void setSwitchToNextTimeout(idx_t idx, uint16_t millis) {
  430. holdNext[idx] = millis;
  431. }
  432. /**
  433. * The core description of what will be displayed in what displaystate
  434. */
  435. void displayRefresh(void) {
  436. //handle mode trainsitions
  437. switchToNextMode(coffeeMode);
  438. //FSM of the display
  439. if (coffeeMode[CURRENT] == MODE_STATE) {
  440. //handle state transitions
  441. if (coffeeState[NEXT] == STATE_BREW) { //Pre brew
  442. //check how long the new state is already in hold ... and either switch or process transition
  443. setSwitchToNextTimeout(state_idx, TIMEOUT_PREBREW);
  444. setRefreshRate(refresh_fast);
  445. displayPrintLogo();
  446. displayPrintLn(1, displayGetString(str_brewing), true);
  447. return;
  448. } else if ((coffeeState[CURRENT] == STATE_BREW || coffeeState[CURRENT] == STATE_BREWMANUAL) && coffeeState[NEXT] != STATE_NULL) { //Post brew
  449. setSwitchToNextTimeout(state_idx, TIMEOUT_POSTBREW);
  450. setRefreshRate(refresh_std);
  451. displayPrintLn(0, displayGetString(str_postBrew), true);
  452. displayPrintPostBrew(1);
  453. return;
  454. } else { //no special state transition -> make new state to the current one
  455. switchToNextState(coffeeState);
  456. }
  457. switch (coffeeState[CURRENT]) { //coffeeGetState()
  458. case STATE_IDLE:
  459. displayPrintLogo();
  460. displayPrintLn(1, displayGetString(str_ready), true);
  461. break;
  462. case STATE_INITALHEATING:
  463. displayPrintLogo();
  464. displayPrintLn(1, displayGetString(str_heating), true);
  465. break;
  466. case STATE_HEATING:
  467. displayPrintLogo();
  468. displayPrintLn(1, displayGetString(str_heatingready), true);
  469. break;
  470. case STATE_BREW:
  471. displayPrintLn(0, displayGetString(str_brewing), true);
  472. displayPrintFlow(1);
  473. break;
  474. case STATE_BREWMANUAL:
  475. setRefreshRate(refresh_fast);
  476. displayPrintLn(0, displayGetString(str_brewing), true);
  477. //displayPrintFlow(1);
  478. displayPrintBrewManual(1);
  479. break;
  480. case STATE_CLEANING:
  481. displayPrintLn(0, displayGetString(str_cleaning), true);
  482. displayPrintCleanCycle(1);
  483. break;
  484. case STATE_ERROR:
  485. displayPrintLogo();
  486. displayPrintLn(1, displayGetString(str_error), true);
  487. break;
  488. case STATE_WAIT_OFF:
  489. displayPrintLogo();
  490. displayPrintLn(1, displayGetString(str_waitoff), true);
  491. break;
  492. case STATE_OFF:
  493. default:
  494. displayPrintLogo();
  495. displayPrintTime(1);
  496. break;
  497. }
  498. } else if (coffeeMode[CURRENT] == MODE_MENU) {
  499. //handle page transitions
  500. switchToNextPage(coffeePage);
  501. switch (coffeePage[CURRENT]) {
  502. case PAGE_SOFTOFF:
  503. displayPrintLn(0, displayGetString(str_menu), true);
  504. displayPrintLn(1, displayGetString(str_menu_softoff), true);
  505. break;
  506. case PAGE_KILL:
  507. displayPrintLn(0, displayGetString(str_menu), true);
  508. displayPrintLn(1, displayGetString(str_menu_kill), true);
  509. break;
  510. case PAGE_STATS:
  511. displayPrintLn(0, displayGetString(str_menu_stats), true);
  512. displayPrintStats(1);
  513. break;
  514. case PAGE_STATS2:
  515. displayPrintLn(0, displayGetString(str_menu_stats2), true);
  516. displayPrintStats2(1);
  517. break;
  518. case PAGE_DESCALING:
  519. displayPrintLn(0, displayGetString(str_menu_nextdesc), true);
  520. displayPrintNextDesc(1);
  521. break;
  522. case PAGE_TEMP:
  523. displayPrintLn(0, displayGetString(str_menu_temp), true);
  524. displayPrintTemp(1);
  525. break;
  526. case PAGE_CLEAN:
  527. displayPrintLn(0, displayGetString(str_menu), true);
  528. displayPrintLn(1, displayGetString(str_menu_clean), true);
  529. break;
  530. case PAGE_RESTART:
  531. displayPrintLn(0, displayGetString(str_menu), true);
  532. displayPrintLn(1, displayGetString(str_menu_restart), true);
  533. break;
  534. case PAGE_EXIT:
  535. displayPrintLn(0, displayGetString(str_menu), true);
  536. displayPrintLn(1, displayGetString(str_menu_exit), true);
  537. break;
  538. case PAGE_SETTINGS:
  539. displayPrintLn(0, displayGetString(str_menu), true);
  540. displayPrintLn(1, displayGetString(str_menu_settings), true);
  541. break;
  542. case PAGE_RESETKWH:
  543. displayPrintLn(0, displayGetString(str_menu_settings), true);
  544. displayPrintLn(1, displayGetString(str_menu_resetkwh), true);
  545. break;
  546. case PAGE_RESETKWH_VAL: //confirmation dialog
  547. displayPrintLn(0, displayGetString(str_menu_resetkwh_val), true);
  548. break;
  549. case PAGE_CHANGEPREINF:
  550. displayPrintLn(0, displayGetString(str_menu_settings), true);
  551. displayPrintLn(1, displayGetString(str_menu_changepreinf), true);
  552. break;
  553. case PAGE_CHANGEPREINF_VAL:
  554. break;
  555. case PAGE_CHANGEBREWTIME:
  556. displayPrintLn(0, displayGetString(str_menu_settings), true);
  557. displayPrintLn(1, displayGetString(str_menu_changebrewtime), true);
  558. break;
  559. case PAGE_CHANGEBREWTIME_VAL:
  560. break;
  561. case PAGE_CHANGEBREWML:
  562. displayPrintLn(0, displayGetString(str_menu_settings), true);
  563. displayPrintLn(1, displayGetString(str_menu_changebrewml), true);
  564. break;
  565. case PAGE_CHANGEBREWML_VAL:
  566. break;
  567. case PAGE_CHANGESOAKTIME:
  568. displayPrintLn(0, displayGetString(str_menu_settings), true);
  569. displayPrintLn(1, displayGetString(str_menu_changesoaktime), true);
  570. break;
  571. case PAGE_CHANGESOAKTIME_VAL:
  572. break;
  573. case PAGE_CHANGEAUTORESET:
  574. displayPrintLn(0, displayGetString(str_menu_settings), true);
  575. displayPrintLn(1, displayGetString(str_menu_changeAutoReset), true);
  576. break;
  577. case PAGE_CHANGEAUTORESET_VAL:
  578. displayPrintLn(0, displayGetString(str_menu_changeAutoReset_val), true);
  579. displayPrintAutoResetVal(1);
  580. break;
  581. case PAGE_LEAVESETTINGS:
  582. displayPrintLn(0, displayGetString(str_menu_settings), true);
  583. displayPrintLn(1, displayGetString(str_menu_leavesettings), true);
  584. break;
  585. default:
  586. displayPrintLn(0, displayGetString(str_menu), true);
  587. displayPrintLn(1, "???", true);
  588. break;
  589. }
  590. }
  591. }
  592. /**
  593. * Returns the matching translation of a string
  594. * @param string Requested string
  595. * @return Translated string
  596. */
  597. const char* displayGetString(display_strings_t string) {
  598. if (displayLang >= lang_last)
  599. displayLang = DEFAULT_LANG;
  600. return display_strings[string].text[displayLang];
  601. }