hal.cpp 14 KB

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