/* * hal.cpp * * Created on: Aug 3, 2016 * Author: Philipp Hinz, Sebastian Vendt */ #include #include #include #include #include #include #include #include #include "hal.h" #include "global.h" #include "logger.h" #include "timer.h" typedef struct timespec timespec; volatile int flowcnt = 0; int Int0Time, Int1Time; int idleCounter; bool idle; bool flagIgnoreRlsInt0, flagIgnoreRlsInt1; //storage for the last state of the buttons, the proximity sensor and the pressure sensor int pinState[4] = {1, 1, 1, 0}; timer Int0Timer(&halInt0TimerHandler); timer Int1Timer(&halInt1TimerHandler); timer idleTimer(&halIdleTimerHandler); time_t heatingCycle[] = {0, 0}; timespec pumpCycle[] = {{0,0},{0,0}}; //delay of the debounce in milliseconds #define DELAY_DEBOUNCE 50 //display turn off after idle time in min //minimal time is 2min #define IDLE_TIME 10 /** * Initializes HAL */ void halInit(void) { pinMode(RELAIS_HEAT, OUTPUT); pinMode(RELAIS_PUMP, OUTPUT); pinMode(RELAIS_POWER, OUTPUT); pinMode(PIN_PRESSURE_CTRL, INPUT); pinMode(PIN_PROXIMITY_SENSOR, INPUT); pinMode(PIN_INT0, INPUT); pinMode(PIN_INT1, INPUT); pinMode(PIN_FLOW, INPUT); pinMode(PIN_DISP, OUTPUT); idleTimer.setDivider(1200); //1 min idleCounter = 0; idle = false; halDisplayOn(); if (optPower) { halMachineOn(); } else { halMachineOff(); } sleep(1); //wait till the machine eventually turned on when optPower pinState[3] = halIsHeating(); Int0Timer.setDivider(4); //200ms Int1Timer.setDivider(4); Int0Time = 0; Int1Time = 0; flagIgnoreRlsInt0 = false; flagIgnoreRlsInt1 = false; if (wiringPiISR(PIN_INT0, INT_EDGE_BOTH, &halInt0) < 0) { logger_error("Unable to setup ISR0: %s\n", strerror(errno)); return; } if (wiringPiISR(PIN_INT1, INT_EDGE_BOTH, &halInt1) < 0) { logger_error("Unable to setup ISR1: %s\n", strerror(errno)); return; } if (wiringPiISR(PIN_FLOW, INT_EDGE_FALLING, &halIntFlow) < 0) { logger_error("Unable to setup ISRFLOW: %s\n", strerror(errno)); return; } if (wiringPiISR(PIN_PRESSURE_CTRL, INT_EDGE_BOTH, &halIntPressure) < 0) { logger_error("Unable to setup ISRPressure: %s\n", strerror(errno)); return; } if (wiringPiISR(PIN_PROXIMITY_SENSOR, INT_EDGE_BOTH, &halIntProximity) < 0) { logger_error("Unable to setup ISRProximity: %s\n", strerror(errno)); return; } } /** * Switches relais on * @param relais Relais ID */ void halRelaisOn(int relais) { halRelaisSet(relais, LOW); } /** * Turn the display off */ void halDisplayOff(){ digitalWrite(PIN_DISP, LOW); } /** * Turn the display on */ void halDisplayOn(){ digitalWrite(PIN_DISP, HIGH); } /** * Switches relais off * @param relais Relais ID */ void halRelaisOff(int relais) { halRelaisSet(relais, HIGH); } /** * Switches relais to state * @param relais Relais ID * @param state LOW(0) or HIGH(1) */ void halRelaisSet(int relais, int state) { if (state != HIGH && state != LOW) return; switch (relais) { case RELAIS_POWER: case RELAIS_HEAT: case RELAIS_PUMP: digitalWrite(relais, state); break; } } /** * Returns the state of the relais relais * Returns HIGH when Relais is ON * @param relais Relais ID */ int halGetRelaisState(int relais) { switch (relais) { case RELAIS_POWER: case RELAIS_HEAT: case RELAIS_PUMP: return !digitalRead(relais); break; } return -1; } /** * Interrupt routine for Int0 (Top button) */ void halInt0(void) { //wait for a debounce delay(DELAY_DEBOUNCE); if (halGetInt0() && !pinState[0]) { //released logger(V_HAL, "Int0 released\n"); pinState[0] = 1; if (flagIgnoreRlsInt0) { flagIgnoreRlsInt0 = false; } else { Int0Time = 0; Int0Timer.stop(); halSendSignal(SigInt0Rls); } } else if(!halGetInt0() && pinState[0]) { //pressed logger(V_HAL, "Int0 pushed\n"); pinState[0] = 0; halSendSignal(SigInt0Psh); Int0Time = 0; Int0Timer.start(); } } /** * */ void halInt0TimerHandler(void) { Int0Time += 200; if (Int0Time >= (TIME_BUTTONLONGPRESS * 1000)) { halSendSignal(SigInt0RlsLong); flagIgnoreRlsInt0 = true; Int0Time = 0; Int0Timer.stop(); } } /** * */ void halIdleTimerHandler(void) { if(++idleCounter == IDLE_TIME){ halEnterIdle(); } } /** * Interrupt routine for Int1 (Bottom button) */ void halInt1(void) { delay(DELAY_DEBOUNCE); if (halGetInt1() && !pinState[1]) { logger(V_HAL, "Int1 released\n"); pinState[1] = 1; if (flagIgnoreRlsInt1) { flagIgnoreRlsInt1 = false; } else { Int1Time = 0; Int1Timer.stop(); halSendSignal(SigInt1Rls); } } else if(!halGetInt1() && pinState[1]) { logger(V_HAL, "Int1 pushed\n"); pinState[1] = 0; halSendSignal(SigInt1Psh); Int1Time = 0; Int1Timer.start(); } } /* * */ void halInt1TimerHandler(void) { Int1Time += 200; if (Int1Time >= (TIME_BUTTONLONGPRESS * 1000)) { halSendSignal(SigInt1RlsLong); flagIgnoreRlsInt1 = true; Int1Time = 0; Int1Timer.stop(); } } /** * Interrupt routine for the flow sensor * It counts the edgdes and stores the value in flowcnt */ void halIntFlow(void) { //halRelaisOff(RELAIS_POWER); logger(V_HAL, "IntFlow triggered #%d total: %.2fml\n", flowcnt, halGetFlow()); if (flowcnt == 99) { halRelaisOff(RELAIS_PUMP); } flowcnt++; } /** * Interrupt routine for the pressure control * It captures the time at closing point and opening point * Reading heating time via the getHeatingTime function */ void halIntPressure(void) { logger(V_HAL, "IntPressure Control triggered\n"); if (halIsHeating() && !pinState[3]) { pinState[3] = 1; time(&heatingCycle[0]); halSendSignal(SigPressCls); } else if(!halIsHeating() && pinState[3]) { pinState[3] = 0; time(&heatingCycle[1]); halSendSignal(SigPressOpn); } } /** * Function to read the heating time in sec * If called during a heating process, it returns the time elapsed since the heating started * If called after a heating process, it returns the total time elapsed during the heating cycle */ double halgetHeatingTime(void){ //TODO check return value on negative times if (halIsHeating()) { logger(V_HAL, "Hot Heating Time: %f\n", difftime(time(NULL), heatingCycle[0])); return difftime(time(0), heatingCycle[0]); } else { logger(V_HAL, "Heating time: %f\n", difftime(heatingCycle[1], heatingCycle[0])); return difftime(heatingCycle[1], heatingCycle[0]); } } /** * Method to handle toggle of the proximity sensor */ void halIntProximity(void) { delay(DELAY_DEBOUNCE); if (halProxSensorCovered() && !pinState[2]) { logger(V_HAL, "IntProximity triggered\n"); pinState[2] = 1; halSendSignal(SigProxCvrd); } else if(!halProxSensorCovered() && pinState[2]){ logger(V_HAL, "IntProximity triggered\n"); pinState[2] = 0; halSendSignal(SigProxOpn); } } /** * Returns total flow trough sensor in ml */ float halGetFlow(void) { return flowcnt * FLOW_ML_PULSE; } /** * Resets the Flow counter */ void halResetFlow(void) { logger(V_HAL, "Flow counter reset, amount so far: %.2f ml\n", halGetFlow()); flowcnt = 0; } /** * Reads the status of the Pressure Control * @return 1 (true) for closed Pressure Control(heating) and 0 (false) for open */ bool halIsHeating(void) { if (digitalRead(PIN_PRESSURE_CTRL) == 0) { return true; } else { return false; } } /** * Returns status of the proximity switch * @return 1 if the proximity switch is covered and 0 if uncovered */ bool halProxSensorCovered(void) { if(digitalRead(PIN_PROXIMITY_SENSOR) == 0){ return false; } else { return true; } } /** * Returns the value of the top button Int0 (low active) * @return LOW or HIGH */ int halGetInt0(void) { return digitalRead(PIN_INT0); } /** * Returns the value of the bottom button Int1 (low active) * @return LOW or HIGH */ int halGetInt1(void) { return digitalRead(PIN_INT1); } /** * send Signal to coffee thread * @param val Integer value assigned to signal */ void halSendSignal(HalSig val) { //catch if machine is idle and drop button event switch (val) { case SigInt0Psh: case SigInt1Psh: if (idle) { return; } break; case SigInt0Rls: case SigInt0RlsLong: case SigInt1Rls: case SigInt1RlsLong: if (idle) { halLeaveIdle(); return; } break; default: break; } sigval value = { 0 }; value.sival_int = (int) val; try { if (pthread_sigqueue(thread[THREAD_COFFEE], SIGUSR2, value)) { logger_error("hal.cpp: Failed to queue signal %d %s\n", val, strerror(errno)); //No Signals reach the state machine anymore... exit(EXIT_FAILURE); } } catch (int e) { logger_error("Whoops.. %d\n", e); } } /** * Turn machine on */ void halMachineOn(void) { halRelaisOn(RELAIS_HEAT); halRelaisOff(RELAIS_PUMP); halRelaisOn(RELAIS_POWER); idleTimer.stop(); logger(V_HAL, "Turning machine on\n"); } /** * Turn machine off */ void halMachineOff(void) { halRelaisOff(RELAIS_HEAT); halRelaisOff(RELAIS_PUMP); halRelaisOff(RELAIS_POWER); idleCounter = 0; idleTimer.start(); logger(V_HAL, "Turning machine off\n"); } /** * */ void halEnterIdle(void){ logger(V_HAL, "Entering Idle Mode\n"); idleTimer.stop(); halDisplayOff(); idle = true; } /** * */ void halLeaveIdle(void){ idleCounter = 0; logger(V_HAL, "Leaving Idle Mode\n"); halDisplayOn(); idleTimer.start(); idle = false; } /** * Wrapper function to turn the pump on * and is used for time measurements on how long the pump is running */ void halPumpOn(void){ halRelaisOn(RELAIS_PUMP); clock_gettime(CLOCK_REALTIME, &pumpCycle[0]); } /** * Wrapper function to turn the pump off * and is used for time measurements on how long the pump is running */ void halPumpOff(void){ halRelaisOff(RELAIS_PUMP); clock_gettime(CLOCK_REALTIME, &pumpCycle[1]); } /** * Function to get the elapsed time the pump is running in ms * when the pump is on, this function returns the time between turning the pump on and the call * when the pump is off, this function returns the last */ double halGetPumpTime(void){ timespec now; timespec diff = {0,0}; if(halGetRelaisState(RELAIS_PUMP) == HIGH){//pump is on clock_gettime(CLOCK_REALTIME, &now); timespec_diff(&pumpCycle[0], &now, &diff); } else { timespec_diff(&pumpCycle[0], &pumpCycle[1], &diff); } return diff.tv_sec * 1000 + diff.tv_nsec/1000000; } /** * Function to calculate the difference between two timespecs */ void timespec_diff(timespec *start, timespec *stop, timespec *result) { long int secDiff = stop->tv_sec - start->tv_sec; long int nsecDiff = stop->tv_nsec - start->tv_nsec; if (secDiff > 0) { if (nsecDiff >= 0) { result->tv_sec = secDiff; result->tv_nsec = nsecDiff; } else if (nsecDiff < 0) { result->tv_sec = --secDiff; result->tv_nsec = 1000000000 + nsecDiff; } } else if (secDiff < 0) { if (nsecDiff >= 0) { result->tv_sec = ++secDiff; result->tv_nsec = -(1000000000 - nsecDiff); } else if (nsecDiff < 0) { result->tv_sec = secDiff; result->tv_nsec = nsecDiff; } } else if (secDiff == 0){ result->tv_sec = secDiff; result->tv_nsec = nsecDiff; } return; }