Support RESET button using "event_gpio". Untested.
authorvolpol <volpol@packet-gain.de>
Fri, 31 Aug 2018 16:06:02 +0000 (18:06 +0200)
committervolpol <volpol@packet-gain.de>
Fri, 31 Aug 2018 16:06:02 +0000 (18:06 +0200)
Makefile
event_gpio.c [new file with mode: 0644]
event_gpio.h [new file with mode: 0644]
main.c

index 1d3fb460353697f98777ae6e316f5b6ba133229e..a167ffb99484a5e87bdae948b42a6ef19bc9227b 100644 (file)
--- 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 (file)
index 0000000..41d139e
--- /dev/null
@@ -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 <pthread.h>
+#include <sys/epoll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#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 (file)
index 0000000..e48cd97
--- /dev/null
@@ -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 59cdc434a540b750a90d70b9b50bb55ea8202b95..b0c62434a33c300a661f2eb1b832a21dacc4310b 100644 (file)
--- a/main.c
+++ b/main.c
@@ -13,6 +13,7 @@
 #include <pthread.h>
 
 #include "c_gpio.h"
+#include "event_gpio.h"
 #include "pump.h"
 #include "mgmt.h"
 #include "hx711.h"
 
 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);