From 5af2be7e5b963d9dbdfd5209167a91f79470419b Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 13 Aug 2020 09:06:17 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + CMakeLists.txt | 19 ++++++++++ LICENSE | 13 +++++++ README.md | 25 +++++++++++++ src/main.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.py | 58 +++++++++++++++++++++++++++++ 6 files changed, 215 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/main.c create mode 100644 src/main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3acc102 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.16) +project(relaycontrol LANGUAGES C) + +set(CFLAGS "${CFLAGS} -Wall -Wextra -pedantic -Wformat-overflow=2 -Wformat-security -Winit-self -Wstrict-overflow=2 -Wstringop-overflow=2 -Walloc-zero -Wduplicated-branches -Wduplicated-cond -Wtrampolines -Wfloat-equal -Wshadow -Wunsafe-loop-optimizations -Wparentheses -fanalyzer -fstack-protector") +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() +if (CMAKE_BUILD_TYPE EQUAL "Debug") + set(CFLAGS "${CFLAGS} -Og") +elseif (CMAKE_BUILD_TYPE EQUAL "Release") + set(CFLAGS "${CFLAGS} -O2 -Werror") +endif() + +add_executable(relaycntl + src/main.c +) +set_property(TARGET relaycntl PROPERTY C_STANDARD 99) + +install(TARGETS relaycntl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..456c488 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100644 index 0000000..afc53af --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Raspberry PI General Purpose Input/Output Controller + +This is a simple GPIO pin controller for UNIX-like kernels/systems. + +## Requirements + +- CMake +- ANSI C compiler +- UNIX standard library + +## Building + +First, generate the build files with CMake: +``` +cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release +``` + +Second, install the binary: +``` +cd build/ +sudo make install +``` + +## License +WTFPL diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..cbe7d17 --- /dev/null +++ b/src/main.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIO_IFACE_MISSING = -1 +#define GPIO_PIN_EXP_FAIL = -2 +#define GPIO_DIR_CONT_FAIL = -3 +#define GPIO_SWITCH_FAIL = -4 +#define GPIO_PIN_UNX_NF = -5 +#define GPIO_PIN_UNX_FAIL = -6 + +#define GPIO_PREFIX "/sys/class/gpio/" +#define GPIO_EXPORT "export" +#define GPIO_UNEXPORT "unexport" + +#define MAX_PATHLEN 512 + +#define PRINTERRANDRET(mesg, varfmt, var) \ + { \ + fprintf(stderr, "%s \"%" varfmt "\". %s (%i)\n", mesg, var, strerror(errno), errno); \ + return 1; \ + } + +ssize_t exportPin(signed int pinno); +ssize_t setPinDirection(signed int pinno, char* value); +ssize_t setPinValue(signed int pinno, int value); + +int main(int argc, char* argv[]) +{ + int pinno = (argc > 1) ? atoi(argv[1]) : 0; + bool value = (argc > 2) ? atoi(argv[2]) : 0; + if (pinno != exportPin(pinno)) + return -1; + if (pinno != setPinDirection(pinno, "out")) + return -1; + if (pinno != setPinValue(pinno, value)) + return -1; + return 0; +} + +ssize_t exportPin(signed int pinno) +{ + if (chdir(GPIO_PREFIX) != 0) + PRINTERRANDRET("Failed to change directory to", "s", GPIO_PREFIX); + char buffer[MAX_PATHLEN]; + int fd; + ssize_t nbytes; + if ((fd = open(GPIO_EXPORT, O_WRONLY)) < 0) + PRINTERRANDRET("Failed to open export", "s", GPIO_EXPORT); + if ((nbytes = snprintf(buffer, MAX_PATHLEN, "%i", pinno)) <= 0) + PRINTERRANDRET("Couldn't format pin number as string", "i", pinno); + if (write(fd, buffer, nbytes) != nbytes && errno != EBUSY) + PRINTERRANDRET("Couldn't write pin number to the exporter", "i", pinno); + if (close(fd) != 0) + PRINTERRANDRET("Couldn't close exporter", "i", pinno); + return pinno; +} + +ssize_t setPinDirection(signed int pinno, char* value) +{ + if (chdir(GPIO_PREFIX) != 0) + PRINTERRANDRET("Failed to change directory to", "s", GPIO_PREFIX); + char buffer[MAX_PATHLEN]; + int fd; + ssize_t nbytes; + if ((nbytes = snprintf(buffer, MAX_PATHLEN, "gpio%i/direction", pinno)) <= 0) + PRINTERRANDRET("Couldn't format pin path as string", "i", pinno); + if ((fd = open(buffer, O_WRONLY)) < 0) + PRINTERRANDRET("Failed to open pin path.", "s", GPIO_EXPORT); + if (write(fd, value, strlen(value)) != nbytes && errno != EBUSY) + PRINTERRANDRET("Couldn't write pin direction", "i", pinno); + if (close(fd) != 0) + PRINTERRANDRET("Couldn't close pin direction file", "i", pinno); + return pinno; +} +ssize_t setPinValue(signed int pinno, int value) +{ + if (chdir(GPIO_PREFIX) != 0) + PRINTERRANDRET("Failed to change directory to", "s", GPIO_PREFIX); + char buffer[MAX_PATHLEN]; + int fd; + ssize_t nbytes; + if ((nbytes = snprintf(buffer, MAX_PATHLEN, "gpio%i/value", pinno)) <= 0) + PRINTERRANDRET("Couldn't format pin path as string", "i", pinno); + if ((fd = open(buffer, O_WRONLY)) < 0) + PRINTERRANDRET("Failed to open pin path.", "s", GPIO_EXPORT); + if ((nbytes = snprintf(buffer, MAX_PATHLEN, "%i", value)) <= 0) + PRINTERRANDRET("Couldn't format pin value as string", "i", value); + if (write(fd, buffer, nbytes) != nbytes && errno != EBUSY) + PRINTERRANDRET("Couldn't write pin value", "i", pinno); + if (close(fd) != 0) + PRINTERRANDRET("Couldn't close pin value file", "i", pinno); + return pinno; +} diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..774eac8 --- /dev/null +++ b/src/main.py @@ -0,0 +1,58 @@ +import time +import sys +import errno + +GPIO_IFACE_MISSING = -1 +GPIO_PIN_EXP_FAIL = -2 +GPIO_DIR_CONT_FAIL = -3 +GPIO_SWITCH_FAIL = -4 +GPIO_PIN_UNX_NF = -5 +GPIO_PIN_UNX_FAIL = -6 + +PINLISTMISS = -1 +PINLISTTOKFAIL = -2 + +def preparePin(pinno): + try: x = open('/sys/class/gpio/export', 'w') + except: + return GPIO_IFACE_MISSING + else: + try: + x.write(f'{pinno}') + x.close() + except OSError as e: + if (e.errno != errno.EBUSY): # Busy devices are already exported + return GPIO_PIN_EXP_FAIL + return + for i in range(0,5): + try: g = open(f'/sys/class/gpio/gpio{pinno}/direction', 'w') + except: + if (i == 4): + return GPIO_DIR_CONT_FAIL + else: time.sleep(1) + else: + try: g.write('out') + except: + g.close() + return GPIO_SWITCH_FAIL + else: + g.close() + break + +def closePin(pinno): + try: x = open('/sys/class/gpio/unexport', 'w') + except: return GPIO_PIN_UNX_NF + else: + try: x.write(pinno) + except: return GPIO_PIN_UNX_FAIL + finally: x.close() + +try: preparePin(sys.argv[1]) +except GPIO_SWITCH_FAIL: print(f'GPIO Pin direction couldn\'t be switched to out. {sys.argv[1]}') +except GPIO_DIR_CONT_FAIL: print(f'GPIO Pin control couldn\'t be opened. {sys.argv[1]}') +except GPIO_PIN_EXP_FAIL: print(f'GPIO Pin couldn\'t be exported. P: {sys.argv[1]}') +except GPIO_IFACE_MISSING: print(f'GPIO Export interface couldn\'t be opened. {sys.argv[1]}') + +with open(f'/sys/class/gpio/gpio{sys.argv[1]}/value', 'w') as x: + if (len(sys.argv) == 3): x.write(sys.argv[2]) + else: x.write('0')