coffee.cpp 22 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. /*
  2. * coffee.cpp
  3. *
  4. * Created on: Sep 25, 2017
  5. * Author: sebastian
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <errno.h>
  11. #include <stdint.h>
  12. #include <wiringPi.h>
  13. #include <pthread.h>
  14. #include <unistd.h>
  15. #include <iostream>
  16. #include <csignal>
  17. #include <time.h>
  18. #include <ctime>
  19. #include "coffee.h"
  20. #include "hal.h"
  21. #include "logger.h"
  22. #include "timer.h"
  23. #include "database.h"
  24. coffee_status_t state;
  25. coffee_menuPage_t page;
  26. coffee_mode_t mode;
  27. int sigValue;
  28. int menuTimeout;
  29. timer menuTimer(&menuTimerHandler);
  30. timer brewTimer(&brewTimerHandler);
  31. uint64_t totalHeatingTime; //local copies of the corresponding database entries
  32. uint16_t brewCounter;
  33. uint16_t brewTime;
  34. uint16_t currentCleanCycle;
  35. bool initalHeating;
  36. bool descaling; //flag to indicate descaling and cleaning
  37. uint16_t descBrewcount;
  38. time_t descRawTimestamp;
  39. const char* PageName[] = { "SoftOff", "Kill", "Stats", "Stats2", "Descaling", "Temp", "Clean", "Restart",
  40. "Exit" };
  41. const char* StateName[] = { "OFF", "HEATING", "INITHEAT", "IDLE", "BREW",
  42. "BREWMAN", "CLEAN", "ERROR", "WAITOFF" };
  43. /**
  44. * Thread for the finite state machine
  45. * It represents the current state of the machine and handles signals coming from
  46. * the pressure control, buttons, the brew switch and the proximity sensor
  47. * @param threadid the ID of the thread
  48. */
  49. void *coffeeThread(void *threadid) {
  50. logger(V_BASIC, "Initializing coffee thread...\n");
  51. //installing new Signal handler for coffeethread
  52. struct sigaction action;
  53. sigemptyset(&action.sa_mask);
  54. action.sa_flags = SA_SIGINFO;
  55. action.sa_sigaction = coffeeHandler;
  56. sigaction(SIGUSR2, &action, NULL);
  57. currentCleanCycle = 0;
  58. menuTimer.setDivider(4);
  59. menuTimer.stop();
  60. menuTimeout = 0;
  61. brewTimer.setDivider(2);
  62. brewTimer.stop();
  63. brewTime = 0;
  64. initalHeating = true;
  65. mode = MODE_STATE; //Unless we enter the menu we start in state mode
  66. page = PAGE_SOFTOFF;
  67. descaling = false;
  68. //read the database values
  69. if (!(totalHeatingTime = sqlGetConf(CFGHeatingTime))) {
  70. logger_error("coffee.cpp: Couldn't read the heating time from the database\n");
  71. //pthread_exit(EXIT_SUCCESS);
  72. exit(EXIT_FAILURE);
  73. }
  74. if (!(brewCounter = sqlGetConf(CFGbrewcounter))) {
  75. logger_error("coffee.cpp: Couldn't read the brew counter from the database\n");
  76. //pthread_exit(EXIT_SUCCESS);
  77. exit(EXIT_FAILURE);
  78. }
  79. if (!(descRawTimestamp = (time_t) sqlGetConf(CFGDescTimestamp))) {
  80. logger_error("coffee.cpp: Couldn't read the descaling time from the database\n");
  81. //pthread_exit(EXIT_SUCCESS);
  82. exit(EXIT_FAILURE);
  83. }
  84. if (!(descBrewcount = sqlGetConf(CFGDescBrewCount))) {
  85. logger_error("coffee.cpp: Couldn't read the descaling brewcount time from the database\n");
  86. //pthread_exit(EXIT_SUCCESS);
  87. exit(EXIT_FAILURE);
  88. }
  89. checkDescaling();
  90. event_subscribe("terminate", &coffeeTerminate);
  91. logger(V_BREW, "Determining inital state\n");
  92. //determine inital state
  93. if (halGetRelaisState(RELAIS_POWER) && halGetRelaisState(RELAIS_HEAT)
  94. && !halGetRelaisState(RELAIS_PUMP)) {
  95. //wait for heat relais to switch
  96. coffeeNap(1,0);
  97. if (halIsHeating()) { //Heating is on
  98. changeState(STATE_INITALHEATING);
  99. } else {
  100. initalHeating = false;
  101. changeState(STATE_IDLE);
  102. }
  103. } else if (halGetRelaisState(RELAIS_PUMP)) {
  104. logger_error("Whoops, why is the pump running...\n");
  105. changeState(STATE_ERROR);
  106. } else {
  107. changeState(STATE_OFF);
  108. }
  109. logger(V_BREW, "Starting Coffee FSM\n");
  110. //begin FSM
  111. while (1) {
  112. /*
  113. * Menue FSM
  114. */
  115. switch (page) {
  116. case PAGE_NULL:
  117. break;
  118. case PAGE_SOFTOFF: //this page is only available when the machine is on
  119. if (SigValueEmpty() && mode == MODE_MENU)
  120. pause();
  121. switch (getSigValue(MODE_MENU)) {
  122. case SigInt0Rls:
  123. if(halIsHeating()){
  124. changeState(STATE_WAIT_OFF);
  125. }
  126. else {
  127. changeState(STATE_OFF);
  128. }
  129. leaveMenu();
  130. break;
  131. case SigRotCW:
  132. changePage(PAGE_KILL);
  133. break;
  134. case SigRotCCW:
  135. changePage(PAGE_EXIT);
  136. break;
  137. }
  138. break;
  139. case PAGE_KILL: //this page is only available when the machine is on
  140. if (SigValueEmpty() && mode == MODE_MENU)
  141. pause();
  142. switch (getSigValue(MODE_MENU)) {
  143. case SigInt0Rls:
  144. if (halIsHeating()) {
  145. coffeeIncreaseHeatingTime(halgetHeatingTime());
  146. }
  147. changeState(STATE_OFF);
  148. leaveMenu();
  149. break;
  150. case SigRotCW:
  151. if (state == STATE_IDLE || state == STATE_HEATING) {
  152. changePage(PAGE_CLEAN);
  153. } else {
  154. changePage(PAGE_RESTART);
  155. }
  156. break;
  157. case SigRotCCW:
  158. changePage(PAGE_SOFTOFF);
  159. break;
  160. }
  161. break;
  162. case PAGE_CLEAN: //this page is only be available when the machine is hot
  163. if (SigValueEmpty() && mode == MODE_MENU)
  164. pause();
  165. switch (getSigValue(MODE_MENU)) {
  166. case SigInt0Rls:
  167. changeMode(MODE_STATE);
  168. if (!halProxSensorCovered()) {
  169. changeState(STATE_CLEANING);
  170. leaveMenu();
  171. } else {
  172. changeState(STATE_FULLTANK);
  173. leaveMenu();
  174. }
  175. break;
  176. case SigRotCW:
  177. changePage(PAGE_RESTART);
  178. break;
  179. case SigRotCCW:
  180. changePage(PAGE_KILL);
  181. break;
  182. }
  183. break;
  184. case PAGE_RESTART:
  185. if (SigValueEmpty() && mode == MODE_MENU)
  186. pause();
  187. switch (getSigValue(MODE_MENU)) {
  188. case SigInt0Rls:
  189. event_trigger("terminate");
  190. coffeeNap(4, 0);
  191. exit(EXIT_SUCCESS);
  192. break;
  193. case SigRotCW:
  194. changePage(PAGE_TEMP);
  195. break;
  196. case SigRotCCW:
  197. if(state == STATE_IDLE || state == STATE_HEATING){
  198. changePage(PAGE_CLEAN);
  199. } else if (state == STATE_ERROR || state == STATE_INITALHEATING) {
  200. changePage(PAGE_KILL);
  201. } else {
  202. changePage(PAGE_EXIT);
  203. }
  204. break;
  205. }
  206. break;
  207. case PAGE_TEMP:
  208. if (SigValueEmpty() && mode == MODE_MENU)
  209. pause();
  210. switch (getSigValue(MODE_MENU)) {
  211. case SigRotCW:
  212. changePage(PAGE_STATS);
  213. break;
  214. case SigRotCCW:
  215. changePage(PAGE_RESTART);
  216. break;
  217. }
  218. break;
  219. case PAGE_STATS:
  220. if (SigValueEmpty() && mode == MODE_MENU)
  221. pause();
  222. switch (getSigValue(MODE_MENU)) {
  223. case SigRotCW:
  224. changePage(PAGE_STATS2);
  225. break;
  226. case SigRotCCW:
  227. changePage(PAGE_TEMP);
  228. break;
  229. }
  230. break;
  231. case PAGE_STATS2:
  232. if (SigValueEmpty() && mode == MODE_MENU)
  233. pause();
  234. switch (getSigValue(MODE_MENU)) {
  235. case SigRotCW:
  236. changePage(PAGE_DESCALING);
  237. break;
  238. case SigRotCCW:
  239. changePage(PAGE_STATS);
  240. break;
  241. }
  242. break;
  243. case PAGE_DESCALING:
  244. if (SigValueEmpty() && mode == MODE_MENU)
  245. pause();
  246. switch (getSigValue(MODE_MENU)) {
  247. case SigRotCW:
  248. changePage(PAGE_EXIT);
  249. break;
  250. case SigRotCCW:
  251. changePage(PAGE_STATS2);
  252. break;
  253. }
  254. break;
  255. case PAGE_EXIT:
  256. if (SigValueEmpty() && mode == MODE_MENU)
  257. pause();
  258. switch (getSigValue(MODE_MENU)) {
  259. case SigInt0Rls:
  260. leaveMenu();
  261. break;
  262. case SigRotCW:
  263. if (state == STATE_HEATING || state == STATE_ERROR
  264. || state == STATE_IDLE
  265. || state == STATE_INITALHEATING) {
  266. changePage(PAGE_SOFTOFF);
  267. } else {
  268. changePage(PAGE_RESTART);
  269. }
  270. break;
  271. case SigRotCCW:
  272. changePage(PAGE_DESCALING);
  273. break;
  274. }
  275. break;
  276. } //end switch (page)
  277. /*
  278. * Hardware FSM
  279. */
  280. switch (state) {
  281. case STATE_NULL:
  282. break;
  283. /*
  284. *
  285. */
  286. case STATE_OFF:
  287. if (mode == MODE_STATE) {
  288. halMachineOff();
  289. writeBackCache();
  290. //TODO this might be a bit confusing to change the page here even if the menu isnt actually displayed
  291. changePage(PAGE_RESTART);
  292. if (SigValueEmpty())
  293. pause();
  294. }
  295. switch (getSigValue(MODE_STATE)) {
  296. case SigInt0Rls:
  297. case SigPowerUp:
  298. //Check waterlevel in gray water tank
  299. //turn machine on
  300. halMachineOn();
  301. coffeeNap(1,0);
  302. if (halIsHeating() && !halProxSensorCovered()) { //check if System starts to heat when turned on
  303. changeState(STATE_INITALHEATING);
  304. } else if (!halIsHeating() && !halProxSensorCovered()) {
  305. changeState(STATE_IDLE);
  306. } else if (halProxSensorCovered()) {
  307. logger_error("Empty Tank please!\n");
  308. changeState(STATE_FULLTANK);
  309. }
  310. if (page != PAGE_SOFTOFF)
  311. changePage(PAGE_SOFTOFF); //the machine is on, the menu starts with the turning off page
  312. break;
  313. case SigRotCCW:
  314. case SigRotCW:
  315. case SigInt1Rls:
  316. //Enter the menu
  317. /* This should be not necessary!
  318. * if (page != PAGE_DEMO)
  319. changePage(PAGE_DEMO); //machine is off, the menu starts with the demo page*/
  320. enterMenu();
  321. break;
  322. }
  323. break;
  324. /*
  325. *
  326. */
  327. case STATE_WAIT_OFF:
  328. if (SigValueEmpty() && mode == MODE_STATE)
  329. pause();
  330. switch (getSigValue(MODE_STATE)) {
  331. case SigPressOpn:
  332. coffeeNap(1,0); //wait so no load will be switched
  333. coffeeIncreaseHeatingTime(halgetHeatingTime());
  334. changeState(STATE_OFF);
  335. break;
  336. case SigInt0Rls:
  337. case SigInt1Rls:
  338. case SigPowerUp:
  339. if (halProxSensorCovered()) {
  340. changeState(STATE_FULLTANK);
  341. } else if (initalHeating) {
  342. changeState(STATE_INITALHEATING);
  343. } else {
  344. changeState(STATE_HEATING);
  345. }
  346. break;
  347. }
  348. break;
  349. /*
  350. *
  351. */
  352. case STATE_INITALHEATING:
  353. initalHeating = true;
  354. if (SigValueEmpty() && mode == MODE_STATE)
  355. pause();
  356. switch (getSigValue(MODE_STATE)) {
  357. // case SigInt0RlsLong:
  358. // //Turn machine off again
  359. // coffeeIncreaseHeatingTime(halgetHeatingTime());
  360. // changeState(STATE_OFF);
  361. // break;
  362. //
  363. // case SigInt0Rls:
  364. // changeState(STATE_WAIT_OFF);
  365. // break;
  366. case SigProxCvrd:
  367. changeState(STATE_FULLTANK);
  368. break;
  369. case SigPressOpn:
  370. //Inital heating finished
  371. initalHeating = false;
  372. coffeeIncreaseHeatingTime(halgetHeatingTime());
  373. changeState(STATE_IDLE);
  374. break;
  375. case SigPowerDown:
  376. changeState(STATE_WAIT_OFF);
  377. break;
  378. case SigRotCCW:
  379. case SigRotCW:
  380. case SigInt1Rls:
  381. enterMenu();
  382. break;
  383. }
  384. break;
  385. /*
  386. *
  387. */
  388. case STATE_HEATING:
  389. if (SigValueEmpty() && mode == MODE_STATE)
  390. pause();
  391. switch (getSigValue(MODE_STATE)) {
  392. // case SigInt1RlsLong:
  393. // //Turn machine _immediately_ off again
  394. // coffeeIncreaseHeatingTime(halgetHeatingTime());
  395. // changeState(STATE_OFF);
  396. // break;
  397. //
  398. // case SigInt1Rls:
  399. // //turn machine off when heating is finished
  400. // changeState(STATE_WAIT_OFF);
  401. // break;
  402. case SigPressOpn:
  403. coffeeIncreaseHeatingTime(halgetHeatingTime());
  404. changeState(STATE_IDLE);
  405. break;
  406. case SigInt0Rls:
  407. //start to brew a delicious coffee
  408. changeState(STATE_BREW);
  409. break;
  410. case SigProxCvrd:
  411. changeState(STATE_FULLTANK);
  412. break;
  413. case SigBrewOn:
  414. //someone brews manually
  415. coffeeManualBrewStart();
  416. changeState(STATE_BREWMANUAL);
  417. break;
  418. case SigPowerDown:
  419. changeState(STATE_WAIT_OFF);
  420. break;
  421. case SigRotCCW:
  422. case SigRotCW:
  423. case SigInt1Rls:
  424. //Enter the menu
  425. enterMenu();
  426. break;
  427. }
  428. break;
  429. /*
  430. *
  431. */
  432. case STATE_IDLE:
  433. if (SigValueEmpty() && mode == MODE_STATE)
  434. pause();
  435. switch (getSigValue(MODE_STATE)) {
  436. // case SigInt1RlsLong:
  437. // //turn machine _immediately_ off
  438. // changeState(STATE_OFF);
  439. // break;
  440. //
  441. // case SigInt1Rls:
  442. // changeState(STATE_OFF);
  443. // break;
  444. case SigPressCls:
  445. changeState(STATE_HEATING);
  446. break;
  447. case SigInt0Rls:
  448. changeState(STATE_BREW);
  449. break;
  450. case SigProxCvrd:
  451. changeState(STATE_FULLTANK);
  452. break;
  453. case SigBrewOn:
  454. //someone brews manually
  455. coffeeManualBrewStart();
  456. changeState(STATE_BREWMANUAL);
  457. break;
  458. case SigPowerDown:
  459. changeState(STATE_OFF);
  460. break;
  461. case SigRotCCW:
  462. case SigRotCW:
  463. case SigInt1Rls:
  464. //Enter the menu
  465. enterMenu();
  466. break;
  467. }
  468. break;
  469. /*
  470. *
  471. */
  472. case STATE_BREW:
  473. //make sure the tank is not full
  474. if (halProxSensorCovered()) {
  475. changeState(STATE_FULLTANK);
  476. logger_error("coffee.cpp: Full tank detection failed..\n");
  477. } else {
  478. coffeeBrew();
  479. checkDescaling();
  480. logger(V_BREW, "Finishing brewing\n");
  481. if (!halProxSensorCovered()) {
  482. if (halIsHeating()) {
  483. changeState(STATE_HEATING);
  484. } else {
  485. changeState(STATE_IDLE);
  486. }
  487. } else {
  488. changeState(STATE_FULLTANK);
  489. }
  490. }
  491. break;
  492. /*
  493. *
  494. */
  495. case STATE_BREWMANUAL:
  496. if (SigValueEmpty() && mode == MODE_STATE)
  497. pause();
  498. switch (getSigValue(MODE_STATE)) {
  499. case SigBrewOff:
  500. if (halIsHeating()) {
  501. changeState(STATE_HEATING);
  502. } else {
  503. changeState(STATE_IDLE);
  504. }
  505. break;
  506. }
  507. break;
  508. /*
  509. *
  510. */
  511. case STATE_CLEANING: //this can only be executed once the machine is hot!
  512. if (!halProxSensorCovered()) {
  513. //execute the cleaning procedure
  514. coffeeClean();
  515. if (halIsHeating()) {
  516. changeState(STATE_HEATING);
  517. } else {
  518. changeState(STATE_IDLE);
  519. }
  520. } else {
  521. changeState(STATE_FULLTANK);
  522. }
  523. break;
  524. /*
  525. * Full tank is detected at the beginning and the end of a brewing process, during
  526. * idle times, initial heating and heating
  527. */
  528. case STATE_FULLTANK:
  529. if (SigValueEmpty() && mode == MODE_STATE)
  530. pause();
  531. switch (getSigValue(MODE_STATE)) {
  532. case SigInt1Rls:
  533. case SigInt0Rls:
  534. if (halIsHeating() && initalHeating) {
  535. changeState(STATE_INITALHEATING);
  536. } else if (halIsHeating() && !initalHeating) {
  537. changeState(STATE_HEATING);
  538. } else {
  539. changeState(STATE_IDLE);
  540. }
  541. break;
  542. }
  543. break;
  544. /*
  545. *
  546. */
  547. case STATE_ERROR:
  548. if (SigValueEmpty() && mode == MODE_STATE)
  549. pause();
  550. switch (getSigValue(MODE_STATE)) {
  551. case SigInt0RlsLong:
  552. if (halIsHeating()) {
  553. coffeeIncreaseHeatingTime(halgetHeatingTime());
  554. }
  555. changeState(STATE_OFF);
  556. break;
  557. }
  558. }
  559. }
  560. pthread_exit(EXIT_SUCCESS);
  561. }
  562. /**
  563. * Handler for the Signal send to this thread
  564. * It saves the type of signal received and tracks the time between a push and release event of up to 4 signals
  565. * The time is stored in the HalEvent variable when a release event is received
  566. * @param signum
  567. * @param siginfo
  568. * @param context
  569. */
  570. void coffeeHandler(int signum, siginfo_t *siginfo, void *context) {
  571. sigval_t sigVal = (siginfo->si_value);
  572. sigValue = sigVal.sival_int;
  573. logger(V_BREW, "coffee.cpp: CoffeeHandler called with Signal %s\n",
  574. SigName[sigValue]);
  575. }
  576. /**
  577. * returns the Signal value from the last received Signal for the given mode and clears the variable
  578. * @return value sent with the last signal
  579. */
  580. int getSigValue(coffee_mode_t mode) {
  581. int tmp = sigValue;
  582. if (mode == MODE_MENU) {
  583. switch (sigValue) {
  584. case SigInt0Psh:
  585. case SigInt0Rls:
  586. case SigInt0RlsLong:
  587. case SigInt1Psh:
  588. case SigInt1Rls:
  589. case SigRotCCW:
  590. case SigRotCW:
  591. sigValue = 0;
  592. menuTimeout = 0;
  593. return tmp;
  594. break;
  595. default:
  596. break;
  597. }
  598. } else { //State Mode
  599. sigValue = 0;
  600. return tmp;
  601. }
  602. //int tmp = sigValue;
  603. //sigValue = 0;
  604. //return tmp;
  605. return 0;
  606. }
  607. bool SigValueEmpty(void) {
  608. if (sigValue == 0)
  609. return true;
  610. else
  611. return false;
  612. }
  613. /**
  614. * Changes the state of the machine to newState
  615. * prints the change to the logger
  616. * @param newState
  617. */
  618. void changeState(coffee_status_t newState) {
  619. logger(V_BREW, "Changing state to %s\n", StateName[newState]);
  620. state = newState;
  621. event_trigger("statechange", &state, sizeof(state));
  622. }
  623. /*
  624. * Change Page to new menu page
  625. */
  626. void changePage(coffee_menuPage_t newPage) {
  627. logger(V_BREW, "Change Page to %s\n", PageName[newPage]);
  628. event_trigger("pagechange", &newPage, sizeof(newPage));
  629. page = newPage;
  630. }
  631. /*
  632. * Changes the mode of the machine to the given mode
  633. */
  634. void changeMode(coffee_mode_t newMode) {
  635. if (newMode == MODE_MENU)
  636. logger(V_BREW, "Changing to menu mode\n");
  637. else
  638. logger(V_BREW, "Changing to state mode\n");
  639. event_trigger("modechange", &newMode, sizeof(newMode));
  640. mode = newMode;
  641. }
  642. /*
  643. * leaving the menu
  644. * sets the start page for the next menu call to softoff
  645. */
  646. void leaveMenu(void) {
  647. logger(V_BREW, "Leaving the menu again\n");
  648. //leave the menu again
  649. changeMode(MODE_STATE);
  650. //change page to initial page
  651. changePage(PAGE_SOFTOFF);
  652. //stop the timeout counter
  653. menuTimeout = 0;
  654. menuTimer.stop();
  655. }
  656. /**
  657. * entering the menu
  658. * starts the timeoutcounter and changes the mode to the Menu Mode
  659. */
  660. void enterMenu(void) {
  661. logger(V_BREW, "Entering the menu\n");
  662. changeMode(MODE_MENU);
  663. menuTimeout = 0;
  664. menuTimer.start();
  665. }
  666. /**
  667. * Returns the current cleaning cycle
  668. */
  669. uint16_t getCurrentCleanCycle(void) {
  670. return currentCleanCycle;
  671. }
  672. /**
  673. * Returns the current state of the FSM
  674. */
  675. coffee_status_t getState(void) {
  676. return state;
  677. }
  678. /**
  679. * Returns the local up-to-date brewcounter
  680. */
  681. uint16_t getBrewCounter(void) {
  682. return brewCounter;
  683. }
  684. /**
  685. * Returns the total heating time in seconds
  686. */
  687. uint64_t getTotalHeatingTime(void) {
  688. return totalHeatingTime;
  689. }
  690. /**
  691. * Returns the value of the brewcounter when the last descaling happened
  692. */
  693. uint16_t getDescBrewCounter (void) {
  694. return descBrewcount;
  695. }
  696. /**
  697. * Returns the raw time stamp when the last descaling happened
  698. */
  699. time_t * getDescTimestamp (void) {
  700. return &descRawTimestamp;
  701. }
  702. /**
  703. * Counter for the menu timeout
  704. * When no input is coming from the user the machine leaves the menu automatically after MENUTIMEOUT seconds
  705. */
  706. void menuTimerHandler(void){
  707. menuTimeout += 200;
  708. if((menuTimeout/1000) >= MENUTIMEOUT) {
  709. leaveMenu();
  710. }
  711. }
  712. /**
  713. * handles program termination
  714. */
  715. void coffeeTerminate(event_t *event) {
  716. logger(V_BREW, "Coffee.cpp: Terminating\n");
  717. //stop brewing
  718. halRelaisOff(RELAIS_PUMP);
  719. writeBackCache();
  720. }
  721. /**
  722. * coffeeNap does almost the same as sleep() but resumes to sleep after a signal got delivered
  723. * sec: seconds to sleep
  724. * nanosec: nanoseconds to sleep
  725. */
  726. void coffeeNap (uint64_t sec, uint64_t nanosec){
  727. struct timespec sleepTime;
  728. clock_gettime(CLOCK_REALTIME, &sleepTime);
  729. sleepTime.tv_sec += sec;
  730. sleepTime.tv_nsec += nanosec;
  731. //overflow
  732. if(sleepTime.tv_nsec >= 1000000000) {
  733. sleepTime.tv_nsec -= 1000000000;
  734. sleepTime.tv_sec++;
  735. }
  736. int errval = 0;
  737. do{
  738. errval = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleepTime, NULL);
  739. }
  740. while(errval == EINTR);
  741. if(errval) logger_error("coffee.cpp: suspending thread failed with error %d\n", errval);
  742. }
  743. /**
  744. * Function to write back the values of the local copies
  745. * brewCounter and totalHeatingTime
  746. *
  747. */
  748. void writeBackCache(void) {
  749. logger(V_BREW, "Writing back cache...\n");
  750. logger(V_BREW, "Writing back brewCounter with %d\n", brewCounter);
  751. if (sqlSetConf(CFGbrewcounter, brewCounter)) {
  752. logger_error("coffee.cpp: Couldn't write brewcounter to database");
  753. return;
  754. }
  755. logger(V_BREW, "Writing back total heating Time with %lld sec\n", totalHeatingTime);
  756. if (sqlSetConf(CFGHeatingTime, totalHeatingTime)) {
  757. logger_error("coffee.cpp: Couldn't write heating time to database");
  758. return;
  759. }
  760. logger(V_BREW, "Writing back descaling brew counter %d\n", descBrewcount);
  761. if (sqlSetConf(CFGDescBrewCount, descBrewcount)) {
  762. logger_error("coffee.cpp: Couldn't write descaling brewcount to database");
  763. return;
  764. }
  765. struct tm * timeinfo;
  766. timeinfo = localtime(&descRawTimestamp);
  767. logger(V_BREW, "Writing back descaling timestamp %d-%d-%d %d:%d\n", timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year+1900, timeinfo->tm_hour, timeinfo->tm_min);
  768. if (sqlSetConf(CFGDescTimestamp, (uint64_t)descRawTimestamp)) {
  769. logger_error("coffee.cpp: Couldn't write descaling timestamp to database");
  770. return;
  771. }
  772. }
  773. /*
  774. * Procedure for cleaning the machine
  775. */
  776. void coffeeClean(void) {
  777. logger(V_BREW, "Cleaning...\n");
  778. for (int i = 0; i < CLEANING_CYCLES; i++) {
  779. currentCleanCycle++;
  780. halPumpOn();
  781. coffeeNap(3,0);
  782. halPumpOff();
  783. coffeeNap(15,0);
  784. }
  785. updateDescaling();
  786. descaling = false;
  787. currentCleanCycle = 0;
  788. event_trigger("descaling", &descaling, sizeof(bool));
  789. }
  790. /*
  791. *
  792. */
  793. void coffeeManualBrewStart (void) {
  794. logger(V_BREW, "Starting manual brewing...\n");
  795. coffeeIncreaseBrewCounter();
  796. }
  797. /**
  798. * Brewing process
  799. */
  800. void coffeeBrew(void) {
  801. coffeeIncreaseBrewCounter();
  802. /*
  803. * Preinfusion
  804. */
  805. logger(V_BREW, "Starting preinfusion...\n");
  806. halPumpOn();
  807. while (halGetFlow() < AMOUNT_PREINFUSION && halGetFlowTime() < TIME_PREINFUSION) {
  808. //TODO don't use coffeeNap here since we don't want to resume to sleep after a signal got caught...
  809. coffeeNap(0, 50000000);
  810. if (getSigValue(MODE_STATE) == SigInt0Rls){
  811. halPumpOff();
  812. return;
  813. }
  814. }
  815. halPumpOff();
  816. /*
  817. * Wait for coffee to soak in infused water
  818. */
  819. brewTimer.start();
  820. while (brewTime < TIME_SOAK) {
  821. coffeeNap(1, 100000000);
  822. if (getSigValue(MODE_STATE) == SigInt0Rls) {
  823. brewTimer.stop();
  824. brewTime = 0;
  825. return;
  826. }
  827. }
  828. brewTimer.stop();
  829. brewTime = 0;
  830. /*
  831. * Brewing the actual espresso
  832. */
  833. logger(V_BREW, "Starting infusion...\n");
  834. halPumpOn();
  835. while (brewTime < TIME_INFUSION && halGetFlow() < AMOUNT_DBLESPRESSO) {
  836. coffeeNap(1, 100000000);
  837. if (getSigValue(MODE_STATE) == SigInt0Rls){
  838. halPumpOff();
  839. return;
  840. }
  841. }
  842. halPumpOff();
  843. return;
  844. }
  845. void brewTimerHandler(){
  846. brewTime += 200;
  847. }
  848. /**
  849. *
  850. */
  851. void coffeeIncreaseBrewCounter(void) {
  852. brewCounter++;
  853. }
  854. /**
  855. *
  856. */
  857. void coffeeIncreaseHeatingTime(uint64_t heatingTime) {
  858. totalHeatingTime += heatingTime;
  859. }
  860. /**
  861. * Checks if the descaling is necessary
  862. * uses descBrewcount and descTimestamp
  863. */
  864. void checkDescaling(){
  865. int16_t dirtyEspresso = checkDirtyEspresso ();
  866. int16_t dirtyTime = checkDirtyTime ();
  867. if(dirtyEspresso <= 0) {
  868. logger(V_BREW, "Descaling necessary due to quantity: %d\n", dirtyEspresso);
  869. descaling = true;
  870. event_trigger("descaling", &descaling, sizeof(bool));
  871. }
  872. if(dirtyTime <= 0) {
  873. logger(V_BREW, "Descaling necessary due to time in days: %d\n", dirtyTime);
  874. descaling = true;
  875. event_trigger("descaling", &descaling, sizeof(bool));
  876. }
  877. }
  878. /**
  879. * this function returns the remaining espressi to be brewed before the descaling event is fired
  880. * returns a positive integer when there are cups remaining
  881. * and a negative when the number of cups are exceeded
  882. * Number of cups after a descaling is defined with DIRTY_ESPRESSO
  883. */
  884. int16_t checkDirtyEspresso (void) {
  885. return descBrewcount + DIRTY_ESPRESSO - brewCounter;
  886. }
  887. /**
  888. * Returns the remaining days before the next descaling event is fired
  889. * returns a positive integer if there is time left and a negative one if the descaling time was exceeded
  890. */
  891. int16_t checkDirtyTime (void) {
  892. time_t rawtime;
  893. time(&rawtime);
  894. double diffseconds = difftime(rawtime, descRawTimestamp);
  895. diffseconds /= 24*60*60; //calculate the days
  896. return DIRTY_TIME - diffseconds;
  897. }
  898. /**
  899. * updates the corresponding variables after a descaling process
  900. */
  901. void updateDescaling(){
  902. descBrewcount = brewCounter;
  903. time_t newDesTimestamp;
  904. time(&newDesTimestamp);
  905. if(newDesTimestamp == -1){
  906. logger(V_BREW, "Whoops, couldn't retrieve new descaling timestamp\n");
  907. }
  908. else {
  909. descRawTimestamp = newDesTimestamp;
  910. }
  911. }