coffee.cpp 27 KB

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