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 2914f00..9e6c79e 100755 Binary files a/release/gpio and b/release/gpio differ diff --git a/release/gpio_monitor b/release/gpio_monitor index 3b17e20..98b4705 100755 Binary files a/release/gpio_monitor and b/release/gpio_monitor differ 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 6017b24..cc26783 100755 Binary files a/release/sprinklerd and b/release/sprinklerd differ diff --git a/release/sprinklerd.test.conf b/release/sprinklerd.test.conf index 2768a78..4a8e867 100644 --- a/release/sprinklerd.test.conf +++ b/release/sprinklerd.test.conf @@ -4,22 +4,22 @@ NAME=My Sprinklers DOCUMENTROOT = /nas/data/Development/Raspberry/SprinklerD/web/ CACHE = /var/cache/sprinklerd.cache # The log level. [DEBUG, INFO, NOTICE, WARNING, ERROR] -#LOG_LEVEL = DEBUG +LOG_LEVEL = DEBUG #LOG_LEVEL = NOTICE -LOG_LEVEL = INFO +#LOG_LEVEL = INFO # mqtt stuff -MQTT_ADDRESS = trident:1883 +#MQTT_ADDRESS = trident:1883 #MQTT_USER = someusername #MQTT_PASSWD = somepassword -MQT_TOPIC = sd_test -MQTT_DZ_PUB_TOPIC = domoticz/in -MQTT_DZ_SUB_TOPIC = domoticz/out +#MQT_TOPIC = sd_test +#MQTT_DZ_PUB_TOPIC = domoticz/in +#MQTT_DZ_SUB_TOPIC = domoticz/out -DZIDX_CALENDAR = 197 -DZIDX_24HDELAY = 198 -DZIDX_ALL_ZONES = 199 -DZIDX_RAINSENSOR = 48 +DZIDX_CALENDAR = 2197 +DZIDX_24HDELAY = 2198 +DZIDX_ALL_ZONES = 2199 +DZIDX_RAINSENSOR = 2248 # Options for the below ZONE and GPIO configuration # @@ -31,12 +31,11 @@ DZIDX_RAINSENSOR = 48 # PUD_UP 2 #NAME = name of zone -#GPIO_PIN = GPIO Pin # This is WiringPi Pin#, not Raspberry Pi board pin#. -#WPI_PIN = use instead of GPIO_PIN if compiled with USE_WIRINGPI flag +#GPIO_PIN = GPIO # This is the GPIO# not the pin#, so (17 = GPIO17 = Pin 11) or another example (5 = GPIO5 = Pin 29) +#WPI_PIN = use instead of GPIO_PIN if compiled with USE_WIRINGPI flag, This is WiringPi Pin#, not Raspberry Pi board pin# #GPIO_PULL_UPDN = setup pull up pull down resistor PUD_OFF|PUD_DOWN|PUD_UP #GPIO_ON_STATE = State GPIO reads when relay for zone is on. HIGH or LOW 1 or 0 #DOMOTICZ_IDX = Domoticz IDX 0 or remove entry if you don;t use Domoticz (only if you use domoticz) -#MASTER_VALVE = YES=1 NO=0 // turn on with any zone # Don't use ZONE:0 for anything other than master valve, if you don't have a master valve simply delete it and start from ZONE:1 [ZONE] @@ -44,7 +43,7 @@ DZIDX_RAINSENSOR = 48 NAME=Master Valve MASTER_VALVE=1 GPIO_PIN=2 -WPI_PIN=0 +#WPI_PIN=0 GPIO_PULL_UPDN=1 GPIO_ON_STATE=0 @@ -53,7 +52,7 @@ NAME=Island DEFAULT_RUNTIME=10 #GPIO_PIN=18 GPIO_PIN=3 -WPI_PIN=1 +#WPI_PIN=1 GPIO_PULL_UPDN=1 GPIO_ON_STATE=0 DOMOTICZ_IDX=2000 @@ -61,9 +60,9 @@ DOMOTICZ_IDX=2000 [ZONE:2] NAME=Driveway DEFAULT_RUNTIME=10 -#GPIO_PIN=27 -GPIO_PIN=40 -WPI_PIN=2 +GPIO_PIN=27 +#GPIO_PIN=40 +#WPI_PIN=2 GPIO_PULL_UPDN=1 GPIO_ON_STATE=0 DOMOTICZ_IDX=2010 @@ -72,20 +71,20 @@ DOMOTICZ_IDX=2010 NAME=Diningroom Flowerbeds DEFAULT_RUNTIME=10 GPIO_PIN=5 -WPI_PIN=3 +#WPI_PIN=3 GPIO_PULL_UPDN=1 GPIO_ON_STATE=0 DOMOTICZ_IDX=2020 -[INPUT] -[INPUT:1] -NAME=Test Switch -GPIO_PIN=6 -WPI_PIN=3 -GPIO_PULL_UPDN=1 -GPIO_ON_STATE=0 -COMMAND_ON=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=24hdelay&state=on' -COMMAND_OFF=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=24hdelay&state=off' +#[INPUT] +#[INPUT:1] +#NAME=Test Switch +#GPIO_PIN=6 +#WPI_PIN=3 +#GPIO_PULL_UPDN=1 +#GPIO_ON_STATE=0 +#COMMAND_ON=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=24hdelay&state=on' +#COMMAND_OFF=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=24hdelay&state=off' #[ZONE:4] #NAME=Diningroom error @@ -153,13 +152,13 @@ COMMAND_OFF=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=2 #COMMAND triggered_event_runcmd = external command to run #DOMOTICZ_IDX Domoticz IDX -[GPIO] -[GPIO:1] -NAME=Rain Sensor -GPIO_PIN=4 -WPI_PIN=7 -PIN_MODE=0 +#[GPIO] +#[GPIO:1] +#NAME=Rain Sensor +#GPIO_PIN=4 +#WPI_PIN=7 +#PIN_MODE=0 #PULL_UPDN=0 -TRIGGER_EVENT_ON=3 -COMMAND_HIGH=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=24hdelay&state=on' -COMMAND_LOW=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=24hdelay&state=off' +#TRIGGER_EVENT_ON=3 +#COMMAND_HIGH=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=24hdelay&state=on' +#COMMAND_LOW=/usr/bin/curl -s -o /dev/null 'http://localhost?type=option&option=24hdelay&state=off' diff --git a/sd_GPIO.c b/sd_GPIO.c.old similarity index 100% rename from sd_GPIO.c rename to sd_GPIO.c.old diff --git a/sd_GPIO.h b/sd_GPIO.h.old similarity index 100% rename from sd_GPIO.h rename to sd_GPIO.h.old diff --git a/sprinkler.c b/sprinkler.c index 5a4189c..713d978 100644 --- a/sprinkler.c +++ b/sprinkler.c @@ -14,7 +14,7 @@ // #include "sd_GPIO.h" #endif -#include "sd_GPIO.h" +#include "GPIO_Pi.h" #include "mongoose.h" @@ -252,6 +252,10 @@ void main_loop () logMessage(LOG_DEBUG, "Setting up GPIO\n"); gpioSetup(); + if (! gpioSetup()) { + logMessage(LOG_ERR, "Failed to setup GPIO\n"); + exit (EXIT_FAILURE); + } for (i=(_sdconfig_.master_valve?0:1); i <= _sdconfig_.zones ; i++) { diff --git a/version.h b/version.h index 2f10d81..2bda58c 100644 --- a/version.h +++ b/version.h @@ -1,6 +1,6 @@ #ifndef SD_VERSION_H #define SD_VERSION_H -#define SD_VERSION "1.0g" +#define SD_VERSION "1.1" #endif diff --git a/zone_ctrl.c b/zone_ctrl.c index 6ad5f76..3d63567 100644 --- a/zone_ctrl.c +++ b/zone_ctrl.c @@ -1,7 +1,7 @@ #ifdef USE_WIRINGPI #include #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; + */ }