coffee.cpp 23 KB

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