coffee.cpp 20 KB

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