coffee.cpp 20 KB

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