coffee.cpp 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324
  1. /*
  2. * coffee.cpp
  3. *
  4. * Created on: Sep 25, 2017
  5. * Author: Sebastian Vendt
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <errno.h>
  11. #include <wiringPi.h>
  12. #include <pthread.h>
  13. #include <unistd.h>
  14. #include <iostream>
  15. #include <csignal>
  16. #include <time.h>
  17. #include <ctime>
  18. #include "coffee.h"
  19. #include "hal.h"
  20. #include "logger.h"
  21. #include "timer.h"
  22. #include "database.h"
  23. coffee_status_t state;
  24. coffee_menuPage_t page;
  25. coffee_mode_t mode;
  26. int sigValue;
  27. int menuTimeout;
  28. timer menuTimer(&menuTimerHandler);
  29. timer brewTimer(&brewTimerHandler);
  30. uint16_t brewCounter;
  31. uint16_t brewTime;
  32. uint16_t currentCleanCycle;
  33. bool initalHeating;
  34. bool descaling; //flag to indicate descaling and cleaning
  35. uint16_t descBrewcount;
  36. time_t descRawTimestamp;
  37. uint16_t oldCoffeeCFGVal; //temporary variable to save any old config value to restore when aborting the config change
  38. const char* PageName[] = {
  39. "SOFTOFF",
  40. "KILL",
  41. "SINGLEESPRESSO",
  42. "STATS",
  43. "STATS2",
  44. "DESCALING",
  45. "TEMP",
  46. "CLEAN",
  47. "REBOOT",
  48. "SETTINGS",
  49. "RESETKWH",
  50. "RESETKWH_VAL",
  51. "CHANGEPREINF",
  52. "CHANGEPREINF_VAL",
  53. "CHANGEBREWTIME",
  54. "CHANGEBREWTIME_VAL",
  55. "CHANGEBREWML",
  56. "CHANGEBREWML_VAL",
  57. "CHANGESOAKTIME",
  58. "CHANGESOAKTIME_VAL",
  59. "CHANGEAUTORESET",
  60. "CHANGEAUTORESET_VAL",
  61. "LEAVESETTINGS",
  62. "EXIT",
  63. "PAGE_NULL"
  64. };
  65. const char* StateName[] = {
  66. "OFF",
  67. "HEATING",
  68. "INITHEAT",
  69. "IDLE",
  70. "BREW",
  71. "BREWMAN",
  72. "CLEAN",
  73. "ERROR",
  74. "WAITOFF",
  75. "FULLTANK",
  76. "STATE_NULL",
  77. };
  78. //the reference configuration for brewing coffees
  79. coffeeConfig coffeeCFG = {AMOUNT_PREINFUSION, TIME_SOAK, TIME_INFUSION, AMOUNT_DBLESPRESSO};
  80. bool brewSingleEspresso = false; //flag to brew a single espresso only
  81. /**
  82. * Thread for the finite state machine
  83. * It represents the current state of the machine and handles signals coming from
  84. * the pressure control, buttons, the brew switch and the proximity sensor
  85. * @param threadid the ID of the thread
  86. */
  87. void *coffeeThread(void *threadid) {
  88. logger(V_BASIC, "coffee.cpp: Initializing coffee thread...\n");
  89. //installing new Signal handler for coffeethread
  90. struct sigaction action;
  91. sigemptyset(&action.sa_mask);
  92. action.sa_flags = SA_SIGINFO;
  93. action.sa_sigaction = coffeeHandler;
  94. sigaction(SIGUSR2, &action, NULL);
  95. currentCleanCycle = 0;
  96. menuTimer.setDivider(ms2Divider(200));
  97. menuTimer.stop();
  98. menuTimeout = 0;
  99. brewTimer.setDivider(ms2Divider(200));
  100. brewTimer.stop();
  101. brewTime = 0;
  102. initalHeating = true;
  103. mode = MODE_STATE; //Unless we enter the menu we start in state mode
  104. page = PAGE_SOFTOFF;
  105. descaling = false;
  106. if (!(brewCounter = sqlGetConf(CFGbrewcounter))) {
  107. logger_error("coffee.cpp: Couldn't read the brew counter from the database\n");
  108. //pthread_exit(EXIT_SUCCESS);
  109. exit(EXIT_FAILURE);
  110. }
  111. if (!(descRawTimestamp = (time_t) sqlGetConf(CFGDescTimestamp))) {
  112. logger_error("coffee.cpp: Couldn't read the descaling time from the database\n");
  113. //pthread_exit(EXIT_SUCCESS);
  114. exit(EXIT_FAILURE);
  115. }
  116. if (!(descBrewcount = sqlGetConf(CFGDescBrewCount))) {
  117. logger_error("coffee.cpp: Couldn't read the descaling brewcount time from the database\n");
  118. //pthread_exit(EXIT_SUCCESS);
  119. exit(EXIT_FAILURE);
  120. }
  121. if (!(coffeeCFG.amount_espresso = sqlGetConf(CFGAmountDblEspresso))) {
  122. coffeeCFG.amount_espresso = AMOUNT_DBLESPRESSO;
  123. logger_error("coffee.cpp: Couldn't read config CFGAmountDblEspresso from database\nFalling back to default!\n");
  124. }
  125. if (!(coffeeCFG.amount_preinfusion = sqlGetConf(CFGAmountPreinfusion))) {
  126. coffeeCFG.amount_preinfusion = AMOUNT_PREINFUSION;
  127. logger_error("coffee.cpp: Couldn't read config CFGAmountPreinfusion from database\nFalling back to default!\n");
  128. }
  129. if (!(coffeeCFG.time_infusion = sqlGetConf(CFGTimeInfusion))) {
  130. coffeeCFG.time_infusion = TIME_INFUSION;
  131. logger_error("coffee.cpp: Couldn't read config CFGTimeInfusion from database\nFalling back to default!\n");
  132. }
  133. if (!(coffeeCFG.time_soak = sqlGetConf(CFGTimeSoak))) {
  134. coffeeCFG.time_soak = TIME_SOAK;
  135. logger_error("coffee.cpp: Couldn't read config CFGTimeSoak from database\nFalling back to default!\n");
  136. }
  137. checkDescaling();
  138. event_subscribe("terminate", &coffeeTerminate, "coffee.cpp");
  139. logger(V_BREW, "Determining inital state\n");
  140. //determine initial state
  141. if (halGetRelaisState(RELAIS_POWER) && halGetRelaisState(RELAIS_HEAT)
  142. && !halGetRelaisState(RELAIS_PUMP)) {
  143. //wait for heat relais to switch
  144. coffeeNap(1,0);
  145. if (halIsHeating()) { //Heating is on
  146. changeState(STATE_INITALHEATING);
  147. } else {
  148. initalHeating = false;
  149. changeState(STATE_IDLE);
  150. }
  151. } else if (halGetRelaisState(RELAIS_PUMP)) {
  152. logger_error("coffee.cpp: Whoops, why is the pump running...\n");
  153. changeState(STATE_ERROR);
  154. } else {
  155. changeState(STATE_OFF);
  156. }
  157. logger(V_BREW, "Starting Coffee FSM\n");
  158. //begin FSM
  159. while (1) {
  160. /*
  161. * Menu FSM
  162. */
  163. switch (page) {
  164. case PAGE_NULL:
  165. break;
  166. case PAGE_SOFTOFF: //this page is only available when the machine is on
  167. if (SigValueEmpty() && mode == MODE_MENU)
  168. pause();
  169. switch (getSigValue(MODE_MENU)) {
  170. case SigInt0Rls:
  171. if(halIsHeating()){
  172. changeState(STATE_WAIT_OFF);
  173. }
  174. else {
  175. changeState(STATE_OFF);
  176. }
  177. leaveMenu();
  178. break;
  179. case SigRotCW:
  180. changePage(PAGE_KILL);
  181. break;
  182. case SigRotCCW:
  183. changePage(PAGE_EXIT);
  184. break;
  185. }
  186. break;
  187. case PAGE_KILL: //this page is only available when the machine is on
  188. if (SigValueEmpty() && mode == MODE_MENU)
  189. pause();
  190. switch (getSigValue(MODE_MENU)) {
  191. case SigInt0Rls:
  192. changeState(STATE_OFF);
  193. leaveMenu();
  194. break;
  195. case SigRotCW:
  196. if (state == STATE_HEATING || state == STATE_IDLE) {
  197. changePage(PAGE_SINGLEESPRESSO);
  198. } else {
  199. changePage(PAGE_REBOOT);
  200. }
  201. break;
  202. case SigRotCCW:
  203. changePage(PAGE_SOFTOFF);
  204. break;
  205. }
  206. break;
  207. case PAGE_SINGLEESPRESSO: //this page is only available when the machine is hot
  208. if (SigValueEmpty() && mode == MODE_MENU)
  209. pause();
  210. switch (getSigValue(MODE_MENU)) {
  211. case SigInt0Rls:
  212. brewSingleEspresso = true;
  213. changeMode(MODE_STATE);
  214. changeState(STATE_BREW);
  215. leaveMenu();
  216. break;
  217. case SigRotCW:
  218. changePage(PAGE_CLEAN);
  219. break;
  220. case SigRotCCW:
  221. changePage(PAGE_KILL);
  222. break;
  223. }
  224. break;
  225. case PAGE_CLEAN: //this page is only be available when the machine is hot
  226. if (SigValueEmpty() && mode == MODE_MENU)
  227. pause();
  228. switch (getSigValue(MODE_MENU)) {
  229. case SigInt0Rls:
  230. changeMode(MODE_STATE);
  231. changeState(STATE_CLEANING);
  232. leaveMenu();
  233. break;
  234. case SigRotCW:
  235. changePage(PAGE_REBOOT);
  236. break;
  237. case SigRotCCW:
  238. changePage(PAGE_SINGLEESPRESSO);
  239. break;
  240. }
  241. break;
  242. case PAGE_REBOOT:
  243. if (SigValueEmpty() && mode == MODE_MENU)
  244. pause();
  245. switch (getSigValue(MODE_MENU)) {
  246. case SigInt0Rls:
  247. event_trigger("terminate");
  248. coffeeNap(4, 0);
  249. system("reboot");
  250. //prevent any further user action
  251. coffeeNap(30, 0);
  252. break;
  253. case SigRotCW:
  254. changePage(PAGE_TEMP);
  255. break;
  256. case SigRotCCW:
  257. if(state == STATE_IDLE || state == STATE_HEATING){
  258. changePage(PAGE_CLEAN);
  259. } else if (state == STATE_ERROR || state == STATE_INITALHEATING) {
  260. changePage(PAGE_KILL);
  261. } else {
  262. changePage(PAGE_EXIT);
  263. }
  264. break;
  265. }
  266. break;
  267. case PAGE_TEMP:
  268. if (SigValueEmpty() && mode == MODE_MENU)
  269. pause();
  270. switch (getSigValue(MODE_MENU)) {
  271. case SigRotCW:
  272. changePage(PAGE_STATS);
  273. break;
  274. case SigRotCCW:
  275. changePage(PAGE_REBOOT);
  276. break;
  277. }
  278. break;
  279. case PAGE_STATS:
  280. if (SigValueEmpty() && mode == MODE_MENU)
  281. pause();
  282. switch (getSigValue(MODE_MENU)) {
  283. case SigRotCW:
  284. changePage(PAGE_STATS2);
  285. break;
  286. case SigRotCCW:
  287. changePage(PAGE_TEMP);
  288. break;
  289. }
  290. break;
  291. case PAGE_STATS2:
  292. if (SigValueEmpty() && mode == MODE_MENU)
  293. pause();
  294. switch (getSigValue(MODE_MENU)) {
  295. case SigRotCW:
  296. changePage(PAGE_DESCALING);
  297. break;
  298. case SigRotCCW:
  299. changePage(PAGE_STATS);
  300. break;
  301. }
  302. break;
  303. case PAGE_DESCALING:
  304. if (SigValueEmpty() && mode == MODE_MENU)
  305. pause();
  306. switch (getSigValue(MODE_MENU)) {
  307. case SigRotCW:
  308. changePage(PAGE_SETTINGS);
  309. break;
  310. case SigRotCCW:
  311. changePage(PAGE_STATS2);
  312. break;
  313. }
  314. break;
  315. case PAGE_SETTINGS:
  316. if (SigValueEmpty() && mode == MODE_MENU)
  317. pause();
  318. switch (getSigValue(MODE_MENU)) {
  319. case SigInt0Rls:
  320. changePage(PAGE_RESETKWH);
  321. break;
  322. case SigRotCW:
  323. changePage(PAGE_EXIT);
  324. break;
  325. case SigRotCCW:
  326. changePage(PAGE_DESCALING);
  327. break;
  328. }
  329. break;
  330. case PAGE_RESETKWH:
  331. if (SigValueEmpty() && mode == MODE_MENU)
  332. pause();
  333. switch (getSigValue(MODE_MENU)) {
  334. case SigInt0Rls:
  335. changePage(PAGE_RESETKWH_VAL);
  336. break;
  337. case SigRotCW:
  338. changePage(PAGE_CHANGEPREINF);
  339. break;
  340. case SigRotCCW:
  341. changePage(PAGE_LEAVESETTINGS);
  342. break;
  343. }
  344. break;
  345. case PAGE_RESETKWH_VAL:
  346. if (SigValueEmpty() && mode == MODE_MENU)
  347. pause();
  348. switch (getSigValue(MODE_MENU)) {
  349. case SigInt0Rls:
  350. //Reset the KWH counter
  351. halSetTotalHeatingTime(0);
  352. halWriteBackCache();
  353. changePage(PAGE_RESETKWH);
  354. break;
  355. case SigInt1Rls:
  356. //Abort reseting the KWH counter
  357. changePage(PAGE_RESETKWH);
  358. break;
  359. case SigRotCW:
  360. break;
  361. case SigRotCCW:
  362. break;
  363. }
  364. break;
  365. case PAGE_CHANGEPREINF:
  366. if (SigValueEmpty() && mode == MODE_MENU)
  367. pause();
  368. switch (getSigValue(MODE_MENU)) {
  369. case SigInt0Rls:
  370. //temporary store the old value of the config to restore when the change process is aborted
  371. oldCoffeeCFGVal = coffeeCFG.amount_preinfusion;
  372. changePage(PAGE_CHANGEPREINF_VAL);
  373. break;
  374. case SigRotCW:
  375. changePage(PAGE_CHANGEBREWTIME);
  376. break;
  377. case SigRotCCW:
  378. changePage(PAGE_RESETKWH);
  379. break;
  380. }
  381. break;
  382. case PAGE_CHANGEPREINF_VAL:
  383. if (SigValueEmpty() && mode == MODE_MENU)
  384. pause();
  385. switch (getSigValue(MODE_MENU)) {
  386. case SigInt0Rls:
  387. writeBackCache();
  388. oldCoffeeCFGVal = 0;
  389. changePage(PAGE_CHANGEPREINF);
  390. break;
  391. case SigInt1Rls:
  392. //Abort the change of the preinfusion
  393. //restore the previous value
  394. coffeeCFG.amount_preinfusion = oldCoffeeCFGVal;
  395. oldCoffeeCFGVal = 0;
  396. changePage(PAGE_CHANGEPREINF);
  397. break;
  398. case SigRotCW:
  399. coffeeCFG.amount_preinfusion += 1;
  400. break;
  401. case SigRotCCW:
  402. if (coffeeCFG.amount_preinfusion >= 1) coffeeCFG.amount_preinfusion -= 1;
  403. break;
  404. }
  405. break;
  406. case PAGE_CHANGEBREWTIME:
  407. if (SigValueEmpty() && mode == MODE_MENU)
  408. pause();
  409. switch (getSigValue(MODE_MENU)) {
  410. case SigInt0Rls:
  411. oldCoffeeCFGVal = coffeeCFG.time_infusion;
  412. changePage(PAGE_CHANGEBREWTIME_VAL);
  413. break;
  414. case SigRotCW:
  415. changePage(PAGE_CHANGEBREWML);
  416. break;
  417. case SigRotCCW:
  418. changePage(PAGE_CHANGEPREINF);
  419. break;
  420. }
  421. break;
  422. case PAGE_CHANGEBREWTIME_VAL:
  423. if (SigValueEmpty() && mode == MODE_MENU)
  424. pause();
  425. switch (getSigValue(MODE_MENU)) {
  426. case SigInt0Rls:
  427. writeBackCache();
  428. oldCoffeeCFGVal = 0;
  429. changePage(PAGE_CHANGEBREWTIME);
  430. break;
  431. case SigInt1Rls:
  432. //abort the change of the brew time
  433. coffeeCFG.time_infusion = oldCoffeeCFGVal;
  434. oldCoffeeCFGVal = 0;
  435. changePage(PAGE_CHANGEBREWTIME);
  436. break;
  437. case SigRotCW:
  438. coffeeCFG.time_infusion += 1000;
  439. break;
  440. case SigRotCCW:
  441. if (coffeeCFG.time_infusion >= 1000) coffeeCFG.time_infusion -= 1000;
  442. break;
  443. }
  444. break;
  445. case PAGE_CHANGEBREWML:
  446. if (SigValueEmpty() && mode == MODE_MENU)
  447. pause();
  448. switch (getSigValue(MODE_MENU)) {
  449. case SigInt0Rls:
  450. oldCoffeeCFGVal = coffeeCFG.amount_espresso;
  451. changePage(PAGE_CHANGEBREWML_VAL);
  452. break;
  453. case SigRotCW:
  454. changePage(PAGE_CHANGESOAKTIME);
  455. break;
  456. case SigRotCCW:
  457. changePage(PAGE_CHANGEBREWTIME);
  458. break;
  459. }
  460. break;
  461. case PAGE_CHANGEBREWML_VAL:
  462. if (SigValueEmpty() && mode == MODE_MENU)
  463. pause();
  464. switch (getSigValue(MODE_MENU)) {
  465. case SigInt0Rls:
  466. writeBackCache();
  467. oldCoffeeCFGVal = 0;
  468. changePage(PAGE_CHANGEBREWML);
  469. break;
  470. case SigInt1Rls:
  471. //abort the change of the brew amout
  472. coffeeCFG.amount_espresso = oldCoffeeCFGVal;
  473. oldCoffeeCFGVal = 0;
  474. changePage(PAGE_CHANGEBREWML);
  475. break;
  476. case SigRotCW:
  477. coffeeCFG.amount_espresso += 1;
  478. break;
  479. case SigRotCCW:
  480. if (coffeeCFG.amount_espresso >= 1) coffeeCFG.amount_espresso -= 1;
  481. break;
  482. }
  483. break;
  484. case PAGE_CHANGESOAKTIME:
  485. if (SigValueEmpty() && mode == MODE_MENU)
  486. pause();
  487. switch (getSigValue(MODE_MENU)) {
  488. case SigInt0Rls:
  489. oldCoffeeCFGVal = coffeeCFG.time_soak;
  490. changePage(PAGE_CHANGESOAKTIME_VAL);
  491. break;
  492. case SigRotCW:
  493. changePage(PAGE_CHANGEAUTORESET);
  494. break;
  495. case SigRotCCW:
  496. changePage(PAGE_CHANGEBREWML);
  497. break;
  498. }
  499. break;
  500. case PAGE_CHANGESOAKTIME_VAL:
  501. if (SigValueEmpty() && mode == MODE_MENU)
  502. pause();
  503. switch (getSigValue(MODE_MENU)) {
  504. case SigInt0Rls:
  505. writeBackCache();
  506. oldCoffeeCFGVal = 0;
  507. changePage(PAGE_CHANGESOAKTIME);
  508. break;
  509. case SigInt1Rls:
  510. //abort the change of the soak time
  511. coffeeCFG.time_soak = oldCoffeeCFGVal;
  512. oldCoffeeCFGVal = 0;
  513. changePage(PAGE_CHANGESOAKTIME);
  514. break;
  515. case SigRotCW:
  516. coffeeCFG.time_soak += 100;
  517. break;
  518. case SigRotCCW:
  519. if (coffeeCFG.time_soak >= 100) coffeeCFG.time_soak -= 100;
  520. break;
  521. }
  522. break;
  523. case PAGE_CHANGEAUTORESET:
  524. if (SigValueEmpty() && mode == MODE_MENU)
  525. pause();
  526. switch (getSigValue(MODE_MENU)) {
  527. case SigInt0Rls:
  528. oldCoffeeCFGVal = halGetFlowResetTime();
  529. changePage(PAGE_CHANGEAUTORESET_VAL);
  530. break;
  531. case SigRotCW:
  532. changePage(PAGE_LEAVESETTINGS);
  533. break;
  534. case SigRotCCW:
  535. changePage(PAGE_CHANGESOAKTIME);
  536. break;
  537. }
  538. break;
  539. case PAGE_CHANGEAUTORESET_VAL:
  540. if (SigValueEmpty() && mode == MODE_MENU)
  541. pause();
  542. switch (getSigValue(MODE_MENU)) {
  543. case SigInt0Rls:
  544. halWriteBackCache();
  545. oldCoffeeCFGVal = 0;
  546. changePage(PAGE_CHANGEAUTORESET);
  547. break;
  548. case SigInt1Rls:
  549. //abort the change of the auto reset
  550. halSetFlowResetTime(oldCoffeeCFGVal);
  551. oldCoffeeCFGVal = 0;
  552. changePage(PAGE_CHANGEAUTORESET);
  553. break;
  554. case SigRotCW:
  555. halSetFlowResetTime(halGetFlowResetTime() + 100);
  556. break;
  557. case SigRotCCW:
  558. if (halGetFlowResetTime() >= 100) halSetFlowResetTime(halGetFlowResetTime() - 100);
  559. break;
  560. }
  561. break;
  562. case PAGE_LEAVESETTINGS:
  563. if (SigValueEmpty() && mode == MODE_MENU)
  564. pause();
  565. switch (getSigValue(MODE_MENU)) {
  566. case SigInt0Rls:
  567. changePage(PAGE_SETTINGS);
  568. break;
  569. case SigRotCW:
  570. changePage(PAGE_RESETKWH);
  571. break;
  572. case SigRotCCW:
  573. changePage(PAGE_CHANGESOAKTIME);
  574. break;
  575. }
  576. break;
  577. case PAGE_EXIT:
  578. if (SigValueEmpty() && mode == MODE_MENU)
  579. pause();
  580. switch (getSigValue(MODE_MENU)) {
  581. case SigInt0Rls:
  582. leaveMenu();
  583. break;
  584. case SigRotCW:
  585. if (state == STATE_HEATING || state == STATE_ERROR
  586. || state == STATE_IDLE
  587. || state == STATE_INITALHEATING) {
  588. changePage(PAGE_SOFTOFF);
  589. } else {
  590. changePage(PAGE_REBOOT);
  591. }
  592. break;
  593. case SigRotCCW:
  594. changePage(PAGE_SETTINGS);
  595. break;
  596. }
  597. break;
  598. } //end switch (page)
  599. /*
  600. * Hardware FSM
  601. */
  602. switch (state) {
  603. case STATE_NULL:
  604. break;
  605. /*
  606. *
  607. */
  608. case STATE_OFF:
  609. if (mode == MODE_STATE) {
  610. halMachineOff();
  611. writeBackCache();
  612. // his might be a bit confusing to change the page here even if the menu isn't actually displayed
  613. changePage(PAGE_REBOOT);
  614. if (SigValueEmpty())
  615. pause();
  616. }
  617. switch (getSigValue(MODE_STATE)) {
  618. case SigInt0Rls:
  619. case SigPowerUp:
  620. //Check waterlevel in gray water tank
  621. //turn machine on
  622. halMachineOn();
  623. coffeeNap(1,500000000);
  624. if (halIsHeating() && !halProxSensorCovered()) { //check if System starts to heat when turned on
  625. changeState(STATE_INITALHEATING);
  626. } else if (!halIsHeating() && !halProxSensorCovered()) {
  627. changeState(STATE_IDLE);
  628. } else if (halProxSensorCovered()) {
  629. logger_error("coffee.cpp: Full graywater tank detected\n");
  630. changeState(STATE_FULLTANK);
  631. }
  632. if (page != PAGE_SOFTOFF)
  633. changePage(PAGE_SOFTOFF); //the machine is on, the menu starts with the turning off page
  634. break;
  635. case SigRotCCW:
  636. case SigRotCW:
  637. case SigInt1Rls:
  638. //Enter the menu
  639. enterMenu();
  640. break;
  641. }
  642. break;
  643. /*
  644. *
  645. */
  646. case STATE_WAIT_OFF:
  647. if (SigValueEmpty() && mode == MODE_STATE)
  648. pause();
  649. switch (getSigValue(MODE_STATE)) {
  650. case SigPressOpn:
  651. coffeeNap(1,0); //wait so no load will be switched
  652. changeState(STATE_OFF);
  653. break;
  654. case SigInt0Rls:
  655. case SigInt1Rls:
  656. case SigPowerUp:
  657. if (halProxSensorCovered()) {
  658. changeState(STATE_FULLTANK);
  659. } else if (initalHeating) {
  660. changeState(STATE_INITALHEATING);
  661. } else {
  662. changeState(STATE_HEATING);
  663. }
  664. break;
  665. }
  666. break;
  667. /*
  668. *
  669. */
  670. case STATE_INITALHEATING:
  671. initalHeating = true;
  672. if (SigValueEmpty() && mode == MODE_STATE)
  673. pause();
  674. switch (getSigValue(MODE_STATE)) {
  675. case SigPressOpn:
  676. //Inital heating finished
  677. initalHeating = false;
  678. changeState(STATE_IDLE);
  679. break;
  680. case SigPowerDown:
  681. changeState(STATE_WAIT_OFF);
  682. break;
  683. case SigRotCCW:
  684. case SigRotCW:
  685. case SigInt1Rls:
  686. enterMenu();
  687. break;
  688. }
  689. break;
  690. /*
  691. *
  692. */
  693. case STATE_HEATING:
  694. if (SigValueEmpty() && mode == MODE_STATE)
  695. pause();
  696. switch (getSigValue(MODE_STATE)) {
  697. case SigPressOpn:
  698. changeState(STATE_IDLE);
  699. break;
  700. case SigInt0Rls:
  701. //start to brew a delicious coffee
  702. changeState(STATE_BREW);
  703. break;
  704. case SigBrewOn:
  705. //someone brews manually
  706. coffeeManualBrewStart();
  707. changeState(STATE_BREWMANUAL);
  708. break;
  709. case SigPowerDown:
  710. changeState(STATE_WAIT_OFF);
  711. break;
  712. case SigRotCCW:
  713. case SigRotCW:
  714. case SigInt1Rls:
  715. //Enter the menu
  716. enterMenu();
  717. break;
  718. }
  719. break;
  720. /*
  721. *
  722. */
  723. case STATE_IDLE:
  724. if (SigValueEmpty() && mode == MODE_STATE)
  725. pause();
  726. switch (getSigValue(MODE_STATE)) {
  727. case SigPressCls:
  728. changeState(STATE_HEATING);
  729. break;
  730. case SigInt0Rls:
  731. changeState(STATE_BREW);
  732. break;
  733. case SigBrewOn:
  734. //someone brews manually
  735. coffeeManualBrewStart();
  736. changeState(STATE_BREWMANUAL);
  737. break;
  738. case SigPowerDown:
  739. changeState(STATE_OFF);
  740. break;
  741. case SigRotCCW:
  742. case SigRotCW:
  743. case SigInt1Rls:
  744. //Enter the menu
  745. enterMenu();
  746. break;
  747. }
  748. break;
  749. /*
  750. *
  751. */
  752. case STATE_BREW:
  753. //make sure the tank is not full
  754. if (halProxSensorCovered()) {
  755. changeState(STATE_FULLTANK);
  756. } else {
  757. if (brewSingleEspresso) {
  758. coffeeBrew(coffeeCFG.amount_preinfusion, coffeeCFG.time_soak, coffeeCFG.time_infusion, coffeeCFG.amount_espresso / 2);
  759. brewSingleEspresso = false;
  760. }
  761. else {
  762. coffeeBrew(coffeeCFG.amount_preinfusion, coffeeCFG.time_soak, coffeeCFG.time_infusion, coffeeCFG.amount_espresso);
  763. }
  764. checkDescaling();
  765. logger(V_BREW, "coffee.cpp: Finished brewing\n");
  766. if (halProxSensorCovered()) {
  767. changeState(STATE_FULLTANK);
  768. } else if (halIsHeating()) {
  769. changeState(STATE_HEATING);
  770. } else {
  771. changeState(STATE_IDLE);
  772. }
  773. }
  774. break;
  775. /*
  776. *
  777. */
  778. case STATE_BREWMANUAL:
  779. if (SigValueEmpty() && mode == MODE_STATE)
  780. pause();
  781. switch (getSigValue(MODE_STATE)) {
  782. case SigBrewOff:
  783. if (halProxSensorCovered()) {
  784. changeState(STATE_FULLTANK);
  785. } else if (halIsHeating()) {
  786. changeState(STATE_HEATING);
  787. } else {
  788. changeState(STATE_IDLE);
  789. }
  790. break;
  791. }
  792. break;
  793. /*
  794. *
  795. */
  796. case STATE_CLEANING: //this can only be executed once the machine is hot!
  797. if (!halProxSensorCovered()) {
  798. //execute the cleaning procedure
  799. coffeeClean();
  800. if (halProxSensorCovered()) {
  801. changeState(STATE_FULLTANK);
  802. } else if (halIsHeating()) {
  803. changeState(STATE_HEATING);
  804. } else {
  805. changeState(STATE_IDLE);
  806. }
  807. } else {
  808. changeState(STATE_FULLTANK);
  809. }
  810. break;
  811. /*
  812. * Full tank is detected at the beginning and the end of a brewing process, during
  813. * idle times, initial heating and heating
  814. */
  815. case STATE_FULLTANK:
  816. if (SigValueEmpty() && mode == MODE_STATE)
  817. pause();
  818. switch (getSigValue(MODE_STATE)) {
  819. case SigInt0RlsLong:
  820. if (!halProxSensorCovered()) {
  821. if (halIsHeating() && initalHeating) {
  822. changeState(STATE_INITALHEATING);
  823. } else if (halIsHeating() && !initalHeating) {
  824. changeState(STATE_HEATING);
  825. } else {
  826. changeState(STATE_IDLE);
  827. }
  828. }
  829. break;
  830. }
  831. break;
  832. /*
  833. *
  834. */
  835. case STATE_ERROR:
  836. if (SigValueEmpty() && mode == MODE_STATE)
  837. pause();
  838. switch (getSigValue(MODE_STATE)) {
  839. case SigInt0RlsLong:
  840. changeState(STATE_OFF);
  841. break;
  842. }
  843. }
  844. }
  845. pthread_exit(EXIT_SUCCESS);
  846. }
  847. /**
  848. * Handler for the Signal send to this thread
  849. * It saves the type of signal received and tracks the time between a push and release event of up to 4 signals
  850. * The time is stored in the HalEvent variable when a release event is received
  851. * @param signum
  852. * @param siginfo
  853. * @param context
  854. */
  855. void coffeeHandler(int signum, siginfo_t *siginfo, void *context) {
  856. sigval_t sigVal = (siginfo->si_value);
  857. sigValue = sigVal.sival_int;
  858. logger(V_BREW, "coffee.cpp: CoffeeHandler called with Signal %s\n",
  859. SigName[sigValue]);
  860. }
  861. /**
  862. * returns the Signal value from the last received Signal for the given mode and clears the variable
  863. * @return value sent with the last signal
  864. */
  865. int getSigValue(coffee_mode_t mode) {
  866. int tmp = sigValue;
  867. if (mode == MODE_MENU) {
  868. switch (sigValue) {
  869. case SigInt0Psh:
  870. case SigInt0Rls:
  871. case SigInt0RlsLong:
  872. case SigInt1Psh:
  873. case SigInt1Rls:
  874. case SigRotCCW:
  875. case SigRotCW:
  876. sigValue = 0;
  877. menuTimeout = 0;
  878. return tmp;
  879. break;
  880. default:
  881. break;
  882. }
  883. } else { //State Mode
  884. sigValue = 0;
  885. return tmp;
  886. }
  887. //int tmp = sigValue;
  888. //sigValue = 0;
  889. //return tmp;
  890. return 0;
  891. }
  892. bool SigValueEmpty(void) {
  893. if (sigValue == 0)
  894. return true;
  895. else
  896. return false;
  897. }
  898. /**
  899. * Changes the state of the machine to newState
  900. * prints the change to the logger
  901. * @param newState
  902. */
  903. void changeState(coffee_status_t newState) {
  904. logger(V_BREW, "coffee.cpp: Changing state to %s\n", StateName[newState]);
  905. state = newState;
  906. event_trigger("statechange", &state, sizeof(state));
  907. }
  908. /*
  909. * Change Page to new menu page
  910. */
  911. void changePage(coffee_menuPage_t newPage) {
  912. logger(V_BREW, "coffee.cpp: Change Page to %s\n", PageName[newPage]);
  913. event_trigger("pagechange", &newPage, sizeof(newPage));
  914. page = newPage;
  915. }
  916. /*
  917. * Changes the mode of the machine to the given mode
  918. */
  919. void changeMode(coffee_mode_t newMode) {
  920. if (newMode == MODE_MENU)
  921. logger(V_BREW, "coffee.cpp: Changing to menu mode\n");
  922. else
  923. logger(V_BREW, "coffee.cpp: Changing to state mode\n");
  924. event_trigger("modechange", &newMode, sizeof(newMode));
  925. mode = newMode;
  926. }
  927. /*
  928. * leaving the menu
  929. * sets the start page for the next menu call to softoff
  930. */
  931. void leaveMenu(void) {
  932. logger(V_BREW, "coffee.cpp: Leaving the menu again\n");
  933. //leave the menu again
  934. changeMode(MODE_STATE);
  935. //change page to initial page
  936. changePage(PAGE_SOFTOFF);
  937. //stop the timeout counter
  938. menuTimeout = 0;
  939. menuTimer.stop();
  940. }
  941. /**
  942. * entering the menu
  943. * starts the timeoutcounter and changes the mode to the Menu Mode
  944. */
  945. void enterMenu(void) {
  946. logger(V_BREW, "coffee.cpp: Entering the menu\n");
  947. changeMode(MODE_MENU);
  948. menuTimeout = 0;
  949. menuTimer.start();
  950. }
  951. /**
  952. * Returns the current cleaning cycle
  953. */
  954. uint16_t getCurrentCleanCycle(void) {
  955. return currentCleanCycle;
  956. }
  957. /**
  958. * Returns the current state of the FSM
  959. */
  960. coffee_status_t getState(void) {
  961. return state;
  962. }
  963. /**
  964. * Returns the local up-to-date brewcounter
  965. */
  966. uint16_t getBrewCounter(void) {
  967. return brewCounter;
  968. }
  969. /**
  970. * Returns the value of the brewcounter when the last descaling happened
  971. */
  972. uint16_t getDescBrewCounter (void) {
  973. return descBrewcount;
  974. }
  975. /**
  976. * Returns the raw time stamp when the last descaling happened
  977. */
  978. time_t * getDescTimestamp (void) {
  979. return &descRawTimestamp;
  980. }
  981. /**
  982. * Returns a pointer to the current coffee config
  983. */
  984. coffeeConfig* getCoffeeCfg (void) {
  985. return &coffeeCFG;
  986. }
  987. /**
  988. * Counter for the menu timeout
  989. * When no input is coming from the user the machine leaves the menu automatically after MENUTIMEOUT seconds
  990. */
  991. void menuTimerHandler(void){
  992. menuTimeout += 200;
  993. if((menuTimeout/1000) >= MENUTIMEOUT) {
  994. leaveMenu();
  995. }
  996. }
  997. /**
  998. * handles program termination
  999. */
  1000. void coffeeTerminate(event_t *event) {
  1001. logger(V_BREW, "Coffee.cpp: Terminating\n");
  1002. //stop brewing
  1003. halRelaisOff(RELAIS_PUMP);
  1004. writeBackCache();
  1005. }
  1006. /**
  1007. * coffeeNap does almost the same as sleep() but resumes to sleep after a signal got delivered
  1008. * sec: seconds to sleep
  1009. * nanosec: nanoseconds to sleep
  1010. */
  1011. void coffeeNap (uint64_t sec, uint64_t nanosec){
  1012. struct timespec sleepTime;
  1013. clock_gettime(CLOCK_REALTIME, &sleepTime);
  1014. sleepTime.tv_sec += sec;
  1015. sleepTime.tv_nsec += nanosec;
  1016. //overflow
  1017. if(sleepTime.tv_nsec >= 1000000000) {
  1018. sleepTime.tv_nsec -= 1000000000;
  1019. sleepTime.tv_sec++;
  1020. }
  1021. int errval = 0;
  1022. do{
  1023. errval = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleepTime, NULL);
  1024. }
  1025. while(errval == EINTR);
  1026. if(errval) logger_error("coffee.cpp: Suspending thread failed with error %d\n", errval);
  1027. }
  1028. /**
  1029. * Function to write back the values of the local copies
  1030. * brewCounter and totalHeatingTime
  1031. *
  1032. */
  1033. void writeBackCache(void) {
  1034. logger(V_BREW, "Writing back cache...\n");
  1035. logger(V_BREW, "Writing back brewCounter: %d\n", brewCounter);
  1036. if (sqlSetConf(CFGbrewcounter, brewCounter)) {
  1037. logger_error("coffee.cpp: Couldn't write brewcounter to database!\n");
  1038. }
  1039. logger(V_BREW, "Writing back descaling brew counter: %d\n", descBrewcount);
  1040. if (sqlSetConf(CFGDescBrewCount, descBrewcount)) {
  1041. logger_error("coffee.cpp: Couldn't write descaling brewcount to database!\n");
  1042. }
  1043. struct tm * timeinfo;
  1044. timeinfo = localtime(&descRawTimestamp);
  1045. 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);
  1046. if (sqlSetConf(CFGDescTimestamp, (uint64_t)descRawTimestamp)) {
  1047. logger_error("coffee.cpp: Couldn't write descaling timestamp to database!\n");
  1048. }
  1049. logger(V_BREW, "Writing back CFGAmountPreinfusion: %d\n", coffeeCFG.amount_preinfusion);
  1050. if (sqlSetConf(CFGAmountPreinfusion, coffeeCFG.amount_preinfusion)) {
  1051. logger_error("coffee.cpp: Couldn't write CFGAmountPreinfusion to database!\n");
  1052. }
  1053. logger(V_BREW, "Writing back CFGTimeSoak: %d\n", coffeeCFG.time_soak);
  1054. if (sqlSetConf(CFGTimeSoak, coffeeCFG.time_soak)) {
  1055. logger_error("coffee.cpp: Couldn't write CFGTimeSoak to database!\n");
  1056. }
  1057. logger(V_BREW, "Writing back CFGTimeInfusion: %d\n", coffeeCFG.time_infusion);
  1058. if (sqlSetConf(CFGTimeInfusion, coffeeCFG.time_infusion)) {
  1059. logger_error("coffee.cpp: Couldn't write CFGTimeInfusion to database!\n");
  1060. }
  1061. logger(V_BREW, "Writing back CFGAmountDblEspresso: %d\n", coffeeCFG.amount_espresso);
  1062. if (sqlSetConf(CFGAmountDblEspresso, coffeeCFG.amount_espresso)) {
  1063. logger_error("coffee.cpp: Couldn't write CFGAmountDblEspresso to database!\n");
  1064. }
  1065. }
  1066. /*
  1067. * Procedure for cleaning the machine
  1068. */
  1069. void coffeeClean(void) {
  1070. logger(V_BREW, "coffee.cpp: Cleaning...\n");
  1071. for (int i = 0; i < CLEANING_CYCLES; i++) {
  1072. currentCleanCycle++;
  1073. halPumpOn();
  1074. coffeeNap(3,0);
  1075. halPumpOff();
  1076. coffeeNap(15,0);
  1077. }
  1078. updateDescaling();
  1079. descaling = false;
  1080. currentCleanCycle = 0;
  1081. event_trigger("descaling", &descaling, sizeof(bool));
  1082. }
  1083. /*
  1084. *
  1085. */
  1086. void coffeeManualBrewStart (void) {
  1087. logger(V_BREW, "coffee.cpp: Starting manual brewing...\n");
  1088. coffeeIncreaseBrewCounter();
  1089. }
  1090. /**
  1091. * Brewing process
  1092. */
  1093. void coffeeBrew(uint16_t amount_preinfusion, uint16_t time_soak, uint16_t time_infusion, uint16_t amount_espresso) {
  1094. coffeeIncreaseBrewCounter();
  1095. /*
  1096. * Preinfusion
  1097. */
  1098. logger(V_BREW, "coffee.cpp: Starting preinfusion...\n");
  1099. halPumpOn();
  1100. while (halGetFlow() < amount_preinfusion && halGetFlowTime() < TIME_PREINFUSION) {
  1101. //Don't use coffeeNap here since we don't want to resume to sleep after a signal got caught...
  1102. //coffeeNap(0, 50000000);
  1103. usleep(50000);
  1104. if (getSigValue(MODE_STATE) == SigInt0Rls) {
  1105. halPumpOff();
  1106. return;
  1107. }
  1108. }
  1109. halPumpOff();
  1110. /*
  1111. * Wait for coffee to soak in infused water
  1112. */
  1113. brewTimer.start();
  1114. while (brewTime < time_soak) {
  1115. //coffeeNap(1, 100000000);
  1116. usleep(100000);
  1117. if (getSigValue(MODE_STATE) == SigInt0Rls) {
  1118. brewTimer.stop();
  1119. brewTime = 0;
  1120. return;
  1121. }
  1122. }
  1123. brewTimer.stop();
  1124. brewTime = 0;
  1125. /*
  1126. * Brewing the actual espresso
  1127. */
  1128. logger(V_BREW, "coffee.cpp: Starting infusion...\n");
  1129. halPumpOn();
  1130. brewTimer.start();
  1131. while (brewTime < time_infusion && halGetFlow() < amount_espresso) {
  1132. //coffeeNap(1, 100000000);
  1133. usleep(100000);
  1134. if (getSigValue(MODE_STATE) == SigInt0Rls){
  1135. halPumpOff();
  1136. return;
  1137. }
  1138. }
  1139. halPumpOff();
  1140. brewTimer.stop();
  1141. brewTime = 0;
  1142. return;
  1143. }
  1144. void brewTimerHandler(){
  1145. brewTime += 200;
  1146. }
  1147. /**
  1148. *
  1149. */
  1150. void coffeeIncreaseBrewCounter(void) {
  1151. brewCounter++;
  1152. }
  1153. /**
  1154. * Checks if the descaling is necessary
  1155. * uses descBrewcount and descTimestamp
  1156. */
  1157. void checkDescaling(){
  1158. int16_t dirtyEspresso = checkDirtyEspresso ();
  1159. int16_t dirtyTime = checkDirtyTime ();
  1160. if(dirtyEspresso <= 0) {
  1161. logger(V_BREW, "coffee.cpp: Descaling necessary due to quantity: %d\n", dirtyEspresso);
  1162. descaling = true;
  1163. event_trigger("descaling", &descaling, sizeof(bool));
  1164. }
  1165. if(dirtyTime <= 0) {
  1166. logger(V_BREW, "coffee.cpp: Descaling necessary due to time in days: %d\n", dirtyTime);
  1167. descaling = true;
  1168. event_trigger("descaling", &descaling, sizeof(bool));
  1169. }
  1170. }
  1171. /**
  1172. * this function returns the remaining espressi to be brewed before the descaling event is fired
  1173. * returns a positive integer when there are cups remaining
  1174. * and a negative when the number of cups are exceeded
  1175. * Number of cups after a descaling is defined with DIRTY_ESPRESSO
  1176. */
  1177. int16_t checkDirtyEspresso (void) {
  1178. return descBrewcount + DIRTY_ESPRESSO - brewCounter;
  1179. }
  1180. /**
  1181. * Returns the remaining days before the next descaling event is fired
  1182. * returns a positive integer if there is time left and a negative one if the descaling time was exceeded
  1183. */
  1184. int16_t checkDirtyTime (void) {
  1185. time_t rawtime;
  1186. time(&rawtime);
  1187. double diffseconds = difftime(rawtime, descRawTimestamp);
  1188. diffseconds /= 24*60*60; //calculate the days
  1189. return DIRTY_TIME - diffseconds;
  1190. }
  1191. /**
  1192. * updates the corresponding variables after a descaling process
  1193. */
  1194. void updateDescaling(){
  1195. descBrewcount = brewCounter;
  1196. time_t newDesTimestamp;
  1197. time(&newDesTimestamp);
  1198. if(newDesTimestamp == -1){
  1199. logger(V_BREW, "coffee.cpp: Whoops, couldn't retrieve new descaling timestamp\n");
  1200. }
  1201. else {
  1202. descRawTimestamp = newDesTimestamp;
  1203. }
  1204. }