coffee.cpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322
  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 SigInt1RlsLong:
  820. case SigInt0RlsLong:
  821. if (!halProxSensorCovered()) {
  822. if (halIsHeating() && initalHeating) {
  823. changeState(STATE_INITALHEATING);
  824. } else if (halIsHeating() && !initalHeating) {
  825. changeState(STATE_HEATING);
  826. } else {
  827. changeState(STATE_IDLE);
  828. }
  829. }
  830. break;
  831. }
  832. break;
  833. /*
  834. *
  835. */
  836. case STATE_ERROR:
  837. if (SigValueEmpty() && mode == MODE_STATE)
  838. pause();
  839. switch (getSigValue(MODE_STATE)) {
  840. case SigInt0RlsLong:
  841. changeState(STATE_OFF);
  842. break;
  843. }
  844. }
  845. }
  846. pthread_exit(EXIT_SUCCESS);
  847. }
  848. /**
  849. * Handler for the Signal send to this thread
  850. * It saves the type of signal received and tracks the time between a push and release event of up to 4 signals
  851. * The time is stored in the HalEvent variable when a release event is received
  852. * @param signum
  853. * @param siginfo
  854. * @param context
  855. */
  856. void coffeeHandler(int signum, siginfo_t *siginfo, void *context) {
  857. sigval_t sigVal = (siginfo->si_value);
  858. sigValue = sigVal.sival_int;
  859. logger(V_BREW, "coffee.cpp: CoffeeHandler called with Signal %s\n",
  860. SigName[sigValue]);
  861. }
  862. /**
  863. * returns the Signal value from the last received Signal for the given mode and clears the variable
  864. * @return value sent with the last signal
  865. */
  866. int getSigValue(coffee_mode_t mode) {
  867. int tmp = sigValue;
  868. if (mode == MODE_MENU) {
  869. switch (sigValue) {
  870. case SigInt0Psh:
  871. case SigInt0Rls:
  872. case SigInt0RlsLong:
  873. case SigInt1Psh:
  874. case SigInt1Rls:
  875. case SigRotCCW:
  876. case SigRotCW:
  877. sigValue = 0;
  878. menuTimeout = 0;
  879. return tmp;
  880. break;
  881. default:
  882. break;
  883. }
  884. } else { //State Mode
  885. sigValue = 0;
  886. return tmp;
  887. }
  888. //int tmp = sigValue;
  889. //sigValue = 0;
  890. //return tmp;
  891. return 0;
  892. }
  893. bool SigValueEmpty(void) {
  894. if (sigValue == 0)
  895. return true;
  896. else
  897. return false;
  898. }
  899. /**
  900. * Changes the state of the machine to newState
  901. * prints the change to the logger
  902. * @param newState
  903. */
  904. void changeState(coffee_status_t newState) {
  905. logger(V_BREW, "coffee.cpp: Changing state to %s\n", StateName[newState]);
  906. state = newState;
  907. event_trigger("statechange", &state, sizeof(state));
  908. }
  909. /*
  910. * Change Page to new menu page
  911. */
  912. void changePage(coffee_menuPage_t newPage) {
  913. logger(V_BREW, "coffee.cpp: Change Page to %s\n", PageName[newPage]);
  914. event_trigger("pagechange", &newPage, sizeof(newPage));
  915. page = newPage;
  916. }
  917. /*
  918. * Changes the mode of the machine to the given mode
  919. */
  920. void changeMode(coffee_mode_t newMode) {
  921. if (newMode == MODE_MENU)
  922. logger(V_BREW, "coffee.cpp: Changing to menu mode\n");
  923. else
  924. logger(V_BREW, "coffee.cpp: Changing to state mode\n");
  925. event_trigger("modechange", &newMode, sizeof(newMode));
  926. mode = newMode;
  927. }
  928. /*
  929. * leaving the menu
  930. * sets the start page for the next menu call to softoff
  931. */
  932. void leaveMenu(void) {
  933. logger(V_BREW, "coffee.cpp: Leaving the menu again\n");
  934. //leave the menu again
  935. changeMode(MODE_STATE);
  936. //change page to initial page
  937. changePage(PAGE_SOFTOFF);
  938. //stop the timeout counter
  939. menuTimeout = 0;
  940. menuTimer.stop();
  941. }
  942. /**
  943. * entering the menu
  944. * starts the timeoutcounter and changes the mode to the Menu Mode
  945. */
  946. void enterMenu(void) {
  947. logger(V_BREW, "coffee.cpp: Entering the menu\n");
  948. changeMode(MODE_MENU);
  949. menuTimeout = 0;
  950. menuTimer.start();
  951. }
  952. /**
  953. * Returns the current cleaning cycle
  954. */
  955. uint16_t getCurrentCleanCycle(void) {
  956. return currentCleanCycle;
  957. }
  958. /**
  959. * Returns the current state of the FSM
  960. */
  961. coffee_status_t getState(void) {
  962. return state;
  963. }
  964. /**
  965. * Returns the local up-to-date brewcounter
  966. */
  967. uint16_t getBrewCounter(void) {
  968. return brewCounter;
  969. }
  970. /**
  971. * Returns the value of the brewcounter when the last descaling happened
  972. */
  973. uint16_t getDescBrewCounter (void) {
  974. return descBrewcount;
  975. }
  976. /**
  977. * Returns the raw time stamp when the last descaling happened
  978. */
  979. time_t * getDescTimestamp (void) {
  980. return &descRawTimestamp;
  981. }
  982. /**
  983. * Returns a pointer to the current coffee config
  984. */
  985. coffeeConfig* getCoffeeCfg (void) {
  986. return &coffeeCFG;
  987. }
  988. /**
  989. * Counter for the menu timeout
  990. * When no input is coming from the user the machine leaves the menu automatically after MENUTIMEOUT seconds
  991. */
  992. void menuTimerHandler(void){
  993. menuTimeout += 200;
  994. if((menuTimeout/1000) >= MENUTIMEOUT) {
  995. leaveMenu();
  996. }
  997. }
  998. /**
  999. * handles program termination
  1000. */
  1001. void coffeeTerminate(event_t *event) {
  1002. logger(V_BREW, "Coffee.cpp: Terminating\n");
  1003. //stop brewing
  1004. halRelaisOff(RELAIS_PUMP);
  1005. writeBackCache();
  1006. }
  1007. /**
  1008. * coffeeNap does almost the same as sleep() but resumes to sleep after a signal got delivered
  1009. * sec: seconds to sleep
  1010. * nanosec: nanoseconds to sleep
  1011. */
  1012. void coffeeNap (uint64_t sec, uint64_t nanosec){
  1013. struct timespec sleepTime;
  1014. clock_gettime(CLOCK_REALTIME, &sleepTime);
  1015. sleepTime.tv_sec += sec;
  1016. sleepTime.tv_nsec += nanosec;
  1017. //overflow
  1018. if(sleepTime.tv_nsec >= 1000000000) {
  1019. sleepTime.tv_nsec -= 1000000000;
  1020. sleepTime.tv_sec++;
  1021. }
  1022. int errval = 0;
  1023. do{
  1024. errval = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleepTime, NULL);
  1025. }
  1026. while(errval == EINTR);
  1027. if(errval) logger_error("coffee.cpp: Suspending thread failed with error %d\n", errval);
  1028. }
  1029. /**
  1030. * Function to write back the values of the local copies
  1031. * brewCounter and totalHeatingTime
  1032. *
  1033. */
  1034. void writeBackCache(void) {
  1035. logger(V_BREW, "Writing back cache...\n");
  1036. logger(V_BREW, "Writing back brewCounter: %d\n", brewCounter);
  1037. if (sqlSetConf(CFGbrewcounter, brewCounter)) {
  1038. logger_error("coffee.cpp: Couldn't write brewcounter to database!\n");
  1039. }
  1040. logger(V_BREW, "Writing back descaling brew counter: %d\n", descBrewcount);
  1041. if (sqlSetConf(CFGDescBrewCount, descBrewcount)) {
  1042. logger_error("coffee.cpp: Couldn't write descaling brewcount to database!\n");
  1043. }
  1044. struct tm * timeinfo;
  1045. timeinfo = localtime(&descRawTimestamp);
  1046. 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);
  1047. if (sqlSetConf(CFGDescTimestamp, (uint64_t)descRawTimestamp)) {
  1048. logger_error("coffee.cpp: Couldn't write descaling timestamp to database!\n");
  1049. }
  1050. logger(V_BREW, "Writing back CFGAmountPreinfusion: %d\n", coffeeCFG.amount_preinfusion);
  1051. if (sqlSetConf(CFGAmountPreinfusion, coffeeCFG.amount_preinfusion)) {
  1052. logger_error("coffee.cpp: Couldn't write CFGAmountPreinfusion to database!\n");
  1053. }
  1054. logger(V_BREW, "Writing back CFGTimeSoak: %d\n", coffeeCFG.time_soak);
  1055. if (sqlSetConf(CFGTimeSoak, coffeeCFG.time_soak)) {
  1056. logger_error("coffee.cpp: Couldn't write CFGTimeSoak to database!\n");
  1057. }
  1058. logger(V_BREW, "Writing back CFGTimeInfusion: %d\n", coffeeCFG.time_infusion);
  1059. if (sqlSetConf(CFGTimeInfusion, coffeeCFG.time_infusion)) {
  1060. logger_error("coffee.cpp: Couldn't write CFGTimeInfusion to database!\n");
  1061. }
  1062. logger(V_BREW, "Writing back CFGAmountDblEspresso: %d\n", coffeeCFG.amount_espresso);
  1063. if (sqlSetConf(CFGAmountDblEspresso, coffeeCFG.amount_espresso)) {
  1064. logger_error("coffee.cpp: Couldn't write CFGAmountDblEspresso to database!\n");
  1065. }
  1066. }
  1067. /*
  1068. * Procedure for cleaning the machine
  1069. */
  1070. void coffeeClean(void) {
  1071. logger(V_BREW, "coffee.cpp: Cleaning...\n");
  1072. for (int i = 0; i < CLEANING_CYCLES; i++) {
  1073. currentCleanCycle++;
  1074. halPumpOn();
  1075. coffeeNap(3,0);
  1076. halPumpOff();
  1077. coffeeNap(15,0);
  1078. }
  1079. updateDescaling();
  1080. descaling = false;
  1081. currentCleanCycle = 0;
  1082. event_trigger("descaling", &descaling, sizeof(bool));
  1083. }
  1084. /*
  1085. *
  1086. */
  1087. void coffeeManualBrewStart (void) {
  1088. logger(V_BREW, "coffee.cpp: Starting manual brewing...\n");
  1089. coffeeIncreaseBrewCounter();
  1090. }
  1091. /**
  1092. * Brewing process
  1093. */
  1094. void coffeeBrew(uint16_t amount_preinfusion, uint16_t time_soak, uint16_t time_infusion, uint16_t amount_espresso) {
  1095. coffeeIncreaseBrewCounter();
  1096. /*
  1097. * Preinfusion
  1098. */
  1099. logger(V_BREW, "coffee.cpp: Starting preinfusion...\n");
  1100. halPumpOn();
  1101. while (halGetFlow() < amount_preinfusion && halGetFlowTime() < TIME_PREINFUSION) {
  1102. //Don't use coffeeNap here since we don't want to resume to sleep after a signal got caught...
  1103. //coffeeNap(0, 50000000);
  1104. usleep(50000);
  1105. if (getSigValue(MODE_STATE) == SigInt0Rls) {
  1106. halPumpOff();
  1107. return;
  1108. }
  1109. }
  1110. halPumpOff();
  1111. /*
  1112. * Wait for coffee to soak in infused water
  1113. */
  1114. brewTimer.start();
  1115. while (brewTime < time_soak) {
  1116. //coffeeNap(1, 100000000);
  1117. usleep(100000);
  1118. if (getSigValue(MODE_STATE) == SigInt0Rls) {
  1119. brewTimer.stop();
  1120. brewTime = 0;
  1121. return;
  1122. }
  1123. }
  1124. brewTimer.stop();
  1125. brewTime = 0;
  1126. /*
  1127. * Brewing the actual espresso
  1128. */
  1129. logger(V_BREW, "coffee.cpp: Starting infusion...\n");
  1130. halPumpOn();
  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. return;
  1141. }
  1142. void brewTimerHandler(){
  1143. brewTime += 200;
  1144. }
  1145. /**
  1146. *
  1147. */
  1148. void coffeeIncreaseBrewCounter(void) {
  1149. brewCounter++;
  1150. }
  1151. /**
  1152. * Checks if the descaling is necessary
  1153. * uses descBrewcount and descTimestamp
  1154. */
  1155. void checkDescaling(){
  1156. int16_t dirtyEspresso = checkDirtyEspresso ();
  1157. int16_t dirtyTime = checkDirtyTime ();
  1158. if(dirtyEspresso <= 0) {
  1159. logger(V_BREW, "coffee.cpp: Descaling necessary due to quantity: %d\n", dirtyEspresso);
  1160. descaling = true;
  1161. event_trigger("descaling", &descaling, sizeof(bool));
  1162. }
  1163. if(dirtyTime <= 0) {
  1164. logger(V_BREW, "coffee.cpp: Descaling necessary due to time in days: %d\n", dirtyTime);
  1165. descaling = true;
  1166. event_trigger("descaling", &descaling, sizeof(bool));
  1167. }
  1168. }
  1169. /**
  1170. * this function returns the remaining espressi to be brewed before the descaling event is fired
  1171. * returns a positive integer when there are cups remaining
  1172. * and a negative when the number of cups are exceeded
  1173. * Number of cups after a descaling is defined with DIRTY_ESPRESSO
  1174. */
  1175. int16_t checkDirtyEspresso (void) {
  1176. return descBrewcount + DIRTY_ESPRESSO - brewCounter;
  1177. }
  1178. /**
  1179. * Returns the remaining days before the next descaling event is fired
  1180. * returns a positive integer if there is time left and a negative one if the descaling time was exceeded
  1181. */
  1182. int16_t checkDirtyTime (void) {
  1183. time_t rawtime;
  1184. time(&rawtime);
  1185. double diffseconds = difftime(rawtime, descRawTimestamp);
  1186. diffseconds /= 24*60*60; //calculate the days
  1187. return DIRTY_TIME - diffseconds;
  1188. }
  1189. /**
  1190. * updates the corresponding variables after a descaling process
  1191. */
  1192. void updateDescaling(){
  1193. descBrewcount = brewCounter;
  1194. time_t newDesTimestamp;
  1195. time(&newDesTimestamp);
  1196. if(newDesTimestamp == -1){
  1197. logger(V_BREW, "coffee.cpp: Whoops, couldn't retrieve new descaling timestamp\n");
  1198. }
  1199. else {
  1200. descRawTimestamp = newDesTimestamp;
  1201. }
  1202. }