From 5c3f29e773c1c31188b65694ed7ab219b524bb10 Mon Sep 17 00:00:00 2001 From: sfeakes Date: Sat, 26 Sep 2020 17:16:36 -0500 Subject: [PATCH] Version 1.1 --- GPIO_Pi.c | 1473 ++++++++++++++++++++++++++++++++++ GPIO_Pi.h | 162 ++++ Makefile | 6 +- config.c | 2 +- json_messages.c | 2 +- net_services.c | 2 +- release/gpio | Bin 19500 -> 22012 bytes release/gpio_monitor | Bin 19596 -> 18668 bytes release/install-new.sh | 162 ++++ release/sprinklerd | Bin 192116 -> 192000 bytes release/sprinklerd.test.conf | 73 +- sd_GPIO.c => sd_GPIO.c.old | 0 sd_GPIO.h => sd_GPIO.h.old | 0 sprinkler.c | 6 +- version.h | 2 +- zone_ctrl.c | 30 +- 16 files changed, 1868 insertions(+), 52 deletions(-) create mode 100644 GPIO_Pi.c create mode 100644 GPIO_Pi.h create mode 100755 release/install-new.sh rename sd_GPIO.c => sd_GPIO.c.old (100%) rename sd_GPIO.h => sd_GPIO.h.old (100%) diff --git a/GPIO_Pi.c b/GPIO_Pi.c new file mode 100644 index 0000000..ea9d9d4 --- /dev/null +++ b/GPIO_Pi.c @@ -0,0 +1,1473 @@ +/* + * Copyright (c) 2017 Shaun Feakes - All rights reserved + * + * You may use redistribute and/or modify this code under the terms of + * the GNU General Public License version 2 as published by the + * Free Software Foundation. For the terms of this license, + * see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * https://github.com/sfeakes/GPIO_pi + */ + +/********************-> GPIO Pi v1.2 <-********************/ + + +/* +* Note, all referances to pin in this code is the GPIO pin# not the physical pin# +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "GPIO_Pi.h" + +#ifndef GPIO_ERR_LOG_DISABELED + //#define LOG_ERROR(fmt, ...) log_message (true, fmt, ##__VA_ARGS__) + #define LOG_ERROR(fmt, ...) log_message (true, "%s: " fmt, _GPIO_pi_NAME_,##__VA_ARGS__) +#else + #define LOG_ERROR(...) {} +#endif + +#ifdef GPIO_LOG_ENABLED + //#define LOG_ERROR(fmt, ...) log_message (false, fmt, ##__VA_ARGS__) + #define LOG(fmt, ...) log_message (false, "%s: " fmt, _GPIO_pi_NAME_,##__VA_ARGS__) +#else + #define LOG(...) {} +#endif + +#ifdef GPIO_DEBUG + #define DEBUG(fmt, ...) log_message (false, "%s: (%s) " fmt, _GPIO_pi_NAME_,"DEBUG",##__VA_ARGS__) +#else + #define DEBUG(...) {} +#endif + + +#ifdef GPIO_SYSFS_MODE + #define GPIO_SYSFS_INTERRUPT +#endif + +const char *_piModelNames [18] = + { + "Model A", // 0 + "Model B", // 1 + "Model A+", // 2 + "Model B+", // 3 + "Pi 2", // 4 + "Alpha", // 5 + "CM", // 6 + "Unknown #07",// 07 + "Pi 3b", // 08 + "Pi Zero", // 09 + "CM3", // 10 + "Unknown #11",// 11 + "Pi Zero-W", // 12 + "Pi 3b+", // 13 + "Pi 3a+", // 14 + "Unknown #15",// 15 + "Pi CM3+", // 16 + "Pi 4b", // 17 + } ; + +static bool _ever = false; +static bool _supressLogging = false; +//static bool _GPIO_setup = false; +static bool _usingGpioMem = false; + +/* RPI 4 has different pullup registers - we need to know if we have that type */ +static bool _pud_type_rpi4 = false; +//static uint8_t _pud_type_rpi4 = 0; + +/* RPI 4 has different pullup operation - make backwards compat */ +//static uint8_t _pud_compat_setting = PUD_OFF; + +//#define GPIOrunning(X) ((X) <= (GPIO_MAX) ? ( ((X) >= (GPIO_MIN) ? (1) : (0)) ) : (0)) +#define GPIOrunning() (_ever) + +void gpioDelay (unsigned int howLong); + +void log_message(bool critical, char *format, ...) +{ + if (_supressLogging && !critical) + return; + + va_list arglist; + va_start( arglist, format ); + + // if terminal don't log to syslod + if ( ! isatty(1) ) { + openlog(_GPIO_pi_NAME_, LOG_NDELAY, LOG_DAEMON); + vsyslog(critical?LOG_ERR:LOG_INFO, format, arglist); + closelog(); + } else if (critical == false) { + vfprintf(stdout, format, arglist ); + fflush(stdout); + } + + // Always send critical errors to stderr + if (critical) { + vfprintf(stderr, format, arglist ); + } + + va_end( arglist ); +} + +void printVersionInformation() +{ +#ifdef GPIO_SYSFS_MODE + LOG ("(sysfs) v%s\n",_GPIO_pi_VERSION_); +#else + LOG ("v%s\n",_GPIO_pi_VERSION_); +#endif +} + +void gpioDelay (unsigned int howLong) // Microseconds (1000000 = 1 second) +{ + struct timespec sleeper, dummy ; + + sleeper.tv_sec = (time_t)(howLong / 1000) ; + sleeper.tv_nsec = (long)(howLong % 1000) * 1000000 ; + + nanosleep (&sleeper, &dummy) ; +} + +#ifndef GPIO_SYSFS_MODE + +static volatile uint32_t * _gpioReg = MAP_FAILED; + +int piBoardId () +{ + FILE *cpuFd ; + char line [120] ; + char *c ; + unsigned int revision ; + //int bRev, bType, bProc, bMfg, bMem, bWarranty; + int bType; + + if ((cpuFd = fopen ("/proc/cpuinfo", "r")) == NULL) + LOG_ERROR ( "Unable to open /proc/cpuinfo") ; + + while (fgets (line, 120, cpuFd) != NULL) + if (strncmp (line, "Revision", 8) == 0) + break ; + + fclose (cpuFd) ; + + if (strncmp (line, "Revision", 8) != 0) + LOG_ERROR ( "Unable to determin pi Board \"Revision\" line") ; + +// Chomp trailing CR/NL + for (c = &line [strlen (line) - 1] ; (*c == '\n') || (*c == '\r') ; --c) + *c = 0 ; + + DEBUG ( "pi Board Revision string: %s\n", line) ; + +// Scan to the first character of the revision number + + for (c = line ; *c ; ++c) + if (*c == ':') + break ; + + if (*c != ':') + LOG_ERROR ( "Unknown pi Board \"Revision\" line (no colon)") ; + +// Chomp spaces + + ++c ; + while (isspace (*c)) + ++c ; + + if (!isxdigit (*c)) + LOG_ERROR ( "Unknown pi Board \"Revision\" line (no hex digit at start of revision)") ; + + revision = (unsigned int)strtol (c, NULL, 16) ; // Hex number with no leading 0x + + if ((revision & (1 << 23)) != 0) // New way + { + /* + bRev = (revision & (0x0F << 0)) >> 0 ;*/ + bType = (revision & (0xFF << 4)) >> 4 ; + /* + bProc = (revision & (0x0F << 12)) >> 12 ; // Not used for now. + bMfg = (revision & (0x0F << 16)) >> 16 ; + bMem = (revision & (0x07 << 20)) >> 20 ; + bWarranty = (revision & (0x03 << 24)) != 0 ; + */ + + LOG ("pi Board Model: %s\n", _piModelNames[bType]) ; + + return bType; + } + + LOG_ERROR ( "pi Board Model: UNKNOWN\n"); + return PI_MODEL_UNKNOWN; +} + +bool gpioSetup() { + int fd; + unsigned int piGPIObase = 0; + unsigned int piGPIOlen = GPIO_LEN; + + printVersionInformation(); + + switch ( piBoardId() ) + { + case PI_MODEL_A: + case PI_MODEL_B: + case PI_MODEL_AP: + case PI_MODEL_BP: + case PI_ALPHA: + case PI_MODEL_CM: + case PI_MODEL_ZERO: + case PI_MODEL_ZERO_W: + //case PI_MODEL_UNKNOWN: + piGPIObase = (GPIO_BASE_P1 + GPIO_OFFSET); + piGPIOlen = GPIO_LEN; + break ; + + case PI_MODEL_4B: + piGPIObase = (GPIO_BASE_P4 + GPIO_OFFSET); + piGPIOlen = GPIO_LEN_P4; + _pud_type_rpi4 = 1; + break ; + + default: // Pi 2 and 3 + piGPIObase = (GPIO_BASE_P2 + GPIO_OFFSET); + piGPIOlen = GPIO_LEN; + break ; + } + + //fd = open("/dev/mem", O_RDWR | O_SYNC); + + if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC)) < 0) + { + //LOG_ERROR ( "Failed to open '/dev/mem' for GPIO access (are we root?)\n"); + //return false; + // Something odd going on with writes on /dev/gpiomem, need to fix this. + if ((fd = open ("/dev/gpiomem", O_RDWR | O_SYNC | O_CLOEXEC) ) >= 0) // We're using gpiomem + { + piGPIObase = 0 ; + _usingGpioMem = true ; + LOG ("Using /dev/gpiomem!\n"); + } else { + LOG_ERROR ( "Failed to open '/dev/mem' or '/dev/gpiomem' for GPIO access (are we root?)\n"); + return false; + } + } + + _gpioReg = mmap + ( + 0, + piGPIOlen, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_SHARED|MAP_LOCKED, + fd, + piGPIObase); + + close(fd); + + _ever = true; + //_GPIO_setup = true; + + return true; +} + +int pinMode(unsigned int gpio, unsigned int mode) { + int reg, shift; + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! GPIOrunning()) + return GPIO_ERR_NOT_SETUP; + + if (mode < INPUT || mode > IO_ALT3) + return GPIO_NOT_IO_MODE; + + reg = gpio / 10; + shift = (gpio % 10) * 3; + + _gpioReg[reg] = (_gpioReg[reg] & ~(7 << shift)) | (mode << shift); + + return GPIO_OK; +} + +int getPinMode(unsigned int gpio) { + int reg, shift; + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! GPIOrunning()) + return GPIO_ERR_NOT_SETUP; + + reg = gpio / 10; + shift = (gpio % 10) * 3; + + //DEBUG("getPinMode read %d from GPIO %d\n",(*(_gpioReg + reg) >> shift) & 7,gpio); + + return (*(_gpioReg + reg) >> shift) & 7; +} + +int digitalRead(unsigned int gpio) { + unsigned int bank, bit; + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! GPIOrunning()) + return GPIO_ERR_NOT_SETUP; + + bank = gpio >> 5; + bit = (1 << (gpio & 0x1F)); + + if ((*(_gpioReg + GPLEV0 + bank) & bit) != LOW) + return HIGH; + else + return LOW; +} + +int digitalWrite(unsigned int gpio, unsigned int level) { + unsigned int bank, bit; + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! GPIOrunning()) + return GPIO_ERR_NOT_SETUP; + + bank = gpio >> 5; + bit = (1 << (gpio & 0x1F)); + + DEBUG("Writing %d to GPIO %d\n",level==LOW?0:1,gpio); + + if (level == LOW) + *(_gpioReg + GPCLR0 + bank) = bit; + else + *(_gpioReg + GPSET0 + bank) = bit; + + return GPIO_OK; +} + +int setPullUpDown_Pi4(unsigned int gpio, unsigned int pud) +{ + if( _pud_type_rpi4 ) + { + unsigned int pull, bit; + + LOG("Pi4 setPullUpDown() expermental support!"); + + switch (pud) + { + case PUD_OFF: pull = 0; break; + case PUD_UP: pull = 1; break; + case PUD_DOWN: pull = 2; break; + default: + return GPIO_ERR_GENERAL; + break; + } + int shift = (gpio & 0xf) << 1; + + bit = *(_gpioReg + GPPUPPDN0 + (gpio>>4)); + bit &= ~(3 << shift); + bit |= (pull << shift); + *(_gpioReg + GPPUPPDN0 + (gpio>>4)) = bit; + + return GPIO_OK; + } + + return GPIO_ERR_GENERAL; +} + +int setPullUpDown(unsigned int gpio, unsigned int pud) +{ + unsigned int bank, bit; + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! GPIOrunning()) + return GPIO_ERR_NOT_SETUP; + + if (pud > PUD_UP || pud < PUD_OFF) + return GPIO_ERR_GENERAL; + + if( _pud_type_rpi4 ) { + return setPullUpDown_Pi4(gpio, pud); + } + + bank = gpio >> 5; + bit = (1 << (gpio & 0x1F)); + + *(_gpioReg + GPPUD) = pud; + gpioDelay(1); + *(_gpioReg + GPPUDCLK0 + bank) = bit; + gpioDelay(1); + *(_gpioReg + GPPUD) = 0; + + *(_gpioReg + GPPUDCLK0 + bank) = 0; + + return GPIO_OK; +} + + + +#else // If GPIO_SYSFS_MODE + +// There is no need to setup GPIO memory in sysfs mode, so simply set setup state. +bool gpioSetup() { + printVersionInformation(); + _ever = true; + return true; +} + +int setPullUpDown(unsigned int gpio, unsigned int pud) { + LOG_ERROR("setPullUpDown() not supported in sysfs mode"); + return GPIO_ERR_GENERAL; +} + +int pinMode (unsigned int pin, unsigned int mode) +{ + //printVersionInformation(); + //static const char s_directions_str[] = "in\0out\0"; + + if (! validGPIO(pin)) + return GPIO_ERR_BAD_PIN; + + if (! isExported(pin)) + return GPIO_NOT_EXPORTED; + + if (mode != INPUT && mode != OUTPUT) + return GPIO_NOT_IO_MODE; + + char path[SYSFS_PATH_MAX]; + int fd; + + /* + if ( pinExport(pin) != true) { + LOG ("start pinMode (pinExport) failed\n"); + return false; + } +*/ + snprintf(path, SYSFS_PATH_MAX, "/sys/class/gpio/gpio%d/direction", pin); + fd = open(path, O_WRONLY); + if (-1 == fd) { + //fprintf(stderr, "Failed to open gpio direction for writing!\n"); + LOG_ERROR ( "Failed to open gpio '%s' for writing!\n",path); + return GPIO_ERR_IO; + } + + //if (-1 == write(fd, &s_directions_str[INPUT == mode ? 0 : 3], INPUT == mode ? 2 : 3)) { + if (-1 == write(fd, (INPUT==mode?"in\n":"out\n"),(INPUT==mode?3:4))) { + //fprintf(stderr, "Failed to set direction!\n"); + LOG_ERROR ( "Failed to setup gpio input/output on '%s'!\n",path); + LOG_ERROR ( "Error (%d) - %s\n",errno, strerror (errno)); + //displayLastSystemError(""); + return GPIO_ERR_IO; + } + + close(fd); + + return GPIO_OK; +} + +int getPinMode(unsigned int gpio) { + char path[SYSFS_PATH_MAX]; + char value_str[SYSFS_READ_MAX]; + int fd; + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! isExported(gpio)) + return GPIO_NOT_EXPORTED; + + snprintf(path, SYSFS_PATH_MAX, "/sys/class/gpio/gpio%d/direction", gpio); + fd = open(path, O_RDONLY); + if (-1 == fd) { + //fprintf(stderr, "Failed to open gpio direction for writing!\n"); + LOG_ERROR ( "Failed to open gpio '%s' for reading!\n",path); + return GPIO_ERR_IO; + } + + if (-1 == read(fd, value_str, SYSFS_READ_MAX)) { + //fprintf(stderr, "Failed to read value!\n"); + LOG_ERROR ( "Failed to read value on '%s'!\n",path); + LOG_ERROR ( "Error (%d) - %s\n",errno, strerror (errno)); + //displayLastSystemError(""); + return GPIO_ERR_IO; + } + + close(fd); + + if (strncasecmp(value_str, "out", 3)==0) + return OUTPUT; + + return INPUT; +} + +int digitalRead (unsigned int gpio) +{ + char path[SYSFS_PATH_MAX]; + char value_str[SYSFS_READ_MAX]; + int fd; + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! isExported(gpio)) + return GPIO_NOT_EXPORTED; + + snprintf(path, SYSFS_PATH_MAX, "/sys/class/gpio/gpio%d/value", gpio); + fd = open(path, O_RDONLY); + if (-1 == fd) { + //fprintf(stderr, "Failed to open gpio value for reading!\n"); + LOG_ERROR ( "Failed to open gpio '%s' for reading!\n",path); + return GPIO_ERR_IO; + } + + if (-1 == read(fd, value_str, SYSFS_READ_MAX)) { + //fprintf(stderr, "Failed to read value!\n"); + LOG_ERROR ( "Failed to read value on '%s'!\n",path); + LOG_ERROR ( "Error (%d) - %s\n",errno, strerror (errno)); + //displayLastSystemError(""); + return GPIO_ERR_IO; + } + + close(fd); + + return(atoi(value_str)); +} + +int digitalWrite (unsigned int gpio, unsigned int value) +{ + //static const char s_values_str[] = "01"; + + char path[SYSFS_PATH_MAX]; + int fd; + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! isExported(gpio)) + return GPIO_NOT_EXPORTED; + + if (getPinMode(gpio) != OUTPUT) + return GPIO_NOT_OUTPUT; + + snprintf(path, SYSFS_PATH_MAX, "/sys/class/gpio/gpio%d/value", gpio); + fd = open(path, O_WRONLY); + if (-1 == fd) { + //fprintf(stderr, "Failed to open gpio value for writing!\n"); + LOG_ERROR ( "Failed to open gpio '%s' for writing!\n",path); + return GPIO_ERR_IO; + } + + //if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1)) { + if (1 != write(fd, (LOW==value?"0":"1"), 1)) { + //fprintf(stderr, "Failed to write value!\n"); + LOG_ERROR ( "Failed to write value to '%s'!\n",path); + LOG_ERROR ( "Error (%d) - %s\n",errno, strerror (errno)); + //displayLastSystemError(""); + return GPIO_ERR_IO; + } + + close(fd); + return GPIO_OK; +} +#endif // GPIO_SYSFS_MODE + +#ifdef GPIO_SYSFS_INTERRUPT + +bool isExported(unsigned int pin) +{ + char path[SYSFS_PATH_MAX]; + struct stat sb; + + snprintf(path, SYSFS_PATH_MAX, "/sys/class/gpio/gpio%d/", pin); + + if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + return true; + } else { + return false; + } +} + +int pinExport(unsigned int pin) +{ + + char buffer[SYSFS_READ_MAX]; + ssize_t bytes_written; + int fd; + + if (! validGPIO(pin)) + return GPIO_ERR_BAD_PIN; + + fd = open("/sys/class/gpio/export", O_WRONLY); + if (-1 == fd) { + //fprintf(stderr, "Failed to open export for writing!\n"); + LOG_ERROR ( "Failed to open '/sys/class/gpio/export' for writing!\n"); + return GPIO_ERR_IO; + } + + bytes_written = snprintf(buffer, SYSFS_READ_MAX, "%d", pin); + write(fd, buffer, bytes_written); + close(fd); + return GPIO_OK; +} + +int pinUnexport(unsigned int pin) +{ + char buffer[SYSFS_READ_MAX]; + ssize_t bytes_written; + int fd; + + if (! validGPIO(pin)) + return GPIO_ERR_BAD_PIN; + + if (! isExported(pin)) + return GPIO_NOT_EXPORTED; + + fd = open("/sys/class/gpio/unexport", O_WRONLY); + if (-1 == fd) { + //fprintf(stderr, "Failed to open unexport for writing!\n"); + LOG_ERROR ( "Failed to open '/sys/class/gpio/unexport' for writing!\n"); + return GPIO_ERR_IO; + } + + bytes_written = snprintf(buffer, SYSFS_READ_MAX, "%d", pin); + write(fd, buffer, bytes_written); + close(fd); + return GPIO_OK; +} + +int edgeSetup (unsigned int pin, unsigned int value) +{ + //static const char s_values_str[] = "01"; + + char path[SYSFS_PATH_MAX]; + int fd; + + if (! validGPIO(pin)) + return GPIO_ERR_BAD_PIN; + + snprintf(path, SYSFS_PATH_MAX, "/sys/class/gpio/gpio%d/edge", pin); + fd = open(path, O_WRONLY); + if (-1 == fd) { + //fprintf(stderr, "Failed to open gpio value for writing!\n"); + LOG_ERROR ( "Failed to open gpio '%s' for writing!\n",path); + return GPIO_ERR_IO; + } + + int rtn = 0; + if (value==INT_EDGE_RISING) + rtn = write(fd, "rising", 6); + else if (value==INT_EDGE_FALLING) + rtn = write(fd, "falling", 7); + else if (value==INT_EDGE_BOTH) + rtn = write(fd, "both", 4); + else + rtn = write(fd, "none", 4); + + if (rtn <= 0) { + LOG_ERROR ( "Failed to setup edge on '%s'!\n",path); + LOG_ERROR ( "Error (%d) - %s\n",errno, strerror (errno)); + //displayLastSystemError(""); + return GPIO_ERR_IO; + } + + close(fd); + return GPIO_OK; +} + +#include +#include +#include + +struct threadGPIOinterupt{ + void (*function)(void *args); + void *args; + unsigned int pin; +}; +static pthread_mutex_t pinMutex ; + +#define MAX_FDS 64 +static unsigned int _sysFds [MAX_FDS] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +} ; + +void pushSysFds(int fd) +{ + int i; + for (i=0; i< MAX_FDS; i++) { + if (_sysFds[i] == -1) { + _sysFds[i] = fd; + return; + } + } +} + +void gpioShutdown() { + int i; + _ever = false; + + for (i=0; i< MAX_FDS; i++) { + if (_sysFds[i] != -1) { + //printf("Closing fd %d\n",i); + close(_sysFds[i]); + _sysFds[i] = -1; + } else { + break; + } + } +} + +int waitForInterrupt (int pin, int mS, int fd) +{ + int x; + uint8_t c ; + struct pollfd polls ; + + // Setup poll structure + polls.fd = fd ; + polls.events = POLLPRI | POLLERR | POLLHUP | POLLNVAL; + + // Wait for something ... + + x = poll (&polls, 1, mS) ; + + // If no error, do a dummy read to clear the interrupt + // A one character read appars to be enough. + + if (x > 0) + { + lseek (fd, 0, SEEK_SET) ; // Rewind + (void)read (fd, &c, 1) ; // Read & clear + } + + return x ; +} + +static void *interruptHandler (void *arg) +{ + struct threadGPIOinterupt *stuff = (struct threadGPIOinterupt *) arg; + int pin = stuff->pin; + void (*function)(void *args) = stuff->function; + void *args = stuff->args; + stuff->pin = -1; + + char path[SYSFS_PATH_MAX]; + int fd, count, i ; + uint8_t c ; + + sprintf(path, "/sys/class/gpio/gpio%d/value", pin); + + if ((fd = open(path, O_RDONLY)) < 0) + { + LOG_ERROR ( "Failed to open '%s'!\n",path); + return NULL; + } + + pushSysFds(fd); + + // Clear any initial pending interrupt + ioctl (fd, FIONREAD, &count) ; + for (i = 0 ; i < count ; ++i) + read (fd, &c, 1); + + while (_ever == true) { + if (waitForInterrupt (pin, -1, fd) > 0) { + function(args); + } else { + LOG_ERROR ("interruptHandler failed for GPIO %d, resetting\n", pin); + gpioDelay(1); + } + } + + LOG("interruptHandler ended for GPIO %d\n", pin); + + close(fd); + return NULL ; +} + +int registerGPIOinterrupt(unsigned int pin, unsigned int mode, void (*function)(void *args), void *args ) +{ + pthread_t threadId ; + struct threadGPIOinterupt stuff; + + if (! validGPIO(pin)) + return false; + + // Check it's exported + if (! isExported(pin)) + pinExport(pin); + + // Setup pin if defined. + if (mode != INT_EDGE_SETUP) { + // if the pin is output, set as input to setup edge then reset to output. + if (getPinMode(pin) == OUTPUT) { + pinMode(pin, INPUT); + edgeSetup(pin, mode); + pinMode(pin, OUTPUT); + } else { + edgeSetup(pin, mode); + } + } +//#endif + stuff.function = function; + stuff.args = args; + stuff.pin = pin; + + pthread_mutex_lock (&pinMutex) ; + if (pthread_create (&threadId, NULL, interruptHandler, (void *)&stuff) < 0) { + LOG_ERROR("Failed to start interruptHandler thread\n"); + return GPIO_ERR_GENERAL; + } else { + while (stuff.pin == pin) + gpioDelay(1); + } + + pthread_mutex_unlock (&pinMutex) ; + + return GPIO_OK ; +} + + +#else // GPIO_SYSFS_INTERRUPT + +#include + +struct GPIOinterupt{ + void (*function)(void *args); + void *args; + unsigned int pin; + unsigned int bit; + unsigned int bank; + unsigned int lastValue; + unsigned int edge; + bool running; + pthread_t threadId; + pthread_mutex_t mutex; + pthread_cond_t cond; + struct GPIOinterupt *next; +}; + +#define POLL_DELAY 10 + +static struct GPIOinterupt *_GPIOinterupts = NULL; +static pthread_t _interruptHandlerThreadId = 0; +static pthread_mutex_t _gpioRegisterMutex; + + +static void *GPIOinterruptHandler (void *arg) +{ + struct GPIOinterupt *data = (struct GPIOinterupt *) arg; + void (*function)(void *args) = data->function; + void *args = data->args; + data->running = true; + + DEBUG("Created Thread for GPIO interrupt %d\n",data->pin); + + pthread_mutex_lock(&data->mutex); + + while (_ever == true && data->running == true) { + //printf("GPIOinterruptHandler() %d wait\n", data->pin); + pthread_cond_wait(&data->cond, &data->mutex); + DEBUG("GPIO interrupt triggered for %d\n",data->pin); + // recheck _ever incase this is a shutdown signal, and running incase we've been told to shutdown + if (_ever == true && data->running == true) { + // Check edge + if ( (data->edge == INT_EDGE_BOTH) || + (data->edge == INT_EDGE_RISING && data->lastValue == HIGH) || + (data->edge == INT_EDGE_FALLING && data->lastValue == LOW)) + { + //DEBUG("GPIO interrupt triggered for %d\n",data->pin); + function(args); + } + //printf("GPIOinterruptHandler() called gpio %d\n",data->pin); + } + } + + pthread_mutex_unlock(&data->mutex); + //printf("GPIOinterruptHandler() stopped gpio %d\n",data->pin); + DEBUG("Stopping thread for GPIO interrupt %d\n",data->pin); + + free(arg); + pthread_exit(0); + return NULL; +} + + +static void *interruptHandler(void *arg) +{ + // Use the global var and not one that passed, just incase we change the global pointer in the future + // to redefine starting position. + //struct GPIOinterupt *GPIOinterupt = (struct GPIOinterupt *) arg; + + //unsigned int bank, bit; + struct GPIOinterupt *i_ptr; + + while (_ever == true && _GPIOinterupts != NULL) { + for (i_ptr = _GPIOinterupts; i_ptr != NULL; i_ptr = i_ptr->next) { + //DEBUG ("Poll %d\n",i_ptr->pin); + if (i_ptr->threadId <= 0) { + if (pthread_create (&i_ptr->threadId, NULL, GPIOinterruptHandler, (void *)i_ptr) < 0) { + LOG_ERROR("Can't create GPIO interrupt handler for GPIO %d\n",i_ptr->pin); + } + } + + if (i_ptr->lastValue != digitalRead(i_ptr->pin)) + //if (i_ptr->lastValue != ((*(_gpioReg + GPLEV0 + i_ptr->bank) & i_ptr->bit)==0?LOW:HIGH) ) + { + i_ptr->lastValue = !i_ptr->lastValue; + pthread_cond_signal(&i_ptr->cond); + } + } + gpioDelay(POLL_DELAY); + } + + // signel so thread can shutdown + for (i_ptr = _GPIOinterupts; i_ptr != NULL; i_ptr = i_ptr->next) { + if (i_ptr->threadId > 0) { + pthread_cond_signal(&i_ptr->cond); + } + } + + DEBUG("interruptHandler stopped\n"); + _interruptHandlerThreadId = 0; + pthread_exit(0); + return NULL; +} + +int registerGPIOinterrupt(unsigned int gpio, unsigned int mode, void (*function)(void *args), void *args ) +{ + + if (! validGPIO(gpio)) + return GPIO_ERR_BAD_PIN; + + if (! GPIOrunning()) + return GPIO_ERR_NOT_SETUP; + + if (mode == INT_EDGE_SETUP) { + LOG("INT_EDGE_SETUP is only valid for sysfs mode"); + mode = INT_EDGE_BOTH; + } + + // Lock the function, this isn't thread safe. + pthread_mutex_lock (&_gpioRegisterMutex); + + struct GPIOinterupt *i_ptr; + struct GPIOinterupt *interupt = calloc(1, sizeof(struct GPIOinterupt)); + + interupt->function = function; + interupt->args = args; + interupt->pin = gpio; + interupt->edge = mode; + interupt->lastValue = digitalRead(gpio); + interupt->threadId = 0; + interupt->running = false; + interupt->next = NULL; + + interupt->bank = gpio >> 5; + interupt->bit = (1 << (gpio & 0x1F)); + //interupt->lastValue = (*(_gpioReg + GPLEV0 + i_ptr->bank) & i_ptr->bit)==0?0:1) + + if (_GPIOinterupts == NULL) { + _GPIOinterupts = interupt; + } else { + for (i_ptr = _GPIOinterupts; i_ptr->next != NULL; i_ptr = i_ptr->next) {} // Simply run to the end of the list + i_ptr->next = interupt; + } + + if ( _interruptHandlerThreadId <= 0 ) { + if (pthread_create (&_interruptHandlerThreadId, NULL, interruptHandler, (void *)_GPIOinterupts) < 0) { + LOG_ERROR("Couldn't start GPIO interrupt handler\n"); + return GPIO_ERR_GENERAL; + } else { + DEBUG("interruptHandler started\n"); + //while (_interupts[_numInterupts-1].running == false) + //gpioDelay(1); + } + } + + pthread_mutex_unlock (&_gpioRegisterMutex) ; + + return GPIO_OK; +} + +int unregisterGPIOinterrupt(unsigned int gpio) +{ + struct GPIOinterupt *i_ptr; + struct GPIOinterupt *d_ptr; + + // Lock the function, this isn't thread safe. + pthread_mutex_lock (&_gpioRegisterMutex); + + // Is it the first pin in the list + if (_GPIOinterupts->pin == gpio) { + DEBUG("First pin ADD CODE"); + d_ptr = _GPIOinterupts; + _GPIOinterupts = d_ptr->next; + d_ptr->running = false; + pthread_cond_signal(&d_ptr->cond); // Wake up the thread so it can exit + // Let's be safe, and let's hope we call this before the thread cleans itself up. + pthread_mutex_unlock(&d_ptr->mutex); + } else { + for (i_ptr = _GPIOinterupts; i_ptr->next != NULL && i_ptr->next->pin != gpio; i_ptr = i_ptr->next) {} + if (i_ptr->next != NULL) { + d_ptr = i_ptr->next; + DEBUG("Removing listner on pin %d\n", d_ptr->pin); + i_ptr->next = i_ptr->next->next; + d_ptr->running = false; + pthread_cond_signal(&d_ptr->cond); // Wake up the thread so it can exit + // Let's be safe, and let's hope we call this before the thread cleans itself up. + pthread_mutex_unlock(&d_ptr->mutex); + } else { + LOG_ERROR("GPIO %d not registered as listner\n",gpio); + return GPIO_ERR_BAD_PIN; + } + } + + pthread_mutex_unlock (&_gpioRegisterMutex) ; + + return GPIO_OK; +} + +void gpioShutdown() { + //int i; + _ever = false; + // Wait enough time for interupt handler poll cycle to catch the shutdown + gpioDelay(POLL_DELAY); +} + +#endif //GPIO_SYSFS_INTERUPT + + +#if defined(TEST_HARNESS) || defined(GPIO_MONITOR) || defined(GPIO_RW) || defined(GPIO_TOOL) + +#include +#include +#include +#include +#include +#include +#include + +struct pin_info { + int pin; + int GPIO; + char *name; +}; + +const struct pin_info _pinDetails[40] = { + {1, -1, "3.3v"}, {2, -1, "5v"}, + {3, 2, "GPIO2"}, {4, -1, "5v"}, + {5, 3, "GPIO3"}, {6, -1, "GND"}, + {7, 4, "GPIO4"}, {8, 14, "GPIO14"}, + {9, -1, "GND"}, {10, 15, "GPIO15"}, + {11, 17, "GPIO17"}, {12, 18, "GPIO18"}, + {13, 27, "GPIO27"}, {14, -1, "GND"}, + {15, 22, "GPIO22"}, {16, 23, "GPIO23"}, + {17, -1, "3.3v"}, {18, 24, "GPIO24"}, + {19, 10, "GPIO10"}, {20, -1, "GND"}, + {21, 9, "GPIO9"}, {22, 25, "GPIO25"}, + {23, 11, "GPIO11"}, {24, 8, "GPIO8"}, + {25, -1, "GND"}, {26, 7, "GPIO7"}, + {27, -1, "DNC"}, {28, -1, "DNC"}, + {29, 5, "GPIO5"}, {30, -1, "GND"}, + {31, 6, "GPIO6"}, {32, 12, "GPIO12"}, + {33, 13, "GPIO13"}, {34, -1, "GND"}, + {35, 19, "GPIO19"}, {36, 16, "GPIO16"}, + {37, 26, "GPIO26"}, {38, 20, "GPIO20"}, + {39, -1, "GND"}, {40, 21, "GPIO21"} +}; + + +int GPIO2physicalPin(int gpio) +{ + int i; + + for(i=0; i<40; i++) { + if (_pinDetails[i].GPIO == gpio) + return _pinDetails[i].pin; + } + return -1; +} + +char *GPIOmode2txt(unsigned int mode) { + switch (mode) { + case INPUT: + return "IN"; + break; + case OUTPUT: + return "OUT"; + break; + case IO_ALT0: + return "ALT0"; + break; + case IO_ALT1: + return "ALT1"; + break; + case IO_ALT2: + return "ALT2"; + break; + case IO_ALT3: + return "ALT3"; + break; + case IO_ALT4: + return "ALT4"; + break; + case IO_ALT5: + return "ALT5"; + break; + default: + return "---"; + break; + } +} + +void printGPIOstatus(int pin) +{ + printf ("GPIO %2d (Pin %2d|%-4s) = %d\n", pin ,GPIO2physicalPin(pin),GPIOmode2txt(getPinMode(pin)) , digitalRead(pin)); +} + +#endif //TEST_HARNESS || GPIO_MONITOR + +#ifdef GPIO_TOOL + +void errorParms(char *fname) +{ + printVersionInformation(); + printf("Missing Parameters:-\n\t[read|write] \t- read/write to GPIO\n" + "\t[input|output] \t\t- set GPIO mode to input or output\n" +#ifdef GPIO_SYSFS_MODE + "\t[export|unexport] \t\t- (un)export GPIO, needed for sysfs mode\n" +#else + "\tmode \t\t- set GPIO mode advanced value=0 to 7\n" + "\t[pud_off|pud_up|pud_down] \t- set pull up / down resistor\n" +#endif + "\treadall\t\t\t\t- Print information on every GPIO\n" + "\t\t-q\t\t\t- Optional LAST parameter to just output result\n" + "\teg :- %s write 17 1 -q\n",fname); + exit(1); +} + +char *GPIOitoa(int val, char *rbuf, int base) { + static char buf[32] = {0}; + if (val < 0) { + sprintf(rbuf,"-"); + } else if (val == 0) { + sprintf(rbuf,"0"); + } else { + int i=30; + for(; val && i; --i, val /= base) + buf[i] = "0123456789abcdef"[val % base]; + + sprintf(rbuf, "%s", &buf[i+1]); + } + + return rbuf; +} + +void readAllGPIOs() +{ + int i; + char buf1[10]; + char buf2[10]; + char buf3[10]; + char buf4[10]; + printf("-----------------------------------------------------------------\n"); + printf("| GPIO | Name | Mode | V | Pin # | V | Mode | Name | GPIO |\n"); + printf("+------+--------+------+---+---------+---+------+--------+------+\n"); + for (i=0; i <= 38; i+=2) { +#ifdef GPIO_SYSFS_MODE + if (!isExported(_pinDetails[i].GPIO)) { + pinExport(_pinDetails[i].GPIO); + //gpioDelay(1); + } + if (!isExported(_pinDetails[i+1].GPIO)) { + pinExport(_pinDetails[i+1].GPIO); + //gpioDelay(1); + } +#endif + printf("| %4s | %6s | %4s |%2s | %2d | %2d |%2s | %4s | %6s | %4s |\n", + GPIOitoa(_pinDetails[i].GPIO,buf1,10), + _pinDetails[i].name, + _pinDetails[i].GPIO==-1?"-":GPIOmode2txt(getPinMode(_pinDetails[i].GPIO)), + GPIOitoa(digitalRead(_pinDetails[i].GPIO),buf2,10), + _pinDetails[i].pin, + _pinDetails[i+1].pin, + GPIOitoa(digitalRead(_pinDetails[i+1].GPIO),buf3,10), + _pinDetails[i+1].GPIO==-1?"-":GPIOmode2txt(getPinMode(_pinDetails[i+1].GPIO)), + _pinDetails[i+1].name, + GPIOitoa(_pinDetails[i+1].GPIO,buf4,10)); + } + printf("-----------------------------------------------------------------\n"); +} + +int main(int argc, char *argv[]) { + + int pin = 0; + int value = 0; + + if (argc < 2) { + errorParms(argv[0]); + } else if (strcmp (argv[argc-1], "-q") == 0) { + _supressLogging = true; + } + + + if (! gpioSetup()) { + LOG_ERROR ( "Failed to setup GPIO\n"); + return 1; + } + + + if (strcmp (argv[1], "read") == 0) { + if (argc < 3) + errorParms(argv[0]); + pin = atoi(argv[2]); + (_supressLogging?printf("%d\n",digitalRead(pin)):printGPIOstatus(pin)); + } else if (strcmp (argv[1], "write") == 0) { + if (argc < 4) + errorParms(argv[0]); + pin = atoi(argv[2]); + value = atoi(argv[3]); + int pmode = getPinMode(pin); + pinMode (pin, OUTPUT); + digitalWrite(pin, value); + if (pmode != OUTPUT) { + if (!_supressLogging){printGPIOstatus(pin);} + usleep(500 * 1000); // Allow any triggers to read value before reset back to origional mode + if (!_supressLogging){printf("Resetting to input mode\n");} + pinMode (pin, pmode); + } + (_supressLogging?printf("%d\n",digitalRead(pin)):printGPIOstatus(pin)); + } else if (strcmp (argv[1], "input") == 0) { + if (argc < 3) + errorParms(argv[0]); + pin = atoi(argv[2]); + int rtn = pinMode(pin, INPUT); + (_supressLogging?printf("%d\n",rtn):printGPIOstatus(pin)); + } else if (strcmp (argv[1], "output") == 0) { + if (argc < 3) + errorParms(argv[0]); + pin = atoi(argv[2]); + int rtn = pinMode(pin, OUTPUT); + (_supressLogging?printf("%d\n",rtn):printGPIOstatus(pin)); +#ifdef GPIO_SYSFS_MODE + } else if (strcmp (argv[1], "export") == 0) { + if (argc < 3) + errorParms(argv[0]); + pin = atoi(argv[2]); + int rtn = pinExport(pin); + (_supressLogging?printf("%d\n",rtn):printGPIOstatus(pin)); + } else if (strcmp (argv[1], "unexport") == 0) { + if (argc < 3) + errorParms(argv[0]); + pin = atoi(argv[2]); + int rtn = pinUnexport(pin); + printf("%d\n",rtn); +#else + } else if (strcmp (argv[1], "mode") == 0) { + if (argc < 4) + errorParms(argv[0]); + pin = atoi(argv[2]); + int mode = atoi(argv[3]); + int rtn = pinMode(pin, mode); + (_supressLogging?printf("%d\n",rtn):printGPIOstatus(pin)); + } else if (strcmp (argv[1], "pud_off") == 0) { + if (argc < 3) + errorParms(argv[0]); + pin = atoi(argv[2]); + int rtn = setPullUpDown(pin, PUD_OFF); + (_supressLogging?printf("%d\n",rtn):printGPIOstatus(pin)); + } else if (strcmp (argv[1], "pud_up") == 0) { + if (argc < 3) + errorParms(argv[0]); + pin = atoi(argv[2]); + int rtn = setPullUpDown(pin, PUD_UP); + (_supressLogging?printf("%d\n",rtn):printGPIOstatus(pin)); + } else if (strcmp (argv[1], "pud_down") == 0) { + if (argc < 3) + errorParms(argv[0]); + pin = atoi(argv[2]); + int rtn = setPullUpDown(pin, PUD_DOWN); + (_supressLogging?printf("%d\n",rtn):printGPIOstatus(pin)); +#endif + } else if (strcmp (argv[1], "readall") == 0) { + readAllGPIOs(); + } else + errorParms(argv[0]); + + + gpioShutdown(); + + return 0; +} +#endif + +#ifdef GPIO_RW + +void errorParms() +{ + printf("Missing Parameters:-\n\t[read|write] pin \n\tgpio write 17 1\n"); + exit(1); +} +int main(int argc, char *argv[]) { + + bool isWrite=false; + int pin = 0; + int value = 0; + + _log_level = LOG_ERR; + + if (argc < 3) { + errorParms(); + } + + if (strcmp (argv[1], "read") == 0) + { + isWrite = false; + } else if (strcmp (argv[1], "write") == 0) { + isWrite = true; + if (argc < 4) + errorParms(); + } else { + errorParms(); + } + + pin = atoi(argv[2]); + + if (! gpioSetup()) { + logMessage (LOG_ERR, "Failed to setup GPIO\n"); + return 1; + } + + if (isWrite) { + value = atoi(argv[3]); + int pmode = getPinMode(pin); + pinMode (pin, OUTPUT); + digitalWrite(pin, value); + //if (pmode != OUTPUT) + // pinMode (pin, pmode); + } + + printf ("%d\n", digitalRead(pin)); + + + return 0; +} +#endif + +#ifdef GPIO_MONITOR + +bool FOREVER = true; + +void errorParms(char *fname) +{ + printVersionInformation(); + printf("Parameters:-\n\t\t- GPIO's to monitor\n"\ + "\t-q\t\t\t- Quiet mode, (must be last param) just print whats been triggered, no startup state\n"\ + "\t-h\t\t\t- This\n"\ + "\t\t\t- Monitor everything\n"\ + "eg :- %s 2 17 27 -q\n",fname); + exit(1); +} + +void intHandler(int signum) { + static int called=0; + LOG ( "Stopping! - signel(%d)\n",signum); + gpioShutdown(); +/* + gpioDelay(100); +*/ + FOREVER = false; + called++; + if (called > 3) + exit(1); +} + + +void event_trigger (int pin) +{ + //printf("Pin %d triggered, state=%d\n",pin,digitalRead(pin)); + printGPIOstatus(pin); +} + +void setup_monitor(int pin) +{ +#ifdef GPIO_SYSFS_MODE + if (! isExported(pin)) { + pinExport(pin); + gpioDelay(10); // 0.1 second + } +#endif + + if (!_supressLogging){printGPIOstatus(pin);} + if (registerGPIOinterrupt(pin, INT_EDGE_BOTH, (void *)&event_trigger, (void *)pin) != GPIO_OK) + { + LOG_ERROR ( "Unable to set interrupt handler for specified pin. Error (%d) - %s\n",errno, strerror (errno)); + } + //gpioDelay(100); // REMOVE THIS WHEN DONE +} + +int main(int argc, char *argv[]) { + + int i; + + if (strcmp (argv[argc-1], "-q") == 0) { + _supressLogging = true; + argc--; + } else if (strcmp (argv[argc-1], "-h") == 0) { + errorParms(argv[0]); + } + + if (! gpioSetup()) { + LOG_ERROR ( "Failed to setup GPIO\n"); + return 1; + } + + signal(SIGINT, intHandler); + signal(SIGTERM, intHandler); + signal(SIGSEGV, intHandler); + + if (argc > 1) { + for (i=1; i < argc; i++) + setup_monitor( atoi(argv[i])); + } else { + for (i=GPIO_MIN; i <= GPIO_MAX; i++) + setup_monitor(i); + } + + while(FOREVER) { + sleep(100); + } + + return 0; +} + +#endif //GPIO_MONITOR + diff --git a/GPIO_Pi.h b/GPIO_Pi.h new file mode 100644 index 0000000..db22c14 --- /dev/null +++ b/GPIO_Pi.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2017 Shaun Feakes - All rights reserved + * + * You may use redistribute and/or modify this code under the terms of + * the GNU General Public License version 2 as published by the + * Free Software Foundation. For the terms of this license, + * see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * https://github.com/sfeakes/GPIO_pi + */ + +/********************-> GPIO Pi v1.2 <-********************/ + + + +#define _GPIO_pi_NAME_ "GPIO Pi" +#define _GPIO_pi_VERSION_ "1.2" + +#ifndef _GPIO_pi_H_ +#define _GPIO_pi_H_ + +/* Use sysfs for ALL gpio activity? + comment out to use memory where we can +*/ +//#define GPIO_SYSFS_MODE + + +#include +#include + +// check number is between 2 and 27 +#ifndef USE_WIRINGPI +#define GPIO_MIN 2 +#define GPIO_MAX 27 +#else // WiringPI valid numbers +#define GPIO_MIN 0 +#define GPIO_MAX 30 +#endif + +#define validGPIO(X) ((X) <= (GPIO_MAX) ? ( ((X) >= (GPIO_MIN) ? (1) : (0)) ) : (0)) + +#ifndef USE_WIRINGPI // Don't include anything below this line if using wiringpi. + +#define INPUT 0 +#define OUTPUT 1 +#define IO_ALT5 2 // PWM Circuit +#define IO_ALT4 3 // SPI Circuit +#define IO_ALT0 4 // PCM Audio Circuit (Also I2C) +#define IO_ALT1 5 // SMI (Secondary Memory Interface) +#define IO_ALT2 6 // Nothing +#define IO_ALT3 7 // BSC - SPI Circuit + + +#define LOW 0 +#define HIGH 1 + +#define INT_EDGE_SETUP 0 +#define INT_EDGE_FALLING 1 +#define INT_EDGE_RISING 2 +#define INT_EDGE_BOTH 3 + +#define PUD_OFF 0 +#define PUD_DOWN 1 +#define PUD_UP 2 + +#define SYSFS_PATH_MAX 35 +#define SYSFS_READ_MAX 3 + +#ifndef GPIO_SYSFS_MODE + #define GPIO_BASE_P4 0xFE000000 // Pi 4 + #define GPIO_BASE_P2 0x3F000000 // Pi 2 & 3 + #define GPIO_BASE_P1 0x20000000 // Pi 1 & Zero + #define GPIO_OFFSET 0x200000 + + //#define GPIO_BASE 0x20200000 + #define GPIO_LEN 0xB4 + #define GPIO_LEN_P4 0xF1 // Pi 4 has more registers BCM2711 + + #define GPSET0 7 + #define GPSET1 8 + + #define GPCLR0 10 + #define GPCLR1 11 + + #define GPLEV0 13 + #define GPLEV1 14 + + #define GPPUD 37 + #define GPPUDCLK0 38 + #define GPPUDCLK1 39 + + /* Pi4 BCM2711 has different pulls */ + #define GPPUPPDN0 57 + #define GPPUPPDN1 58 + #define GPPUPPDN2 59 + #define GPPUPPDN3 60 + + + #define PI_MODEL_UNKNOWN -1 + #define PI_MODEL_A 0 + #define PI_MODEL_B 1 + #define PI_MODEL_AP 2 + #define PI_MODEL_BP 3 + #define PI_MODEL_2 4 + #define PI_ALPHA 5 + #define PI_MODEL_CM 6 + #define PI_MODEL_07 7 + #define PI_MODEL_3 8 + #define PI_MODEL_ZERO 9 + #define PI_MODEL_CM3 10 + #define PI_MODEL_ZERO_W 12 + #define PI_MODEL_3P 13 + #define PI_MODEL_3AP 14 + #define PI_MODEL_CM3P 16 + #define PI_MODEL_4B 17 + +#endif + +#define GPIO_ERR_GENERAL -7 +#define GPIO_NOT_IO_MODE -6 +#define GPIO_NOT_OUTPUT -5 +#define GPIO_NOT_EXPORTED -4 +#define GPIO_ERR_IO -3 +#define GPIO_ERR_NOT_SETUP -2 +#define GPIO_ERR_BAD_PIN -1 +#define GPIO_OK 0 + +//#ifndef SYSFS_MODE +int pinExport(unsigned int gpio); +int pinUnexport(unsigned int gpio); +bool isExported(unsigned int gpio); +int pinMode(unsigned int gpio, unsigned int mode); +int getPinMode(unsigned int gpio); +int digitalRead(unsigned int gpio); +int digitalWrite(unsigned int gpio, unsigned int level); +int setPullUpDown(unsigned int gpio, unsigned int pud); +int edgeSetup (unsigned int pin, unsigned int value); +bool gpioSetup(); +void gpioShutdown(); +int registerGPIOinterrupt(unsigned int gpio, unsigned int mode, void (*function)(void *args), void *args ); + +#ifndef GPIO_SYSFS_INTERRUPT +int unregisterGPIOinterrupt(unsigned int gpio); +#endif + +/* +#else +bool pinExport(int pin); +bool pinUnexport(int pin); +bool pinMode (int pin, int mode); +int digitalRead (int pin); +bool digitalWrite (int pin, int value); +#endif +*/ + +#endif /* WiringPI */ +#endif /* _GPIO_pi_H_ */ diff --git a/Makefile b/Makefile index 2124074..401f5e1 100755 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ CC = gcc #USE_WIRINGPI := 1 ifeq ($(USE_WIRINGPI),) - sd_GPIO_C := sd_GPIO.c + sd_GPIO_C := GPIO_Pi.c else #WPI_LIB := -D USE_WIRINGPI -lwiringPi -lwiringPiDev WPI_LIB := -D USE_WIRINGPI -lwiringPi @@ -58,8 +58,8 @@ $(MAIN): $(OBJS) $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LFLAGS) $(LIBS) gpio_tools: - $(CC) -o $(GMON) sd_GPIO.c -lm -lpthread -D GPIO_MONITOR - $(CC) -o $(GPIO) sd_GPIO.c -lm -lpthread -D GPIO_RW + $(CC) -o $(GMON) GPIO_Pi.c -lm -lpthread -D GPIO_MONITOR + $(CC) -o $(GPIO) GPIO_Pi.c -lm -lpthread -D GPIO_TOOL # this is a suffix replacement rule for building .o's from .c's # it uses automatic variables $<: the name of the prerequisite of diff --git a/config.c b/config.c index 1be9411..9fb0839 100644 --- a/config.c +++ b/config.c @@ -13,7 +13,7 @@ #define PIN_CFG_NAME "GPIO_PIN" #endif -#include "sd_GPIO.h" +#include "GPIO_Pi.h" #include "minIni.h" #include "utils.h" #include "config.h" diff --git a/json_messages.c b/json_messages.c index e73e145..b7edf60 100644 --- a/json_messages.c +++ b/json_messages.c @@ -6,7 +6,7 @@ #ifdef USE_WIRINGPI #include #else - #include "sd_GPIO.h" + #include "GPIO_Pi.h" #endif #include "json_messages.h" diff --git a/net_services.c b/net_services.c index 7122fbc..a6858d1 100644 --- a/net_services.c +++ b/net_services.c @@ -9,7 +9,7 @@ #ifdef USE_WIRINGPI #include #else - #include "sd_GPIO.h" + #include "GPIO_Pi.h" #endif #include "mongoose.h" diff --git a/release/gpio b/release/gpio index 2914f003c17d4569de58afd2761cadfffe5cccb8..9e6c79e9dfcbcf80313d7c63d14ede4217f92dbe 100755 GIT binary patch literal 22012 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 BTxS3P literal 19500 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 diff --git a/release/gpio_monitor b/release/gpio_monitor index 3b17e2038fc1aa3530a4233f366ba1d0cb04b763..98b4705daa8bbab2c0806163f6b86c4740102a30 100755 GIT binary patch literal 18668 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≺h+P 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 diff --git a/release/install-new.sh b/release/install-new.sh new file mode 100755 index 0000000..99a8340 --- /dev/null +++ b/release/install-new.sh @@ -0,0 +1,162 @@ +#!/bin/bash +# +# ROOT=/nas/data/Development/Raspberry/gpiocrtl/test-install +# + + +BUILD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +SERVICE="sprinklerd" + +BIN="sprinklerd" +CFG="sprinklerd.conf" +SRV="sprinklerd.service" +DEF="sprinklerd" +MDNS="sprinklerd.service" + +BINLocation="/usr/local/bin" +CFGLocation="/etc" +SRVLocation="/etc/systemd/system" +DEFLocation="/etc/default" +WEBLocation="/var/www/sprinklerd/" +MDNSLocation="/etc/avahi/services/" + +function check_cron() { + + # Look for cron running with LSB name support + if [[ ! $(pgrep -af cron | grep '\-l') ]]; then + # look for cron running + if [[ ! $(pgrep -af cron) ]]; then + # look for cron installed + if [[ ! $(command -v cron) ]]; then + echo "Can't find cron, please install" + else + echo "cron is not running, please start cron" + fi + else + # Cron is running, but not with LSB name support + if [ ! -d "/etc/cron.d" ] || [ ! -f "/etc/default/cron" ]; then + echo "The version of Cron may not support chron.d, if so the calendar schedule will not work" + echo "Please check cron for LSB name support before using calendar schedule feature of $SERVICE" + else + # Check and see if we can add LSB support + #if [ -f "/etc/default/cron" ]; then + echo ... + fi + fi + fi +} + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit 1 +fi + +if [[ $(mount | grep " / " | grep "(ro,") ]]; then + echo "Root filesystem is readonly, can't install" + exit 1 +fi + +check_cron +exit 0 + +if [ ! -d "/etc/cron.d" ]; then + echo "The version of Cron may not support chron.d, if so the calendar schedule will not work" + echo "Please check before starting" +else + if [ -f "/etc/default/cron" ]; then + CD=$(cat /etc/default/cron | grep -v ^# | grep "\-l") + if [ -z "$CD" ]; then + echo "Please enabled cron.d support, if not the calendar will not work" + echo "Edit /etc/default/cron and look for the -l option" + fi + else + echo "Please make sure the version if Cron supports chron.d, if not the calendar schedule will not work" + fi +fi + +exit 0 + + +if [ "$1" == "uninstall" ] || [ "$1" == "-u" ] || [ "$1" == "remove" ]; then + systemctl stop $SERVICE > /dev/null 2>&1 + systemctl disable $SERVICE > /dev/null 2>&1 + rm -f $BINLocation/$BIN + rm -f $SRVLocation/$SRV + rm -f $DEFLocation/$DEF + rm -f $MDNSLocation/$MDNS + rm -rf $WEBLocation + if [ -f $CFGLocation/$CFG ]; then + cache=$(cat $CFGLocation/$CFG | grep CACHE | cut -d= -f2 | sed -e 's/^[ \t]*//' | sed -e 's/ *$//') + rm -f $cache + rm -f $CFGLocation/$CFG + fi + rm -f "/etc/cron.d/sprinklerd" + echo "SprinklerD & configuration removed from system" + exit +fi + +# Check cron.d options +if [ ! -d "/etc/cron.d" ]; then + echo "The version of Cron may not support chron.d, if so the calendar will not work" + echo "Please check before starting" +else + if [ -f "/etc/default/cron" ]; then + CD=$(cat /etc/default/cron | grep -v ^# | grep "\-l") + if [ -z "$CD" ]; then + echo "Please enabled cron.d support, if not the calendar will not work" + echo "Edit /etc/default/cron and look for the -l option" + fi + else + echo "Please make sure the version if Cron supports chron.d, if not the calendar will not work" + fi +fi + +# Exit if we can't find systemctl +command -v systemctl >/dev/null 2>&1 || { echo "This script needs systemd's systemctl manager, Please check path or install manually" >&2; exit 1; } + +# stop service, hide any error, as the service may not be installed yet +systemctl stop $SERVICE > /dev/null 2>&1 +SERVICE_EXISTS=$(echo $?) + +# copy files to locations, but only copy cfg if it doesn;t already exist + +cp $BUILD/$BIN $BINLocation/$BIN +cp $BUILD/$SRV $SRVLocation/$SRV + +if [ -f $CFGLocation/$CFG ]; then + echo "Config exists, did not copy new config, you may need to edit existing! $CFGLocation/$CFG" +else + cp $BUILD/$CFG $CFGLocation/$CFG +fi + +if [ -f $DEFLocation/$DEF ]; then + echo "Defaults exists, did not copy new defaults to $DEFLocation/$DEF" +else + cp $BUILD/$DEF.defaults $DEFLocation/$DEF +fi + +if [ -f $MDNSLocation/$MDNS ]; then + echo "Avahi/mDNS defaults exists, did not copy new defaults to $MDNSLocation/$MDNS" +else + if [ -d "$MDNSLocation" ]; then + cp $BUILD/$MDNS.avahi $MDNSLocation/$MDNS + else + echo "Avahi/mDNS may not be installed, not copying $MDNSLocation/$MDNS" + fi +fi + +if [ ! -d "$WEBLocation" ]; then + mkdir -p $WEBLocation +fi + +cp -r $BUILD/../web/* $WEBLocation + +systemctl enable $SERVICE +systemctl daemon-reload + +if [ $SERVICE_EXISTS -eq 0 ]; then + echo "Starting daemon $SERVICE" + systemctl start $SERVICE +fi + diff --git a/release/sprinklerd b/release/sprinklerd index 6017b24032c9ae4cc6495ab835baa5f007c2ef1c..cc267837b1cd8e58de8028354f1a054bf1b72ed9 100755 GIT binary patch delta 50177 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 EAAgpt{{R30 delta 50206 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
  • t>+B(OI_TiS>AE3o!q$L3llb z6nJl8pary4O;7LF%0PW+5a`Do7jgzt1RsOP(Sv|7{6P{z@3L>G6M3@w4v{tz$il(2kA6yKHDRXD6VaX_5A_=o<4 zoBADTB~gjplikg9^3a71ou-#tUX8t26H07Rs)k3plFkgzjl7qxa(*iER~C*sEW31m zTyJn#dZWV%zujT!I~|s;wI-np^39lt;~|4`hZRAE!_s>kmcGYf>FMKQ33w$AOK)&E z_$IQ_@sNQxA|AmAho!eTtPHd{EWNQH9)6?4(swz0r7C-`;~|5IBjXViJ1o7tFw(_Z z+7h~P-iG50bLlk(azC8m@iTlH497&Yf+ciCUiKjjbwj;(KDrX9aWeFWf1wwRwc!iAzPO$ZBItJv`r`(He$>%b;-h9R93yDn-~WYu zv6(xkhD7!&9ljE78syyT_xZ<I~z_J z&SNy|Qws6rNWV(<^&#gmdZiTY^gn7>kfYj9=!DCS2=v7cFI0Hr80*SxjU6Ji&i}H8nqlrug{1Ft6hpJ~bMC3$D4B&1VCqKl}g> zedd`@rWIh6nswupKpNJ^)iREye-hS+{83|n0j%GCRRW`_TGzr|PJ%bVS{pFagkK5k zqs&BwSAVz`kM7f%t4D*b1UA6>3Zv0k@x!n_l&v)U9IWq78DswiqTlQ3%M0}EBn-yjQ8_)&+iVOb!1}_z$?!CFl*TE6%iulCya`WXfj8ID z)9CdM%YGmCo1My;=DxiXy_*7Z#oFj(O7JYaV^5w}!>uOX z3uk!o?Kb*LlQ}R>3BHZqMv3b;n2liK^>xHtx=r_Z?RlY|ag9LAfyPN%>e^dtUhOJY2_rd!sSPHLqMj33uL%qp# zL~%E)#=pw&)3D|m%`w~y>pd#NFsAJH!`3N*za={zV&%rHM`5q@Bh~Onyy;n=17kKRT;njX@h*u$|4!sXsr}$I) zT4J1dCx5ozt~H=IRf_Q#=*_YXRt_h@QKcE@O@TF9v)+`r9M%JZy(E{(o|!rJu9*+( z4Q!Q(?`Akfo#-_DkiIX4Niz>nD5xjMq?rsc^^Y^1q zTpKmjkHH%5*kkIfN~}{p3)Zl(hNcx^6|6C{R&??8iSTs^Ug>aF z34RxkPOfRnB(NUVyw!|}@MrK@FYrQb5_}BS1i5;nKMz}{_zuF_C0unzPeex(`bd5B z44wLaDqQ3d@U#S1!kUL#W-{=3xZ)Ky-X*NgNG+_vGTXE^z&odf%Oi9~z721AH#&nk zSd%WQ@`C=q!22m4{dA8NCc-CRO@g$Q>`$RU{tHj=p~8g zhZ%6NdV$6+h~Qd0^l_uo1kfJRJE$ZyaDkqIHQv~6_zt+7T3;fj*53nLrwsla)}YM+ zj+MbqSg%_14L=2I5@vS3X^DFAXyBT1rv(8H!FqEET0#=|Kk3vFDsynkhtRR>Gi{HF za1^Z1N9)Za+R3nW%1{{`d}uT2T?F%LW%_>-xC#$VX4ceI<@g4;o!MJjB_m!6YaC2- z@5K$Urhiosx%eJf^W7$z3_J*H`nU=p{nMv%V4O0v6@9S(mB;V!=;xH%?sP9~pIQrf zYR#LX0HKrp0ra_4%2gaIfnoILGY>{}Xterw<77V>?zHzk_GiGGv#dxdHzoRXBET0C zyfnezNbovXpL^_WxhbLl8t(aF^w{155B7h`;cIxbQ>lXco=fsBtWSnbXey!RmlWO? zj%$fW!CLZCgRa}~1X#-@Qie-l>lA-EtTjPu7Zm>L@lpDCc-}3E0L>1k(eH)JXjm#Z z9?d2BIjs5Z_7Zf#^N*#3rVgE+cnW^niBIYEj;H+gEB|PJocBh;z-La8rhjEx)v_)3)^y?(&g}G zr_y{?oc6t4#(p(iu_3yq8(^(=3!C0i-h=RAR&Cg4!bf3kSzbzpRG{oGJTybU-Q?&X zY@JGU6xN55og6E`=!qmqXH)_!gA-uuWPe72FN6;dzHS$IbK-i|yAh9plpnTaM7RvD zqdP7#*YKP0^aJ5zv=VHVJriXrNNhAOb}vr0$t#1N_Y-stXZLZe3_k>G0)(x^kBJ$s zJ`m{i|G&kPLbeQ94o4r)ct5qg-{>F0?jzVSCy)UKB#9zCWALUY z@`3?Qg`WWHJ9xdGDb@c^!$V)zZOb-W20X{p9Rv|n!PY5*b+D!a*zWmju%?^amhKMO zbKAD@pAu|^wX$cv;dbf91^xeHcWSn`kPZaRMj?tGT*@E=_rcoRK`ogw^a!lafi)&Wzk&5lqsvs% z9@sh+=#>P&m*8P1@mSB$>|C|KO5lUUBT^v-)+xeCuqMi`G6_zFwbHB+R*5PS_H$v4 zZr2c2@y~~~dTWm1#jxIlvM?2=$g9Vr?LVStx;tRE|NkD`MN2kxc-WG)!CQEZnQJ24 z1ZyJg${}HbPr&-(JC75Xf zTny{CI8~yP$;MUiW@>q((Qkw`E1@GVOyKMA&{yeR=^Ioh?t=9(zuefj!QHeIwo*R` zXHz^7>@Wt8tjBxZ4lv|u$GJ2OV9>uGNLWO&tQF0S#L756}C

    fd2r4El`6K@2p1!ZwxfB_bFynfw07tZBeLC?RZ}5-5SYcrmHu zSP7g1?_h|g1eX1ka3domg@&uMcxdKIp&uG#;3J&Elri`gY@HHV495892ICP;YL`i zHYjwUZ-w=dNvB-)Pr}wIzFlx5Z$5p-{vfQ0c)e)B`~O=`fHa=|DO^WlSFKipBVo;m ztuZAkf~`{mpGFWP)!b2MeH5h{i*g7R}H$0I6 z3EOtBhxd0!BPKsh*muEN5>QWACHQ+-zwJ8|Ry*>dV;}VYhw;!ZM>0@={6znMBCM6^ zsJXToV}^(JF~VZy!&E^+$*8x#7iaL=2;6IM2e@EdsOOJ$3R@NT$iTR7=e8ES*I zionWz)1ZR8hWM_BH~yLdi?Lz+UxG_{ zzN=CHrvz`qLn~?9Ob+jXdw8z5m8K1DygPc+=~M*&<_|t2ZiltptbceI-)`8tGzag% zn%h`x?y8X`ysS3y{=e9GOvb}HmGC@x9?yi$94mvD!kV8N-Zll^bqV`gSZkHHn*w|l zUjL)0Z@4F6zY%5u9iPZe0uLt)eh0gQ%e&xio>*kBSE<+FB3@RD6`qFaJy=VHkC+U2 ziP!bf2`&y;=U)-1){DmVu$$l)VJ)84U7<7dEqI>OeXobD zQ~aA?O_5G>Ec-Rwb(PxDMs5ttW4@3M4ud}m@Mt1}r{JN#r2jW1d=YlPc=RXiKT7b& zPA5F`SGSltavGd1dzoF$NK7R@8;`{_wJF0F!kS#&4lBo3!CG3JF&S70YbkJ#(O1Im zQ0yvL(<;-Zj@=I*<_zmLR(cPg?p?%T=v3loFwm0zc^oUj7ho;#vST{0!J4yHYwr8E zPv7=*m;Y)id#-oOvP`Brv$TH6!l`GUT{dlc=yA)6n**GY(9b_Vrk`)j2gA=eC#KIh z*JBs9O!bP)g3O|76X}dHuR2I|;nHoTzx7Y_XRoNOzO3r`hZ&kfCGfA6=HfS=W7UD~Uw zx_ZH)1vf3J{^B{c7&k1*R8(9x=kiM~tv+XJS#JEZepg}LibadQP{`}{GvmVN#&w|(YC|GPuRP|~`k zH#^aLxutvkak-aY_HW4_JX70hUh&g;`II<}E|-1PzhK+$SAE~l9eT~bVcX5G`-MYt z4R83({QAYPgZ@eR+UBU1%DgdmhETg44Q0gV!Q5ws+T`eAJ|>LK-3|`d9!ERI=6-z8 zpEOMS8x`fAI_OW%*VabUdHE{L{S~WW+Ty1&_lZL&;a*SF;M}Z3{z=DbH=M?kxFO!p zeff}o;xK)8T9x}Amd9$qrR8|WPOr&5AxYa29m&0nG)$8(j^^_F{b%@jhv)l=M?;E5 z%k^nF_hG+(V!pbK23qbpxl`WsC;M!1bj6$gss5i= #else - #include "sd_GPIO.h" + #include "GPIO_Pi.h" #endif #include "zone_ctrl.h" @@ -218,6 +218,7 @@ bool zc_zone(zcRunType type, int zone, zcState state, int length) { } bool zc_start(/*zcRunType type,*/ int zone) { + int rtn = false; // Check if zone is already on if ( _sdconfig_.zonecfg[zone].on_state == digitalRead (_sdconfig_.zonecfg[zone].pin)) { logMessage (LOG_DEBUG, "Request to turn zone %d on. Zone %d is already on, ignoring!\n",zone,zone); @@ -225,20 +226,28 @@ bool zc_start(/*zcRunType type,*/ int zone) { } logMessage (LOG_NOTICE, "Turning on Zone %d\n",zone); #ifndef USE_WIRINGPI - int rtn = digitalWrite(_sdconfig_.zonecfg[zone].pin, _sdconfig_.zonecfg[zone].on_state ); + //int rtn = digitalWrite(_sdconfig_.zonecfg[zone].pin, _sdconfig_.zonecfg[zone].on_state ); + //logMessage (LOG_NOTICE, "digitalWrite return %d\n",rtn); + if (digitalWrite(_sdconfig_.zonecfg[zone].pin, _sdconfig_.zonecfg[zone].on_state ) == GPIO_OK ) + rtn = true; + else + rtn = false; #else digitalWrite(_sdconfig_.zonecfg[zone].pin, _sdconfig_.zonecfg[zone].on_state ); int rtn = true; #endif _sdconfig_.eventToUpdateHappened = true; + + return rtn; // store what's running - if (rtn == true) - return true; - else - return false; + //if (rtn == true) + // return true; + //else + // return false; } bool zc_stop(/*zcRunType type,*/ int zone) { + int rtn = false; // Check if zone is alreay off if ( _sdconfig_.zonecfg[zone].on_state != digitalRead (_sdconfig_.zonecfg[zone].pin)) { logMessage (LOG_DEBUG, "Request to turn zone %d off. Zone %d is already off, ignoring!\n",zone,zone); @@ -246,15 +255,22 @@ bool zc_stop(/*zcRunType type,*/ int zone) { } logMessage (LOG_NOTICE, "Turning off Zone %d\n",zone); #ifndef USE_WIRINGPI - int rtn = digitalWrite(_sdconfig_.zonecfg[zone].pin, !_sdconfig_.zonecfg[zone].on_state ); + //int rtn = digitalWrite(_sdconfig_.zonecfg[zone].pin, !_sdconfig_.zonecfg[zone].on_state ); + if (digitalWrite(_sdconfig_.zonecfg[zone].pin, !_sdconfig_.zonecfg[zone].on_state ) == GPIO_OK ) + rtn = true; + else + rtn = false; #else digitalWrite(_sdconfig_.zonecfg[zone].pin, !_sdconfig_.zonecfg[zone].on_state ); int rtn = true; #endif _sdconfig_.eventToUpdateHappened = true; + return rtn; + /* if (rtn == true) return true; else return false; + */ }