hal.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. /*
  2. * hal.cpp
  3. *
  4. * Created on: Aug 3, 2016
  5. * Author: Philipp Hinz, Sebastian Vendt
  6. */
  7. #include <wiringPi.h>
  8. #include <stdlib.h>
  9. #include <stdint.h>
  10. #include <errno.h>
  11. #include <string.h>
  12. #include <signal.h>
  13. #include <ctime>
  14. #include <time.h>
  15. #include <unistd.h>
  16. #include "hal.h"
  17. #include "global.h"
  18. #include "logger.h"
  19. #include "timer.h"
  20. #include "database.h"
  21. typedef struct timespec timespec;
  22. volatile int flowcnt = 0;
  23. int Int0Time, Int1Time;
  24. int idleCounter;
  25. bool idle;
  26. bool flagIgnoreRlsInt0, flagIgnoreRlsInt1;
  27. //storage for the last state of the buttons, the proximity sensor and the pressure sensor
  28. int pinState[4] = {1, 1, 1, 0};
  29. //sweep counter to log every brew
  30. uint16_t logcycle = 0;
  31. timer Int0Timer(&halInt0TimerHandler);
  32. timer Int1Timer(&halInt1TimerHandler);
  33. timer idleTimer(&halIdleTimerHandler);
  34. time_t heatingCycle[] = {0, 0};
  35. timespec flowTimestep[] = {{0,0},{0,0}};
  36. uint8_t flowIndex = 0;
  37. timespec pumpCycle[] = {{0,0},{0,0}};
  38. //delay of the debounce in milliseconds
  39. #define DELAY_DEBOUNCE 50
  40. //display turn off after idle time in min
  41. //minimal time is 2min
  42. #define IDLE_TIME 10
  43. /**
  44. * Initializes HAL
  45. */
  46. void halInit(void) {
  47. pinMode(RELAIS_HEAT, OUTPUT);
  48. pinMode(RELAIS_PUMP, OUTPUT);
  49. pinMode(RELAIS_POWER, OUTPUT);
  50. pinMode(PIN_PRESSURE_CTRL, INPUT);
  51. pinMode(PIN_PROXIMITY_SENSOR, INPUT);
  52. pinMode(PIN_INT0, INPUT);
  53. pinMode(PIN_INT1, INPUT);
  54. pinMode(PIN_FLOW, INPUT);
  55. pinMode(PIN_DISP, OUTPUT);
  56. idleTimer.setDivider(1200); //1 min
  57. idleCounter = 0;
  58. idle = false;
  59. clock_gettime(CLOCK_REALTIME, &flowTimestep[0]);
  60. clock_gettime(CLOCK_REALTIME, &flowTimestep[1]);
  61. halDisplayOn();
  62. if (optPower) {
  63. halMachineOn();
  64. } else {
  65. halMachineOff();
  66. }
  67. sleep(1); //wait till the machine eventually turned on when optPower
  68. pinState[3] = halIsHeating();
  69. Int0Timer.setDivider(4); //200ms
  70. Int1Timer.setDivider(4);
  71. Int0Time = 0;
  72. Int1Time = 0;
  73. flagIgnoreRlsInt0 = false;
  74. flagIgnoreRlsInt1 = false;
  75. event_subscribe("terminate", &halTerminate);
  76. if (wiringPiISR(PIN_INT0, INT_EDGE_BOTH, &halInt0) < 0) {
  77. logger_error("Unable to setup ISR0: %s\n", strerror(errno));
  78. return;
  79. }
  80. if (wiringPiISR(PIN_INT1, INT_EDGE_BOTH, &halInt1) < 0) {
  81. logger_error("Unable to setup ISR1: %s\n", strerror(errno));
  82. return;
  83. }
  84. if (wiringPiISR(PIN_FLOW, INT_EDGE_FALLING, &halIntFlow) < 0) {
  85. logger_error("Unable to setup ISRFLOW: %s\n", strerror(errno));
  86. return;
  87. }
  88. if (wiringPiISR(PIN_PRESSURE_CTRL, INT_EDGE_BOTH, &halIntPressure) < 0) {
  89. logger_error("Unable to setup ISRPressure: %s\n", strerror(errno));
  90. return;
  91. }
  92. if (wiringPiISR(PIN_PROXIMITY_SENSOR, INT_EDGE_BOTH, &halIntProximity) < 0) {
  93. logger_error("Unable to setup ISRProximity: %s\n", strerror(errno));
  94. return;
  95. }
  96. if (!(logcycle = sqlGetConf(CFGSweepCounter))) {
  97. logger_error("hal.cpp: Couldn't read the logcycle counter from the database\n");
  98. //pthread_exit(EXIT_SUCCESS);
  99. exit(EXIT_FAILURE);
  100. }
  101. }
  102. /**
  103. * Switches relais on
  104. * @param relais Relais ID
  105. */
  106. void halRelaisOn(int relais) {
  107. halRelaisSet(relais, LOW);
  108. }
  109. /**
  110. * Turn the display off
  111. */
  112. void halDisplayOff(){
  113. digitalWrite(PIN_DISP, LOW);
  114. }
  115. /**
  116. * Turn the display on
  117. */
  118. void halDisplayOn(){
  119. digitalWrite(PIN_DISP, HIGH);
  120. }
  121. /**
  122. * Switches relais off
  123. * @param relais Relais ID
  124. */
  125. void halRelaisOff(int relais) {
  126. halRelaisSet(relais, HIGH);
  127. }
  128. /**
  129. * Switches relais to state
  130. * @param relais Relais ID
  131. * @param state LOW(0) or HIGH(1)
  132. */
  133. void halRelaisSet(int relais, int state) {
  134. if (state != HIGH && state != LOW)
  135. return;
  136. switch (relais) {
  137. case RELAIS_POWER:
  138. case RELAIS_HEAT:
  139. case RELAIS_PUMP:
  140. digitalWrite(relais, state);
  141. break;
  142. }
  143. }
  144. /**
  145. * Returns the state of the relais relais
  146. * Returns HIGH when Relais is ON
  147. * @param relais Relais ID
  148. */
  149. int halGetRelaisState(int relais) {
  150. switch (relais) {
  151. case RELAIS_POWER:
  152. case RELAIS_HEAT:
  153. case RELAIS_PUMP:
  154. return !digitalRead(relais);
  155. break;
  156. }
  157. return -1;
  158. }
  159. /**
  160. * Interrupt routine for Int0 (Top button)
  161. */
  162. void halInt0(void) {
  163. //wait for a debounce
  164. delay(DELAY_DEBOUNCE);
  165. if (halGetInt0() && !pinState[0]) { //released
  166. logger(V_HAL, "Int0 released\n");
  167. pinState[0] = 1;
  168. if (flagIgnoreRlsInt0) {
  169. flagIgnoreRlsInt0 = false;
  170. } else {
  171. Int0Time = 0;
  172. Int0Timer.stop();
  173. halSendSignal(SigInt0Rls);
  174. }
  175. } else if(!halGetInt0() && pinState[0]) { //pressed
  176. logger(V_HAL, "Int0 pushed\n");
  177. pinState[0] = 0;
  178. halSendSignal(SigInt0Psh);
  179. Int0Time = 0;
  180. Int0Timer.start();
  181. }
  182. }
  183. /**
  184. *
  185. */
  186. void halInt0TimerHandler(void) {
  187. Int0Time += 200;
  188. if (Int0Time >= (TIME_BUTTONLONGPRESS * 1000)) {
  189. halSendSignal(SigInt0RlsLong);
  190. flagIgnoreRlsInt0 = true;
  191. Int0Time = 0;
  192. Int0Timer.stop();
  193. }
  194. }
  195. /**
  196. *
  197. */
  198. void halIdleTimerHandler(void) {
  199. if(++idleCounter == IDLE_TIME){
  200. halEnterIdle();
  201. }
  202. }
  203. /**
  204. * Interrupt routine for Int1 (Bottom button)
  205. */
  206. void halInt1(void) {
  207. delay(DELAY_DEBOUNCE);
  208. if (halGetInt1() && !pinState[1]) {
  209. logger(V_HAL, "Int1 released\n");
  210. pinState[1] = 1;
  211. if (flagIgnoreRlsInt1) {
  212. flagIgnoreRlsInt1 = false;
  213. } else {
  214. Int1Time = 0;
  215. Int1Timer.stop();
  216. halSendSignal(SigInt1Rls);
  217. }
  218. } else if(!halGetInt1() && pinState[1]) {
  219. logger(V_HAL, "Int1 pushed\n");
  220. pinState[1] = 0;
  221. halSendSignal(SigInt1Psh);
  222. Int1Time = 0;
  223. Int1Timer.start();
  224. }
  225. }
  226. /*
  227. *
  228. */
  229. void halInt1TimerHandler(void) {
  230. Int1Time += 200;
  231. if (Int1Time >= (TIME_BUTTONLONGPRESS * 1000)) {
  232. halSendSignal(SigInt1RlsLong);
  233. flagIgnoreRlsInt1 = true;
  234. Int1Time = 0;
  235. Int1Timer.stop();
  236. }
  237. }
  238. /**
  239. * Interrupt routine for the flow sensor
  240. * It counts the edgdes and stores the value in flowcnt
  241. */
  242. void halIntFlow(void) {
  243. //halRelaisOff(RELAIS_POWER);
  244. logger(V_HAL, "IntFlow triggered #%d total: %.2fml\n", flowcnt, halGetFlow());
  245. flowcnt++;
  246. //subroutine to log the flow to the database
  247. timespec deltaT;
  248. clock_gettime(CLOCK_REALTIME, &flowTimestep[flowIndex]);
  249. timespec_diff(&flowTimestep[((flowIndex + 1) % 2)], &flowTimestep[flowIndex], &deltaT);
  250. if (sqlLogFlow(logcycle, halGetFlow()*1000, deltaT.tv_sec * 1000 + deltaT.tv_nsec/1000000)) {
  251. logger_error("hal.cpp: could not log flow to database!");
  252. return;
  253. }
  254. flowIndex = (flowIndex + 1) % 2;
  255. }
  256. /**
  257. * Interrupt routine for the pressure control
  258. * It captures the time at closing point and opening point
  259. * Reading heating time via the getHeatingTime function
  260. */
  261. void halIntPressure(void) {
  262. logger(V_HAL, "IntPressure Control triggered\n");
  263. if (halIsHeating() && !pinState[3]) {
  264. pinState[3] = 1;
  265. time(&heatingCycle[0]);
  266. halSendSignal(SigPressCls);
  267. } else if(!halIsHeating() && pinState[3]) {
  268. pinState[3] = 0;
  269. time(&heatingCycle[1]);
  270. halSendSignal(SigPressOpn);
  271. }
  272. }
  273. /**
  274. * Function to read the heating time in sec
  275. * If called during a heating process, it returns the time elapsed since the heating started
  276. * If called after a heating process, it returns the total time elapsed during the heating cycle
  277. */
  278. double halgetHeatingTime(void){
  279. //TODO check return value on negative times
  280. if (halIsHeating()) {
  281. logger(V_HAL, "Hot Heating Time: %f\n", difftime(time(NULL), heatingCycle[0]));
  282. return difftime(time(0), heatingCycle[0]);
  283. }
  284. else {
  285. logger(V_HAL, "Heating time: %f\n", difftime(heatingCycle[1], heatingCycle[0]));
  286. return difftime(heatingCycle[1], heatingCycle[0]);
  287. }
  288. }
  289. /**
  290. * Method to handle toggle of the proximity sensor
  291. */
  292. void halIntProximity(void) {
  293. delay(DELAY_DEBOUNCE);
  294. if (halProxSensorCovered() && !pinState[2]) {
  295. logger(V_HAL, "IntProximity triggered\n");
  296. pinState[2] = 1;
  297. halSendSignal(SigProxCvrd);
  298. } else if(!halProxSensorCovered() && pinState[2]){
  299. logger(V_HAL, "IntProximity triggered\n");
  300. pinState[2] = 0;
  301. halSendSignal(SigProxOpn);
  302. }
  303. }
  304. /**
  305. * Returns total flow trough sensor in ml
  306. */
  307. float halGetFlow(void) {
  308. return flowcnt * FLOW_ML_PULSE;
  309. }
  310. /**
  311. * Resets the Flow counter
  312. */
  313. void halResetFlow(void) {
  314. logger(V_HAL, "Flow counter reset, amount so far: %.2f ml\n", halGetFlow());
  315. flowcnt = 0;
  316. }
  317. /**
  318. * Reads the status of the Pressure Control
  319. * @return 1 (true) for closed Pressure Control(heating) and 0 (false) for open
  320. */
  321. bool halIsHeating(void) {
  322. if (digitalRead(PIN_PRESSURE_CTRL) == 0) {
  323. return true;
  324. } else {
  325. return false;
  326. }
  327. }
  328. /**
  329. * Returns status of the proximity switch
  330. * @return 1 if the proximity switch is covered and 0 if uncovered
  331. */
  332. bool halProxSensorCovered(void) {
  333. if(digitalRead(PIN_PROXIMITY_SENSOR) == 0){
  334. return false;
  335. } else {
  336. return true;
  337. }
  338. }
  339. /**
  340. * Returns the value of the top button Int0 (low active)
  341. * @return LOW or HIGH
  342. */
  343. int halGetInt0(void) {
  344. return digitalRead(PIN_INT0);
  345. }
  346. /**
  347. * Returns the value of the bottom button Int1 (low active)
  348. * @return LOW or HIGH
  349. */
  350. int halGetInt1(void) {
  351. return digitalRead(PIN_INT1);
  352. }
  353. /**
  354. * send Signal to coffee thread
  355. * @param val Integer value assigned to signal
  356. */
  357. void halSendSignal(HalSig val) {
  358. //catch if machine is idle and drop button event
  359. switch (val) {
  360. case SigInt0Psh:
  361. case SigInt1Psh:
  362. if (idle) {
  363. return;
  364. }
  365. break;
  366. case SigInt0Rls:
  367. case SigInt0RlsLong:
  368. case SigInt1Rls:
  369. case SigInt1RlsLong:
  370. if (idle) {
  371. halLeaveIdle();
  372. return;
  373. }
  374. break;
  375. default:
  376. break;
  377. }
  378. sigval value = { 0 };
  379. value.sival_int = (int) val;
  380. try {
  381. if (pthread_sigqueue(thread[THREAD_COFFEE], SIGUSR2, value)) {
  382. logger_error("hal.cpp: Failed to queue signal %d %s\n", val, strerror(errno));
  383. //No Signals reach the state machine anymore...
  384. exit(EXIT_FAILURE);
  385. }
  386. } catch (int e) {
  387. logger_error("Whoops.. %d\n", e);
  388. }
  389. }
  390. /**
  391. * Turn machine on
  392. */
  393. void halMachineOn(void) {
  394. halRelaisOn(RELAIS_HEAT);
  395. halRelaisOff(RELAIS_PUMP);
  396. halRelaisOn(RELAIS_POWER);
  397. idleTimer.stop();
  398. logger(V_HAL, "Turning machine on\n");
  399. }
  400. /**
  401. * Turn machine off
  402. */
  403. void halMachineOff(void) {
  404. halRelaisOff(RELAIS_HEAT);
  405. halRelaisOff(RELAIS_PUMP);
  406. halRelaisOff(RELAIS_POWER);
  407. idleCounter = 0;
  408. idleTimer.start();
  409. halWriteBackCache();
  410. logger(V_HAL, "Turning machine off\n");
  411. }
  412. /**
  413. *
  414. */
  415. void halEnterIdle(void){
  416. logger(V_HAL, "Entering Idle Mode\n");
  417. idleTimer.stop();
  418. halDisplayOff();
  419. idle = true;
  420. }
  421. /**
  422. *
  423. */
  424. void halLeaveIdle(void){
  425. idleCounter = 0;
  426. logger(V_HAL, "Leaving Idle Mode\n");
  427. halDisplayOn();
  428. idleTimer.start();
  429. idle = false;
  430. }
  431. /**
  432. * Wrapper function to turn the pump on
  433. * and to measure how long the pump is running
  434. * @param cycle the number of the sweep in the database
  435. */
  436. void halPumpOn(){
  437. halRelaisOn(RELAIS_PUMP);
  438. clock_gettime(CLOCK_REALTIME, &pumpCycle[0]);
  439. }
  440. /**
  441. *
  442. */
  443. void halIncreaseLogCycleCounter(void){
  444. logcycle++;
  445. }
  446. /**
  447. * Wrapper function to turn the pump off
  448. * and to measure how long the pump is running
  449. */
  450. void halPumpOff(void){
  451. halRelaisOff(RELAIS_PUMP);
  452. clock_gettime(CLOCK_REALTIME, &pumpCycle[1]);
  453. }
  454. /**
  455. * Function to get the elapsed time the pump is running in ms
  456. * when the pump is on, this function returns the time between turning the pump on and the call
  457. * when the pump is off, this function returns the time elapsed in the last pump cycle
  458. */
  459. double halGetPumpTime(void){
  460. timespec now;
  461. timespec diff = {0,0};
  462. if(halGetRelaisState(RELAIS_PUMP) == HIGH){//pump is on
  463. clock_gettime(CLOCK_REALTIME, &now);
  464. timespec_diff(&pumpCycle[0], &now, &diff);
  465. }
  466. else {
  467. timespec_diff(&pumpCycle[0], &pumpCycle[1], &diff);
  468. }
  469. return diff.tv_sec * 1000 + diff.tv_nsec/1000000;
  470. }
  471. /**
  472. * Function to calculate the difference between two timespecs
  473. */
  474. void timespec_diff(timespec *start, timespec *stop, timespec *result) {
  475. long int secDiff = stop->tv_sec - start->tv_sec;
  476. long int nsecDiff = stop->tv_nsec - start->tv_nsec;
  477. if (secDiff > 0) {
  478. if (nsecDiff >= 0) {
  479. result->tv_sec = secDiff;
  480. result->tv_nsec = nsecDiff;
  481. } else if (nsecDiff < 0) {
  482. result->tv_sec = --secDiff;
  483. result->tv_nsec = 1000000000 + nsecDiff;
  484. }
  485. } else if (secDiff < 0) {
  486. if (nsecDiff >= 0) {
  487. result->tv_sec = ++secDiff;
  488. result->tv_nsec = -(1000000000 - nsecDiff);
  489. } else if (nsecDiff < 0) {
  490. result->tv_sec = secDiff;
  491. result->tv_nsec = nsecDiff;
  492. }
  493. } else if (secDiff == 0){
  494. result->tv_sec = secDiff;
  495. result->tv_nsec = nsecDiff;
  496. }
  497. return;
  498. }
  499. /*
  500. * Handler for Termination of the hal
  501. */
  502. void halTerminate(event_t *event){
  503. halWriteBackCache();
  504. }
  505. /*
  506. * writing back non volatile variables of the hal to the database: SweepCounter
  507. */
  508. void halWriteBackCache(){
  509. if (sqlSetConf(CFGSweepCounter, logcycle)) {
  510. logger_error("hall.cpp: Couldn't write logcycle to database");
  511. return;
  512. }
  513. logger(V_BREW, "Writing back logcycle %d\n", logcycle);
  514. }