Moin,
da ich für meinen Gaszähler eine kleine Lichtschranke gebaut habe (auf der 0,006 ist ein kleiner Spiegel), und diese an den GPIO-Pin meines ARM-Boards gehängt habe, brauchte ich eine Software, die das Vorbeiziehen der 0,006 mitzählt.
Natürlich habe ich es mit OpenHAB versucht, aber leider wurden damit viele Durchläufe verpasst (obwohl die 0,006 selbst bei Therme und Herd gleichzeitig nur alle 20-30s vorbeikommt, dann allerdings auch nur etwa 1-2s (das sind ja nur 1000-2000ms ;-) ) die Lichtschranke auslöst).
Daher habe ich ein kleines C-Programm geschrieben (zusammenkopiert), welches nun direkt in (m)eine InfluxDB-Datenbank schreibt. Da das ganze nun gut funktioniert, wollte ich das Progrämmchen gerne teilen.
Ich weiß, es hat nicht direkt mit OpenHAB zu tun, aber da die InfluxDB ja dann von OpenHAB aus genutzt werden kann, sehe ich es schon als eine Art Erweiterung an.
Um es unabhängig ständig laufen zu lassen, starte ich es mit "nohup". Und da es Zugriff auf GPIO braucht, muss es als "root" laufen.
da ich für meinen Gaszähler eine kleine Lichtschranke gebaut habe (auf der 0,006 ist ein kleiner Spiegel), und diese an den GPIO-Pin meines ARM-Boards gehängt habe, brauchte ich eine Software, die das Vorbeiziehen der 0,006 mitzählt.
Natürlich habe ich es mit OpenHAB versucht, aber leider wurden damit viele Durchläufe verpasst (obwohl die 0,006 selbst bei Therme und Herd gleichzeitig nur alle 20-30s vorbeikommt, dann allerdings auch nur etwa 1-2s (das sind ja nur 1000-2000ms ;-) ) die Lichtschranke auslöst).
Daher habe ich ein kleines C-Programm geschrieben (zusammenkopiert), welches nun direkt in (m)eine InfluxDB-Datenbank schreibt. Da das ganze nun gut funktioniert, wollte ich das Progrämmchen gerne teilen.
Ich weiß, es hat nicht direkt mit OpenHAB zu tun, aber da die InfluxDB ja dann von OpenHAB aus genutzt werden kann, sehe ich es schon als eine Art Erweiterung an.
Um es unabhängig ständig laufen zu lassen, starte ich es mit "nohup". Und da es Zugriff auf GPIO braucht, muss es als "root" laufen.
Code:
/* This program was developed on basis of software by RidgeRun, found here: * * https://www.ridgerun.com/developer/wiki/index.php/Gpio-int-test.c * * who provided the polling of GPIO interrupt, and Alexandre Jasmin, found here: * * http://stackoverflow.com/questions/2329571/c-libcurl-get-output-into-a-string * * who provided libcurl usage (for accessing InfluxDB). * * Under Linux, compile by running * gcc -o countGPIO countGPIO.c -L/usr/lib/arm-linux-gnueabihf -lcurl * * or find your libcurl parameters by running * curl-config --libs * * If your (Debian) Linux does not come with libcurl, run * apt-cache search libcurl * * and install a libcurl-dev of your choice. * * * * parts of this Copyright (c) 2011, RidgeRun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the RidgeRun. * 4. Neither the name of the RidgeRun nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY RIDGERUN ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL RIDGERUN BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/syscall.h> #include <curl/curl.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <poll.h> /**************************************************************** * Constants ****************************************************************/ #define SYSFS_GPIO_DIR "/sys/class/gpio" #define POLL_TIMEOUT (3 * 1000) /* 3 seconds */ #define MAX_BUF 64 /**************************************************************** * gpio_export ****************************************************************/ int gpio_export(unsigned int gpio) { int fd, len; char buf[MAX_BUF]; fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY); if (fd < 0) { perror("gpio/export"); return fd; } len = snprintf(buf, sizeof(buf), "%d", gpio); write(fd, buf, len); close(fd); return 0; } /**************************************************************** * gpio_unexport ****************************************************************/ int gpio_unexport(unsigned int gpio) { int fd, len; char buf[MAX_BUF]; fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY); if (fd < 0) { perror("gpio/export"); return fd; } len = snprintf(buf, sizeof(buf), "%d", gpio); write(fd, buf, len); close(fd); return 0; } /**************************************************************** * gpio_set_dir ****************************************************************/ int gpio_set_dir(char* gpio, unsigned int out_flag) { int fd, len; char buf[MAX_BUF]; len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%s/direction", gpio); fd = open(buf, O_WRONLY); if (fd < 0) { perror("gpio/direction"); return fd; } if (out_flag) write(fd, "out", 4); else write(fd, "in", 3); close(fd); return 0; } /**************************************************************** * gpio_set_value ****************************************************************/ int gpio_set_value(char* gpio, unsigned int value) { int fd, len; char buf[MAX_BUF]; len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%s/value", gpio); fd = open(buf, O_WRONLY); if (fd < 0) { perror("gpio/set-value"); return fd; } if (value) write(fd, "1", 2); else write(fd, "0", 2); close(fd); return 0; } /**************************************************************** * gpio_get_value ****************************************************************/ int gpio_get_value(char* gpio, unsigned int *value) { int fd, len; char buf[MAX_BUF]; char ch; len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%s/value", gpio); fd = open(buf, O_RDONLY); if (fd < 0) { perror("gpio/get-value"); return fd; } read(fd, &ch, 1); if (ch != '0') { *value = 1; } else { *value = 0; } close(fd); return 0; } /**************************************************************** * gpio_set_edge ****************************************************************/ int gpio_set_edge(char* gpio, char *edge) { int fd, len; char buf[MAX_BUF]; len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%s/edge", gpio); fd = open(buf, O_WRONLY); if (fd < 0) { perror("gpio/set-edge"); return fd; } write(fd, edge, strlen(edge) + 1); close(fd); return 0; } /**************************************************************** * gpio_fd_open ****************************************************************/ int gpio_fd_open(char* gpio) { int fd, len; char buf[MAX_BUF]; len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%s/value", gpio); fd = open(buf, O_RDONLY | O_NONBLOCK ); if (fd < 0) { perror("gpio/fd_open"); } return fd; } /**************************************************************** * gpio_fd_close ****************************************************************/ int gpio_fd_close(int fd) { return close(fd); } /**************************************************************** * Write counter to InfluxDB via Curl call ****************************************************************/ int writeInfluxDB(CURL *curl, char* database, char* series, long int counter) { CURLcode res; char data[160]; if(curl) { curl_easy_setopt(curl, CURLOPT_URL, database); sprintf(data, "[{\"name\":\"%s\",\"columns\":[\"value\"],\"points\":[[%d]]}]", series, counter); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); /* Perform the request, res will get the return code */ res = curl_easy_perform(curl); /* Check for errors */ if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } } /**************************************************************** * Read last InfluxDB value into counter via Curl call ****************************************************************/ struct string { char *ptr; size_t len; }; void init_string(struct string *text) { text->len = 0; text->ptr = malloc(text->len+1); if (text->ptr == NULL) { fprintf(stderr, "malloc() failed\n"); exit(EXIT_FAILURE); } text->ptr[0] = '\0'; } size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *text) //wird an curl_easy_setopt übergeben { size_t new_len = text->len + size*nmemb; text->ptr = realloc(text->ptr, new_len+1); if (text->ptr == NULL) { fprintf(stderr, "realloc() failed\n"); exit(EXIT_FAILURE); } memcpy(text->ptr+text->len, ptr, size*nmemb); text->ptr[new_len] = '\0'; text->len = new_len; return size*nmemb; } long int readInfluxDB(CURL *curl, char* database, char* series) { CURLcode res; long int counter = -1; char data[255]; if(curl) { struct string text; init_string(&text); sprintf(data, "%s&q=select+*+from+%s+limit+1", database, series); curl_easy_setopt(curl, CURLOPT_URL, data); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &text); res = curl_easy_perform(curl); if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); else { char zahlString[]=" "; printf("Received string: %s\n",text.ptr); char* ziffer = strrchr(text.ptr, ','); ziffer++; strncpy(zahlString, ziffer, strspn(ziffer,"0123456789")); printf("Extracted value: %s\n",zahlString); counter = atoi(zahlString); } } return counter; } /**************************************************************** * Main ****************************************************************/ int main(int argc, char **argv, char **envp) { struct pollfd fdset[2]; int nfds = 2; int gpio_fd, timeout, rc; char *buf[MAX_BUF]; unsigned int gpio; char gpioName[16]; int len; int systemCall; CURL *curl; char database[128]; char series[64]; long int counter = 0; unsigned int tickTack = 0; // 0 == tick; 1 == tack if (argc < 5 || strlen(argv[2]) > 15 || strlen(argv[3]) > 127 || strlen(argv[4]) > 63) { printf("Usage: countGPIO <gpio-pin> <gpio-pin-name> <database-URL> <series>\n"); printf("\ngpio-pin-name: /sys/class/gpio/gpioXXXX <- XXXX is the pin-name.\n"); printf("Database-URL e.g.: \"http://localhost:8086/db/yourdb/series?u=user&p=passwd\"\n"); printf("Counts GPIO pin value changes (1 -> 0) and writes count to InfluxDB.\n"); printf("Counts only slow changes of 1s or longer!\n"); printf("Only works on pins with interrupt: /sys/class/gpio/gpio_nn/edge must be present.\n"); printf("Parts of this program Copyright (c) 2011, RidgeRun\n"); printf("No distribution without source code!\n"); exit(-1); } gpio = atoi(argv[1]); strcpy(gpioName, argv[2]); strcpy(database, argv[3]); strcpy(series, argv[4]); gpio_export(gpio); // export pin gpio_set_dir(gpioName, 0); // set pin for input gpio_set_edge(gpioName, "rising"); // set edge gpio_fd = gpio_fd_open(gpioName); // open GPIO "file" curl = curl_easy_init(); // init curl printf("Attempt to query database <%s> . . .\n",database); counter = readInfluxDB(curl, database, series); // read last value of series in database timeout = POLL_TIMEOUT; while (1) { memset((void*)fdset, 0, sizeof(fdset)); /* fdset[0].fd = STDIN_FILENO; fdset[0].events = POLLIN; */ fdset[1].fd = gpio_fd; fdset[1].events = POLLPRI; rc = poll(fdset, nfds, timeout); // start polling for interrupt if (rc < 0) { printf("\npoll() failed!\n"); return -1; } /* if (rc == 0) { printf("."); }*/ if (fdset[1].revents & POLLPRI) { len = read(fdset[1].fd, buf, MAX_BUF); printf("\npoll() GPIO %d interrupt occurred\n", gpio); unsigned int highLow; gpio_get_value(gpioName, &highLow); // read value of GPIO pin printf("Value is %d\n",highLow); sleep(1); // wait 1 second, till edge is over and high or low reached // If you count a faster change, comment it out or try // shorter pause. if (highLow != tickTack) { // if the value has changed if (tickTack == 0) tickTack = 1; // set tickTack to 1 first else { // after change back to original tickTack = 0; // state of value increase counter. counter++; writeInfluxDB(curl, database, series, counter); printf("Written <<%d>> to InfluxDB.\n",counter); } } if (systemCall == -1) printf("\nSystem Call failed!\n"); } /* if (fdset[0].revents & POLLIN) { (void)read(fdset[0].fd, buf, 1); printf("\npoll() stdin read 0x%2.2X\n", (unsigned int) buf[0]); }*/ fflush(stdout); } gpio_fd_close(gpio_fd); if (curl) curl_easy_cleanup(curl); return 0; }