From dd22816f86dab2a79896295bfb81b6b434c4cf07 Mon Sep 17 00:00:00 2001 From: volpol Date: Fri, 31 Aug 2018 18:06:02 +0200 Subject: [PATCH] Support RESET button using "event_gpio". Untested. --- Makefile | 2 +- event_gpio.c | 585 +++++++++++++++++++++++++++++++++++++++++++++++++++ event_gpio.h | 36 ++++ main.c | 26 ++- 4 files changed, 646 insertions(+), 3 deletions(-) create mode 100644 event_gpio.c create mode 100644 event_gpio.h diff --git a/Makefile b/Makefile index 1d3fb46..a167ffb 100644 --- a/Makefile +++ b/Makefile @@ -9,5 +9,5 @@ all: bbd clean: rm -f *.o bbd -bbd: hx711.o c_gpio.o pump.o net.o main.o mgmt.o led.o +bbd: hx711.o c_gpio.o pump.o net.o main.o mgmt.o led.o event_gpio.o $(CC) $(LDFLAGS) -o $@ -pthread $^ diff --git a/event_gpio.c b/event_gpio.c new file mode 100644 index 0000000..41d139e --- /dev/null +++ b/event_gpio.c @@ -0,0 +1,585 @@ +/* +Copyright (c) 2013-2016 Ben Croston + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "event_gpio.h" + +const char *stredge[4] = {"none", "rising", "falling", "both"}; + +struct gpios +{ + unsigned int gpio; + int value_fd; + int exported; + int edge; + int initial_thread; + int initial_wait; + int thread_added; + int bouncetime; + unsigned long long lastcall; + struct gpios *next; +}; +struct gpios *gpio_list = NULL; + +// event callbacks +struct callback +{ + unsigned int gpio; + void (*func)(unsigned int gpio); + struct callback *next; +}; +struct callback *callbacks = NULL; + +pthread_t threads; +int event_occurred[54] = { 0 }; +int thread_running = 0; +int epfd_thread = -1; +int epfd_blocking = -1; + +/************* /sys/class/gpio functions ************/ +int gpio_export(unsigned int gpio) +{ + int fd, len; + char str_gpio[3]; + + if ((fd = open("/sys/class/gpio/export", O_WRONLY)) < 0) + return -1; + + len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio); + write(fd, str_gpio, len); + close(fd); + + return 0; +} + +int gpio_unexport(unsigned int gpio) +{ + int fd, len; + char str_gpio[3]; + + if ((fd = open("/sys/class/gpio/unexport", O_WRONLY)) < 0) + return -1; + + len = snprintf(str_gpio, sizeof(str_gpio), "%d", gpio); + write(fd, str_gpio, len); + close(fd); + + return 0; +} + +int gpio_set_direction(unsigned int gpio, unsigned int in_flag) +{ + int retry; + struct timespec delay; + int fd; + char filename[33]; + + snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/direction", gpio); + + // retry waiting for udev to set correct file permissions + delay.tv_sec = 0; + delay.tv_nsec = 10000000L; // 10ms + for (retry=0; retry<100; retry++) { + if ((fd = open(filename, O_WRONLY)) >= 0) + break; + nanosleep(&delay, NULL); + } + if (retry >= 100) + return -1; + + if (in_flag) + write(fd, "in", 3); + else + write(fd, "out", 4); + + close(fd); + return 0; +} + +int gpio_set_edge(unsigned int gpio, unsigned int edge) +{ + int fd; + char filename[28]; + + snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/edge", gpio); + + if ((fd = open(filename, O_WRONLY)) < 0) + return -1; + + write(fd, stredge[edge], strlen(stredge[edge]) + 1); + close(fd); + return 0; +} + +int open_value_file(unsigned int gpio) +{ + int fd; + char filename[29]; + + // create file descriptor of value file + snprintf(filename, sizeof(filename), "/sys/class/gpio/gpio%d/value", gpio); + if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) + return -1; + return fd; +} + +/********* gpio list functions **********/ +struct gpios *get_gpio(unsigned int gpio) +{ + struct gpios *g = gpio_list; + while (g != NULL) { + if (g->gpio == gpio) + return g; + g = g->next; + } + return NULL; +} + +struct gpios *get_gpio_from_value_fd(int fd) +{ + struct gpios *g = gpio_list; + while (g != NULL) { + if (g->value_fd == fd) + return g; + g = g->next; + } + return NULL; +} + +struct gpios *new_gpio(unsigned int gpio) +{ + struct gpios *new_gpio; + + new_gpio = malloc(sizeof(struct gpios)); + if (new_gpio == 0) { + return NULL; // out of memory + } + + new_gpio->gpio = gpio; + if (gpio_export(gpio) != 0) { + free(new_gpio); + return NULL; + } + new_gpio->exported = 1; + + if (gpio_set_direction(gpio,1) != 0) { // 1==input + free(new_gpio); + return NULL; + } + + if ((new_gpio->value_fd = open_value_file(gpio)) == -1) { + gpio_unexport(gpio); + free(new_gpio); + return NULL; + } + + new_gpio->initial_thread = 1; + new_gpio->initial_wait = 1; + new_gpio->bouncetime = -666; + new_gpio->lastcall = 0; + new_gpio->thread_added = 0; + + if (gpio_list == NULL) { + new_gpio->next = NULL; + } else { + new_gpio->next = gpio_list; + } + gpio_list = new_gpio; + return new_gpio; +} + +void delete_gpio(unsigned int gpio) +{ + struct gpios *g = gpio_list; + struct gpios *temp; + struct gpios *prev = NULL; + + while (g != NULL) { + if (g->gpio == gpio) { + if (prev == NULL) + gpio_list = g->next; + else + prev->next = g->next; + temp = g; + g = g->next; + free(temp); + return; + } else { + prev = g; + g = g->next; + } + } +} + +int gpio_event_added(unsigned int gpio) +{ + struct gpios *g = gpio_list; + while (g != NULL) { + if (g->gpio == gpio) + return g->edge; + g = g->next; + } + return 0; +} + +/******* callback list functions ********/ +int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)) +{ + struct callback *cb = callbacks; + struct callback *new_cb; + + new_cb = malloc(sizeof(struct callback)); + if (new_cb == 0) + return -1; // out of memory + + new_cb->gpio = gpio; + new_cb->func = func; + new_cb->next = NULL; + + if (callbacks == NULL) { + // start new list + callbacks = new_cb; + } else { + // add to end of list + while (cb->next != NULL) + cb = cb->next; + cb->next = new_cb; + } + return 0; +} + +int callback_exists(unsigned int gpio) +{ + struct callback *cb = callbacks; + while (cb != NULL) { + if (cb->gpio == gpio) + return 1; + cb = cb->next; + } + return 0; +} + +void run_callbacks(unsigned int gpio) +{ + struct callback *cb = callbacks; + while (cb != NULL) + { + if (cb->gpio == gpio) + cb->func(cb->gpio); + cb = cb->next; + } +} + +void remove_callbacks(unsigned int gpio) +{ + struct callback *cb = callbacks; + struct callback *temp; + struct callback *prev = NULL; + + while (cb != NULL) + { + if (cb->gpio == gpio) + { + if (prev == NULL) + callbacks = cb->next; + else + prev->next = cb->next; + temp = cb; + cb = cb->next; + free(temp); + } else { + prev = cb; + cb = cb->next; + } + } +} + +void *poll_thread(void *threadarg) +{ + struct epoll_event events; + char buf; + struct timeval tv_timenow; + unsigned long long timenow; + struct gpios *g; + int n; + + thread_running = 1; + while (thread_running) { + n = epoll_wait(epfd_thread, &events, 1, -1); + if (n > 0) { + lseek(events.data.fd, 0, SEEK_SET); + if (read(events.data.fd, &buf, 1) != 1) { + thread_running = 0; + pthread_exit(NULL); + } + g = get_gpio_from_value_fd(events.data.fd); + if (g->initial_thread) { // ignore first epoll trigger + g->initial_thread = 0; + } else { + gettimeofday(&tv_timenow, NULL); + timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec; + if (g->bouncetime == -666 || timenow - g->lastcall > g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) { + g->lastcall = timenow; + event_occurred[g->gpio] = 1; + run_callbacks(g->gpio); + } + } + } else if (n == -1) { + /* If a signal is received while we are waiting, + epoll_wait will return with an EINTR error. + Just try again in that case. */ + if (errno == EINTR) { + continue; + } + thread_running = 0; + pthread_exit(NULL); + } + } + thread_running = 0; + pthread_exit(NULL); +} + +void remove_edge_detect(unsigned int gpio) +{ + struct epoll_event ev; + struct gpios *g = get_gpio(gpio); + + if (g == NULL) + return; + + // delete epoll of fd + + ev.events = EPOLLIN | EPOLLET | EPOLLPRI; + ev.data.fd = g->value_fd; + epoll_ctl(epfd_thread, EPOLL_CTL_DEL, g->value_fd, &ev); + + // delete callbacks for gpio + remove_callbacks(gpio); + + // btc fixme - check return result?? + gpio_set_edge(gpio, NO_EDGE); + g->edge = NO_EDGE; + + if (g->value_fd != -1) + close(g->value_fd); + + // btc fixme - check return result?? + gpio_unexport(gpio); + event_occurred[gpio] = 0; + + delete_gpio(gpio); +} + +int event_detected(unsigned int gpio) +{ + if (event_occurred[gpio]) { + event_occurred[gpio] = 0; + return 1; + } else { + return 0; + } +} + +void event_cleanup(unsigned int gpio) +// gpio of -666 means clean every channel used +{ + struct gpios *g = gpio_list; + struct gpios *temp = NULL; + + while (g != NULL) { + temp = g->next; + if ((gpio == -666) || (g->gpio == gpio)) + remove_edge_detect(g->gpio); + g = temp; + } + if (gpio_list == NULL){ + if (epfd_blocking != -1){ + close(epfd_blocking); + epfd_blocking = -1; + } + if (epfd_thread != -1){ + close(epfd_thread); + epfd_thread = -1; + } + thread_running = 0; + } +} + +void event_cleanup_all(void) +{ + event_cleanup(-666); +} + +int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime) +// return values: +// 0 - Success +// 1 - Edge detection already added +// 2 - Other error +{ + pthread_t threads; + struct epoll_event ev; + long t = 0; + struct gpios *g; + int i = -1; + + i = gpio_event_added(gpio); + if (i == 0) { // event not already added + if ((g = new_gpio(gpio)) == NULL) + return 2; + + gpio_set_edge(gpio, edge); + g->edge = edge; + g->bouncetime = bouncetime; + } else if (i == edge) { // get existing event + g = get_gpio(gpio); + if ((bouncetime != -666 && g->bouncetime != bouncetime) || // different event bouncetime used + (g->thread_added)) // event already added + return 1; + } else { + return 1; + } + + // create epfd_thread if not already open + if ((epfd_thread == -1) && ((epfd_thread = epoll_create(1)) == -1)) + return 2; + + // add to epoll fd + ev.events = EPOLLIN | EPOLLET | EPOLLPRI; + ev.data.fd = g->value_fd; + if (epoll_ctl(epfd_thread, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) { + remove_edge_detect(gpio); + return 2; + } + g->thread_added = 1; + + // start poll thread if it is not already running + if (!thread_running) { + if (pthread_create(&threads, NULL, poll_thread, (void *)t) != 0) { + remove_edge_detect(gpio); + return 2; + } + } + return 0; +} + +int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout) +// return values: +// 1 - Success (edge detected) +// 0 - Timeout +// -1 - Edge detection already added +// -2 - Other error +{ + int n, ed; + struct epoll_event events, ev; + char buf; + struct gpios *g = NULL; + struct timeval tv_timenow; + unsigned long long timenow; + int finished = 0; + int initial_edge = 1; + + if (callback_exists(gpio)) + return -1; + + // add gpio if it has not been added already + ed = gpio_event_added(gpio); + if (ed == edge) { // get existing record + g = get_gpio(gpio); + if (g->bouncetime != -666 && g->bouncetime != bouncetime) { + return -1; + } + } else if (ed == NO_EDGE) { // not found so add event + if ((g = new_gpio(gpio)) == NULL) { + return -2; + } + gpio_set_edge(gpio, edge); + g->edge = edge; + g->bouncetime = bouncetime; + } else { // ed != edge - event for a different edge + g = get_gpio(gpio); + gpio_set_edge(gpio, edge); + g->edge = edge; + g->bouncetime = bouncetime; + g->initial_wait = 1; + } + + // create epfd_blocking if not already open + if ((epfd_blocking == -1) && ((epfd_blocking = epoll_create(1)) == -1)) { + return -2; + } + + // add to epoll fd + ev.events = EPOLLIN | EPOLLET | EPOLLPRI; + ev.data.fd = g->value_fd; + if (epoll_ctl(epfd_blocking, EPOLL_CTL_ADD, g->value_fd, &ev) == -1) { + return -2; + } + + // wait for edge + while (!finished) { + n = epoll_wait(epfd_blocking, &events, 1, timeout); + if (n == -1) { + /* If a signal is received while we are waiting, + epoll_wait will return with an EINTR error. + Just try again in that case. */ + if (errno == EINTR) { + continue; + } + epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev); + return -2; + } + if (initial_edge) { // first time triggers with current state, so ignore + initial_edge = 0; + } else { + gettimeofday(&tv_timenow, NULL); + timenow = tv_timenow.tv_sec*1E6 + tv_timenow.tv_usec; + if (g->bouncetime == -666 || timenow - g->lastcall > g->bouncetime*1000 || g->lastcall == 0 || g->lastcall > timenow) { + g->lastcall = timenow; + finished = 1; + } + } + } + + // check event was valid + if (n > 0) { + lseek(events.data.fd, 0, SEEK_SET); + if ((read(events.data.fd, &buf, 1) != 1) || (events.data.fd != g->value_fd)) { + epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev); + return -2; + } + } + + epoll_ctl(epfd_blocking, EPOLL_CTL_DEL, g->value_fd, &ev); + if (n == 0) { + return 0; // timeout + } else { + return 1; // edge found + } +} diff --git a/event_gpio.h b/event_gpio.h new file mode 100644 index 0000000..e48cd97 --- /dev/null +++ b/event_gpio.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2013-2015 Ben Croston + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#define NO_EDGE 0 +#define RISING_EDGE 1 +#define FALLING_EDGE 2 +#define BOTH_EDGE 3 + +int add_edge_detect(unsigned int gpio, unsigned int edge, int bouncetime); +void remove_edge_detect(unsigned int gpio); +void remove_callbacks(unsigned int gpio); +int add_edge_callback(unsigned int gpio, void (*func)(unsigned int gpio)); +int event_detected(unsigned int gpio); +int gpio_event_added(unsigned int gpio); +void event_cleanup(unsigned int gpio); +void event_cleanup_all(void); +int blocking_wait_for_edge(unsigned int gpio, unsigned int edge, int bouncetime, int timeout); diff --git a/main.c b/main.c index 59cdc43..b0c6243 100644 --- a/main.c +++ b/main.c @@ -13,6 +13,7 @@ #include #include "c_gpio.h" +#include "event_gpio.h" #include "pump.h" #include "mgmt.h" #include "hx711.h" @@ -22,16 +23,29 @@ static volatile int force_quit; +#define FQ_NONE 0 +#define FQ_RESET 1 +#define FQ_EXIT 2 + static void handle_signal(int sig){ fprintf (stderr, "SIGNAL: %d\n", sig); switch (sig){ case SIGTERM: case SIGINT: - force_quit = 1; + force_quit = FQ_EXIT; break; } } +#define GPIO_RESET 14 +#define RESET_BOUNCE_TIME 10 + +static void reset_cb(unsigned int gpio){ + if (GPIO_RESET != gpio) return; + fprintf (stderr, "RESET EVENT on GPIO %u\n",gpio); + force_quit = FQ_RESET; +} + //0 - ready, >0 - busy, -1 - err/unknown static volatile int mix_state; @@ -151,7 +165,11 @@ int bbd(void){ } mgmt_init(); status_ready(); - while (!force_quit){ + while (force_quit != FQ_EXIT){ + if (FQ_RESET == force_quit){ + force_quit = FQ_NONE; + mix_state = 0; + } if ((as = net_accept( ss )) > 0){ switch (mix_state){ case 0: @@ -198,8 +216,12 @@ int main(void){ //initialize gpio susbsystem if (SETUP_OK == (err = setup())){ led_init(); + add_edge_callback (GPIO_RESET, reset_cb); + add_edge_detect (GPIO_RESET, FALLING_EDGE, RESET_BOUNCE_TIME); err = bbd(); led_deinit(); + remove_callbacks(GPIO_RESET); + event_cleanup_all(); cleanup(); } else { fprintf (stderr, "setup: %d\n", err); -- 2.30.2