coffee.cpp 22 KB

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