coffee.cpp 22 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  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. case SigInt1Psh:
  305. //Enter the menu
  306. /* This should be not necessary!
  307. * if (page != PAGE_DEMO)
  308. changePage(PAGE_DEMO); //machine is off, the menu starts with the demo page*/
  309. enterMenu();
  310. break;
  311. }
  312. break;
  313. /*
  314. *
  315. */
  316. case STATE_WAIT_OFF:
  317. if (SigValueEmpty() && mode == MODE_STATE)
  318. pause();
  319. switch (getSigValue(MODE_STATE)) {
  320. case SigPressOpn:
  321. coffeeNap(1,0); //wait so no load will be switched
  322. coffeeIncreaseHeatingTime(halgetHeatingTime());
  323. changeState(STATE_OFF);
  324. break;
  325. case SigInt0Psh:
  326. case SigInt1Psh:
  327. case SigPowerUp:
  328. if (halProxSensorCovered()) {
  329. changeState(STATE_FULLTANK);
  330. } else if (initalHeating) {
  331. changeState(STATE_INITALHEATING);
  332. } else {
  333. changeState(STATE_HEATING);
  334. }
  335. break;
  336. }
  337. break;
  338. /*
  339. *
  340. */
  341. case STATE_INITALHEATING:
  342. initalHeating = true;
  343. if (SigValueEmpty() && mode == MODE_STATE)
  344. pause();
  345. switch (getSigValue(MODE_STATE)) {
  346. // case SigInt0RlsLong:
  347. // //Turn machine off again
  348. // coffeeIncreaseHeatingTime(halgetHeatingTime());
  349. // changeState(STATE_OFF);
  350. // break;
  351. //
  352. // case SigInt0Rls:
  353. // changeState(STATE_WAIT_OFF);
  354. // break;
  355. case SigProxCvrd:
  356. changeState(STATE_FULLTANK);
  357. break;
  358. case SigPressOpn:
  359. //Inital heating finished
  360. initalHeating = false;
  361. coffeeIncreaseHeatingTime(halgetHeatingTime());
  362. changeState(STATE_IDLE);
  363. break;
  364. case SigPowerDown:
  365. changeState(STATE_WAIT_OFF);
  366. break;
  367. case SigRotCCW:
  368. case SigRotCW:
  369. case SigInt1Psh:
  370. enterMenu();
  371. break;
  372. }
  373. break;
  374. /*
  375. *
  376. */
  377. case STATE_HEATING:
  378. if (SigValueEmpty() && mode == MODE_STATE)
  379. pause();
  380. switch (getSigValue(MODE_STATE)) {
  381. // case SigInt1RlsLong:
  382. // //Turn machine _immediately_ off again
  383. // coffeeIncreaseHeatingTime(halgetHeatingTime());
  384. // changeState(STATE_OFF);
  385. // break;
  386. //
  387. // case SigInt1Rls:
  388. // //turn machine off when heating is finished
  389. // changeState(STATE_WAIT_OFF);
  390. // break;
  391. case SigPressOpn:
  392. coffeeIncreaseHeatingTime(halgetHeatingTime());
  393. changeState(STATE_IDLE);
  394. break;
  395. case SigInt0Psh:
  396. //start to brew a delicious coffee
  397. changeState(STATE_BREW);
  398. break;
  399. case SigProxCvrd:
  400. changeState(STATE_FULLTANK);
  401. break;
  402. case SigBrewOn:
  403. //someone brews manually
  404. changeState(STATE_BREWMANUAL);
  405. break;
  406. case SigPowerDown:
  407. changeState(STATE_WAIT_OFF);
  408. break;
  409. case SigRotCCW:
  410. case SigRotCW:
  411. case SigInt1Psh:
  412. //Enter the menu
  413. enterMenu();
  414. break;
  415. }
  416. break;
  417. /*
  418. *
  419. */
  420. case STATE_IDLE:
  421. if (SigValueEmpty() && mode == MODE_STATE)
  422. pause();
  423. switch (getSigValue(MODE_STATE)) {
  424. // case SigInt1RlsLong:
  425. // //turn machine _immediately_ off
  426. // changeState(STATE_OFF);
  427. // break;
  428. //
  429. // case SigInt1Rls:
  430. // changeState(STATE_OFF);
  431. // break;
  432. case SigPressCls:
  433. changeState(STATE_HEATING);
  434. break;
  435. case SigInt0Psh:
  436. changeState(STATE_BREW);
  437. break;
  438. case SigProxCvrd:
  439. changeState(STATE_FULLTANK);
  440. break;
  441. case SigBrewOn:
  442. //someone brews manually
  443. changeState(STATE_BREWMANUAL);
  444. break;
  445. case SigPowerDown:
  446. changeState(STATE_OFF);
  447. break;
  448. case SigRotCCW:
  449. case SigRotCW:
  450. case SigInt1Psh:
  451. //Enter the menu
  452. enterMenu();
  453. break;
  454. }
  455. break;
  456. /*
  457. *
  458. */
  459. case STATE_BREW:
  460. //make sure the tank is not full
  461. if (halProxSensorCovered()) {
  462. changeState(STATE_FULLTANK);
  463. logger_error("coffee.cpp: Full tank detection failed..\n");
  464. } else {
  465. coffeeBrew();
  466. checkDescaling();
  467. logger(V_BREW, "Finishing brewing\n");
  468. if (!halProxSensorCovered()) {
  469. if (halIsHeating()) {
  470. changeState(STATE_HEATING);
  471. } else {
  472. changeState(STATE_IDLE);
  473. }
  474. } else {
  475. changeState(STATE_FULLTANK);
  476. }
  477. }
  478. break;
  479. /*
  480. *
  481. */
  482. case STATE_BREWMANUAL:
  483. if (SigValueEmpty() && mode == MODE_STATE)
  484. pause();
  485. break;
  486. /*
  487. *
  488. */
  489. case STATE_CLEANING: //this can only be executed once the machine is hot!
  490. //TODO Show the progress of the cleaning process
  491. if (!halProxSensorCovered()) {
  492. //execute the cleaning procedure
  493. coffeeClean();
  494. if (halIsHeating()) {
  495. changeState(STATE_HEATING);
  496. } else {
  497. changeState(STATE_IDLE);
  498. }
  499. } else {
  500. changeState(STATE_FULLTANK);
  501. }
  502. break;
  503. /*
  504. * Full tank is detected at the beginning and the end of a brewing process, during
  505. * idle times, initial heating and heating
  506. */
  507. case STATE_FULLTANK:
  508. if (SigValueEmpty() && mode == MODE_STATE)
  509. pause();
  510. switch (getSigValue(MODE_STATE)) {
  511. case SigInt1Psh:
  512. case SigInt0Psh:
  513. if (halIsHeating() && initalHeating) {
  514. changeState(STATE_INITALHEATING);
  515. } else if (halIsHeating() && !initalHeating) {
  516. changeState(STATE_HEATING);
  517. } else {
  518. changeState(STATE_IDLE);
  519. }
  520. break;
  521. }
  522. break;
  523. /*
  524. *
  525. */
  526. case STATE_ERROR:
  527. if (SigValueEmpty() && mode == MODE_STATE)
  528. pause();
  529. switch (getSigValue(MODE_STATE)) {
  530. case SigInt1RlsLong:
  531. case SigInt0RlsLong:
  532. if (halIsHeating()) {
  533. coffeeIncreaseHeatingTime(halgetHeatingTime());
  534. }
  535. changeState(STATE_OFF);
  536. break;
  537. }
  538. }
  539. }
  540. pthread_exit(EXIT_SUCCESS);
  541. }
  542. /**
  543. * Handler for the Signal send to this thread
  544. * It saves the type of signal received and tracks the time between a push and release event of up to 4 signals
  545. * The time is stored in the HalEvent variable when a release event is received
  546. * @param signum
  547. * @param siginfo
  548. * @param context
  549. */
  550. void coffeeHandler(int signum, siginfo_t *siginfo, void *context) {
  551. sigval_t sigVal = (siginfo->si_value);
  552. sigValue = sigVal.sival_int;
  553. logger(V_BREW, "coffee.cpp: CoffeeHandler called with Signal %d\n",
  554. sigValue);
  555. }
  556. /**
  557. * returns the Signal value from the last received Signal for the given mode and clears the variable
  558. * @return value sent with the last signal
  559. */
  560. int getSigValue(coffee_mode_t mode) {
  561. int tmp = sigValue;
  562. if (mode == MODE_MENU) {
  563. switch (sigValue) {
  564. case SigInt0Psh:
  565. case SigInt0Rls:
  566. case SigInt0RlsLong:
  567. case SigInt1Psh:
  568. case SigInt1Rls:
  569. case SigInt1RlsLong:
  570. case SigRotCCW:
  571. case SigRotCW:
  572. sigValue = 0;
  573. menuTimeout = 0;
  574. return tmp;
  575. break;
  576. default:
  577. break;
  578. }
  579. } else { //State Mode
  580. sigValue = 0;
  581. return tmp;
  582. }
  583. //int tmp = sigValue;
  584. //sigValue = 0;
  585. //return tmp;
  586. return 0;
  587. }
  588. bool SigValueEmpty(void) {
  589. if (sigValue == 0)
  590. return true;
  591. else
  592. return false;
  593. }
  594. /**
  595. * Changes the state of the machine to newState
  596. * prints the change to the logger
  597. * @param newState
  598. */
  599. void changeState(coffee_status_t newState) {
  600. logger(V_BREW, "Changing state to %s\n", StateName[newState]);
  601. state = newState;
  602. event_trigger("statechange", &state, sizeof(state));
  603. }
  604. /*
  605. * Change Page to new menu page
  606. */
  607. void changePage(coffee_menuPage_t newPage) {
  608. logger(V_BREW, "Change Page to %s\n", PageName[newPage]);
  609. event_trigger("pagechange", &newPage, sizeof(newPage));
  610. page = newPage;
  611. }
  612. /*
  613. * Changes the mode of the machine to the given mode
  614. */
  615. void changeMode(coffee_mode_t newMode) {
  616. if (newMode == MODE_MENU)
  617. logger(V_BREW, "Changing to menu mode\n");
  618. else
  619. logger(V_BREW, "Changing to state mode\n");
  620. event_trigger("modechange", &newMode, sizeof(newMode));
  621. mode = newMode;
  622. }
  623. /*
  624. * leaving the menu
  625. * sets the start page for the next menu call to softoff
  626. */
  627. void leaveMenu(void) {
  628. logger(V_BREW, "Leaving the menu again\n");
  629. //leave the menu again
  630. changeMode(MODE_STATE);
  631. //change page to initial page
  632. changePage(PAGE_SOFTOFF);
  633. //stop the timeout counter
  634. menuTimeout = 0;
  635. menuTimer.stop();
  636. }
  637. /**
  638. * entering the menu
  639. * starts the timeoutcounter and changes the mode to the Menu Mode
  640. */
  641. void enterMenu(void) {
  642. logger(V_BREW, "Entering the menu\n");
  643. changeMode(MODE_MENU);
  644. menuTimeout = 0;
  645. menuTimer.start();
  646. }
  647. /**
  648. * Returns the current state of the FSM
  649. */
  650. coffee_status_t getState(void) {
  651. return state;
  652. }
  653. /**
  654. * Returns the local up-to-date brewcounter
  655. */
  656. uint16_t getBrewCounter(void) {
  657. return brewCounter;
  658. }
  659. /**
  660. * Returns the total heating time in seconds
  661. */
  662. uint64_t getTotalHeatingTime(void) {
  663. return totalHeatingTime;
  664. }
  665. /**
  666. * Returns the value of the brewcounter when the last descaling happened
  667. */
  668. uint16_t getDescBrewCounter (void) {
  669. return descBrewcount;
  670. }
  671. /**
  672. * Returns the raw time stamp when the last descaling happened
  673. */
  674. time_t * getDescTimestamp (void) {
  675. return &descRawTimestamp;
  676. }
  677. /**
  678. * Counter for the brew time
  679. * refresh every 200ms
  680. */
  681. void brewTimeHandler(void) {
  682. brewTime += 200;
  683. }
  684. /**
  685. * Counter for the menu timeout
  686. * When no input is coming from the user the machine leaves the menu automatically after MENUTIMEOUT seconds
  687. */
  688. void menuTimeHandler(void){
  689. menuTimeout += 200;
  690. if((menuTimeout/1000) >= MENUTIMEOUT) {
  691. leaveMenu();
  692. }
  693. }
  694. /**
  695. * handles program termination
  696. */
  697. void coffeeTerminate(event_t *event) {
  698. logger(V_BREW, "Coffee.cpp: Terminating\n");
  699. //stop brewing
  700. halRelaisOff(RELAIS_PUMP);
  701. brewTimer.stop();
  702. writeBackCache();
  703. }
  704. /**
  705. * coffeeNap does almost the same as sleep() but resumes to sleep after a signal got delivered
  706. * sec: seconds to sleep
  707. * nanosec: nanoseconds to sleep
  708. */
  709. void coffeeNap (uint64_t sec, uint64_t nanosec){
  710. struct timespec sleepTime;
  711. clock_gettime(CLOCK_REALTIME, &sleepTime);
  712. sleepTime.tv_sec += sec;
  713. sleepTime.tv_nsec += nanosec;
  714. //overflow
  715. if(sleepTime.tv_nsec >= 1000000000) {
  716. sleepTime.tv_nsec -= 1000000000;
  717. sleepTime.tv_sec++;
  718. }
  719. int errval = 0;
  720. do{
  721. errval = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleepTime, NULL);
  722. }
  723. while(errval == EINTR);
  724. if(errval) logger_error("coffee.cpp: suspending thread failed with error %d\n", errval);
  725. }
  726. /**
  727. * Function to write back the values of the local copies
  728. * brewCounter and totalHeatingTime
  729. *
  730. */
  731. void writeBackCache(void) {
  732. logger(V_BREW, "Writing back cache...\n");
  733. logger(V_BREW, "Writing back brewCounter with %d\n", brewCounter);
  734. if (sqlSetConf(CFGbrewcounter, brewCounter)) {
  735. logger_error("coffee.cpp: Couldn't write brewcounter to database");
  736. return;
  737. }
  738. logger(V_BREW, "Writing back total heating Time with %lld sec\n", totalHeatingTime);
  739. if (sqlSetConf(CFGHeatingTime, totalHeatingTime)) {
  740. logger_error("coffee.cpp: Couldn't write heating time to database");
  741. return;
  742. }
  743. logger(V_BREW, "Writing back descaling brew counter %d\n", descBrewcount);
  744. if (sqlSetConf(CFGDescBrewCount, descBrewcount)) {
  745. logger_error("coffee.cpp: Couldn't write descaling brewcount to database");
  746. return;
  747. }
  748. struct tm * timeinfo;
  749. timeinfo = localtime(&descRawTimestamp);
  750. 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);
  751. if (sqlSetConf(CFGDescTimestamp, (uint64_t)descRawTimestamp)) {
  752. logger_error("coffee.cpp: Couldn't write descaling timestamp to database");
  753. return;
  754. }
  755. }
  756. /*
  757. * Procedure for cleaning the machine
  758. */
  759. void coffeeClean(void) {
  760. logger(V_BREW, "Cleaning...\n");
  761. for (int i = 0; i < 20; i++) {
  762. halPumpOn();
  763. coffeeNap(3,0);
  764. halPumpOff();
  765. coffeeNap(15,0);
  766. }
  767. updateDescaling();
  768. descaling = false;
  769. event_trigger("descaling", &descaling, sizeof(bool));
  770. }
  771. /**
  772. * Brewing process
  773. */
  774. void coffeeBrew(void) {
  775. coffeeIncreaseBrewCounter();
  776. halIncreaseLogCycleCounter();
  777. /*
  778. * Preinfusion
  779. */
  780. logger(V_BREW, "Starting preinfusion...\n");
  781. halResetFlow();
  782. halPumpOn();
  783. brewTime = 0;
  784. brewTimer.start();
  785. while (halGetFlow() < AMOUNT_PREINFUSION && brewTime < TIME_PREINFUSION) {
  786. //TODO don't use coffeeNap here since we don't want to resume to sleep after a signal got caught...
  787. coffeeNap(0, 50000000);
  788. if (getSigValue(MODE_STATE) == SigInt0Psh){
  789. stopBrewing();
  790. return;
  791. }
  792. }
  793. stopBrewing();
  794. /*
  795. * Wait for coffee to soak in infused water
  796. */
  797. brewTimer.start();
  798. while (brewTime < TIME_SOAK) {
  799. coffeeNap(1, 100000000);
  800. if (getSigValue(MODE_STATE) == SigInt0Psh) {
  801. stopBrewing();
  802. return;
  803. }
  804. }
  805. stopBrewing();
  806. /*
  807. * Brewing the actual espresso
  808. */
  809. logger(V_BREW, "Starting infusion...\n");
  810. halPumpOn();
  811. brewTimer.start();
  812. while (brewTime < TIME_INFUSION && halGetFlow() < AMOUNT_DBLESPRESSO) {
  813. coffeeNap(1, 100000000);
  814. if (getSigValue(MODE_STATE) == SigInt0Psh){
  815. stopBrewing();
  816. break;
  817. }
  818. }
  819. stopBrewing();
  820. halIncreaseLogCycleCounter();
  821. return;
  822. //TODO: I want to see the total elapsed brewing time!!
  823. }
  824. /*
  825. * Wrapper function for the end of a brewing process
  826. * this function stops the pump, brewtimer and resets the flow and brew time to zero
  827. */
  828. void stopBrewing() {
  829. halPumpOff();
  830. brewTimer.stop();
  831. brewTime = 0;
  832. halResetFlow();
  833. }
  834. /**
  835. *
  836. */
  837. void coffeeIncreaseBrewCounter(void) {
  838. brewCounter++;
  839. }
  840. /**
  841. *
  842. */
  843. void coffeeIncreaseHeatingTime(uint64_t heatingTime) {
  844. totalHeatingTime += heatingTime;
  845. }
  846. /**
  847. * Checks if the descaling is necessary
  848. * uses descBrewcount and descTimestamp
  849. */
  850. void checkDescaling(){
  851. int16_t dirtyEspresso = checkDirtyEspresso ();
  852. int16_t dirtyTime = checkDirtyTime ();
  853. if(dirtyEspresso <= 0) {
  854. logger(V_BREW, "Descaling necessary due to quantity: %d\n", dirtyEspresso);
  855. descaling = true;
  856. event_trigger("descaling", &descaling, sizeof(bool));
  857. }
  858. if(dirtyTime <= 0) {
  859. logger(V_BREW, "Descaling necessary due to time in days: %d\n", dirtyTime);
  860. descaling = true;
  861. event_trigger("descaling", &descaling, sizeof(bool));
  862. }
  863. }
  864. /**
  865. * this function returns the remaining espressi to be brewed before the descaling event is fired
  866. * returns a positive integer when there are cups remaining
  867. * and a negative when the number of cups are exceeded
  868. * Number of cups after a descaling is defined with DIRTY_ESPRESSO
  869. */
  870. int16_t checkDirtyEspresso (void) {
  871. return descBrewcount + DIRTY_ESPRESSO - brewCounter;
  872. }
  873. /**
  874. * Returns the remaining days before the next descaling event is fired
  875. * returns a positive integer if there is time left and a negative one if the descaling time was exceeded
  876. */
  877. int16_t checkDirtyTime (void) {
  878. time_t rawtime;
  879. time(&rawtime);
  880. double diffseconds = difftime(rawtime, descRawTimestamp);
  881. diffseconds /= 24*60*60; //calculate the days
  882. return DIRTY_TIME - diffseconds;
  883. }
  884. /**
  885. * updates the corresponding variables after a descaling process
  886. */
  887. void updateDescaling(){
  888. descBrewcount = brewCounter;
  889. time_t newDesTimestamp;
  890. time(&newDesTimestamp);
  891. if(newDesTimestamp == -1){
  892. logger(V_BREW, "Whoops, couldn't retrieve new descaling timestamp\n");
  893. }
  894. else {
  895. descRawTimestamp = newDesTimestamp;
  896. }
  897. }