coffee.cpp 22 KB

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